1# frozen_string_literal: true
2
3require "blather"
4require "em-http"
5require "em_promise"
6require "em-synchrony/em-http" # For apost vs post
7require "link-header-parser"
8
9module Snikket
10 class Launch < Blather::Stanza::Iq
11 register nil, "launch", "xmpp:snikket.org/hosting/v1"
12
13 def self.new(type=nil, to=nil, id=nil, domain: nil)
14 stanza = super(type || :set, to, id)
15 node = Nokogiri::XML::Node.new("launch", stanza.document)
16 node.default_namespace = registered_ns
17 stanza << node
18 stanza.domain = domain if domain
19 stanza
20 end
21
22 def domain=(domain)
23 query.at_xpath("./ns:domain", ns: self.class.registered_ns)&.remove
24 node = Nokogiri::XML::Node.new("domain", document)
25 node.default_namespace = self.class.registered_ns
26 node.content = domain
27 query << node
28 end
29
30 def query
31 at_xpath("./ns:launch", ns: self.class.registered_ns)
32 end
33 end
34
35 class Launched < Blather::Stanza::Iq
36 register :snikket_launched, "launched", "xmpp:snikket.org/hosting/v1"
37
38 def instance_id
39 query
40 .at_xpath("./ns:instance-id", ns: self.class.registered_ns)
41 &.content
42 end
43
44 def bootstrap_token
45 query
46 .at_xpath("./ns:bootstrap/ns:token", ns: self.class.registered_ns)
47 &.content
48 end
49
50 def bootstrap_uri(instance_domain)
51 "https://#{instance_domain}/invites_bootstrap?token=#{bootstrap_token}"
52 end
53
54 def fetch_invite(instance_domain)
55 url = bootstrap_uri(instance_domain)
56 EM::HttpRequest.new(
57 url, tls: { verify_peer: true }
58 ).ahead(redirects: 5).then { |res|
59 LinkHeaderParser.parse(
60 Array(res.response_header["LINK"]), base: url
61 ).group_by_relation_type[:alternate]&.find do |header|
62 URI.parse(header.target_uri).scheme == "xmpp"
63 end&.target_uri
64 }.catch { nil }
65 end
66
67 def query
68 at_xpath("./ns:launched", ns: self.class.registered_ns)
69 end
70 end
71
72 class Instance < Blather::Stanza::Iq
73 register :snikket_instance, "instance", "xmpp:snikket.org/hosting/v1"
74
75 def self.new(type=nil, to=nil, id=nil, instance_id: nil)
76 stanza = super(type || :get, to, id)
77 node = Nokogiri::XML::Node.new("instance", stanza.document)
78 node.default_namespace = registered_ns
79 stanza << node
80 stanza.instance_id = instance_id if instance_id
81 stanza
82 end
83
84 def inherit(*)
85 query.remove
86 super
87 end
88
89 def instance_id=(instance_id)
90 query.at_xpath("./ns:instance-id", ns: self.class.registered_ns)&.remove
91 node = Nokogiri::XML::Node.new("instance-id", document)
92 node.default_namespace = self.class.registered_ns
93 node.content = instance_id
94 query << node
95 end
96
97 def instance_id
98 query
99 .at_xpath("./ns:instance-id", ns: self.class.registered_ns)
100 &.content
101 end
102
103 def update_needed?
104 !!query.at_xpath("./ns:update-needed", ns: self.class.registered_ns)
105 end
106
107 def operation
108 query.at_xpath("./ns:operation", ns: self.class.registered_ns)
109 end
110
111 def status
112 query
113 .at_xpath("./ns:status", ns: self.class.registered_ns)
114 &.content&.to_sym
115 end
116
117 def query
118 at_xpath("./ns:instance", ns: self.class.registered_ns)
119 end
120 end
121end