Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppGetter.cs
1using System;
3using System.IO;
4using System.Linq;
5using System.Security.Cryptography.X509Certificates;
6using System.Text;
7using System.Threading.Tasks;
8using Microsoft.Maui.Storage;
10using Waher.Content;
18using System.Xml;
19using System.Xml.Schema;
21
23{
29 {
33 public string[] UriSchemes => new string[] { "xmpp" };
34
38 public bool CanGet(Uri uri, out Grade grade)
39 {
40 if (uri.Scheme.Equals("xmpp", StringComparison.OrdinalIgnoreCase))
41 {
42 grade = Grade.Ok;
43 return true;
44 }
45 else
46 {
47 grade = Grade.NotAtAll;
48 return false;
49 }
50 }
51
55 public Task<ContentResponse> GetAsync(
56 Uri uri,
57 X509Certificate certificate,
58 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
59 params KeyValuePair<string, string>[] headers)
60 {
61 return this.GetAsync(uri, certificate, remoteCertificateValidator, 60000, headers);
62 }
63
67 public async Task<ContentResponse> GetAsync(
68 Uri uri,
69 X509Certificate certificate,
70 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
71 int timeoutMs,
72 params KeyValuePair<string, string>[] headers)
73 {
74 //
75 // 1. Parse the URI into node, domain, and resource.
76 // Format expected: xmpp:node@domain/resource
77 //
78 string Full = uri.OriginalString; // e.g. "xmpp:foo@pubsub.lab.tagroot.io/ResourceId"
79 string WithoutScheme = Full.Substring(uri.Scheme.Length + 1);
80
81 // Split on the first '/' to separate "node@domain" from "resource"
82 string NodeAtDomain;
83 string Resource;
84 int SlashIndex = WithoutScheme.IndexOf('/');
85 if (SlashIndex < 0)
86 {
87 NodeAtDomain = WithoutScheme;
88 Resource = string.Empty;
89 }
90 else
91 {
92 NodeAtDomain = WithoutScheme.Substring(0, SlashIndex);
93 Resource = WithoutScheme.Substring(SlashIndex + 1);
94 }
95
96 // Split node@domain into node and domain
97 string Node, Domain;
98 int AtIndex = NodeAtDomain.IndexOf('@');
99 if (AtIndex < 0)
100 {
101 // If there is no '@', treat entire left part as domain, leave node empty.
102 Node = string.Empty;
103 Domain = NodeAtDomain;
104 }
105 else
106 {
107 Node = NodeAtDomain.Substring(0, AtIndex);
108 Domain = NodeAtDomain.Substring(AtIndex + 1);
109 }
110 /*
111 // DNS LOOKUPS To determine if it is an xmpp component or a server account:
112 // Currently disabled, as Androids MONO runtime is missing a correct implemention of unix getaddrinfo(),
113 //
114 //
115 // 2. DNS SRV Lookup for _xmpp-server._tcp.DOMAIN
116 //
117 string srvName = $"_xmpp-server._tcp.{domain}";
118 ResourceRecord[] records;
119 try
120 {
121 records = await DnsResolver.Resolve(srvName, QTYPE.SRV, QCLASS.IN);
122 }
123 catch (Exception dnsEx)
124 {
125 // Unable to resolve SRV records
126 return new ContentResponse(dnsEx);
127 }
128
129 // Filter for SRV entries
130 SRV[] SrvRecords = records
131 .OfType<SRV>()
132 .OrderBy(r => r.Priority)
133 .ThenByDescending(r => r.Weight)
134 .ToArray();
135
136 if (SrvRecords.Length == 0)
137 {
138 // No SRV records found�cannot locate XMPP server
139 InvalidOperationException ex = new InvalidOperationException($"No SRV records found for {srvName}");
140 return new ContentResponse(ex);
141 }
142
143 // Pick the first SRV record (lowest priority, highest weight)
144 SRV chosenSrv = SrvRecords[0];
145 string xmppHost = chosenSrv.TargetHost; // e.g. "xmpp1.pubsub.lab.tagroot.io"
146 int xmppPort = chosenSrv.Port; // typically 5222
147 */
148 bool IsComponent = true;
149
150 //
151 // 3. If this target is a PubSub component (i.e. SRV target differs from domain),
152 // fetch the PubSub item (and validate its XML payload). Otherwise, treat as account JID.
153 //
154 if (IsComponent)
155 {
156 // Ensure we have a TagProfile to compare against
158 if (TagProfile is null)
159 {
160 return new ContentResponse(
161 new InvalidOperationException("TagProfile service is not available."));
162 }
163
164 // We expect the "domain" part of the URI to match the PubSub JID in the TagProfile.
165 if (!Domain.Equals(TagProfile.PubSubJid, StringComparison.OrdinalIgnoreCase))
166 {
167 return new ContentResponse(
168 new InvalidOperationException($"Domain '{Domain}' is not the configured PubSub JID."));
169 }
170
171 // Try fetching the item from the PubSub service:
172 PubSubItem? Item;
173 try
174 {
175 // ServiceRef.XmppService.GetItemAsync(node, resource) should connect,
176 // authenticate, discover the pubsub component (domain) and retrieve the item.
177 if (await ServiceRef.XmppService.WaitForConnectedState(Constants.Timeouts.XmppConnect))
178 Item = await ServiceRef.XmppService.GetItemAsync(Node, Resource);
179 else
180 throw new InvalidOperationException("XMPP service is not connected. Cannot fetch PubSub item.");
181 }
182 catch (Exception ex)
183 {
184 return new ContentResponse(ex);
185 }
186
187 if (Item is null)
188 {
189 return new ContentResponse(
190 new InvalidOperationException($"No PubSub item found for node '{Node}' and ID '{Resource}'."));
191 }
192
193 // The payload is returned as XML string
194 string ItemXml = Item.Item.InnerXml ?? string.Empty;
195 byte[] ItemBytes = Encoding.UTF8.GetBytes(Item.Item.InnerXml ?? string.Empty);
196
197 // Validate that the XML is well-formed
198 XmlDocument XmlDoc = new XmlDocument();
199 try
200 {
201 XmlDoc.LoadXml(ItemXml);
202 }
203 catch (XmlException XmlEx)
204 {
205 return new ContentResponse(
206 new InvalidOperationException("PubSub payload is not well-formed XML.", XmlEx));
207 }
208
209
210 // If valid, return the XML payload
211 // ContentType = "application/xml"; Decoded = itemXml; Encoded = itemBytes
212 return new ContentResponse("application/xml", ItemXml, ItemBytes);
213 }
214 else
215 {
216 //
217 // 4. The SRV target equals the domain: treat this as a user/account JID.
218 // For example: xmpp:username@domain/ResourceId might map to presence, vCard, etc.
219 // Here, we leave a stub for future expansion.
220 //
221 return new ContentResponse(
222 new NotSupportedException($"Fetching for account JID '{Node}@{Domain}' is not implemented."));
223 }
224 }
225
229 public async Task<ContentStreamResponse> GetTempStreamAsync(
230 Uri uri,
231 X509Certificate certificate,
232 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
233 params KeyValuePair<string, string>[] headers)
234 {
235 ContentResponse Response = await this.GetAsync(uri, certificate, remoteCertificateValidator, headers);
236 if (Response.Error is not null)
237 return new ContentStreamResponse(Response.Error);
238
239 TemporaryStream Temp = new TemporaryStream();
240 await Temp.WriteAsync(Response.Encoded, 0, Response.Encoded.Length);
241 Temp.Position = 0;
242 return new ContentStreamResponse(Response.ContentType, Temp);
243 }
244
248 public async Task<ContentStreamResponse> GetTempStreamAsync(
249 Uri uri,
250 X509Certificate certificate,
251 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
252 int timeoutMs,
253 params KeyValuePair<string, string>[] headers)
254 {
255 ContentResponse Response = await this.GetAsync(uri, certificate, remoteCertificateValidator, timeoutMs, headers);
256 if (Response.Error is not null)
257 return new ContentStreamResponse(Response.Error);
258
259 TemporaryStream Temp = new TemporaryStream();
260 await Temp.WriteAsync(Response.Encoded, 0, Response.Encoded.Length);
261 Temp.Position = 0;
262 return new ContentStreamResponse(Response.ContentType, Temp);
263 }
264
268 Task<ContentStreamResponse> IContentGetter.GetTempStreamAsync(
269 Uri uri,
270 X509Certificate certificate,
271 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
272 TemporaryStream destination,
273 params KeyValuePair<string, string>[] headers)
274 {
275 // We ignore the supplied 'destination' since PubSub payloads are usually small.
276 // In a real streaming scenario, you'd write directly into 'destination'.
277 return this.GetTempStreamAsync(uri, certificate, remoteCertificateValidator, headers);
278 }
279
283 Task<ContentStreamResponse> IContentGetter.GetTempStreamAsync(
284 Uri uri,
285 X509Certificate certificate,
286 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
287 int timeoutMs,
288 params KeyValuePair<string, string>[] headers)
289 {
290 return this.GetTempStreamAsync(uri, certificate, remoteCertificateValidator, timeoutMs, headers);
291 }
292
296 Task<ContentStreamResponse> IContentGetter.GetTempStreamAsync(
297 Uri uri,
298 X509Certificate certificate,
299 EventHandler<RemoteCertificateEventArgs> remoteCertificateValidator,
300 int timeoutMs,
301 TemporaryStream destination,
302 params KeyValuePair<string, string>[] headers)
303 {
304 // We ignore 'destination' here as well.
305 return this.GetTempStreamAsync(uri, certificate, remoteCertificateValidator, timeoutMs, headers);
306 }
307 }
308}
static readonly TimeSpan XmppConnect
XMPP Connect timeout
Definition: Constants.cs:702
A set of never changing property constants and helpful values.
Definition: Constants.cs:24
Android Resource Designer class. Exposes the Android Resource designer assembly into the project Name...
An IContentGetter that handles XMPP URIs of the form "xmpp:node@domain/resource". Example: xmpp:foo@p...
Definition: XmppGetter.cs:29
async Task< ContentStreamResponse > GetTempStreamAsync(Uri uri, X509Certificate certificate, EventHandler< RemoteCertificateEventArgs > remoteCertificateValidator, int timeoutMs, params KeyValuePair< string, string >[] headers)
Gets a (possibly large) resource from an XMPP URI, writing it to a temporary stream,...
Definition: XmppGetter.cs:248
string[] UriSchemes
Supported URI schemes for this getter.
Definition: XmppGetter.cs:33
bool CanGet(Uri uri, out Grade grade)
If the getter is able to get a resource, given its URI.
Definition: XmppGetter.cs:38
Task< ContentResponse > GetAsync(Uri uri, X509Certificate certificate, EventHandler< RemoteCertificateEventArgs > remoteCertificateValidator, params KeyValuePair< string, string >[] headers)
Gets a resource from an XMPP URI.
Definition: XmppGetter.cs:55
async Task< ContentResponse > GetAsync(Uri uri, X509Certificate certificate, EventHandler< RemoteCertificateEventArgs > remoteCertificateValidator, int timeoutMs, params KeyValuePair< string, string >[] headers)
Gets a resource from an XMPP URI, with timeout.
Definition: XmppGetter.cs:67
async Task< ContentStreamResponse > GetTempStreamAsync(Uri uri, X509Certificate certificate, EventHandler< RemoteCertificateEventArgs > remoteCertificateValidator, params KeyValuePair< string, string >[] headers)
Gets a (possibly large) resource from an XMPP URI, writing it to a temporary stream.
Definition: XmppGetter.cs:229
Base class that references services in the app.
Definition: ServiceRef.cs:43
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:202
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
The TAG Profile is the heart of the digital identity for a specific user/device. Use this instance to...
Definition: TagProfile.cs:28
string? PubSubJid
The XMPP server's PubSub JID.
Definition: TagProfile.cs:756
Contains information about a response to a content request.
byte[] Encoded
Encoded object.
string ContentType
Internet Content-Type of encoded object.
Exception Error
Error response.
Contains information about a stream response to a content request.
Represents a published item.
Definition: PubSubItem.cs:12
XmlElement Item
Item XML Element.
Definition: PubSubItem.cs:86
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
Asynchronously writes a sequence of bytes to the current stream, advances the current position within...
The TAG Profile is the heart of the digital identity for a specific user/device. Use this instance to...
Definition: ITagProfile.cs:18
Basic interface for Internet Content getters. A class implementing this interface and having a defaul...
Task< ContentStreamResponse > GetTempStreamAsync(Uri Uri, X509Certificate Certificate, EventHandler< RemoteCertificateEventArgs > RemoteCertificateValidator, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, using a Uniform Resource Identifier (or Locator).
Grade
Grade enumeration
Definition: Grade.cs:7