Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
LegalComponent.cs
1using Paiwise;
2using System;
4using System.IO;
5using System.Net;
6using System.Net.Http;
7using System.Reflection;
9using System.Text;
10using System.Threading.Tasks;
11using System.Xml;
12using System.Xml.Schema;
13using Waher.Content;
19using Waher.Events;
38using Waher.Security;
61
63{
68 {
72 public const string NamespaceLegalIdentityIeeeV1 = "urn:ieee:iot:leg:id:1.0";
73
77 public const string NamespaceLegalIdentityNeuroFoundationV1 = "urn:nf:iot:leg:id:1.0";
78
82 public static readonly string[] NamespacesLegalIdentity = new string[]
83 {
86 };
87
93 public static bool IsNamespaceLegalIdentity(string Namespace)
94 {
95 return Array.IndexOf(NamespacesLegalIdentity, Namespace) >= 0;
96 }
97
101 public const string NamespaceSmartContractsIeeeV1 = "urn:ieee:iot:leg:sc:1.0";
102
106 public const string NamespaceSmartContractsNeuroFoundationV1 = "urn:nf:iot:leg:sc:1.0";
107
111 public static readonly string[] NamespacesSmartContracts = new string[]
112 {
115 };
116
122 public static bool IsNamespaceSmartContract(string Namespace)
123 {
124 return Array.IndexOf(NamespacesSmartContracts, Namespace) >= 0;
125 }
126
130 public const string NamespaceE2EIeeeV1 = "urn:ieee:iot:e2e:1.0";
131
135 public const string NamespaceE2ENeuroFoundationV1 = "urn:nf:iot:e2e:1.0";
136
142 public static string NamespaceLegalIdentity(NamespaceSet Version)
143 {
144 switch (Version)
145 {
146 case NamespaceSet.XsfV0:
147 case NamespaceSet.IeeeV1: return NamespaceLegalIdentityIeeeV1;
148 default:
149 case NamespaceSet.NeuroFoundationV1: return NamespaceLegalIdentityNeuroFoundationV1;
150 }
151 }
152
158 public static string NamespaceSmartContracts(NamespaceSet Version)
159 {
160 switch (Version)
161 {
162 case NamespaceSet.XsfV0:
164 default:
165 case NamespaceSet.NeuroFoundationV1: return NamespaceSmartContractsNeuroFoundationV1;
166 }
167 }
168
174 public static string NamespaceE2E(NamespaceSet Version)
175 {
176 switch (Version)
177 {
178 case NamespaceSet.XsfV0:
179 case NamespaceSet.IeeeV1: return NamespaceE2EIeeeV1;
180 default:
181 case NamespaceSet.NeuroFoundationV1: return NamespaceE2ENeuroFoundationV1;
182 }
183 }
184
185 private Cache<CaseInsensitiveString, object> remoteComponents = new Cache<CaseInsensitiveString, object>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromHours(1));
186 private Cache<CaseInsensitiveString, int> petitions = new Cache<CaseInsensitiveString, int>(int.MaxValue, TimeSpan.FromHours(1), TimeSpan.FromHours(1), true);
187 private Cache<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Parameter>> transientParameters = new Cache<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Parameter>>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromHours(1), true);
188 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, CaseInsensitiveString>> petitionsByBareJid = new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, CaseInsensitiveString>>();
189 private readonly AttachmentsResource attachmentsResource;
190 private readonly HttpServer httpServer;
191 private readonly PubSubComponent pubsub;
192 private readonly GeoSpatialComponent geo;
193 private readonly string attachmentsFolder;
194 private EDalerComponent eDaler;
195
209 : base(Server, Subdomain, Name)
210 {
211 this.httpServer = HttpServer;
212 this.attachmentsFolder = AttachmentsFolder;
213 this.eDaler = EDaler;
214 this.pubsub = PubSub;
215 this.geo = Geo;
216
217 if (!Directory.Exists(this.attachmentsFolder))
218 Directory.CreateDirectory(this.attachmentsFolder);
219
220 this.attachmentsResource = new AttachmentsResource(this);
221 this.httpServer.Register(this.attachmentsResource);
222
223 #region Neuro-Foundation V1 handlers
224
225 this.RegisterIqGetHandler("getPublicKey", NamespaceLegalIdentityNeuroFoundationV1, this.GetPublicKeyHandler, true);
226 this.RegisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityNeuroFoundationV1, this.IdApplicationAttributesHandler, false); ;
227 this.RegisterIqSetHandler("apply", NamespaceLegalIdentityNeuroFoundationV1, this.ApplyHandler, false);
228 this.RegisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentitiesHandler, false);
229 this.RegisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentityHandler, false);
230 this.RegisterIqGetHandler("validateSignature", NamespaceLegalIdentityNeuroFoundationV1, this.ValidateSignatureHandler, false);
231 this.RegisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.ObsoleteLegalIdentityHandler, false);
232 this.RegisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.CompromiseLegalIdentityHandler, false);
233 this.RegisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityHandler, false);
234 this.RegisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityResponseHandler, false);
235 this.RegisterIqSetHandler("addAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.AddLegalIdAttachmentHandler, false);
236 this.RegisterIqSetHandler("removeAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.RemoveLegalIdAttachmentHandler, false);
237 this.RegisterIqSetHandler("petitionSignature", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureHandler, false);
238 this.RegisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureResponseHandler, false);
239 this.RegisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityNeuroFoundationV1, this.AuthorizeAccessToIdHandler, false);
240 this.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkLegalIdentityHandler, false);
241 this.RegisterIqGetHandler("canSignAs", NamespaceLegalIdentityNeuroFoundationV1, this.CanSignAsHandler, false);
242 this.RegisterIqSetHandler("readyForApproval", NamespaceLegalIdentityNeuroFoundationV1, this.ReadyForApprovalHandler, false);
243 this.RegisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityNeuroFoundationV1, this.GetReviewIdProvidersHandler, false);
244 this.RegisterIqSetHandler("selectReviewService", NamespaceLegalIdentityNeuroFoundationV1, this.SelectReviewServiceHandler, false);
245
246 this.Server.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkXmppIdentityHandler, false);
247 this.Server.RegisterIqGetHandler("getTrustChain", NamespaceLegalIdentityNeuroFoundationV1, this.GetTrustChainHandler, false);
248 this.Server.RegisterIqGetHandler("getIdentityReferences", NamespaceLegalIdentityNeuroFoundationV1, this.GetIdentityReferencesHandler, false);
249
250 this.RegisterIqSetHandler("createContract", NamespaceSmartContractsNeuroFoundationV1, this.CreateContractHandler, true);
251 this.RegisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetCreatedContractsHandler, false);
252 this.RegisterIqSetHandler("signContract", NamespaceSmartContractsNeuroFoundationV1, this.SignContractHandler, false);
253 this.RegisterIqGetHandler("getSignedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetSignedContractsHandler, false);
254 this.RegisterIqGetHandler("getContract", NamespaceSmartContractsNeuroFoundationV1, this.GetContractHandler, false);
255 this.RegisterIqGetHandler("getContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetContractsHandler, false);
256 this.RegisterIqGetHandler("isPart", NamespaceSmartContractsNeuroFoundationV1, this.IsPartHandler, false);
257 this.RegisterIqSetHandler("obsoleteContract", NamespaceSmartContractsNeuroFoundationV1, this.ObsoleteContractHandler, false);
258 this.RegisterIqSetHandler("deleteContract", NamespaceSmartContractsNeuroFoundationV1, this.DeleteContractHandler, false);
259 this.RegisterIqSetHandler("updateContract", NamespaceSmartContractsNeuroFoundationV1, this.UpdateContractHandler, false);
260 this.RegisterIqGetHandler("getSchemas", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemasHandler, false);
261 this.RegisterIqGetHandler("getSchema", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemaHandler, false);
262 this.RegisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetLegalIdentitiesOfContractHandler, false);
263 this.RegisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetNetworkIdentitiesHandler, false);
264 this.RegisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsNeuroFoundationV1, this.SearchPublicContractsHandler, false);
265 this.RegisterIqSetHandler("petitionContract", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractHandler, false);
266 this.RegisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseHandler, false);
267 this.RegisterIqSetHandler("addAttachment", NamespaceSmartContractsNeuroFoundationV1, this.AddContractAttachmentHandler, false);
268 this.RegisterIqSetHandler("removeAttachment", NamespaceSmartContractsNeuroFoundationV1, this.RemoveContractAttachmentHandler, false);
269 this.RegisterIqSetHandler("authorizeAccess", NamespaceSmartContractsNeuroFoundationV1, this.AuthorizeAccessToContractHandler, false);
270 this.RegisterIqSetHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedHandler, false);
271 this.RegisterMessageHandler("failContract", NamespaceSmartContractsNeuroFoundationV1, this.FailContractHandler, false);
272
273 #endregion
274
275 #region IEEE V1 handlers
276
277 this.RegisterIqGetHandler("getPublicKey", NamespaceLegalIdentityIeeeV1, this.GetPublicKeyHandler, true);
278 this.RegisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityIeeeV1, this.IdApplicationAttributesHandler, false); ;
279 this.RegisterIqSetHandler("apply", NamespaceLegalIdentityIeeeV1, this.ApplyHandler, false);
280 this.RegisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentitiesHandler, false);
281 this.RegisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentityHandler, false);
282 this.RegisterIqGetHandler("validateSignature", NamespaceLegalIdentityIeeeV1, this.ValidateSignatureHandler, false);
283 this.RegisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityIeeeV1, this.ObsoleteLegalIdentityHandler, false);
284 this.RegisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityIeeeV1, this.CompromiseLegalIdentityHandler, false);
285 this.RegisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityHandler, false);
286 this.RegisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityResponseHandler, false);
287 this.RegisterIqSetHandler("addAttachment", NamespaceLegalIdentityIeeeV1, this.AddLegalIdAttachmentHandler, false);
288 this.RegisterIqSetHandler("removeAttachment", NamespaceLegalIdentityIeeeV1, this.RemoveLegalIdAttachmentHandler, false);
289 this.RegisterIqSetHandler("petitionSignature", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureHandler, false);
290 this.RegisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureResponseHandler, false);
291 this.RegisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityIeeeV1, this.AuthorizeAccessToIdHandler, false);
292 this.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkLegalIdentityHandler, false);
293 this.RegisterIqGetHandler("canSignAs", NamespaceLegalIdentityIeeeV1, this.CanSignAsHandler, false);
294 this.RegisterIqSetHandler("readyForApproval", NamespaceLegalIdentityIeeeV1, this.ReadyForApprovalHandler, false);
295 this.RegisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityIeeeV1, this.GetReviewIdProvidersHandler, false);
296 this.RegisterIqSetHandler("selectReviewService", NamespaceLegalIdentityIeeeV1, this.SelectReviewServiceHandler, false);
297
298 this.Server.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkXmppIdentityHandler, false);
299 this.Server.RegisterIqGetHandler("getTrustChain", NamespaceLegalIdentityIeeeV1, this.GetTrustChainHandler, false);
300 this.Server.RegisterIqGetHandler("getIdentityReferences", NamespaceLegalIdentityIeeeV1, this.GetIdentityReferencesHandler, false);
301
302 this.RegisterIqSetHandler("createContract", NamespaceSmartContractsIeeeV1, this.CreateContractHandler, true);
303 this.RegisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsIeeeV1, this.GetCreatedContractsHandler, false);
304 this.RegisterIqSetHandler("signContract", NamespaceSmartContractsIeeeV1, this.SignContractHandler, false);
305 this.RegisterIqGetHandler("getSignedContracts", NamespaceSmartContractsIeeeV1, this.GetSignedContractsHandler, false);
306 this.RegisterIqGetHandler("getContract", NamespaceSmartContractsIeeeV1, this.GetContractHandler, false);
307 this.RegisterIqGetHandler("getContracts", NamespaceSmartContractsIeeeV1, this.GetContractsHandler, false);
308 this.RegisterIqGetHandler("isPart", NamespaceSmartContractsIeeeV1, this.IsPartHandler, false);
309 this.RegisterIqSetHandler("obsoleteContract", NamespaceSmartContractsIeeeV1, this.ObsoleteContractHandler, false);
310 this.RegisterIqSetHandler("deleteContract", NamespaceSmartContractsIeeeV1, this.DeleteContractHandler, false);
311 this.RegisterIqSetHandler("updateContract", NamespaceSmartContractsIeeeV1, this.UpdateContractHandler, false);
312 this.RegisterIqGetHandler("getSchemas", NamespaceSmartContractsIeeeV1, this.GetSchemasHandler, false);
313 this.RegisterIqGetHandler("getSchema", NamespaceSmartContractsIeeeV1, this.GetSchemaHandler, false);
314 this.RegisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsIeeeV1, this.GetLegalIdentitiesOfContractHandler, false);
315 this.RegisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsIeeeV1, this.GetNetworkIdentitiesHandler, false);
316 this.RegisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsIeeeV1, this.SearchPublicContractsHandler, false);
317 this.RegisterIqSetHandler("petitionContract", NamespaceSmartContractsIeeeV1, this.PetitionContractHandler, false);
318 this.RegisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseHandler, false);
319 this.RegisterIqSetHandler("addAttachment", NamespaceSmartContractsIeeeV1, this.AddContractAttachmentHandler, false);
320 this.RegisterIqSetHandler("removeAttachment", NamespaceSmartContractsIeeeV1, this.RemoveContractAttachmentHandler, false);
321 this.RegisterIqSetHandler("authorizeAccess", NamespaceSmartContractsIeeeV1, this.AuthorizeAccessToContractHandler, false);
322 this.RegisterIqSetHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedHandler, false);
323 this.RegisterMessageHandler("failContract", NamespaceSmartContractsIeeeV1, this.FailContractHandler, false);
324
325 #endregion
326
327 this.RegisterIqSetHandler("legalIdReferenceAdded", QuickLogin.NamespaceTagSignature, this.LegalIdReferenceAddedHandler, true);
328 this.RegisterIqSetHandler("legalIdReferenceRemoved", QuickLogin.NamespaceTagSignature, this.LegalIdReferenceRemovedHandler, true);
329 Security.Users.User.UpdatingUserLegalId += this.User_UpdatingUserLegalId;
330
331 this.Server.OnPresenceLocalSender += this.Server_OnPresenceLocalSender;
332 this.petitions.Removed += this.Petitions_Removed;
333 this.remoteComponents.Removed += this.RemoteComponents_Removed;
334
336 NeuroFeaturesProcessor.RegisterHandlers(this);
337 }
338
342 public override void Dispose()
343 {
344 this.Server.OnPresenceLocalSender -= this.Server_OnPresenceLocalSender;
345
346 if (!(this.petitions is null))
347 {
348 this.petitions.Removed -= this.Petitions_Removed;
349 this.remoteComponents.Removed -= this.RemoteComponents_Removed;
350 this.petitions.Dispose();
351 this.petitions = null;
352 }
353
354 this.transientParameters?.Dispose();
355 this.transientParameters = null;
356
357 this.httpServer.Unregister(this.attachmentsResource);
358
359 #region Neuro-Foundation V1 handlers
360
361 this.UnregisterIqGetHandler("getPublicKey", NamespaceLegalIdentityNeuroFoundationV1, this.GetPublicKeyHandler, true);
362 this.UnregisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityNeuroFoundationV1, this.IdApplicationAttributesHandler, false); ;
363 this.UnregisterIqSetHandler("apply", NamespaceLegalIdentityNeuroFoundationV1, this.ApplyHandler, false);
364 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentitiesHandler, false);
365 this.UnregisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentityHandler, false);
366 this.UnregisterIqGetHandler("validateSignature", NamespaceLegalIdentityNeuroFoundationV1, this.ValidateSignatureHandler, false);
367 this.UnregisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.ObsoleteLegalIdentityHandler, false);
368 this.UnregisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.CompromiseLegalIdentityHandler, false);
369 this.UnregisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityHandler, false);
370 this.UnregisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityResponseHandler, false);
371 this.UnregisterIqSetHandler("addAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.AddLegalIdAttachmentHandler, false);
372 this.UnregisterIqSetHandler("removeAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.RemoveLegalIdAttachmentHandler, false);
373 this.UnregisterIqSetHandler("petitionSignature", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureHandler, false);
374 this.UnregisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureResponseHandler, false);
375 this.UnregisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityNeuroFoundationV1, this.AuthorizeAccessToIdHandler, false);
376 this.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkLegalIdentityHandler, false);
377 this.UnregisterIqGetHandler("canSignAs", NamespaceLegalIdentityNeuroFoundationV1, this.CanSignAsHandler, false);
378 this.UnregisterIqSetHandler("readyForApproval", NamespaceLegalIdentityNeuroFoundationV1, this.ReadyForApprovalHandler, false);
379 this.UnregisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityNeuroFoundationV1, this.GetReviewIdProvidersHandler, false);
380 this.UnregisterIqSetHandler("selectReviewService", NamespaceLegalIdentityNeuroFoundationV1, this.SelectReviewServiceHandler, false);
381
382 this.Server.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkXmppIdentityHandler, false);
383 this.Server.UnregisterIqGetHandler("getTrustChain", NamespaceLegalIdentityNeuroFoundationV1, this.GetTrustChainHandler, false);
384 this.Server.UnregisterIqGetHandler("getIdentityReferences", NamespaceLegalIdentityNeuroFoundationV1, this.GetIdentityReferencesHandler, false);
385
386 this.UnregisterIqSetHandler("createContract", NamespaceSmartContractsNeuroFoundationV1, this.CreateContractHandler, true);
387 this.UnregisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetCreatedContractsHandler, false);
388 this.UnregisterIqSetHandler("signContract", NamespaceSmartContractsNeuroFoundationV1, this.SignContractHandler, false);
389 this.UnregisterIqGetHandler("getSignedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetSignedContractsHandler, false);
390 this.UnregisterIqGetHandler("getContract", NamespaceSmartContractsNeuroFoundationV1, this.GetContractHandler, false);
391 this.UnregisterIqGetHandler("getContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetContractsHandler, false);
392 this.UnregisterIqGetHandler("isPart", NamespaceSmartContractsNeuroFoundationV1, this.IsPartHandler, false);
393 this.UnregisterIqSetHandler("obsoleteContract", NamespaceSmartContractsNeuroFoundationV1, this.ObsoleteContractHandler, false);
394 this.UnregisterIqSetHandler("deleteContract", NamespaceSmartContractsNeuroFoundationV1, this.DeleteContractHandler, false);
395 this.UnregisterIqSetHandler("updateContract", NamespaceSmartContractsNeuroFoundationV1, this.UpdateContractHandler, false);
396 this.UnregisterIqGetHandler("getSchemas", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemasHandler, false);
397 this.UnregisterIqGetHandler("getSchema", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemaHandler, false);
398 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetLegalIdentitiesOfContractHandler, false);
399 this.UnregisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetNetworkIdentitiesHandler, false);
400 this.UnregisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsNeuroFoundationV1, this.SearchPublicContractsHandler, false);
401 this.UnregisterIqSetHandler("petitionContract", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractHandler, false);
402 this.UnregisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseHandler, false);
403 this.UnregisterIqSetHandler("addAttachment", NamespaceSmartContractsNeuroFoundationV1, this.AddContractAttachmentHandler, false);
404 this.UnregisterIqSetHandler("removeAttachment", NamespaceSmartContractsNeuroFoundationV1, this.RemoveContractAttachmentHandler, false);
405 this.UnregisterIqSetHandler("authorizeAccess", NamespaceSmartContractsNeuroFoundationV1, this.AuthorizeAccessToContractHandler, false);
406 this.UnregisterIqSetHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedHandler, false);
407 this.UnregisterMessageHandler("failContract", NamespaceSmartContractsNeuroFoundationV1, this.FailContractHandler, false);
408
409 #endregion
410
411 #region IEEE V1 handlers
412
413 this.UnregisterIqGetHandler("getPublicKey", NamespaceLegalIdentityIeeeV1, this.GetPublicKeyHandler, true);
414 this.UnregisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityIeeeV1, this.IdApplicationAttributesHandler, false); ;
415 this.UnregisterIqSetHandler("apply", NamespaceLegalIdentityIeeeV1, this.ApplyHandler, false);
416 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentitiesHandler, false);
417 this.UnregisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentityHandler, false);
418 this.UnregisterIqGetHandler("validateSignature", NamespaceLegalIdentityIeeeV1, this.ValidateSignatureHandler, false);
419 this.UnregisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityIeeeV1, this.ObsoleteLegalIdentityHandler, false);
420 this.UnregisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityIeeeV1, this.CompromiseLegalIdentityHandler, false);
421 this.UnregisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityHandler, false);
422 this.UnregisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityResponseHandler, false);
423 this.UnregisterIqSetHandler("addAttachment", NamespaceLegalIdentityIeeeV1, this.AddLegalIdAttachmentHandler, false);
424 this.UnregisterIqSetHandler("removeAttachment", NamespaceLegalIdentityIeeeV1, this.RemoveLegalIdAttachmentHandler, false);
425 this.UnregisterIqSetHandler("petitionSignature", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureHandler, false);
426 this.UnregisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureResponseHandler, false);
427 this.UnregisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityIeeeV1, this.AuthorizeAccessToIdHandler, false);
428 this.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkLegalIdentityHandler, false);
429 this.UnregisterIqGetHandler("canSignAs", NamespaceLegalIdentityIeeeV1, this.CanSignAsHandler, false);
430 this.UnregisterIqSetHandler("readyForApproval", NamespaceLegalIdentityIeeeV1, this.ReadyForApprovalHandler, false);
431 this.UnregisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityIeeeV1, this.GetReviewIdProvidersHandler, false);
432 this.UnregisterIqSetHandler("selectReviewService", NamespaceLegalIdentityIeeeV1, this.SelectReviewServiceHandler, false);
433
434 this.Server.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkXmppIdentityHandler, false);
435 this.Server.UnregisterIqGetHandler("getTrustChain", NamespaceLegalIdentityIeeeV1, this.GetTrustChainHandler, false);
436 this.Server.UnregisterIqGetHandler("getIdentityReferences", NamespaceLegalIdentityIeeeV1, this.GetIdentityReferencesHandler, false);
437
438 this.UnregisterIqSetHandler("createContract", NamespaceSmartContractsIeeeV1, this.CreateContractHandler, true);
439 this.UnregisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsIeeeV1, this.GetCreatedContractsHandler, false);
440 this.UnregisterIqSetHandler("signContract", NamespaceSmartContractsIeeeV1, this.SignContractHandler, false);
441 this.UnregisterIqGetHandler("getSignedContracts", NamespaceSmartContractsIeeeV1, this.GetSignedContractsHandler, false);
442 this.UnregisterIqGetHandler("getContract", NamespaceSmartContractsIeeeV1, this.GetContractHandler, false);
443 this.UnregisterIqGetHandler("getContracts", NamespaceSmartContractsIeeeV1, this.GetContractsHandler, false);
444 this.UnregisterIqGetHandler("isPart", NamespaceSmartContractsIeeeV1, this.IsPartHandler, false);
445 this.UnregisterIqSetHandler("obsoleteContract", NamespaceSmartContractsIeeeV1, this.ObsoleteContractHandler, false);
446 this.UnregisterIqSetHandler("deleteContract", NamespaceSmartContractsIeeeV1, this.DeleteContractHandler, false);
447 this.UnregisterIqSetHandler("updateContract", NamespaceSmartContractsIeeeV1, this.UpdateContractHandler, false);
448 this.UnregisterIqGetHandler("getSchemas", NamespaceSmartContractsIeeeV1, this.GetSchemasHandler, false);
449 this.UnregisterIqGetHandler("getSchema", NamespaceSmartContractsIeeeV1, this.GetSchemaHandler, false);
450 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsIeeeV1, this.GetLegalIdentitiesOfContractHandler, false);
451 this.UnregisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsIeeeV1, this.GetNetworkIdentitiesHandler, false);
452 this.UnregisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsIeeeV1, this.SearchPublicContractsHandler, false);
453 this.UnregisterIqSetHandler("petitionContract", NamespaceSmartContractsIeeeV1, this.PetitionContractHandler, false);
454 this.UnregisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseHandler, false);
455 this.UnregisterIqSetHandler("addAttachment", NamespaceSmartContractsIeeeV1, this.AddContractAttachmentHandler, false);
456 this.UnregisterIqSetHandler("removeAttachment", NamespaceSmartContractsIeeeV1, this.RemoveContractAttachmentHandler, false);
457 this.UnregisterIqSetHandler("authorizeAccess", NamespaceSmartContractsIeeeV1, this.AuthorizeAccessToContractHandler, false);
458 this.UnregisterIqSetHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedHandler, false);
459 this.UnregisterMessageHandler("failContract", NamespaceSmartContractsIeeeV1, this.FailContractHandler, false);
460
461 #endregion
462
463 this.UnregisterIqSetHandler("legalIdReferenceAdded", QuickLogin.NamespaceTagSignature, this.LegalIdReferenceAddedHandler, true);
464 this.UnregisterIqSetHandler("legalIdReferenceRemoved", QuickLogin.NamespaceTagSignature, this.LegalIdReferenceRemovedHandler, true);
465 Security.Users.User.UpdatingUserLegalId -= this.User_UpdatingUserLegalId;
466
467 this.remoteComponents?.Dispose();
468 this.remoteComponents = null;
469
471 NeuroFeaturesProcessor.UnregisterHandlers(this);
472 }
473
478 {
479 if (!(Gateway.ContractsClient is null))
480 Gateway.ContractsClient.PetitionForPeerReviewIDReceived += this.ContractsClient_PetitionForPeerReviewIDReceived;
481 }
482
487 {
488 if (!(Gateway.ContractsClient is null))
489 Gateway.ContractsClient.PetitionForPeerReviewIDReceived -= this.ContractsClient_PetitionForPeerReviewIDReceived;
490 }
491
496 public override bool SupportsAccounts => false;
497
501 internal HttpServer HttpServer => this.httpServer;
502
507 {
508 get => this.eDaler;
509 set
510 {
511 if (this.eDaler is null)
512 this.eDaler = value;
513 else if (this.eDaler != value)
514 throw new InvalidOperationException("Not allowed to change component reference.");
515 }
516 }
517
521 public GeoSpatialComponent Geo => this.geo;
522
526 public string AttachmentsFolder => this.attachmentsFolder;
527
528 internal byte[] Sign(byte[] Data)
529 {
531 }
532
533 internal byte[] Sign(Stream Data)
534 {
536 }
537
538 internal bool Verify(byte[] Data, byte[] Signature)
539 {
541 }
542
547 public static Task<string> GetOnboardingNeuronDomainName()
548 {
549 return RuntimeSettings.GetAsync("Onboarding.DomainName", "id.tagroot.io");
550 }
551
552 #region Legal Identities
553
554 private Task GetPublicKeyHandler(object Sender, IqEventArgs e)
555 {
556 StringBuilder Xml = new StringBuilder();
557 NamespaceSet Version = XmppServerModule.GetVersion(e.Query.NamespaceURI);
558 DateTime Timestamp = XML.Attribute(e.Query, "ts", DateTime.UtcNow);
559
560 // TODO: Check Timestamp
561
562 Xml.Append("<publicKey xmlns='");
563 Xml.Append(NamespaceLegalIdentity(Version));
564 Xml.Append("'><ed448 pub='");
565 Xml.Append(Convert.ToBase64String(LedgerConfiguration.Instance.PublicKey));
566 Xml.Append("' xmlns='");
567 Xml.Append(NamespaceE2E(Version));
568 Xml.Append("'/></publicKey>");
569
570 e.IqResult(Xml.ToString(), e.To);
571
572 return Task.CompletedTask;
573 }
574
581 public static Tuple<IE2eEndpoint, DateTime?, DateTime?> GetPublicKey(DateTime? Timestamp)
582 {
583 IE2eEndpoint Endpoint;
584 DateTime? From;
585 DateTime? To;
586
587 if (!Timestamp.HasValue ||
588 Timestamp.Value >= LedgerConfiguration.Instance.Created)
589 {
590 // TODO: Multiple keys. Switch to PQC.
591
594 To = null;
595 }
596 else
597 {
598 Endpoint = null;
599 From = null;
600 To = null;
601 }
602
603 return new Tuple<IE2eEndpoint, DateTime?, DateTime?>(Endpoint, From, To);
604 }
605
606 private Task IdApplicationAttributesHandler(object Sender, IqEventArgs e)
607 {
608 StringBuilder Xml = new StringBuilder();
609 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
610
611 Xml.Append("<idApplicationAttributes xmlns='");
612 Xml.Append(NamespaceLegalIdentity(QueryVersion));
613
615 {
616 Xml.Append("' peerReview='true' nrReviewers='");
618 Xml.Append("' nrPhotos='");
620 Xml.Append("' iso3166='");
622 Xml.Append("'>");
623
625 Xml.Append("<required>FIRST</required>");
626
628 Xml.Append("<required>MIDDLE</required>");
629
631 Xml.Append("<required>LAST</required>");
632
634 Xml.Append("<required>PNR</required>");
635
637 Xml.Append("<required>ADDR</required>");
638
640 Xml.Append("<required>ZIP</required>");
641
643 Xml.Append("<required>AREA</required>");
644
646 Xml.Append("<required>CITY</required>");
647
649 Xml.Append("<required>REGION</required>");
650
652 Xml.Append("<required>COUNTRY</required>");
653
655 Xml.Append("<required>NATIONALITY</required>");
656
658 Xml.Append("<required>GENDER</required>");
659
661 {
662 Xml.Append("<required>BDAY</required>");
663 Xml.Append("<required>BMONTH</required>");
664 Xml.Append("<required>BYEAR</required>");
665 }
666
667 Xml.Append("</idApplicationAttributes>");
668 }
669 else
670 Xml.Append("' peerReview='false'/>");
671
672 e.IqResult(Xml.ToString(), e.To);
673
674 return Task.CompletedTask;
675 }
676
677 private async Task ApplyHandler(object Sender, IqEventArgs e)
678 {
679 try
680 {
681 if (!this.Server.IsServerDomain(e.From.Domain, true))
682 {
683 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
684 return;
685 }
686
687 if (!(Gateway.LoginAuditor is null))
688 {
689 DateTime? Next = await Gateway.LoginAuditor.GetEarliestLoginOpportunity(e.From.BareJid.Value, "XMPP");
690
691 if (Next.HasValue)
692 {
693 StringBuilder sb = new StringBuilder();
694 DateTime TP = Next.Value;
695 DateTime Today = DateTime.Today;
696
697 if (Next.Value == DateTime.MaxValue)
698 {
699 sb.Append("This endpoint (");
700 sb.Append(e.From.BareJid.Value);
701 sb.Append(") has been blocked from the system.");
702 }
703 else
704 {
705 sb.Append("Too many failed identity applications in a row registered. Try again after ");
706 sb.Append(TP.ToLongTimeString());
707
708 if (TP.Date != Today)
709 {
710 if (TP.Date == Today.AddDays(1))
711 sb.Append(" tomorrow");
712 else
713 {
714 sb.Append(", ");
715 sb.Append(TP.ToShortDateString());
716 }
717 }
718
719 sb.Append(". Remote Endpoint: ");
720 sb.Append(e.From.BareJid.Value);
721 }
722
723 await e.IqErrorForbidden(e.To, sb.ToString(), "en");
724 return;
725 }
726 }
727
728 LegalIdentity Identity = null;
729 bool Preview = XML.Attribute(e.Query, "preview", false);
730
731 foreach (XmlNode N in e.Query.ChildNodes)
732 {
733 if (N is XmlElement E && E.LocalName == "identity" && E.NamespaceURI == e.Query.NamespaceURI)
734 {
735 Identity = LegalIdentity.Parse(E, out bool HasStatus, out _);
736 if (HasStatus)
737 {
738 await e.IqErrorBadRequest(e.To, "Status element not permitted when applying for a legal identity.", "en");
739 return;
740 }
741 }
742 }
743
744 if (Identity is null)
745 {
746 await e.IqErrorBadRequest(e.To, "Identity missing.", "en");
747 return;
748 }
749
751 {
752 await e.IqErrorBadRequest(e.To, "id attribute must not be set by client.", "en");
753 return;
754 }
755
756 if (!Identity.HasClientPublicKey)
757 {
758 await e.IqErrorBadRequest(e.To, "Client public key missing.", "en");
759 return;
760 }
761
762 if (!Identity.HasClientSignature)
763 {
764 await e.IqErrorBadRequest(e.To, "Client signature missing.", "en");
765 return;
766 }
767
768 if (!(Identity.ServerSignature is null))
769 {
770 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
771 return;
772 }
773
774 if (!Identity.ValidateClientSignature(this))
775 {
776 await e.IqErrorBadRequest(e.To, "Invalid client signature.", "en");
777 return;
778 }
779
781 {
782 await e.IqErrorBadRequest(e.To, "AGENT property is reserved.", "en");
783 return;
784 }
785
786 string JID = Identity[PersonalInformation.JidTag];
787 if (!string.IsNullOrEmpty(JID) && JID != e.From.BareJid)
788 {
789 await e.IqErrorBadRequest(e.To, "JID does not match sender Bare JID.", "en");
790 return;
791 }
792
793 foreach (Property P in Identity.Properties)
794 {
795 if (!CheckNameWhitespace(P.Value))
796 {
797 await e.IqErrorBadRequest(e.To, "Invalid space characters used in " + P.Name, "en");
798 return;
799 }
800 }
801
802 IAccount Account = await XmppServerModule.GetAccountAsync(e.From.Account);
803 if (Account is null)
804 await e.IqErrorBadRequest(e.To, "Account not found.", "en");
805
806 string EMail = Identity[PersonalInformation.EMailTag];
807 if (!string.IsNullOrEmpty(EMail) && !string.IsNullOrEmpty(Account.EMail) && Account.EMail != EMail)
808 {
809 await e.IqErrorBadRequest(e.To, "EMAIL does not match account e-mail.", "en");
810 return;
811 }
812
813 string PhoneNr = Identity[PersonalInformation.PhoneTag];
814 if (!string.IsNullOrEmpty(PhoneNr) && !string.IsNullOrEmpty(Account.PhoneNr) && Account.PhoneNr != PhoneNr)
815 {
816 await e.IqErrorBadRequest(e.To, "PHONE does not match account phone number.", "en");
817 return;
818 }
819
820 string Domain = Identity[PersonalInformation.DomainTag];
821 if (!string.IsNullOrEmpty(Domain))
822 {
823 if (!this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) ||
824 string.IsNullOrEmpty(Connection.RemoteEndPoint))
825 {
826 await e.IqErrorForbidden(e.To, "Connection to corroborate DOMAIN missing.", "en");
827 return;
828 }
829
830 if (!IPAddress.TryParse(Connection.RemoteEndPoint.RemovePortNumber(), out IPAddress RemoteAddress))
831 {
832 await e.IqErrorForbidden(e.To, "Requests for a DOMAIN identity requires a connection using TCP/IP.", "en");
833 return;
834 }
835
836 if (IPAddress.TryParse(Domain, out IPAddress _))
837 {
838 await e.IqErrorForbidden(e.To, "DOMAIN name cannot be an IP Address.", "en");
839 return;
840 }
841
842 switch (RemoteAddress.AddressFamily)
843 {
844 case System.Net.Sockets.AddressFamily.InterNetwork:
845 try
846 {
847 IPAddress[] Ip4Addresses = await DnsResolver.TryLookupIP4Addresses(Domain);
848
849 if (Ip4Addresses is null || Array.IndexOf(Ip4Addresses, RemoteAddress) < 0)
850 {
851 await e.IqErrorForbidden(e.To, "Request did not come from the domain specified in DOMAIN.", "en");
852 return;
853 }
854 }
855 catch
856 {
857 await e.IqErrorForbidden(e.To, "Unable to lookup IPv4 address(es) of " + Domain + ".", "en");
858 return;
859 }
860 break;
861
862 case System.Net.Sockets.AddressFamily.InterNetworkV6:
863 try
864 {
865 IPAddress[] Ip6Addresses = await DnsResolver.TryLookupIP6Addresses(Domain);
866
867 if (Ip6Addresses is null || Array.IndexOf(Ip6Addresses, RemoteAddress) < 0)
868 {
869 await e.IqErrorForbidden(e.To, "Request did not come from the domain specified in DOMAIN.", "en");
870 return;
871 }
872 }
873 catch
874 {
875 await e.IqErrorForbidden(e.To, "Unable to lookup IPv6 address(es) of " + Domain + ".", "en");
876 return;
877 }
878 break;
879
880 default:
881 await e.IqErrorForbidden(e.To, "Requests for a DOMAIN identity requires a connection using TCP/IP.", "en");
882 return;
883 }
884 }
885
886 Identity.Provider = e.To.Domain;
887 Identity.Account = e.From.Account;
888 Identity.State = Legal.Identity.IdentityState.Created;
889 Identity.Created = UtcNowSecond;
890 Identity.Updated = DateTime.MinValue;
891 Identity.From = Identity.Created.Date;
892 Identity.To = Identity.From.AddMonths((int)await RuntimeSettings.GetAsync("LegalIdentity.Months", 24));
893
894 if (Preview)
895 Identity.ObjectId = Guid.NewGuid().ToString();
896 else
897 {
898 foreach (LegalIdentity ToRemove in await Database.FindDelete<LegalIdentity>(new FilterAnd(
899 new FilterFieldEqualTo("Account", e.From.Account),
900 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Created))))
901 {
902 Log.Informational("Obsolete Legal Identity Registration deleted.",
903 ToRemove.Id.Value, e.From.BareJid.Value,
904 "LegalIdDeleted", ToRemove.GetTags());
905 }
906
907 await Database.Insert(Identity);
908 }
909
910 Identity.Id = Identity.ObjectId + "@" + this.SubdomainSuffixed + e.From.Domain;
911 Identity.Sign(this);
912
913 if (!Preview)
914 await Database.Update(Identity);
915
916 KeyValuePair<string, object>[] Tags = Identity.GetTags();
917
918 if (!Preview)
919 {
920 Log.Informational("Legal Identity application registered.", Identity.Id.Value, e.From.BareJid.Value,
921 "LegalIdRegistered", Tags);
922
923 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Identity.Id, true);
924 }
925
926 StringBuilder Xml = new StringBuilder();
927 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
928 string IdentityXml = Xml.ToString();
929
930 await e.IqResult(IdentityXml, e.To);
931
932 if (Preview)
933 {
934 await RuntimeCounters.IncrementCounter("Legal.IDPreview." + Identity.State.ToString());
935 await AddPreview(Identity);
936 }
937 else
938 {
939 await RuntimeCounters.IncrementCounter("Legal.ID." + Identity.State.ToString());
940
942 {
943 StringBuilder Markdown = new StringBuilder();
944
945 Markdown.Append("Legal identity application received: [`");
946 Markdown.Append(Identity.Id);
947 Markdown.Append("`](");
948 Markdown.Append(Gateway.GetUrl("/LegalIdentity.md?Id="));
949 Markdown.Append(Identity.Id);
950 Markdown.AppendLine(")");
951 Markdown.AppendLine();
952 Output(Markdown, Tags);
953
954 await Gateway.SendNotification(Markdown.ToString());
955
957 {
958 bool First = true;
959
960 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
961 new FilterFieldEqualTo("Account", Identity.Account),
962 new FilterFieldNotEqualTo("Id", Identity.Id)), "Created"))
963 {
964 switch (ID.State)
965 {
966 case Legal.Identity.IdentityState.Created:
967 case Legal.Identity.IdentityState.Approved:
968 if (First)
969 {
970 First = false;
971 Markdown.Clear();
972 Markdown.AppendLine("Other identities registered for the same account:");
973
974 await Gateway.SendNotification(Markdown.ToString());
975 }
976
977 Markdown.Clear();
978
979 Markdown.Append("[`");
980 Markdown.Append(ID.Id);
981 Markdown.Append("`](");
982 Markdown.Append(Gateway.GetUrl("/LegalIdentity.md?Id="));
983 Markdown.Append(ID.Id);
984 Markdown.AppendLine(")");
985 Markdown.AppendLine();
986
987 Output(Markdown, ID.GetTags());
988 await Gateway.SendNotification(Markdown.ToString());
989 break;
990 }
991 }
992 }
993 }
994 }
995
996 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
997 new XmppAddress(e.From.BareJid), string.Empty, IdentityXml);
998 }
999 catch (Exception ex)
1000 {
1001 await e.IqError(ex, e.To);
1002 }
1003 }
1004
1005 internal static bool CheckNameWhitespace(string FullName)
1006 {
1007 bool LastWhiteSpace = true;
1008
1009 foreach (char ch in FullName)
1010 {
1011 if (char.IsWhiteSpace(ch))
1012 {
1013 if (LastWhiteSpace || ch != ' ')
1014 return false;
1015 else
1016 LastWhiteSpace = true;
1017 }
1018 else
1019 LastWhiteSpace = false;
1020 }
1021
1022 return !LastWhiteSpace;
1023 }
1024
1030 internal static PersonalInformation GetPersonalInformation(LegalIdentity Identity)
1031 {
1032 return GetPersonalInformation(Identity.Properties);
1033 }
1034
1040 internal static PersonalInformation GetPersonalInformation(IEnumerable<Property> Properties)
1041 {
1043
1044 foreach (Property P in Properties)
1045 {
1046 switch (P.Name)
1047 {
1049 Result.FirstName = P.Value;
1050 break;
1051
1053 Result.MiddleNames = P.Value;
1054 break;
1055
1057 Result.LastNames = P.Value;
1058 break;
1059
1061 Result.FullName = P.Value;
1062 break;
1063
1065 Result.Address = P.Value;
1066 break;
1067
1069 Result.Address2 = P.Value;
1070 break;
1071
1073 Result.PostalCode = P.Value;
1074 break;
1075
1077 Result.Area = P.Value;
1078 break;
1079
1081 Result.City = P.Value;
1082 break;
1083
1085 Result.Region = P.Value;
1086 break;
1087
1089 Result.Country = P.Value;
1090 break;
1091
1093 Result.Nationality = P.Value;
1094 break;
1095
1097 switch (P.Value.LowerCase)
1098 {
1099 case "m":
1100 Result.Gender = Gender.Male;
1101 break;
1102
1103 case "f":
1104 Result.Gender = Gender.Female;
1105 break;
1106
1107 case "x":
1108 Result.Gender = Gender.Other;
1109 break;
1110 }
1111 break;
1112
1114 if (int.TryParse(P.Value, out int i) && i >= 1 && i <= 31)
1115 Result.BirthDay = i;
1116 break;
1117
1119 if (int.TryParse(P.Value, out i) && i >= 1 && i <= 12)
1120 Result.BirthMonth = i;
1121 break;
1122
1124 if (int.TryParse(P.Value, out i) && i >= 1900 && i <= 2100)
1125 Result.BirthYear = i;
1126 break;
1127
1129 if (int.TryParse(P.Value.ToString(), out i) && i >= 0)
1130 Result.AgeAbove = i;
1131 break;
1132
1134 Result.PersonalNumber = P.Value;
1135 break;
1136
1138 Result.OrgName = P.Value;
1139 Result.HasOrg = true;
1140 break;
1141
1143 Result.OrgDepartment = P.Value;
1144 Result.HasOrg = true;
1145 break;
1146
1148 Result.OrgRole = P.Value;
1149 Result.HasOrg = true;
1150 break;
1151
1153 Result.OrgAddress = P.Value;
1154 Result.HasOrg = true;
1155 break;
1156
1158 Result.OrgAddress2 = P.Value;
1159 Result.HasOrg = true;
1160 break;
1161
1163 Result.OrgPostalCode = P.Value;
1164 Result.HasOrg = true;
1165 break;
1166
1168 Result.OrgArea = P.Value;
1169 Result.HasOrg = true;
1170 break;
1171
1173 Result.OrgCity = P.Value;
1174 Result.HasOrg = true;
1175 break;
1176
1178 Result.OrgRegion = P.Value;
1179 Result.HasOrg = true;
1180 break;
1181
1183 Result.OrgCountry = P.Value;
1184 Result.HasOrg = true;
1185 break;
1186
1188 Result.OrgNumber = P.Value;
1189 Result.HasOrg = true;
1190 break;
1191
1193 Result.Phone = P.Value;
1194 break;
1195
1197 Result.EMail = P.Value;
1198 break;
1199
1201 Result.Jid = P.Value;
1202 break;
1203 }
1204 }
1205
1206 Result.HasBirthDate =
1207 Result.BirthDay.HasValue &&
1208 Result.BirthMonth.HasValue &&
1209 Result.BirthYear.HasValue &&
1210 Result.BirthDay.Value <= DateTime.DaysInMonth(Result.BirthYear.Value, Result.BirthMonth.Value);
1211
1212 if (!Result.HasBirthDate)
1213 {
1214 Result.BirthDay = null;
1215 Result.BirthMonth = null;
1216 Result.BirthYear = null;
1217 }
1218
1220 Result.FullName = LegalIdentity.JoinNames(Result.FirstName, Result.MiddleNames, Result.LastNames);
1224 {
1225 LegalIdentity.SeparateNames(Result.FullName, out Result.FirstName, out Result.MiddleNames, out Result.LastNames);
1226 }
1227
1228 return Result;
1229 }
1230
1231 internal static async Task<KeyValuePair<IPhoto[], XmlDocument[]>> GetPhotosAndDocuments(LegalIdentity Identity, bool Review)
1232 {
1235
1236 if (!(Identity.Attachments is null))
1237 {
1238 foreach (AttachmentReference Ref in Identity.Attachments)
1239 {
1240 string s = Ref.ContentType.ToLower();
1241 int i = s.IndexOf(';');
1242 if (i > 0)
1243 s = s[..i].TrimEnd();
1244
1245 bool IsImage = Array.IndexOf(ImageCodec.ImageContentTypes, s) >= 0;
1246 bool IsXml = Array.IndexOf(XmlCodec.XmlContentTypes, s) >= 0;
1247
1248 if (!IsImage && !IsXml)
1249 continue;
1250
1251 byte[] Bin;
1252
1253 if (Review)
1254 Bin = await GetPreviewAttachment(Ref);
1255 else
1256 {
1257 Attachment Attachment = await Database.FindFirstIgnoreRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
1258 if (Attachment is null || Attachment.Size > int.MaxValue)
1259 continue;
1260
1261 using FileStream AttachmentFile = File.OpenRead(Attachment.LocalFileName);
1262 Aes Aes = Aes.Create();
1263
1264 Aes.BlockSize = 128;
1265 Aes.KeySize = 256;
1266 Aes.Mode = CipherMode.CBC;
1267 Aes.Padding = PaddingMode.Zeros;
1268
1269 byte[] Key = new byte[32];
1270 byte[] IV = new byte[16];
1271
1272 Buffer.BlockCopy(Attachment.Salt, 0, Key, 0, 32);
1273 Buffer.BlockCopy(Attachment.Salt, 32, IV, 0, 16);
1274
1275 using ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV);
1276 using CryptoStream DecryptedAttachmentFile = new CryptoStream(AttachmentFile, Decryptor, CryptoStreamMode.Read);
1277 int c = (int)Attachment.Size;
1278
1279 Bin = await DecryptedAttachmentFile.ReadAllAsync(c);
1280 }
1281
1282 if (IsImage)
1283 Photos.Add(new Photo(Ref.ContentType, Ref.FileName, Bin));
1284 else
1285 {
1286 XmlDocument Doc = new XmlDocument();
1287
1288 try
1289 {
1290 Doc.Load(new MemoryStream(Bin));
1291 }
1292 catch (Exception ex)
1293 {
1294 Log.Exception(ex);
1295 continue;
1296 }
1297
1298 Documents.Add(Doc);
1299 }
1300 }
1301 }
1302
1303 return new KeyValuePair<IPhoto[], XmlDocument[]>(Photos.ToArray(), Documents.ToArray());
1304 }
1305
1309 public static DateTime UtcNowSecond
1310 {
1311 get
1312 {
1313 DateTime TP = DateTime.UtcNow;
1314 TP = new DateTime(TP.Year, TP.Month, TP.Day, TP.Hour, TP.Minute, TP.Second, DateTimeKind.Utc);
1315 return TP;
1316 }
1317 }
1318
1319 internal static void Output(StringBuilder Markdown, KeyValuePair<string, object>[] Tags)
1320 {
1321 Output(Markdown, Tags, true);
1322 }
1323
1324 internal static void Output(StringBuilder Markdown, KeyValuePair<string, object>[] Tags, bool IncludeHeader)
1325 {
1326 if (IncludeHeader)
1327 {
1328 Markdown.AppendLine("| Key | Value |");
1329 Markdown.AppendLine("|:----|:------|");
1330 }
1331
1332 foreach (KeyValuePair<string, object> Tag in Tags)
1333 {
1334 Markdown.Append("| ");
1335 Markdown.Append(MarkdownDocument.Encode(Tag.Key).Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>"));
1336 Markdown.Append(" | ");
1337
1338 if (!(Tag.Value is null))
1339 Markdown.Append(MarkdownDocument.Encode(Tag.Value.ToString()).Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>"));
1340
1341 Markdown.AppendLine(" |");
1342 }
1343 }
1344
1345 private async Task GetLegalIdentitiesHandler(object Sender, IqEventArgs e)
1346 {
1347 try
1348 {
1349 if (!this.Server.IsServerDomain(e.From.Domain, true))
1350 {
1351 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
1352 return;
1353 }
1354
1355 NamespaceSet Version = XmppServerModule.GetVersion(e.Query.NamespaceURI);
1356 string Xml = this.SerializeIdentities(await Database.Find<LegalIdentity>(
1357 new FilterFieldEqualTo("Account", e.From.Account)), null, Version);
1358
1359 await e.IqResult(Xml, e.To);
1360 }
1361 catch (Exception ex)
1362 {
1363 await e.IqError(ex, e.To);
1364 }
1365 }
1366
1367 private string SerializeIdentities(IEnumerable<LegalIdentity> Identities, IEnumerable<Dictionary<string, string>> AttachmentUrls,
1368 NamespaceSet Version)
1369 {
1370 using IEnumerator<Dictionary<string, string>> e = AttachmentUrls?.GetEnumerator();
1371 StringBuilder Xml = new StringBuilder();
1372
1373 Xml.Append("<identities xmlns='");
1374 Xml.Append(NamespaceLegalIdentity(Version));
1375 Xml.Append("'>");
1376
1377 foreach (LegalIdentity Identity in Identities)
1378 Identity.Serialize(Xml, Identity.Version != Version, true, true, true, true, true, true, (e?.MoveNext() ?? false) ? e.Current : null, this);
1379
1380 Xml.Append("</identities>");
1381
1382 return Xml.ToString();
1383 }
1384
1385 internal static async Task<LegalIdentity> GetLocalLegalIdentity(string LegalId)
1386 {
1387 KeyValuePair<LegalIdentity, bool> P = await GetLocalLegalIdentity(LegalId, false);
1388 return P.Key;
1389 }
1390
1396 public static string GetPreviewFolder()
1397 {
1398 string Folder = Gateway.AppDataFolder is null ? "Previews" :
1399 Path.Combine(Gateway.AppDataFolder, "Previews");
1400
1401 if (!Directory.Exists(Folder))
1402 Directory.CreateDirectory(Folder);
1403
1404 return Folder;
1405 }
1406
1407 internal static string GetPreviewFileName(string Id, string Extension)
1408 {
1409 string Folder = GetPreviewFolder();
1410 int i = Id.IndexOf('@');
1411 if (i > 0)
1412 Id = Id[..i];
1413
1414 string FileName = Path.Combine(Folder, Id + Extension);
1415 FileName = Path.GetFullPath(FileName);
1416
1417 return FileName;
1418 }
1419
1420 internal static string GetPreviewKey(string FileName)
1421 {
1422 StringBuilder sb = new StringBuilder();
1423 sb.Append(FileName);
1424 sb.Append('|');
1425 sb.Append(Gateway.Domain?.Value);
1426
1427 string Key = sb.ToString();
1428 Key = Hashes.ComputeSHA256HashString(Encoding.UTF8.GetBytes(Key));
1429
1430 return Key;
1431 }
1432
1433 internal static async Task<byte[]> CreatePreviewSalt(string FileName, string UserName)
1434 {
1435 string Key = GetPreviewKey(FileName);
1436 return await XmppServerModule.CreateSalt(Key, Encoding.UTF8.GetBytes(UserName));
1437 }
1438
1439 internal static async Task<KeyValuePair<byte[], string>> GetPreviewSalt(string FileName)
1440 {
1441 string Key = GetPreviewKey(FileName);
1442 KeyValuePair<byte[], byte[]> P = await XmppServerModule.GetSaltWithAdditionalData(Key);
1443 string UserName = Encoding.UTF8.GetString(P.Value);
1444 return new KeyValuePair<byte[], string>(P.Key, UserName);
1445 }
1446
1447 internal static async Task<bool> RemovePreviewSalt(string FileName)
1448 {
1449 string Key = GetPreviewKey(FileName);
1450 return await XmppServerModule.RemoveSalt(Key);
1451 }
1452
1453 private static async Task AddPreview(LegalIdentity Identity)
1454 {
1455 string FileName = GetPreviewFileName(Identity.Id, ".id");
1456 byte[] Salt = await CreatePreviewSalt(FileName, Identity.Account.Value);
1457
1458 StringBuilder sb = new StringBuilder();
1459 Identity.Serialize(sb, true, true, true, true, true, true, true, null, XmppServerModule.Legal);
1460
1461 byte[] Data = Encoding.UTF8.GetBytes(sb.ToString());
1462
1463 await XmppServerModule.SaveEncryptedFile(FileName, Salt, Data);
1464
1465 await PreviewIdApplicationSource.PreviewFileSaved(FileName, Identity);
1466 }
1467
1468 internal static async Task<KeyValuePair<LegalIdentity, bool>> GetLocalLegalIdentity(string LegalId, bool IncludePreviews)
1469 {
1470 LegalIdentity Identity = await Database.FindFirstDeleteRest<LegalIdentity>(new FilterFieldEqualTo("Id", LegalId), "Created");
1471 if (!(Identity is null))
1472 return new KeyValuePair<LegalIdentity, bool>(Identity, false);
1473
1474 if (!IncludePreviews)
1475 return new KeyValuePair<LegalIdentity, bool>(null, false);
1476
1477 string FileName = GetPreviewFileName(LegalId, ".id");
1478 if (!File.Exists(FileName))
1479 return new KeyValuePair<LegalIdentity, bool>(null, false);
1480
1481 Identity = await GetPreviewIdentity(FileName);
1482 return new KeyValuePair<LegalIdentity, bool>(Identity, !(Identity is null));
1483 }
1484
1485 internal static async Task<LegalIdentity> GetPreviewIdentity(string FileName)
1486 {
1487 KeyValuePair<byte[], string> P = await GetPreviewSalt(FileName);
1488 if (P.Key is null)
1489 return null;
1490
1491 byte[] Salt = P.Key;
1492 string UserName = P.Value;
1493
1494 byte[] Data = await XmppServerModule.LoadEncryptedFile(FileName, Salt);
1495 string s = Encoding.UTF8.GetString(Data);
1496
1497 XmlDocument Doc = new XmlDocument();
1498 Doc.LoadXml(s);
1499
1500 LegalIdentity Identity = LegalIdentity.Parse(Doc.DocumentElement, out _, out _);
1501 Identity.Account = UserName;
1502
1503 return Identity;
1504 }
1505
1506 internal static async Task<byte[]> GetPreviewAttachment(string AttachmentId)
1507 {
1508 string AttachmentFileName = GetPreviewFileName(AttachmentId, ".att");
1509 KeyValuePair<byte[], string> P = await GetPreviewSalt(AttachmentFileName);
1510 byte[] Salt = P.Key;
1511
1512 byte[] Data = await XmppServerModule.LoadEncryptedFile(AttachmentFileName, Salt);
1513 Stream Decoded = await XmppServerModule.DecodeBlob(new MemoryStream(Data));
1514
1515 return await Decoded.ReadAllAsync();
1516 }
1517
1524 {
1525 return GetPreviewAttachment(Attachment.Id);
1526 }
1527
1528 internal async Task<int> DeleteOldPreviewApplications(int KeepDays)
1529 {
1530 string Folder = GetPreviewFolder();
1531 string[] FileNames = Directory.GetFiles(Folder, "*.*", SearchOption.TopDirectoryOnly);
1532 DateTime Limit = DateTime.UtcNow.AddDays(-KeepDays);
1533 int Count = 0;
1534
1535 foreach (string FileName in FileNames)
1536 {
1537 try
1538 {
1539 if (File.GetCreationTimeUtc(FileName) > Limit)
1540 continue;
1541
1542 bool IsPreviewIdFile = string.Compare(Path.GetExtension(FileName), ".id", true) == 0;
1543
1544 if (IsPreviewIdFile)
1545 {
1546 try
1547 {
1548 LegalIdentity Identity = await GetPreviewIdentity(FileName);
1549
1550 if (!(Identity is null))
1551 {
1552 await this.UpdateState(Identity, Legal.Identity.IdentityState.Obsoleted,
1553 Array.Empty<string>(), null, true, false, null);
1554 }
1555 }
1556 catch (Exception ex)
1557 {
1558 Log.Exception(ex);
1559 }
1560 }
1561
1562 File.Delete(FileName);
1563 Count++;
1564
1565 if (IsPreviewIdFile)
1566 await PreviewIdApplicationSource.PreviewFileDeleted(FileName);
1567 }
1568 catch (Exception ex)
1569 {
1570 Log.Exception(ex, FileName);
1571 }
1572 }
1573
1574 return Count;
1575 }
1576
1577 private async Task GetLegalIdentityHandler(object Sender, IqEventArgs e)
1578 {
1579 try
1580 {
1582 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase);
1583 KeyValuePair<LegalIdentity, bool> P = await GetLocalLegalIdentity(Id, true);
1584 LegalIdentity Identity = P.Key;
1585 bool Preview = P.Value;
1586
1587 if (Identity is null)
1588 {
1589 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1590 return;
1591 }
1592
1593 if ((e.From.Account == Identity.Account &&
1594 this.Server.IsServerDomain(e.From.Domain, true)) ||
1595 (!Preview && this.IsAccessToIdentityAuthorized(e.From.BareJid, Identity.Id)))
1596 {
1597 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Id, true);
1598
1599 StringBuilder Xml = new StringBuilder();
1600 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
1601
1602 await e.IqResult(Xml.ToString(), e.To);
1603 }
1604 else
1605 {
1606 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Id, false);
1607 await e.IqErrorForbidden(e.To, "A client can only access its own legal identities, and the legal identities of parts in smart contracts to which the client is part.", "en");
1608 }
1609 }
1610 catch (Exception ex)
1611 {
1612 await e.IqError(ex, e.To);
1613 }
1614 }
1615
1616 private async Task PetitionIdentityHandler(object Sender, IqEventArgs e)
1617 {
1618 try
1619 {
1620 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1621 string PetitionId = XML.Attribute(e.Query, "pid");
1622 string Purpose = XML.Attribute(e.Query, "purpose");
1623 string Nonce = XML.Attribute(e.Query, "nonce");
1624 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
1625 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase);
1626
1627 if (!TryGetContext(e.Query, out XmlElement ContextXml, out string _,
1628 out string[] Properties, out string[] Attachments))
1629 {
1630 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1631 return;
1632 }
1633
1634 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
1635 if (Identity is null)
1636 {
1637 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1638 return;
1639 }
1640
1641 XmppAddress LegalIdAddr = new XmppAddress(LegalId);
1642 int i = LegalIdAddr.Domain.IndexOf('.');
1643 string JidDomain = i < 0 ? this.Server.Domain : LegalIdAddr.Domain.Substring(i + 1);
1644
1645 (LegalIdentity RequestorIdentity, Dictionary<string, string> RequestorAttachmentUrls) = await this.ValidateSenderSignature(
1646 e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, Identity.Account + "@" + JidDomain);
1647
1648 if (RequestorIdentity is null)
1649 return;
1650
1651 StringBuilder Msg = new StringBuilder();
1652
1653 Msg.Append("<petitionIdentityMsg id=\"");
1654 Msg.Append(XML.Encode(LegalId));
1655 Msg.Append("\" pid=\"");
1656 Msg.Append(XML.Encode(PetitionId));
1657 Msg.Append("\" from=\"");
1658 Msg.Append(XML.Encode(e.From.Address.Value));
1659 Msg.Append("\" purpose=\"");
1660 Msg.Append(XML.Encode(Purpose));
1661
1662 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1663 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
1664 {
1665 Msg.Append("\" clientEp=\"");
1666 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
1667 }
1668 else
1669 {
1670 ClientInformation ClientInfo = await this.GetNetworkIdentity(RequestorIdentity.Id, true, false, Identity.Version);
1671 if (!(ClientInfo is null))
1672 {
1673 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
1674
1675 if (!string.IsNullOrEmpty(ClientEndpoint))
1676 {
1677 Msg.Append("\" clientEp=\"");
1678 Msg.Append(XML.Encode(ClientEndpoint));
1679 }
1680 }
1681 }
1682
1683 Msg.Append("\" xmlns=\"");
1684 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1685 Msg.Append("\">");
1686
1687 this.Append(Msg, Properties, Attachments, ContextXml, null, RequestorIdentity,
1688 Identity.Version, RequestorAttachmentUrls);
1689
1690 Msg.Append("</petitionIdentityMsg>");
1691
1692 await this.Server.SendMessage(string.Empty, string.Empty, e.To, new XmppAddress(Identity.Account + "@" + JidDomain),
1693 string.Empty, Msg.ToString());
1694
1695 await e.IqResult(string.Empty, e.To);
1696 }
1697 catch (Exception ex)
1698 {
1699 await e.IqError(ex, e.To);
1700 }
1701 }
1702
1703 private void Append(StringBuilder Msg, string[] Properties, string[] Attachments,
1704 XmlElement ContextXml, string Content, LegalIdentity RequestorIdentity,
1705 NamespaceSet RequestNamespace, Dictionary<string, string> RequestorAttachmentUrls)
1706 {
1707 if (!(Properties is null))
1708 {
1709 Msg.Append("<properties>");
1710
1711 foreach (string Property in Properties)
1712 {
1713 Msg.Append("<property>");
1714 Msg.Append(XML.Encode(Property));
1715 Msg.Append("</property>");
1716 }
1717
1718 Msg.Append("</properties>");
1719 }
1720
1721 if (!(Attachments is null))
1722 {
1723 Msg.Append("<attachments>");
1724
1725 foreach (string Attachment in Attachments)
1726 {
1727 Msg.Append("<attachment>");
1728 Msg.Append(XML.Encode(Attachment));
1729 Msg.Append("</attachment>");
1730 }
1731
1732 Msg.Append("</attachments>");
1733 }
1734
1735 RequestorIdentity?.Serialize(Msg, RequestorIdentity.Version != RequestNamespace,
1736 true, true, true, true, true, true, RequestorAttachmentUrls, this);
1737
1738 if (!string.IsNullOrEmpty(Content))
1739 {
1740 Msg.Append("<content>");
1741 Msg.Append(Content);
1742 Msg.Append("</content>");
1743 }
1744
1745 if (!(ContextXml is null))
1746 Msg.Append(ContextXml.OuterXml);
1747 }
1748
1749 private static bool TryGetContext(XmlElement Query, out XmlElement Context,
1750 out string Content, out string[] Properties, out string[] Attachments)
1751 {
1752 ChunkedList<string> PropertyList = null;
1753 ChunkedList<string> AttachmentList = null;
1754 bool IsIdentityNamespace = IsNamespaceLegalIdentity(Query.NamespaceURI);
1755 bool IsContractNamespace = IsNamespaceSmartContract(Query.NamespaceURI);
1756 Context = null;
1757 Properties = null;
1758 Attachments = null;
1759 Content = null;
1760
1761 foreach (XmlNode N in Query)
1762 {
1763 if (!(N is XmlElement E))
1764 continue;
1765
1766 if (IsIdentityNamespace)
1767 {
1768 if (!IsNamespaceLegalIdentity(E.NamespaceURI))
1769 continue;
1770 }
1771 else if (IsContractNamespace)
1772 {
1773 if (!IsNamespaceSmartContract(E.NamespaceURI))
1774 continue;
1775 }
1776 else
1777 {
1778 if (E.NamespaceURI != Query.NamespaceURI)
1779 continue;
1780 }
1781
1782 switch (E.LocalName)
1783 {
1784 case "content":
1785 if (string.IsNullOrEmpty(Content))
1786 {
1787 Content = E.InnerText;
1788 continue;
1789 }
1790 else
1791 return false;
1792
1793 case "properties":
1794 foreach (XmlNode N2 in E.ChildNodes)
1795 {
1796 if (!(N2 is XmlElement E2))
1797 continue;
1798
1799 if (E2.LocalName == "property")
1800 {
1801 PropertyList ??= new ChunkedList<string>();
1802 PropertyList.Add(E2.InnerText);
1803 }
1804 else
1805 return false;
1806 }
1807 continue;
1808
1809 case "attachments":
1810 foreach (XmlNode N2 in E.ChildNodes)
1811 {
1812 if (!(N2 is XmlElement E2))
1813 continue;
1814
1815 if (E2.LocalName == "attachment")
1816 {
1817 AttachmentList ??= new ChunkedList<string>();
1818 AttachmentList.Add(E2.InnerText);
1819 }
1820 else
1821 return false;
1822 }
1823 continue;
1824 }
1825
1826 if (Context is null)
1827 Context = E;
1828 else
1829 return false;
1830 break;
1831 }
1832
1833 Properties = PropertyList?.ToArray();
1834 Attachments = AttachmentList?.ToArray();
1835
1836 return true;
1837 }
1838
1839 private async Task PetitionIdentityResponseHandler(object Sender, IqEventArgs e)
1840 {
1841 try
1842 {
1843 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1844 string PetitionId = XML.Attribute(e.Query, "pid");
1845 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
1846 bool Response = XML.Attribute(e.Query, "response", false);
1847 LegalIdentity Identity;
1848 XmlElement ContextXml = null;
1849
1850 foreach (XmlNode N in e.Query)
1851 {
1852 if (!(N is XmlElement E))
1853 continue;
1854
1855 if (ContextXml is null)
1856 ContextXml = E;
1857 else
1858 {
1859 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1860 return;
1861 }
1862 }
1863
1864 Identity = await GetLocalLegalIdentity(LegalId);
1865 if (Identity is null)
1866 {
1867 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1868 return;
1869 }
1870
1871 StringBuilder Msg = new StringBuilder();
1872
1873 Msg.Append("<petitionIdentityResponseMsg pid=\"");
1874 Msg.Append(XML.Encode(PetitionId));
1875 Msg.Append("\" response=\"");
1876 Msg.Append(CommonTypes.Encode(Response));
1877
1878 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1879 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
1880 {
1881 Msg.Append("\" clientEp=\"");
1882 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
1883 }
1884
1885 Msg.Append("\" xmlns=\"");
1886 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1887 Msg.Append("\">");
1888
1889 if (Response)
1890 {
1891 this.IdentityAuthorization(RequestorFullJid.BareJid, e.From.BareJid, LegalId, Response);
1892
1893 Identity.Serialize(Msg, false, true, true, true, true, true, true, null, this);
1894 }
1895
1896 if (!(ContextXml is null))
1897 Msg.Append(ContextXml.OuterXml);
1898
1899 Msg.Append("</petitionIdentityResponseMsg>");
1900
1901 await this.Server.SendMessage(string.Empty, string.Empty, e.To, RequestorFullJid, string.Empty, Msg.ToString());
1902
1903 await e.IqResult(string.Empty, e.To);
1904 }
1905 catch (Exception ex)
1906 {
1907 await e.IqError(ex, e.To);
1908 }
1909 }
1910
1911 internal void IdentityAuthorization(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1912 CaseInsensitiveString LegalId, bool Authorized)
1913 {
1914 this.Authorization("I:", ToBareJid, FromBareJid, LegalId, Authorized ? 1 : 0);
1915 }
1916
1931 internal void Authorization(CaseInsensitiveString Prefix, CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1932 CaseInsensitiveString Id, int Value)
1933 {
1934 CaseInsensitiveString Key1 = Prefix + Id;
1935 CaseInsensitiveString Key2 = Key1 + ":" + ToBareJid;
1936
1937 if (Value > 0)
1938 {
1939 this.petitions?.Add(Key2, Value);
1940
1941 lock (this.petitionsByBareJid)
1942 {
1943 if (!this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
1944 {
1945 ByLegalId = new Dictionary<CaseInsensitiveString, CaseInsensitiveString>();
1946 this.petitionsByBareJid[ToBareJid] = ByLegalId;
1947 }
1948
1949 ByLegalId[Key1] = FromBareJid;
1950 }
1951 }
1952 else
1953 {
1954 this.petitions?.Remove(Key2);
1955
1956 lock (this.petitionsByBareJid)
1957 {
1958 if (this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId) &&
1959 ByLegalId.Remove(Key1) &&
1960 ByLegalId.Count == 0)
1961 {
1962 this.petitionsByBareJid.Remove(ToBareJid);
1963 }
1964 }
1965 }
1966 }
1967
1968 private Task Petitions_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, int> e)
1969 {
1970 if (e.Reason != RemovedReason.Manual)
1971 {
1972 string Key = e.Key;
1973 int i1 = Key.IndexOf(':');
1974 if (i1 >= 0)
1975 {
1976 int i2 = Key.IndexOf(':', i1 + 1);
1977 if (i2 >= 0)
1978 {
1979 CaseInsensitiveString Key1 = Key[..i2];
1980 CaseInsensitiveString ToBareJid = Key[(i2 + 1)..];
1981
1982 lock (this.petitionsByBareJid)
1983 {
1984 if (this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId) &&
1985 ByLegalId.Remove(Key1) &&
1986 ByLegalId.Count == 0)
1987 {
1988 this.petitionsByBareJid.Remove(ToBareJid);
1989 }
1990 }
1991 }
1992 }
1993 }
1994
1995 return Task.CompletedTask;
1996 }
1997
1998 private Task RemoteComponents_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, object> e)
1999 {
2000 if (e.Value is IDisposableAsync DisposableAsync)
2001 return DisposableAsync.DisposeAsync();
2002
2003 if (e.Value is IDisposable Disposable)
2004 Disposable.Dispose();
2005
2006 return Task.CompletedTask;
2007 }
2008
2009 internal bool IsAccessToIdentityAuthorized(CaseInsensitiveString BareJid, CaseInsensitiveString LegalId)
2010 {
2011 CaseInsensitiveString Key = "I:" + LegalId + ":" + BareJid;
2012 return (this.petitions?.TryGetValue(Key, out int i) ?? false) && (i > 0);
2013 }
2014
2015 private async Task Server_OnPresenceLocalSender(object Sender, PresenceEventArgs e)
2016 {
2017 if (!e.To.IsEmpty && !(e.Stanza?.StanzaElement is null))
2018 {
2019 switch (e.Type)
2020 {
2021 case "subscribe":
2022 string Namespace = NamespaceLegalIdentity(XmppServerModule.GetVersion(e.Content?.NamespaceURI ?? string.Empty));
2023
2024 foreach (XmlNode N in e.Stanza.StanzaElement.ChildNodes)
2025 {
2026 if (N is XmlElement E && E.LocalName == "identity" && E.NamespaceURI == Namespace)
2027 {
2028 LegalIdentity Identity = LegalIdentity.Parse(E, out _, out _);
2029 if (!(Identity is null))
2030 {
2031 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Identity.Id.LowerCase);
2032 LegalIdentity Identity2 = await GetLocalLegalIdentity(Identity.Id);
2033 if (Identity is null)
2034 {
2035 await e.PresenceErrorItemNotFound(e.To, "Legal identity not found.", "en");
2036 return;
2037 }
2038
2039 if (e.From.Account != Identity2.Account)
2040 {
2041 Log.Warning("Client tried to forward someone else's identity in a presence subscription request.",
2042 Identity.Id.Value, e.From.Address.Value, "IdValidationError",
2043 new KeyValuePair<string, object>("Sender", e.From.BareJid.Value),
2044 new KeyValuePair<string, object>("ID", Identity.Id.Value),
2045 new KeyValuePair<string, object>("ID Account", Identity.Account.Value));
2046
2047 await e.PresenceErrorForbidden(e.To, "A client can only forward its own identity objects.", "en");
2048 return;
2049 }
2050 else
2051 {
2052 StringBuilder Id1 = new StringBuilder();
2053 Identity.Serialize(Id1, true, true, true, true, true, true, false, null, this);
2054 string Id1Xml = Id1.ToString();
2055
2056 StringBuilder Id2 = new StringBuilder();
2057 Identity.Serialize(Id2, true, true, true, true, true, true, false, null, this);
2058 string Id2Xml = Id2.ToString();
2059
2060 if (Id1Xml == Id2Xml)
2061 this.IdentityAuthorization(e.To.BareJid, e.From.BareJid, Identity.Id, true);
2062 else
2063 {
2064 Log.Warning("Client tried to forward invalid ID in a presence subscription request.",
2065 Identity.Id.Value, e.From.Address.Value, "IdValidationError",
2066 new KeyValuePair<string, object>("Sender", e.From.BareJid.Value),
2067 new KeyValuePair<string, object>("ID", Identity.Id.Value),
2068 new KeyValuePair<string, object>("Attempt", Id1Xml),
2069 new KeyValuePair<string, object>("Actual", Id2Xml));
2070
2071 await e.PresenceErrorForbidden(e.To, "Invalid ID representation.", "en");
2072 return;
2073 }
2074 }
2075 }
2076 }
2077 }
2078 break;
2079
2080 case "unsubscribed":
2081 LinkedList<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> ToRemove = null;
2082
2083 lock (this.petitionsByBareJid)
2084 {
2085 if (this.petitionsByBareJid.TryGetValue(e.To.BareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
2086 {
2087 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in ByLegalId)
2088 {
2089 if (P.Value == e.From.BareJid && P.Key.Length > 2)
2090 {
2091 ToRemove ??= new LinkedList<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>>();
2092 ToRemove.AddLast(P);
2093 }
2094 }
2095 }
2096 }
2097
2098 if (!(ToRemove is null))
2099 {
2100 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in ToRemove)
2101 this.Authorization(P.Key.Substring(0, 2), e.To.BareJid, P.Value, P.Key.Substring(2), 0);
2102 }
2103 break;
2104 }
2105 }
2106 }
2107
2108 private async Task PetitionSignatureHandler(object Sender, IqEventArgs e)
2109 {
2110 try
2111 {
2112 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
2113 string PetitionId = XML.Attribute(e.Query, "pid");
2114 string Purpose = XML.Attribute(e.Query, "purpose");
2115 string Nonce = XML.Attribute(e.Query, "nonce");
2116 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
2117
2118 if (!TryGetContext(e.Query, out XmlElement ContextXml, out string ContentStr,
2119 out string[] Properties, out string[] Attachments))
2120 {
2121 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
2122 return;
2123 }
2124
2125 if (ContentStr is null)
2126 {
2127 if (ContextXml is null)
2128 ContentStr = e.Query.InnerText;
2129 else
2130 {
2131 await e.IqErrorBadRequest(e.To, "No content to sign.", "en");
2132 return;
2133 }
2134 }
2135
2136 byte[] Content = Convert.FromBase64String(ContentStr);
2137 string DataStr = PetitionId + ":" + LegalId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase + ":" + ContentStr;
2138 byte[] Data = Encoding.UTF8.GetBytes(DataStr);
2139 string s = Encoding.UTF8.GetString(Content);
2140 LegalIdentity ReqIdentity = null;
2141 Dictionary<string, string> ReqAttachmentUrls = null;
2142 bool PeerReview = false;
2143
2144 if (s.StartsWith("<identity") && s.EndsWith("</identity>"))
2145 {
2146 try
2147 {
2148 XmlDocument Doc = XML.ParseXml(s, true);
2149
2150 if (Doc.DocumentElement.LocalName == "identity")
2151 {
2152 LegalIdentity TempId = LegalIdentity.Parse(Doc.DocumentElement, out bool HasStatus, out _);
2154
2155 if (HasStatus &&
2156 TempId.State == Legal.Identity.IdentityState.Created &&
2157 Jid == e.From.BareJid)
2158 {
2159 ReqIdentity = TempId;
2160 PeerReview = true;
2161
2162 if (!TempId.ValidateSignature(Data, Signature))
2163 {
2164 await e.IqErrorForbidden(e.To, "Signature of identity proving access to private keys not valid.", "en");
2165 return;
2166 }
2167 }
2168 }
2169 }
2170 catch (Exception)
2171 {
2172 // Ignore
2173 }
2174 }
2175
2176 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
2177 if (Identity is null)
2178 {
2179 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2180 return;
2181 }
2182
2183 XmppAddress LegalIdAddr = new XmppAddress(LegalId);
2184 int i = LegalIdAddr.Domain.IndexOf('.');
2185 string JidDomain = i < 0 ? this.Server.Domain : LegalIdAddr.Domain.Substring(i + 1);
2186
2187 if (ReqIdentity is null)
2188 {
2189 (ReqIdentity, ReqAttachmentUrls) = await this.ValidateSenderSignature(e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, Identity.Account + "@" + JidDomain);
2190 if (ReqIdentity is null)
2191 return;
2192 }
2193
2194 StringBuilder Msg = new StringBuilder();
2195
2196 Msg.Append("<petitionSignatureMsg id=\"");
2197 Msg.Append(XML.Encode(LegalId));
2198 Msg.Append("\" pid=\"");
2199 Msg.Append(XML.Encode(PetitionId));
2200 Msg.Append("\" from=\"");
2201 Msg.Append(XML.Encode(e.From.Address.Value));
2202 Msg.Append("\" purpose=\"");
2203 Msg.Append(XML.Encode(Purpose));
2204
2205 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
2206 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
2207 {
2208 Msg.Append("\" clientEp=\"");
2209 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
2210 }
2211 else
2212 {
2213 ClientInformation ClientInfo = await this.GetNetworkIdentity(ReqIdentity.Id, true, false, Identity.Version);
2214 if (!(ClientInfo is null))
2215 {
2216 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
2217
2218 if (!string.IsNullOrEmpty(ClientEndpoint))
2219 {
2220 Msg.Append("\" clientEp=\"");
2221 Msg.Append(XML.Encode(ClientEndpoint));
2222 }
2223 }
2224 }
2225
2226 Msg.Append("\" xmlns=\"");
2227 Msg.Append(NamespaceLegalIdentity(Identity.Version));
2228 Msg.Append("\">");
2229
2230 this.Append(Msg, Properties, Attachments, ContextXml,
2231 ContentStr, PeerReview ? null : ReqIdentity,
2232 Identity.Version, ReqAttachmentUrls);
2233
2234 XmppAddress ClientBareJid = new XmppAddress(Identity.Account + "@" + JidDomain);
2235
2236 if (!(ContextXml is null) &&
2237 ContextXml.LocalName == "agentApi" &&
2238 ContextXml.NamespaceURI == QuickLogin.NamespaceTagSignature)
2239 {
2240 CaseInsensitiveString Domain = XML.Attribute(ContextXml, "domain");
2241 int Timeout = XML.Attribute(ContextXml, "timeout", 3600);
2242
2243 if (Timeout < 1 || Timeout > 3600)
2244 {
2245 await e.IqErrorBadRequest(e.To, "Agent API Timeout out of range.", "en");
2246 return;
2247 }
2248
2250 {
2251 await e.IqErrorBadRequest(e.To, "Missing domain.", "en");
2252 return;
2253 }
2254
2255 CaseInsensitiveString ReqDomain = ReqIdentity[PersonalInformation.DomainTag];
2256 if (CaseInsensitiveString.IsNullOrEmpty(ReqDomain))
2257 {
2258 await e.IqErrorBadRequest(e.To, "Requestor lacks DOMAIN property in its legal identity.", "en");
2259 return;
2260 }
2261
2262 if (ReqDomain != Domain)
2263 {
2264 await e.IqErrorBadRequest(e.To, "Requestor domain does not match stated domain in agent API context.", "en");
2265 return;
2266 }
2267
2268 this.AgentApiLoginCorrelation(e.From.BareJid, ClientBareJid.Address, PetitionId, Timeout);
2269 }
2270
2271 Msg.Append("</petitionSignatureMsg>");
2272
2273 await this.Server.SendMessage(string.Empty, string.Empty, e.To, ClientBareJid, string.Empty, Msg.ToString());
2274
2275 await e.IqResult(string.Empty, e.To);
2276 }
2277 catch (Exception ex)
2278 {
2279 await e.IqError(ex, e.To);
2280 }
2281 }
2282
2283 internal void AgentApiLoginCorrelation(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
2284 CaseInsensitiveString PetitionId, int Timeout)
2285 {
2286 this.Authorization("A:", ToBareJid, FromBareJid, PetitionId, Timeout);
2287 }
2288
2289 internal bool IsAgentApiLoginCorrelated(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
2290 CaseInsensitiveString PetitionId, out int Timeout)
2291 {
2292 CaseInsensitiveString Key1 = "A:" + PetitionId;
2293 CaseInsensitiveString Key2 = Key1 + ":" + ToBareJid;
2294 if (!(this.petitions?.TryGetValue(Key2, out Timeout) ?? false))
2295 {
2296 Timeout = 0;
2297 return false;
2298 }
2299
2300 lock (this.petitionsByBareJid)
2301 {
2302 if (!this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
2303 {
2304 Timeout = 0;
2305 return false;
2306 }
2307
2308 if (!ByLegalId.TryGetValue(Key1, out CaseInsensitiveString FromBareJid2) || FromBareJid != FromBareJid2)
2309 {
2310 Timeout = 0;
2311 return false;
2312 }
2313 }
2314
2315 return true;
2316 }
2317
2318 private async Task PetitionSignatureResponseHandler(object Sender, IqEventArgs e)
2319 {
2320 try
2321 {
2322 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
2323 string PetitionId = XML.Attribute(e.Query, "pid");
2324 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
2325 bool Response = XML.Attribute(e.Query, "response", false);
2326 string ContentStr = null;
2327 string SignatureStr = null;
2328 byte[] Content = null;
2329 byte[] Signature = null;
2330 LegalIdentity Identity;
2331 XmlElement ContextXml = null;
2332
2333 foreach (XmlNode N in e.Query.ChildNodes)
2334 {
2335 if (!(N is XmlElement E))
2336 continue;
2337
2338 if (E.NamespaceURI == e.Query.NamespaceURI)
2339 {
2340 switch (E.LocalName)
2341 {
2342 case "content":
2343 ContentStr = E.InnerText;
2344 Content = Convert.FromBase64String(ContentStr);
2345 break;
2346
2347 case "signature":
2348 SignatureStr = E.InnerText;
2349 Signature = Convert.FromBase64String(SignatureStr);
2350 break;
2351 }
2352 }
2353 else if (ContextXml is null)
2354 ContextXml = E;
2355 else
2356 {
2357 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
2358 return;
2359 }
2360 }
2361
2362 Identity = await GetLocalLegalIdentity(LegalId);
2363 if (Identity is null)
2364 {
2365 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2366 return;
2367 }
2368
2369 StringBuilder Msg = new StringBuilder();
2370
2371 Msg.Append("<petitionSignatureResponseMsg pid=\"");
2372 Msg.Append(XML.Encode(PetitionId));
2373 Msg.Append("\" response=\"");
2374 Msg.Append(CommonTypes.Encode(Response));
2375
2376 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
2377 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
2378 {
2379 Msg.Append("\" clientEp=\"");
2380 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
2381 }
2382
2383 Msg.Append("\" xmlns=\"");
2384 Msg.Append(NamespaceLegalIdentity(Identity.Version));
2385 Msg.Append("\">");
2386
2387 if (Response)
2388 {
2389 if (Content is null)
2390 {
2391 await e.IqErrorBadRequest(e.To, "Content missing.", "en");
2392 return;
2393 }
2394
2395 if (Signature is null)
2396 {
2397 await e.IqErrorBadRequest(e.To, "Signature missing.", "en");
2398 return;
2399 }
2400
2401 if (!Identity.ValidateSignature(Content, Signature))
2402 {
2403 await e.IqErrorBadRequest(e.To, "Signature incorrect.", "en");
2404 return;
2405 }
2406
2407 Msg.Append("<signature>");
2408 Msg.Append(SignatureStr);
2409 Msg.Append("</signature>");
2410 Identity.Serialize(Msg, false, true, true, true, true, true, true, null, this);
2411
2412 this.IdentityAuthorization(RequestorFullJid.BareJid, e.From.BareJid, LegalId, Response);
2413
2414 if (ContextXml is null &&
2415 this.IsAgentApiLoginCorrelated(RequestorFullJid.BareJid, e.From.BareJid, PetitionId, out int Timeout) &&
2416 Timeout > 0 &&
2417 Timeout <= 3600)
2418 {
2419 string RemoteEndPoint;
2420
2422 RemoteEndPoint = ClientConnection.RemoteEndPoint;
2423 else if (e.Sender is XmppS2SEndpoint XmppServerConnection)
2424 RemoteEndPoint = XmppServerConnection.RemoteEndPoint;
2425 else
2426 RemoteEndPoint = string.Empty;
2427
2428 if (!string.IsNullOrEmpty(RemoteEndPoint))
2429 LoginAuditor.Success("Successful Agent API account login.", e.From.Account, RemoteEndPoint, "XMPP");
2430
2431 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
2432 int Expires = IssuedAt + Timeout;
2433
2435 new KeyValuePair<string, object>(JwtClaims.JwtId, Convert.ToBase64String(Gateway.NextBytes(32))),
2436 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain?.Value ?? string.Empty),
2437 new KeyValuePair<string, object>(JwtClaims.Subject, e.From.BareJid),
2438 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
2439 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
2440
2441 Msg.Append("<agentApiToken xmlns='");
2443 Msg.Append("'>");
2444 Msg.Append(XML.Encode(Token));
2445 Msg.Append("</agentApiToken>");
2446 }
2447 }
2448
2449 if (!(ContextXml is null))
2450 Msg.Append(ContextXml.OuterXml);
2451
2452 Msg.Append("</petitionSignatureResponseMsg>");
2453
2454 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
2455 RequestorFullJid, string.Empty, Msg.ToString());
2456
2457 await e.IqResult(string.Empty, e.To);
2458 }
2459 catch (Exception ex)
2460 {
2461 await e.IqError(ex, e.To);
2462 }
2463 }
2464
2465 private async Task ValidateSignatureHandler(object Sender, IqEventArgs e)
2466 {
2467 try
2468 {
2469 CaseInsensitiveString Id = null;
2470 CaseInsensitiveString BareJid = null;
2471 byte[] Data = null;
2472 byte[] Signature = null;
2473 string ForBareJid = null;
2474
2475 foreach (XmlAttribute Attr in e.Query.Attributes)
2476 {
2477 switch (Attr.Name)
2478 {
2479 case "id":
2480 Id = Attr.Value;
2481 break;
2482
2483 case "bareJid":
2484 BareJid = Attr.Value;
2485 break;
2486
2487 case "data":
2488 Data = Convert.FromBase64String(Attr.Value);
2489 break;
2490
2491 case "s":
2492 Signature = Convert.FromBase64String(Attr.Value);
2493 break;
2494
2495 case "for":
2496 ForBareJid = Attr.Value;
2497 break;
2498 }
2499 }
2500
2501 if (Data is null || Signature is null)
2502 {
2503 await e.IqErrorBadRequest(e.To, "Request attributes missing.", "en");
2504 return;
2505 }
2506
2508 {
2509 await e.IqErrorBadRequest(e.To, "Both the id and bareJid attributes cannot be specified at the same time.", "en");
2510 return;
2511 }
2512
2513 LegalIdentity Identity = null;
2514
2516 {
2517 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase);
2518
2519 Identity = await GetLocalLegalIdentity(Id);
2520 if (Identity is null)
2521 {
2522 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2523 return;
2524 }
2525
2526 if (!Identity.ValidateSignature(Data, Signature))
2527 {
2528 await e.IqErrorForbidden(e.To, "signature not valid.", "en");
2529 return;
2530 }
2531 }
2532 else
2533 {
2534 CaseInsensitiveString AccountName;
2535
2537 {
2538 XmppAddress Addr = new XmppAddress(BareJid);
2539 if (!this.Server.IsServerDomain(Addr.Domain, true))
2540 {
2541 await e.IqErrorForbidden(e.To, "Bare JID does not correspond to this Trust Provider.", "en");
2542 return;
2543 }
2544
2545 AccountName = Addr.Account;
2546 }
2547 else
2548 {
2549 AccountName = e.From.Account;
2550 BareJid = e.From.BareJid;
2551 }
2552
2553 DateTime UtcNow = DateTime.UtcNow;
2554
2555 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
2556 new FilterFieldEqualTo("Account", AccountName),
2557 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
2558 new FilterFieldLesserOrEqualTo("From", UtcNow),
2559 new FilterFieldGreaterOrEqualTo("To", UtcNow)), "-Created"))
2560 {
2561 if (ID.ValidateSignature(Data, Signature))
2562 {
2563 Identity = ID;
2564 break;
2565 }
2566 }
2567
2568 if (Identity is null)
2569 {
2570 await e.IqErrorForbidden(e.To, "No matching legal identity found that can validate the signature.", "en");
2571 return;
2572 }
2573 }
2574
2575 if (!string.IsNullOrEmpty(ForBareJid))
2576 this.IdentityAuthorization(ForBareJid, BareJid, Identity.Id, true);
2577 else
2578 this.IdentityAuthorization(e.From.BareJid, BareJid, Identity.Id, true);
2579
2580 StringBuilder Xml = new StringBuilder();
2581 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
2582
2583 await e.IqResult(Xml.ToString(), e.To);
2584 }
2585 catch (Exception ex)
2586 {
2587 await e.IqError(ex, e.To);
2588 }
2589 }
2590
2591 private Task ObsoleteLegalIdentityHandler(object Sender, IqEventArgs e)
2592 {
2593 return this.ChangeStateLegalIdentityHandler(e, Identity.IdentityState.Obsoleted, true);
2594 }
2595
2596 private Task CompromiseLegalIdentityHandler(object Sender, IqEventArgs e)
2597 {
2598 return this.ChangeStateLegalIdentityHandler(e, Identity.IdentityState.Compromised, false);
2599 }
2600
2601 private async Task ChangeStateLegalIdentityHandler(IqEventArgs e, Identity.IdentityState State, bool IncludePreviews)
2602 {
2603 try
2604 {
2606 using Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase);
2607
2608 if (!this.Server.IsServerDomain(e.From.Domain, true))
2609 {
2610 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
2611 return;
2612 }
2613
2614 KeyValuePair<LegalIdentity, bool> P = await GetLocalLegalIdentity(Id, IncludePreviews);
2615 LegalIdentity Identity = P.Key;
2616 bool IsPreview = P.Value;
2617
2618 if (Identity is null)
2619 {
2620 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2621 return;
2622 }
2623
2624 if (Identity.Account != e.From.Account)
2625 {
2626 await e.IqErrorForbidden(e.To, "Only allowed to obsolete your own legal identities.", "en");
2627 return;
2628 }
2629
2630 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account);
2631 if (Account is null)
2632 {
2633 await e.IqErrorForbidden(e.To, "Account has been removed.", "en");
2634 return;
2635 }
2636
2637 if (!Account.Enabled)
2638 {
2639 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
2640 return;
2641 }
2642
2643 if (Identity.State != State)
2644 {
2645 switch (Identity.State)
2646 {
2647 case Legal.Identity.IdentityState.Created:
2648 State = Legal.Identity.IdentityState.Rejected;
2649 break;
2650
2651 case Legal.Identity.IdentityState.Rejected:
2652 await e.IqErrorForbidden(e.To, "Legal identity has been rejected, and cannot be changed.", "en");
2653 return;
2654
2655 case Legal.Identity.IdentityState.Compromised:
2656 await e.IqErrorForbidden(e.To, "Legal identity has been flagged as compromised, and cannot be changed.", "en");
2657 return;
2658 }
2659
2660 await this.UpdateState(Identity, State, new string[] { e.From.Address },
2661 Account as Account, IsPreview, true, null);
2662 }
2663
2664 StringBuilder Xml = new StringBuilder();
2665 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
2666
2667 await e.IqResult(Xml.ToString(), e.To);
2668 }
2669 catch (Exception ex)
2670 {
2671 await e.IqError(ex, e.To);
2672 }
2673 }
2674
2675 internal static async Task CopyPropertiesFromApprovedIdentity(LegalIdentity Identity, Account Account)
2676 {
2677 if (Account is null)
2678 return;
2679
2681
2682 Account.LatestIdentity = Identity.Id;
2683 Account.LatestIdentityState = Identity.State;
2684 Account.FirstName = CaseInsensitiveString.Empty;
2685 Account.MiddleNames = CaseInsensitiveString.Empty;
2686 Account.LastNames = CaseInsensitiveString.Empty;
2687 Account.PersonalNumber = CaseInsensitiveString.Empty;
2688 Account.Country = CaseInsensitiveString.Empty;
2689 Account.OrgName = CaseInsensitiveString.Empty;
2690 Account.OrgNumber = CaseInsensitiveString.Empty;
2691 Account.OrgDepartment = CaseInsensitiveString.Empty;
2692 Account.OrgRole = CaseInsensitiveString.Empty;
2693 Account.OrgCountry = CaseInsensitiveString.Empty;
2694
2695 foreach (Property P in Identity.Properties)
2696 {
2697 s = P.Value;
2699 continue;
2700
2701 switch (P.Name.Value.ToUpper())
2702 {
2704 if (Account.EMail != s)
2705 {
2706 Account.EMail = s;
2707 Account.EMailVerified = null;
2708 }
2709 break;
2710
2712 if (Account.PhoneNr != s)
2713 {
2714 Account.PhoneNr = s;
2715 Account.PhoneNrVerified = null;
2716 }
2717 break;
2718
2720 Account.FirstName = s;
2721 break;
2722
2724 Account.MiddleNames = s;
2725 break;
2726
2728 Account.LastNames = s;
2729 break;
2730
2733 out CaseInsensitiveString MiddleNames,
2734 out CaseInsensitiveString LastNames);
2735
2736 Account.FirstName = FirstName;
2737 Account.MiddleNames = MiddleNames;
2738 Account.LastNames = LastNames;
2739 break;
2740
2742 Account.PersonalNumber = s;
2743 break;
2744
2746 Account.Country = s;
2747 break;
2748
2750 Account.OrgName = s;
2751 break;
2752
2754 Account.OrgNumber = s;
2755 break;
2756
2758 Account.OrgDepartment = s;
2759 break;
2760
2762 Account.OrgRole = s;
2763 break;
2764
2766 Account.OrgCountry = s;
2767 break;
2768 }
2769 }
2770
2771 Account.Updated = DateTime.UtcNow;
2772
2773 await Database.Update(Account);
2774 }
2775
2776 internal Task UpdateState(LegalIdentity Identity, Identity.IdentityState State,
2777 string Actor, Account Account, bool Preview, bool Save, IdentityApplication Application)
2778 {
2779 return this.UpdateState(Identity, State, new string[] { Actor }, Account,
2780 Preview, Save, Application);
2781 }
2782
2783 internal async Task UpdateState(LegalIdentity Identity, Identity.IdentityState State,
2784 string[] Actors, Account Account, bool Preview, bool Save, IdentityApplication Application)
2785 {
2786 if (Identity.State != State)
2787 {
2788 bool ApprovedBefore = Identity.State == Legal.Identity.IdentityState.Approved;
2789
2790 Identity.State = State;
2791 Identity.Updated = UtcNowSecond;
2792 Identity.Sign(this);
2793
2794 if (Save)
2795 {
2796 if (Preview)
2797 await AddPreview(Identity);
2798 else
2799 await Database.Update(Identity);
2800 }
2801
2802 if (Preview)
2803 await RuntimeCounters.IncrementCounter("Legal.IDPreview." + Identity.State.ToString());
2804 else
2805 await RuntimeCounters.IncrementCounter("Legal.ID." + Identity.State.ToString());
2806
2807 string Msg = Preview
2808 ? "Legal Identity preview registration updated."
2809 : "Legal Identity registration updated.";
2810
2811 if (Actors.Length == 0)
2812 {
2813 Log.Informational(Msg, Identity.Id.Value, string.Empty,
2814 "LegalIdUpdated", Identity.GetTags(Preview));
2815 }
2816 else
2817 {
2818 foreach (string Actor in Actors)
2819 {
2820 Log.Informational(Msg, Identity.Id.Value, Actor,
2821 "LegalIdUpdated", Identity.GetTags(Preview));
2822 }
2823 }
2824
2825 bool ApprovedAfter = Identity.State == Legal.Identity.IdentityState.Approved;
2826
2827 if (!Preview && Save)
2828 {
2829 if (ApprovedBefore && ApprovedAfter)
2830 await UpdateLegalIdentityReference(Identity);
2831 else if (ApprovedBefore)
2832 await DeleteLegalIdentityReference(Identity.Id);
2833 else if (ApprovedAfter)
2834 await AddLegalIdentityReference(Identity);
2835 }
2836
2837 StringBuilder Xml = new StringBuilder();
2838 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
2839
2840 await this.Server.SendMessage(string.Empty, string.Empty, Identity.Provider,
2841 Identity.Account + "@" + Identity.Provider.Substring(this.SubdomainSuffixed.Length), string.Empty, Xml.ToString());
2842
2843 if (!(Account is null) && !Preview && Save)
2844 {
2845 if (State == Legal.Identity.IdentityState.Approved)
2846 await CopyPropertiesFromApprovedIdentity(Identity, Account);
2847 else if (Identity.Id == Account.LatestIdentity)
2848 {
2849 Account.LatestIdentityState = State;
2850 Account.Updated = DateTime.UtcNow;
2851
2852 await Database.Update(Account);
2853 }
2854 }
2855
2856 if (Application is null)
2857 {
2858 KeyValuePair<IPhoto[], XmlDocument[]> P = await GetPhotosAndDocuments(Identity, Preview);
2859 Application = new IdentityApplication(Identity.Id,
2860 NamespaceLegalIdentity(Identity.Version), Preview,
2861 GetPersonalInformation(Identity), Identity.GetTags(true, Preview),
2862 P.Key, P.Value, Account);
2863 }
2864
2865 IIdentityStatefulService[] StatefulServices = Types.FindSupport<IIdentityStatefulService, IIdentityApplication>(Application);
2866 if (StatefulServices.Length > 0)
2867 {
2869 (Security.IdentityState)(int)Identity.State);
2870
2871 foreach (IIdentityStatefulService Service in StatefulServices)
2872 {
2873 try
2874 {
2875 await Service.ApplicationUpdated(CurrentState);
2876 }
2877 catch (Exception ex)
2878 {
2879 Log.Exception(ex);
2880 }
2881 }
2882 }
2883 }
2884 }
2885
2892 {
2893 return this.GetApprovedLegalIdentityAsync(Account, DateTime.Now);
2894 }
2895
2902 public async Task<LegalIdentity> GetApprovedLegalIdentityAsync(CaseInsensitiveString Account, DateTime Timestamp)
2903 {
2904 LegalIdentity Result = null;
2905
2906 Timestamp = Timestamp.ToUniversalTime();
2907
2908 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
2909 new FilterFieldEqualTo("Account", Account),
2910 new FilterFieldEqualTo("State", Identity.IdentityState.Approved))))
2911 {
2912 if (ID.From.ToUniversalTime() <= Timestamp &&
2913 ID.To.ToUniversalTime() >= Timestamp &&
2914 (Result is null || ID.Created.ToUniversalTime() > Result.Created.ToUniversalTime()))
2915 {
2916 Result = ID;
2917 }
2918 }
2919
2920 return Result;
2921 }
2922
2923 private async Task AddLegalIdAttachmentHandler(object Sender, IqEventArgs e)
2924 {
2925 try
2926 {
2928 string GetUrl = XML.Attribute(e.Query, "getUrl");
2929 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
2930 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
2931 string Namespace = NamespaceLegalIdentity(QueryVersion);
2932
2933 using Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase);
2934
2935 if (!this.Server.IsServerDomain(e.From.Domain, true))
2936 {
2937 await e.IqErrorForbidden(e.To, "Only accounts on the broker can add attachments.", "en");
2938 return;
2939 }
2940
2941 if (!Uri.TryCreate(GetUrl, UriKind.Absolute, out Uri GetUri))
2942 {
2943 await e.IqErrorBadRequest(e.To, "Invalid Get URL.", "en");
2944 return;
2945 }
2946
2947 IAccount Account = await XmppServerModule.GetAccountAsync(e.From.Account);
2948 if (Account is null)
2949 {
2950 await e.IqErrorForbidden(e.To, "Account not found.", "en");
2951 return;
2952 }
2953
2954 if (!Account.Enabled)
2955 {
2956 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
2957 return;
2958 }
2959
2960 if (!(Account is Account Account2))
2961 {
2962 await e.IqErrorForbidden(e.To, "Forbidden to add attachments.", "en");
2963 return;
2964 }
2965
2966 KeyValuePair<LegalIdentity, bool> P = await GetLocalLegalIdentity(Id, true);
2967 LegalIdentity Identity = P.Key;
2968 bool Preview = P.Value;
2969
2970 if (Identity is null)
2971 {
2972 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2973 return;
2974 }
2975
2976 if (Identity.Account != Account.UserName)
2977 {
2978 await e.IqErrorForbidden(e.To, "Only allowed to add attachments to your own legal identities.", "en");
2979 return;
2980 }
2981
2982 string PreapprovedAttachment = null;
2983 string PreapprovedAttachmentKey = null;
2984 bool PreapprovedAttachmentValid = false;
2985 string ReviewKey = Identity.Id + "|identityReview";
2986
2987 using ContentStreamResponse P2 = await LocalContent.GetTempStreamAsync(GetUri);
2988 if (P2.HasError)
2989 {
2990 if (!(P2.Encoded is null))
2991 await P2.Encoded.DisposeAsync();
2992
2993 await e.IqError(P2.Error, e.To);
2994 return;
2995 }
2996
2997 string ContentType = P2.ContentType;
2998 using TemporaryStream File = P2.Encoded;
2999
3000 if (this.remoteComponents.TryGetValue(ReviewKey, out object Obj) &&
3001 Obj is string IdentityReview &&
3002 XML.IsValidXml(IdentityReview))
3003 {
3004 if (ContentType.StartsWith(XmlCodec.DefaultContentType) ||
3006 {
3007 PreapprovedAttachment = IdentityReview;
3008 PreapprovedAttachmentKey = ReviewKey;
3009 PreapprovedAttachmentValid = true;
3010 }
3011 }
3012
3013 if (Identity.State != Legal.Identity.IdentityState.Created)
3014 {
3015 if (!PreapprovedAttachmentValid)
3016 {
3017 await e.IqErrorForbidden(e.To, "Attachments can only be added to newly created identities before they are approved.", "en");
3018 return;
3019 }
3020 }
3021
3022 if (!(Identity.Attachments is null))
3023 {
3024 string s = Convert.ToBase64String(Signature);
3025
3026 foreach (AttachmentReference Ref in Identity.Attachments)
3027 {
3028 if (Convert.ToBase64String(Ref.Signature) == s)
3029 {
3030 await e.IqErrorForbidden(e.To, "Attachment already assigned to identity.", "en");
3031 return;
3032 }
3033 }
3034 }
3035
3036 File.Position = 0;
3037 if (!Identity.ValidateSignature(File, Signature))
3038 {
3039 await e.IqErrorForbidden(e.To, "Attachment signature is invalid.", "en");
3040 return;
3041 }
3042
3043 XmlDocument Doc;
3044 bool IncNrPeerReviews = false;
3045 StringBuilder sb = new StringBuilder();
3046 List<LegalIdentity> Reviewers = null;
3047
3048 if (ContentType.StartsWith(XmlCodec.DefaultContentType) ||
3050 {
3051 File.Position = 0;
3052
3053 Doc = new XmlDocument()
3054 {
3055 PreserveWhitespace = true
3056 };
3057 Doc.Load(File);
3058
3059 if (Doc.DocumentElement.LocalName == "identityReview" &&
3060 Doc.DocumentElement.NamespaceURI == Namespace)
3061 {
3062 if (string.IsNullOrEmpty(PreapprovedAttachment))
3063 {
3064 await e.IqErrorForbidden(e.To, "Identity Review not expected.", "en");
3065 return;
3066 }
3067
3068 if (XML.NormalizeXml(Doc.DocumentElement) != XML.NormalizeXml(PreapprovedAttachment))
3069 {
3070 await e.IqErrorForbidden(e.To, "Identity Review manipulated.", "en");
3071 return;
3072 }
3073
3074 this.remoteComponents?.Remove(PreapprovedAttachmentKey);
3075 }
3076 else if (Doc.DocumentElement.LocalName == "peerReview" &&
3077 Doc.DocumentElement.NamespaceURI == Namespace)
3078 {
3079 LegalIdentity ReviewedIdentity = null;
3080 LegalIdentity ReviewerIdentity = null;
3083 bool ReviewedHasStatus = false;
3084 bool ReviewerHasStatus = false;
3085 byte[] PeerSignature = Convert.FromBase64String(XML.Attribute(Doc.DocumentElement, "s"));
3086 DateTime TP = XML.Attribute(Doc.DocumentElement, "tp", DateTime.Now);
3087 byte[] SignedIdentity = null;
3088 DateTime UtcNow = DateTime.UtcNow;
3089
3090 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
3091 {
3092 if (!(N is XmlElement E) || N.NamespaceURI != Namespace)
3093 continue;
3094
3095 foreach (XmlNode N2 in E.ChildNodes)
3096 {
3097 if (N2 is XmlElement E2 &&
3098 E2.LocalName == "identity" &&
3099 E2.NamespaceURI == Namespace)
3100 {
3101 switch (E.LocalName)
3102 {
3103 case "reviewed":
3104 ReviewedIdentity = LegalIdentity.Parse(E2, out ReviewedHasStatus,
3105 out Dictionary<string, string> ReviewedIdentityAttachmentsUrls);
3106
3107 sb.Clear();
3108 ReviewedIdentity.Serialize(sb, true, true, true, false, false, false, false, null, this);
3109 string s1 = sb.ToString();
3110
3111 sb.Clear();
3112 Identity.Serialize(sb, true, true, true, false, false, false, false, null, this);
3113 string s2 = sb.ToString();
3114
3115 if (s1 != s2)
3116 {
3117 await e.IqErrorForbidden(e.To, "Reviewed identity mismatch.", "en");
3118 return;
3119 }
3120
3121 sb.Clear();
3122 ReviewedIdentity.Serialize(sb, true, true, true, true, true, true, true, ReviewedIdentityAttachmentsUrls, this);
3123 SignedIdentity = Encoding.UTF8.GetBytes(sb.ToString());
3124 break;
3125
3126 case "reviewer":
3127 ReviewerIdentity = LegalIdentity.Parse(E2, out ReviewerHasStatus,
3128 out Dictionary<string, string> ReviewerIdentityAttachmentsUrls);
3129
3130 sb.Clear();
3131 ReviewerIdentity.Serialize(sb, false, false, false, false, false, false, false, null, this);
3132 byte[] Data = Encoding.UTF8.GetBytes(sb.ToString());
3133 ReviewerJid = ReviewerIdentity[PersonalInformation.JidTag];
3134
3135 if (!CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
3136 {
3137 (ReviewerIdentity, _) = await this.ValidateSignature(new XmppAddress(ReviewerJid), TP, Data,
3138 ReviewerIdentity.ClientSignature);
3139
3140 if (ReviewerIdentity is null || ReviewerJid != ReviewerIdentity[PersonalInformation.JidTag])
3141 {
3142 await e.IqErrorForbidden(e.To, "Peer identity signature invalid.", "en");
3143 return;
3144 }
3145 }
3146
3147 if (ReviewerIdentity.State != Legal.Identity.IdentityState.Approved)
3148 {
3149 await e.IqErrorForbidden(e.To, "Reviewer identity not approved.", "en");
3150 return;
3151 }
3152
3153 if (UtcNow < ReviewerIdentity.From.ToUniversalTime() ||
3154 UtcNow > ReviewerIdentity.To.ToUniversalTime())
3155 {
3156 await e.IqErrorForbidden(e.To, "Reviewer identity not valid.", "en");
3157 return;
3158 }
3159
3160 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerIdentity.Provider))
3161 {
3162 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a provider.", "en");
3163 return;
3164 }
3165
3166 if (string.IsNullOrEmpty(ReviewerIdentity.ClientKeyName) ||
3167 ReviewerIdentity.ClientPubKey is null ||
3168 ReviewerIdentity.ClientPubKey.Length == 0)
3169 {
3170 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a key.", "en");
3171 return;
3172 }
3173
3174 if (ReviewerIdentity.ClientSignature is null ||
3175 ReviewerIdentity.ClientSignature.Length == 0)
3176 {
3177 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a signature.", "en");
3178 return;
3179 }
3180
3181 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
3182 {
3183 await e.IqErrorForbidden(e.To, "Reviewer identity not trusted for reviewing.", "en");
3184 return;
3185 }
3186 break;
3187 }
3188 }
3189 }
3190 }
3191
3192 if (ReviewedIdentity is null || !ReviewedHasStatus ||
3193 ReviewerIdentity is null || !ReviewerHasStatus ||
3194 SignedIdentity is null)
3195 {
3196 await e.IqErrorForbidden(e.To, "Attachment not a correctly formed peer-review document.", "en");
3197 return;
3198 }
3199
3201
3202 if (Pnr == ReviewerIdentity[PersonalInformation.PersonalNumberTag] && !CaseInsensitiveString.IsNullOrEmpty(Pnr))
3203 {
3204 await e.IqErrorForbidden(e.To, "Reviewer cannot be the same person as the reviewed person.", "en");
3205 return;
3206 }
3207
3208 if (!ReviewerIdentity.ValidateSignature(SignedIdentity, PeerSignature))
3209 {
3210 await e.IqErrorForbidden(e.To, "Peer signature invalid.", "en");
3211 return;
3212 }
3213
3215 {
3216 int NrPhotos = 0;
3217
3218 if (!(Identity.Attachments is null))
3219 {
3220 foreach (AttachmentReference Ref in Identity.Attachments)
3221 {
3222 if (Ref.ContentType.StartsWith("image/"))
3223 NrPhotos++;
3224 }
3225 }
3226
3228 {
3229 await e.IqErrorForbidden(e.To, "Peer review not accepted: Identity lacks sufficient photos (" +
3230 PeerReviewConfiguration.Instance.NrPhotosRequired.ToString() + ").", "en");
3231 return;
3232 }
3233
3234 PersonalInformation RI = GetPersonalInformation(ReviewerIdentity);
3235
3236 if (!PeerReviewConfiguration.Instance.IsReviewerAllowed(RI.Jid, ReviewerIdentity.Id))
3237 {
3238 await e.IqErrorBadRequest(e.To, "Reviewer not white-listed. Peer review not accepted.", "en");
3239 return;
3240 }
3241
3243 {
3244 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a First name. Peer review not accepted.", "en");
3245 return;
3246 }
3247
3249 {
3250 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Middle name. Peer review not accepted.", "en");
3251 return;
3252 }
3253
3255 {
3256 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Last name. Peer review not accepted.", "en");
3257 return;
3258 }
3259
3261 {
3262 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Personal number. Peer review not accepted.", "en");
3263 return;
3264 }
3265
3267 {
3268 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an address. Peer review not accepted.", "en");
3269 return;
3270 }
3271
3273 {
3274 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Postal Code (ZIP). Peer review not accepted.", "en");
3275 return;
3276 }
3277
3279 {
3280 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an Area. Peer review not accepted.", "en");
3281 return;
3282 }
3283
3285 {
3286 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a City. Peer review not accepted.", "en");
3287 return;
3288 }
3289
3291 {
3292 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Region. Peer review not accepted.", "en");
3293 return;
3294 }
3295
3297 {
3298 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Country. Peer review not accepted.", "en");
3299 return;
3300 }
3301
3303 {
3305 {
3306 await e.IqErrorBadRequest(e.To, "Reviewer Country is not an ISO 3166-1 Country Code. Peer review not accepted.", "en");
3307 return;
3308 }
3309
3311 {
3312 bool? Valid = await PersonalNumberSchemes.IsValid(RI.Country, RI.PersonalNumber);
3313 if (Valid.HasValue && !Valid.Value)
3314 {
3315 await e.IqErrorBadRequest(e.To, "The personal number format used by the reviewer does not comply with regulations.", "en");
3316 return;
3317 }
3318 }
3319 }
3320
3322 {
3323 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Nationality. Peer review not accepted.", "en");
3324 return;
3325 }
3326
3328 {
3329 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Gender. Peer review not accepted.", "en");
3330 return;
3331 }
3332
3334 {
3335 if (!RI.HasBirthDate)
3336 {
3337 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Birth Date. Peer review not accepted.", "en");
3338 return;
3339 }
3340
3341 DateTime BirthDate = RI.BirthDate.Value;
3342
3343 if (RI.Age < 18)
3344 {
3345 await e.IqErrorBadRequest(e.To, "Reviewer identity too young. Peer review not accepted.", "en");
3346 return;
3347 }
3348 }
3349
3350 if (RI.HasOrg)
3351 {
3352 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgName)) // TODO: Configurable
3353 {
3354 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization name. Peer review not accepted.", "en");
3355 return;
3356 }
3357
3358 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgDepartment)) // TODO: Configurable
3359 {
3360 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a department. Peer review not accepted.", "en");
3361 return;
3362 }
3363
3364 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgRole)) // TODO: Configurable
3365 {
3366 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a role. Peer review not accepted.", "en");
3367 return;
3368 }
3369
3370 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgNumber)) // TODO: Configurable
3371 {
3372 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization number. Peer review not accepted.", "en");
3373 return;
3374 }
3375
3377 {
3378 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization address. Peer review not accepted.", "en");
3379 return;
3380 }
3381
3383 {
3384 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Postal Code (ZIP). Peer review not accepted.", "en");
3385 return;
3386 }
3387
3389 {
3390 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Area. Peer review not accepted.", "en");
3391 return;
3392 }
3393
3395 {
3396 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization City. Peer review not accepted.", "en");
3397 return;
3398 }
3399
3401 {
3402 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Region. Peer review not accepted.", "en");
3403 return;
3404 }
3405
3407 {
3408 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Country. Peer review not accepted.", "en");
3409 return;
3410 }
3411
3413 {
3415 {
3416 await e.IqErrorBadRequest(e.To, "Reviewer organization Country is not an ISO 3166-1 Country Code. Peer review not accepted.", "en");
3417 return;
3418 }
3419 }
3420 }
3421
3422 if (!await CheckPeerReviewFields(e, ReviewedIdentity))
3423 return;
3424
3425 PersonalInformation PI = GetPersonalInformation(ReviewedIdentity);
3426
3427 if (PI.HasOrg)
3428 {
3429 if (!RI.HasOrg)
3430 {
3431 await e.IqErrorBadRequest(e.To, "Peer reviewer lacks organization information. Peer review not accepted.", "en");
3432 return;
3433 }
3434
3435 if (PI.OrgName != RI.OrgName ||
3436 PI.OrgNumber != RI.OrgNumber ||
3437 PI.OrgCountry != RI.OrgCountry)
3438 {
3439 await e.IqErrorBadRequest(e.To, "Peer reviewer must be from same company. Peer review not accepted.", "en");
3440 return;
3441 }
3442 }
3443
3444 if (!(Identity.Attachments is null))
3445 {
3446 LegalIdentity[] Reviewers2 = await GetPeerReviewers(Identity);
3447
3448 foreach (LegalIdentity ReviewerID in Reviewers2)
3449 {
3450 if (ReviewerID[PersonalInformation.PersonalNumberTag] == Pnr)
3451 {
3452 await e.IqErrorBadRequest(e.To, "A reviewer can only review the application once.", "en");
3453 return;
3454 }
3455 }
3456
3457 Reviewers ??= new List<LegalIdentity>();
3458 Reviewers.AddRange(Reviewers2);
3459 }
3460 }
3461
3462 Reviewers ??= new List<LegalIdentity>();
3463 Reviewers.Add(ReviewerIdentity);
3464 IncNrPeerReviews = true;
3465 }
3466 }
3467 else
3468 {
3469 Doc = null;
3470
3471 // Check again to avoid malicious user to add attachment after identity created.
3472 if (Identity.State != Legal.Identity.IdentityState.Created)
3473 {
3474 await e.IqErrorForbidden(e.To, "Attachments can only be added to newly created identities before they are approved.", "en");
3475 return;
3476 }
3477 }
3478
3479 List<AttachmentReference> References = new List<AttachmentReference>();
3480 bool Approved = false;
3481
3482 if (!(Identity.Attachments is null))
3483 References.AddRange(Identity.Attachments);
3484
3485 KeyValuePair<Attachment, AttachmentReference> A = await this.CreateAttachment(
3486 GetUri, Identity, Signature, File, ContentType, Account2, null,
3487 Identity.To.ToUniversalTime(), Preview);
3488
3489 References.Add(A.Value);
3490
3491 Identity.Attachments = References.ToArray();
3492 Identity.Updated = UtcNowSecond;
3493
3494 if (IncNrPeerReviews)
3495 {
3496 Identity.NrPeerReviews++;
3497
3499 Identity.NrPeerReviews >= (PeerReviewConfiguration.Instance?.NrReviewersToApprove ?? 2) &&
3500 Identity.State == Legal.Identity.IdentityState.Created)
3501 {
3502 Identity.State = Legal.Identity.IdentityState.Approved;
3503 Approved = true;
3504
3505 if (!Preview &&
3507 {
3508 StringBuilder Markdown = new StringBuilder();
3509
3510 Markdown.AppendLine("Peer reviewed Legal ID approved");
3511 Markdown.AppendLine("===================================");
3512 Markdown.AppendLine();
3513
3514 this.AppendMarkdown(Markdown, Identity, "Applicant");
3515
3516 int Index = 0;
3517
3518 foreach (LegalIdentity Reviewer in Reviewers)
3519 {
3520 Index++;
3521 this.AppendMarkdown(Markdown, Reviewer, "Reviewer " + Index.ToString());
3522 }
3523
3524 await Gateway.SendNotification(Markdown.ToString());
3525 }
3526 }
3527 }
3528
3529 Identity.Sign(this);
3530
3531 if (Preview)
3532 {
3533 await AddPreview(Identity);
3534 await RuntimeCounters.IncrementCounter("Legal.IDPreview." + Identity.State.ToString());
3535 }
3536 else
3537 {
3538 await Database.Update(Identity);
3539 await RuntimeCounters.IncrementCounter("Legal.ID." + Identity.State.ToString());
3540
3541 if (Identity.State == Legal.Identity.IdentityState.Approved)
3542 await CopyPropertiesFromApprovedIdentity(Identity, Account as Account);
3543
3544 Log.Informational("Attachment added to Legal Identity registration.",
3545 Identity.Id.Value, e.From.Address.Value,
3546 "LegalIdUpdated", Identity.GetTags());
3547 }
3548
3549 sb.Clear();
3550 Identity.Serialize(sb, true, true, true, true, true, true, true, null, this);
3551 string Xml = sb.ToString();
3552
3553 if (!Preview && Approved)
3554 await AddLegalIdentityReference(Identity);
3555
3556 if (Approved || Identity.State == Legal.Identity.IdentityState.Approved)
3557 {
3558 XmppAddress IdentityAddress = new XmppAddress(Identity.Id);
3559 int i = IdentityAddress.Domain.IndexOf('.');
3560 string JidDomain = i < 0 ? this.Server.Domain : IdentityAddress.Domain.Substring(i + 1);
3561
3562 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
3563 new XmppAddress(Identity.Account + "@" + this.Server.Domain), string.Empty, Xml);
3564 }
3565
3566 await e.IqResult(Xml, e.To);
3567 }
3568 catch (Exception ex)
3569 {
3570 await e.IqError(ex, e.To);
3571 }
3572 }
3573
3574 internal static async Task<bool> CheckPeerReviewFields(IqEventArgs e, LegalIdentity Identity)
3575 {
3577 {
3578 PersonalInformation PI = GetPersonalInformation(Identity);
3579
3581 {
3582 await e.IqErrorBadRequest(e.To, "First name is a required field.", "en");
3583 return false;
3584 }
3585
3587 {
3588 await e.IqErrorBadRequest(e.To, "Middle name is a required field.", "en");
3589 return false;
3590 }
3591
3593 {
3594 await e.IqErrorBadRequest(e.To, "Last name is a required field.", "en");
3595 return false;
3596 }
3597
3599 {
3600 await e.IqErrorBadRequest(e.To, "Personal Number is a required field.", "en");
3601 return false;
3602 }
3603
3605 {
3606 await e.IqErrorBadRequest(e.To, "Address is a required field.", "en");
3607 return false;
3608 }
3609
3611 {
3612 await e.IqErrorBadRequest(e.To, "Postal Code (ZIP) is a required field.", "en");
3613 return false;
3614 }
3615
3617 {
3618 await e.IqErrorBadRequest(e.To, "Area is a required field.", "en");
3619 return false;
3620 }
3621
3623 {
3624 await e.IqErrorBadRequest(e.To, "City is a required field.", "en");
3625 return false;
3626 }
3627
3629 {
3630 await e.IqErrorBadRequest(e.To, "Region is a required field.", "en");
3631 return false;
3632 }
3633
3635 {
3636 await e.IqErrorBadRequest(e.To, "Country is a required field.", "en");
3637 return false;
3638 }
3639
3641 {
3643 {
3644 await e.IqErrorBadRequest(e.To, "Country must be a ISO 3166-1 Country Code.", "en");
3645 return false;
3646 }
3647
3649 {
3650 bool? Valid = await PersonalNumberSchemes.IsValid(PI.Country, PI.PersonalNumber);
3651 if (Valid.HasValue && !Valid.Value)
3652 {
3653 await e.IqErrorBadRequest(e.To, "The personal number format does not comply with regulations in your country.", "en");
3654 return false;
3655 }
3656 }
3657 }
3658
3660 {
3661 await e.IqErrorBadRequest(e.To, "Nationality is a required field.", "en");
3662 return false;
3663 }
3664
3666 {
3667 await e.IqErrorBadRequest(e.To, "Gender is a required field.", "en");
3668 return false;
3669 }
3670
3672 {
3673 await e.IqErrorBadRequest(e.To, "Birth Date is a required field.", "en");
3674 return false;
3675 }
3676
3677 if (PI.HasOrg)
3678 {
3679 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgName)) // TODO: Make configurable
3680 {
3681 await e.IqErrorBadRequest(e.To, "Organization name is a required field for work identities.", "en");
3682 return false;
3683 }
3684
3685 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgDepartment)) // TODO: Make configurable
3686 {
3687 await e.IqErrorBadRequest(e.To, "Department is a required field for work identities.", "en");
3688 return false;
3689 }
3690
3691 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgRole)) // TODO: Make configurable
3692 {
3693 await e.IqErrorBadRequest(e.To, "Role is a required field for work identites.", "en");
3694 return false;
3695 }
3696
3698 {
3699 await e.IqErrorBadRequest(e.To, "Organization Number is a required field for work identites.", "en");
3700 return false;
3701 }
3702
3704 {
3705 await e.IqErrorBadRequest(e.To, "Organization Address is a required field for work identites.", "en");
3706 return false;
3707 }
3708
3710 {
3711 await e.IqErrorBadRequest(e.To, "Organization Postal Code (ZIP) is a required field for work identites.", "en");
3712 return false;
3713 }
3714
3716 {
3717 await e.IqErrorBadRequest(e.To, "Organization Area is a required field for work identites.", "en");
3718 return false;
3719 }
3720
3722 {
3723 await e.IqErrorBadRequest(e.To, "Organization City is a required field for work identites.", "en");
3724 return false;
3725 }
3726
3728 {
3729 await e.IqErrorBadRequest(e.To, "Organization Region is a required field for work identites.", "en");
3730 return false;
3731 }
3732
3734 {
3735 await e.IqErrorBadRequest(e.To, "Organization Country is a required field for work identites.", "en");
3736 return false;
3737 }
3738
3740 {
3742 {
3743 await e.IqErrorBadRequest(e.To, "Organization Country must be a ISO 3166-1 Country Code.", "en");
3744 return false;
3745 }
3746 }
3747 }
3748 }
3749
3750 return true;
3751 }
3752
3753 internal static async Task<LegalIdentity[]> GetPeerReviewers(LegalIdentity Identity)
3754 {
3755 List<LegalIdentity> Result = new List<LegalIdentity>();
3756 string Namespace = NamespaceLegalIdentity(Identity.Version);
3757
3758 if (!(Identity?.Attachments is null))
3759 {
3760 foreach (AttachmentReference Ref in Identity.Attachments)
3761 {
3762 if (Ref.ContentType.StartsWith(XmlCodec.DefaultContentType) ||
3763 Ref.ContentType.StartsWith(XmlCodec.SchemaContentType))
3764 {
3765 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
3766 if (Attachment is null)
3767 continue;
3768
3769 using FileStream AttachmentFile = File.OpenRead(Attachment.LocalFileName);
3770 Aes Aes = Aes.Create();
3771
3772 Aes.BlockSize = 128;
3773 Aes.KeySize = 256;
3774 Aes.Mode = CipherMode.CBC;
3775 Aes.Padding = PaddingMode.Zeros;
3776
3777 byte[] Key = new byte[32];
3778 byte[] IV = new byte[16];
3779
3780 Buffer.BlockCopy(Attachment.Salt, 0, Key, 0, 32);
3781 Buffer.BlockCopy(Attachment.Salt, 32, IV, 0, 16);
3782
3783 using ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV);
3784 using CryptoStream DecryptedAttachmentFile = new CryptoStream(AttachmentFile, Decryptor, CryptoStreamMode.Read);
3785 XmlDocument Doc = new XmlDocument()
3786 {
3787 PreserveWhitespace = true
3788 };
3789 Doc.Load(DecryptedAttachmentFile);
3790
3791 if (Doc.DocumentElement.LocalName == "peerReview" &&
3792 Doc.DocumentElement.NamespaceURI == Namespace)
3793 {
3794 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
3795 {
3796 if (N is XmlElement E &&
3797 E.LocalName == "reviewer" &&
3798 E.NamespaceURI == Namespace)
3799 {
3800 foreach (XmlNode N2 in E.ChildNodes)
3801 {
3802 if (N2 is XmlElement E2 &&
3803 E2.LocalName == "identity" &&
3804 E2.NamespaceURI == Namespace)
3805 {
3806 LegalIdentity ReviewerID = LegalIdentity.Parse(E2, out _, out _);
3807 Result.Add(ReviewerID);
3808 }
3809 }
3810 }
3811 }
3812 }
3813 }
3814 }
3815 }
3816
3817 return Result.ToArray();
3818 }
3819
3820 internal void AppendMarkdown(StringBuilder Markdown, LegalIdentity Identity, string Title)
3821 {
3822 Markdown.AppendLine(Title);
3823 Markdown.AppendLine(new string('-', Title.Length + 3));
3824 Markdown.AppendLine();
3825
3826 Markdown.Append("| `");
3827 Markdown.Append(Identity.Id);
3828 Markdown.AppendLine("` ||");
3829 Markdown.AppendLine("|:----|:----|");
3830
3831 foreach (Property P in Identity.Properties)
3832 {
3833 Markdown.Append("| `");
3834 Markdown.Append(P.Name);
3835 Markdown.Append("` | ");
3836 Markdown.Append(MarkdownDocument.Encode(P.Value));
3837 Markdown.AppendLine(" |");
3838 }
3839
3840 Markdown.AppendLine();
3841 }
3842
3843 private Task<KeyValuePair<Attachment, AttachmentReference>> CreateAttachment(Uri GetUri,
3844 LegalIdentity UploadingIdentity, byte[] Signature, TemporaryStream File, string ContentType,
3845 Account Account, CaseInsensitiveString ContractId, DateTime ExpiresUtc, bool Preview)
3846 {
3847 return CreateAttachment(Path.GetFileName(GetUri.AbsolutePath), UploadingIdentity,
3848 Signature, File, ContentType, Account, ContractId, this.attachmentsFolder, ExpiresUtc, Preview);
3849 }
3850
3851 internal static string GetFolderName(string AttachmentsFolder, out DateTime Timestamp)
3852 {
3853 Timestamp = DateTime.UtcNow;
3854 string Folder = Path.Combine(AttachmentsFolder, Timestamp.Year.ToString("D4"),
3855 Timestamp.Month.ToString("D2"), Timestamp.Day.ToString("D2"));
3856
3857 if (!Directory.Exists(Folder))
3858 Directory.CreateDirectory(Folder);
3859
3860 return Folder;
3861 }
3862
3863 internal static async Task<KeyValuePair<Attachment, AttachmentReference>> CreateAttachment(string RemoteFileName,
3864 LegalIdentity UploadingIdentity, byte[] Signature, TemporaryStream File, string ContentType,
3866 DateTime ExpiresUtc, bool Preview)
3867 {
3868 byte[] Salt;
3869 DateTime Timestamp;
3870 string Folder;
3871
3872 if (Preview)
3873 {
3874 Timestamp = DateTime.UtcNow;
3875 Folder = GetPreviewFolder();
3876 Salt = null;
3877 }
3878 else
3879 {
3880 Folder = GetFolderName(AttachmentsFolder, out Timestamp);
3881 Salt = Gateway.NextBytes(48);
3882 }
3883
3885 {
3888 ContractId = ContractId,
3889 RemoteFileName = RemoteFileName,
3891 Timestamp = Timestamp,
3892 UploaderLegalId = UploadingIdentity.Id,
3893 Size = File.Length,
3894 Salt = Salt
3895 };
3896
3897 if (Preview)
3898 Attachment.ObjectId = Guid.NewGuid().ToString();
3899 else
3900 await Database.Insert(Attachment);
3901
3902 string AttachmentId = Attachment.ObjectId;
3903 string FileName = Preview ? GetPreviewFileName(AttachmentId, ".att") :
3904 Path.Combine(Folder, AttachmentId + ".bin");
3905
3906 Attachment.Id = AttachmentId + "@" + UploadingIdentity.Provider;
3907 Attachment.LocalFileName = FileName;
3908
3909 File.Position = 0;
3910
3911 if (Preview)
3912 {
3913 Salt = await CreatePreviewSalt(FileName, Account.UserName);
3914 Attachment.Salt = Salt;
3915
3916 using Stream Blob = await XmppServerModule.EncodeBlob(File);
3917
3918 await XmppServerModule.SaveEncryptedFile(FileName, Salt, Blob);
3919 }
3920 else
3921 {
3922 await Database.Update(Attachment);
3923
3924 byte[] Key = new byte[32];
3925 byte[] IV = new byte[16];
3926
3927 Buffer.BlockCopy(Salt, 0, Key, 0, 32);
3928 Buffer.BlockCopy(Salt, 32, IV, 0, 16);
3929
3930 using FileStream AttachmentFile = System.IO.File.Create(FileName);
3931 Aes Aes = Aes.Create();
3932
3933 Aes.BlockSize = 128;
3934 Aes.KeySize = 256;
3935 Aes.Mode = CipherMode.CBC;
3936 Aes.Padding = PaddingMode.Zeros;
3937
3938 using ICryptoTransform Encryptor = Aes.CreateEncryptor(Key, IV);
3939 using CryptoStream EncryptedAttachmentFile = new CryptoStream(AttachmentFile, Encryptor, CryptoStreamMode.Write);
3940
3941 await File.CopyToAsync(EncryptedAttachmentFile);
3942 EncryptedAttachmentFile.FlushFinalBlock();
3943
3945 {
3946 AttachmentId = Attachment.Id,
3947 Created = DateTime.UtcNow,
3948 Expires = ExpiresUtc,
3950 LocalAttachmentId = Attachment.ObjectId,
3951 FileName = Attachment.LocalFileName,
3952 Size = Attachment.Size,
3953 Salt = Salt
3954 };
3955
3956 await Database.Insert(CacheItem);
3957 }
3958
3960 {
3962 FileName = Attachment.RemoteFileName,
3963 Id = Attachment.Id,
3964 LegalId = Attachment.UploaderLegalId,
3966 Timestamp = Timestamp,
3967 Preview = Preview
3968 };
3969
3970 return new KeyValuePair<Attachment, AttachmentReference>(Attachment, Ref);
3971 }
3972
3973 private async Task RemoveLegalIdAttachmentHandler(object Sender, IqEventArgs e)
3974 {
3975 try
3976 {
3977 CaseInsensitiveString AttachmentId = XML.Attribute(e.Query, "attachmentId");
3978
3979 if (!this.Server.IsServerDomain(e.From.Domain, true))
3980 {
3981 await e.IqErrorForbidden(e.To, "Only accounts on the broker can remove attachments.", "en");
3982 return;
3983 }
3984
3985 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", AttachmentId));
3986 if (Attachment is null)
3987 {
3988 await e.IqErrorItemNotFound(e.To, "Attachment not found.", "en");
3989 return;
3990 }
3991
3993 {
3994 await e.IqErrorItemNotFound(e.To, "Attachment not assigned to legal identity.", "en");
3995 return;
3996 }
3997
3999 LegalIdentity Identity = await GetLocalLegalIdentity(Attachment.UploaderLegalId);
4000 if (Identity is null)
4001 {
4002 await e.IqErrorItemNotFound(e.To, "Associated Legal identity not found.", "en");
4003 return;
4004 }
4005
4006 if (Identity.Account != e.From.Account)
4007 {
4008 await e.IqErrorForbidden(e.To, "Only allowed to remove attachments from your own legal identities.", "en");
4009 return;
4010 }
4011
4012 if (Identity.State != Legal.Identity.IdentityState.Created)
4013 {
4014 await e.IqErrorForbidden(e.To, "Attachments can only be removed from newly created identities before they are approved.", "en");
4015 return;
4016 }
4017
4018 if (File.Exists(Attachment.LocalFileName))
4019 File.Delete(Attachment.LocalFileName);
4020
4021 if (!(Identity.Attachments is null))
4022 {
4023 List<AttachmentReference> Attachments = new List<AttachmentReference>();
4024
4025 foreach (AttachmentReference Ref in Identity.Attachments)
4026 {
4027 if (Ref.Id != AttachmentId)
4028 Attachments.Add(Ref);
4029 }
4030
4031 Identity.Attachments = Attachments.ToArray();
4032 Identity.Sign(this);
4033
4034 await Database.Update(Identity);
4035 }
4036
4037 await Database.Delete(Attachment);
4038
4039 StringBuilder Xml = new StringBuilder();
4040 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
4041
4042 await e.IqResult(Xml.ToString(), e.To);
4043 }
4044 catch (Exception ex)
4045 {
4046 await e.IqError(ex, e.To);
4047 }
4048 }
4049
4050 internal async Task<KeyValuePair<byte[], string>> GetAttachment(string AttachmentId,
4051 string AttachmentUrl, DateTime Expires)
4052 {
4053 Expires = Expires.ToUniversalTime();
4054
4055 AttachmentCacheItem Item = await AttachmentCache.TryGetAttachment(AttachmentId);
4056 string ContentType;
4057 byte[] Data;
4058
4059 if (!(Item is null))
4060 {
4061 if (Expires > Item.Expires)
4062 {
4063 Item.Expires = Expires;
4064 await Database.Update(Item);
4065 }
4066
4067 Data = await Item.LoadContent();
4068 return new KeyValuePair<byte[], string>(Data, Item.ContentType);
4069 }
4070
4071 int i = AttachmentId.IndexOf('@');
4072
4073 if (i > 0)
4074 {
4075 string AttachmentDomain = AttachmentId[(i + 1)..];
4076 AttachmentId = AttachmentId[..i];
4077
4078 if (!this.IsComponentDomain(AttachmentDomain, true))
4079 {
4080 KeyValuePair<string, TemporaryFile> P = await Gateway.ContractsClient.GetAttachmentAsync(AttachmentUrl,
4081 Networking.XMPP.Contracts.SignWith.LatestApprovedId, 30000);
4082 using TemporaryFile File = P.Value;
4083
4084 File.Position = 0;
4085
4086 ContentType = P.Key;
4087 Data = await File.ReadAllAsync();
4088
4089 await AttachmentCache.AddRemoteAttachment(AttachmentId,
4090 Expires, Data, ContentType, this);
4091
4092 return new KeyValuePair<byte[], string>(Data, ContentType);
4093 }
4094 }
4095
4097 ?? throw new NotFoundException("Attachment not found.");
4098
4100
4101 using FileStream AttachmentFile = File.OpenRead(Attachment.LocalFileName);
4102 Aes Aes = Aes.Create();
4103
4104 Aes.BlockSize = 128;
4105 Aes.KeySize = 256;
4106 Aes.Mode = CipherMode.CBC;
4107 Aes.Padding = PaddingMode.Zeros;
4108
4109 byte[] Key = new byte[32];
4110 byte[] IV = new byte[16];
4111
4112 Buffer.BlockCopy(Attachment.Salt, 0, Key, 0, 32);
4113 Buffer.BlockCopy(Attachment.Salt, 32, IV, 0, 16);
4114
4115 using ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV);
4116 using (CryptoStream DecryptedAttachmentFile = new CryptoStream(AttachmentFile, Decryptor, CryptoStreamMode.Read))
4117 {
4118 Data = await DecryptedAttachmentFile.ReadAllAsync((int)Attachment.Size);
4119 }
4120
4122
4123 return new KeyValuePair<byte[], string>(Data, ContentType);
4124 }
4125
4134 internal async Task<ClientInformation> GetNetworkIdentity(CaseInsensitiveString Id, bool CanBeLegal, bool CanBeJid, NamespaceSet Version)
4135 {
4137
4138 if (this.IsComponentDomain(Address.Domain, true))
4139 {
4140 if (!CanBeLegal)
4141 throw new Exception("Expected server domain, not a component domain: " + Address.Domain);
4142
4143 LegalIdentity Identity = await GetLocalLegalIdentity(Id)
4144 ?? throw new Exception("Legal Identity not found: " + Id.Value);
4145
4147
4149 {
4150 if (Address.Domain.StartsWith(this.SubdomainSuffixed, StringComparison.CurrentCultureIgnoreCase))
4151 BareJid = Identity.Account + "@" + Address.Domain.Substring(this.SubdomainSuffixed.Length);
4152 else
4153 BareJid = Identity.Account + "@" + this.Server.Domain;
4154 }
4155
4156 if (!this.Server.TryGetClientConnections(BareJid, out IClientConnection[] Connections))
4157 Connections = null;
4158
4159 return new ClientInformation()
4160 {
4161 Jid = BareJid,
4162 Connections = this.RemoveDisconnected(Connections)
4163 };
4164 }
4165 else if (this.Server.IsServerDomain(Address.Domain, true))
4166 {
4167 if (!CanBeJid)
4168 throw new Exception("Expected legal component domain, not server domain: " + Address.Domain);
4169
4170 IClientConnection[] Connections;
4171
4172 if (Address.IsFullJID)
4173 {
4174 if (this.Server.TryGetClientConnection(Address.Address, out IClientConnection Connection))
4175 Connections = new IClientConnection[] { Connection };
4176 else
4177 Connections = null;
4178 }
4179 else if (Address.IsBareJID)
4180 {
4181 if (!this.Server.TryGetClientConnections(Address.BareJid, out Connections))
4182 Connections = null;
4183 }
4184 else
4185 Connections = null;
4186
4187 return new ClientInformation()
4188 {
4189 Jid = Address.BareJid,
4190 Connections = this.RemoveDisconnected(Connections)
4191 };
4192 }
4193 else
4194 {
4195 TaskCompletionSource<ClientInformation> Result = new TaskCompletionSource<ClientInformation>();
4196 StringBuilder Xml = new StringBuilder();
4197 string Namespace = NamespaceLegalIdentity(Version);
4198
4199 Xml.Append("<getNetworkIdentity xmlns='");
4200 Xml.Append(Namespace);
4201 Xml.Append("' id='");
4202 Xml.Append(XML.Encode(Id));
4203 Xml.Append("'/>");
4204
4205 await this.Server.SendIqRequest("get", this.MainDomain.Address, Address.Domain, string.Empty, Xml.ToString(), true, (sender2, e2) =>
4206 {
4207 if (e2.Ok)
4208 {
4209 XmlElement E = e2.FirstElement;
4210
4211 if (!(E is null) && E.LocalName == "networkIdentity" && E.NamespaceURI == Namespace)
4212 {
4213 string Jid = XML.Attribute(E, "jid");
4214 List<ClientConnectionInformation> Connections = new List<ClientConnectionInformation>();
4215
4216 foreach (XmlNode N in E.ChildNodes)
4217 {
4218 if (N is XmlElement E2 &&
4219 E2.LocalName == "connection" &&
4220 E2.NamespaceURI == Namespace)
4221 {
4222 Connections.Add(new ClientConnectionInformation()
4223 {
4224 Endpoint = XML.Attribute(E2, "clientEp"),
4225 LastPresenceTimestamp = XML.Attribute(E2, "ts", DateTimeOffset.MinValue)
4226 });
4227 }
4228 }
4229
4230 Result.TrySetResult(new ClientInformation()
4231 {
4232 Jid = Jid,
4233 Connections = Connections.ToArray()
4234 });
4235 }
4236 else
4237 Result.TrySetException(new Exception("Unexpected response received."));
4238 }
4239 else
4240 Result.TrySetException(new Exception(string.IsNullOrEmpty(e2.ErrorText) ? "Unable to get network identity." : e2.ErrorText));
4241
4242 return Task.CompletedTask;
4243 }, null);
4244
4245 return await Result.Task;
4246 }
4247 }
4248
4249 private ClientConnectionInformation[] RemoveDisconnected(IClientConnection[] Connections)
4250 {
4251 if (Connections is null)
4252 return Array.Empty<ClientConnectionInformation>();
4253
4254 List<ClientConnectionInformation> Result = new List<ClientConnectionInformation>();
4255
4256 foreach (IClientConnection Connection in Connections)
4257 {
4258 if (!Connection.CheckLive())
4259 continue;
4260
4261 if (string.IsNullOrEmpty(Connection.RemoteEndPoint))
4262 continue;
4263
4264 if (Connection.LastPresence is null)
4265 continue;
4266
4267 Result.Add(new ClientConnectionInformation()
4268 {
4269 Endpoint = Connection.RemoteEndPoint,
4270 LastPresenceTimestamp = Connection.LastPresence.Timestamp
4271 });
4272 }
4273
4274 return Result.ToArray();
4275 }
4276
4277 internal class ClientInformation
4278 {
4279 public CaseInsensitiveString Jid { get; set; }
4280 public ClientConnectionInformation[] Connections { get; set; }
4281
4282 public string MostRecentEndpoint
4283 {
4284 get
4285 {
4286 DateTimeOffset Best = DateTimeOffset.MinValue;
4287 string Endpoint = null;
4288
4289 if (!(this.Connections is null))
4290 {
4291 foreach (ClientConnectionInformation Info in this.Connections)
4292 {
4293 if (Info.LastPresenceTimestamp > Best)
4294 {
4295 Best = Info.LastPresenceTimestamp;
4296 Endpoint = Info.Endpoint;
4297 }
4298 }
4299 }
4300
4301 return Endpoint;
4302 }
4303 }
4304 }
4305
4306 internal class ClientConnectionInformation
4307 {
4308 public string Endpoint { get; set; }
4309 public DateTimeOffset LastPresenceTimestamp { get; set; }
4310 }
4311
4312 private Task GetNetworkLegalIdentityHandler(object Sender, IqEventArgs e)
4313 {
4314 return this.GetNetworkIdentityHandler(e, true);
4315 }
4316
4317 private Task GetNetworkXmppIdentityHandler(object Sender, IqEventArgs e)
4318 {
4319 return this.GetNetworkIdentityHandler(e, false);
4320 }
4321
4322 private async Task GetNetworkIdentityHandler(IqEventArgs e, bool ToLegalComponent)
4323 {
4324 try
4325 {
4326 if (!e.From.IsDomain)
4327 {
4328 await e.IqErrorForbidden(e.To, "Service only provided to federated servers.", "en");
4329 return;
4330 }
4331
4334 {
4335 await e.IqErrorBadRequest(e.To, "No ID reference provided.", "en");
4336 return;
4337 }
4338
4339 CaseInsensitiveString NetworkIdentity;
4340 IClientConnection[] Connections;
4341 XmppAddress IdAddress = new XmppAddress(Id);
4342
4343 if (this.IsComponentDomain(IdAddress.Domain, true))
4344 {
4345 if (!ToLegalComponent)
4346 {
4347 await e.IqErrorBadRequest(e.To, "Request must be made to legal component.", "en");
4348 return;
4349 }
4350
4351 if (!IdAddress.IsBareJID)
4352 {
4353 await e.IqErrorBadRequest(e.To, "Invalid Legal ID.", "en");
4354 return;
4355 }
4356
4357 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase);
4358 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
4359 if (Identity is null)
4360 {
4361 await e.IqErrorItemNotFound(e.To, "Identity not found.", "en");
4362 return;
4363 }
4364
4365 if (IdAddress.Domain.StartsWith(this.SubdomainSuffixed, StringComparison.CurrentCultureIgnoreCase))
4366 NetworkIdentity = Identity.Account + "@" + IdAddress.Domain.Substring(this.SubdomainSuffixed.Length);
4367 else
4368 NetworkIdentity = Identity.Account + "@" + this.Server.Domain;
4369
4370 if (!this.Server.TryGetClientConnections(NetworkIdentity, out Connections))
4371 Connections = null;
4372 }
4373 else if (this.Server.IsServerDomain(IdAddress.Domain, true))
4374 {
4375 if (ToLegalComponent)
4376 {
4377 await e.IqErrorBadRequest(e.To, "Request must be made to server.", "en");
4378 return;
4379 }
4380
4381 if (IdAddress.IsBareJID)
4382 {
4383 NetworkIdentity = IdAddress.Address;
4384
4385 if (!this.Server.TryGetClientConnections(NetworkIdentity, out Connections))
4386 Connections = null;
4387 }
4388 else if (IdAddress.IsFullJID)
4389 {
4390 NetworkIdentity = IdAddress.BareJid;
4391
4392 if (this.Server.TryGetClientConnection(IdAddress.Address, out IClientConnection Connection))
4393 Connections = new IClientConnection[] { Connection };
4394 else
4395 Connections = null;
4396 }
4397 else
4398 {
4399 await e.IqErrorBadRequest(e.To, "Invalid JID.", "en");
4400 return;
4401 }
4402 }
4403 else
4404 {
4405 await e.IqErrorForbidden(e.To, "Identity not hosted by this neuron.", "en");
4406 return;
4407 }
4408
4409 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
4410 StringBuilder Xml = new StringBuilder();
4411
4412 Xml.Append("<networkIdentity xmlns='");
4413 Xml.Append(NamespaceLegalIdentity(QueryVersion));
4414 Xml.Append("' jid='");
4415 Xml.Append(XML.Encode(NetworkIdentity));
4416 Xml.Append("'>");
4417
4418 foreach (ClientConnectionInformation ClientInfo in this.RemoveDisconnected(Connections))
4419 {
4420 Xml.Append("<connection clientEp='");
4421 Xml.Append(XML.Encode(ClientInfo.Endpoint));
4422 Xml.Append("' ts='");
4423 Xml.Append(XML.Encode(ClientInfo.LastPresenceTimestamp));
4424 Xml.Append("'/>");
4425 }
4426
4427 Xml.Append("</networkIdentity>");
4428
4429 await e.IqResult(Xml.ToString(), e.To);
4430 }
4431 catch (Exception ex)
4432 {
4433 await e.IqError(ex, e.To);
4434 }
4435 }
4436
4437 private async Task GetIdentityReferencesHandler(object Sender, IqEventArgs e)
4438 {
4439 try
4440 {
4441 if (!e.From.IsDomain)
4442 {
4443 await e.IqErrorForbidden(e.To, "Service only provided to federated servers.", "en");
4444 return;
4445 }
4446
4447 CaseInsensitiveString Jid = XML.Attribute(e.Query, "jid");
4449 {
4450 await e.IqErrorBadRequest(e.To, "No JID reference provided.", "en");
4451 return;
4452 }
4453
4454 XmppAddress Address = new XmppAddress(Jid);
4455 if (!Address.IsBareJID)
4456 {
4457 await e.IqErrorBadRequest(e.To, "JID not a Bare JID.", "en");
4458 return;
4459 }
4460
4461 if (!this.Server.IsServerDomain(Address.Domain, true))
4462 {
4463 await e.IqErrorForbidden(e.To, "JID domain part not a domain name of the broker.", "en");
4464 return;
4465 }
4466
4467 IAccount Account = await XmppServerModule.GetAccountAsync(Address.Account);
4468 if (Account is null)
4469 {
4470 await e.IqErrorItemNotFound(e.To, "JID not found.", "en");
4471 return;
4472 }
4473
4474 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
4475 StringBuilder Xml = new StringBuilder();
4476 DateTime UtcNow = DateTime.UtcNow;
4477
4478 Xml.Append("<identityReferences xmlns='");
4479 Xml.Append(NamespaceLegalIdentity(QueryVersion));
4480 Xml.Append("'>");
4481
4482 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
4483 new FilterFieldEqualTo("Account", Account.UserName),
4484 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
4485 new FilterFieldLesserOrEqualTo("From", UtcNow),
4486 new FilterFieldGreaterOrEqualTo("To", UtcNow)), "-Created"))
4487 {
4488 Xml.Append("<identityReference>");
4489 Xml.Append(XML.Encode(ID.Id.Value));
4490 Xml.Append("</identityReference>");
4491 }
4492
4493 Xml.Append("</identityReferences>");
4494
4495 await e.IqResult(Xml.ToString(), e.To);
4496 }
4497 catch (Exception ex)
4498 {
4499 await e.IqError(ex, e.To);
4500 }
4501 }
4502
4508 internal async Task<string[]> GetIdentityReferences(XmppAddress Jid)
4509 {
4510 string BareJid = Jid.BareJid;
4511 string Key = BareJid + "|idRefs";
4512
4513 if (this.remoteComponents.TryGetValue(Key, out object Obj) && Obj is string[] CachedResult)
4514 return CachedResult;
4515
4516 ChunkedList<string> References = new ChunkedList<string>();
4517
4518 if (this.Server.IsServerDomain(Jid.Domain, true))
4519 {
4520 DateTime UtcNow = DateTime.UtcNow;
4521
4522 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
4523 new FilterFieldEqualTo("Account", Jid.Account),
4524 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
4525 new FilterFieldLesserOrEqualTo("From", UtcNow),
4526 new FilterFieldGreaterOrEqualTo("To", UtcNow)), "-Created"))
4527 {
4528 References.Add(ID.Id);
4529 }
4530
4531 CachedResult = References.ToArray();
4532 this.remoteComponents[Key] = CachedResult;
4533
4534 return CachedResult;
4535 }
4536
4537 if (!Gateway.HasDomain)
4538 return Array.Empty<string>();
4539
4540 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
4541 StringBuilder Xml = new StringBuilder();
4542
4543 Xml.Append("<identityReferences xmlns='");
4544 Xml.Append(NamespaceLegalIdentity(NamespaceSet.Current));
4545 Xml.Append("' jid='");
4546 Xml.Append(XML.Encode(BareJid));
4547 Xml.Append("'/>");
4548
4549 await this.Server.SendIqRequest("get", this.Server.Domain, Jid.Domain, null,
4550 Xml.ToString(), (sender, e) =>
4551 {
4552 XmlElement E;
4553
4554 if (!e.Ok)
4555 Result.TrySetException(XmppClient.GetExceptionObject(e.ErrorElement));
4556 else if ((E = e.FirstElement) is null ||
4557 E.LocalName != "identityReferences" ||
4558 !IsNamespaceLegalIdentity(E.NamespaceURI))
4559 {
4560 Result.TrySetException(new Exception("Unable to get the legal identity references for " +
4561 BareJid + ". Invalid response returned."));
4562 }
4563 else
4564 {
4565 foreach (XmlNode N in E.ChildNodes)
4566 {
4567 if (N is XmlElement E2 &&
4568 E2.LocalName == "identityReference" &&
4569 IsNamespaceLegalIdentity(E2.NamespaceURI))
4570 {
4571 References.Add(E2.InnerText);
4572 }
4573 }
4574
4575 CachedResult = References.ToArray();
4576 this.remoteComponents[Key] = CachedResult;
4577 Result.TrySetResult(CachedResult);
4578 }
4579
4580 return Task.CompletedTask;
4581 }, null);
4582
4583 return await Result.Task;
4584 }
4585
4592 internal bool TryGetItemFromCache(string Key, out object Obj)
4593 {
4594 return this.remoteComponents.TryGetValue(Key, out Obj);
4595 }
4596
4602 internal void AddItemToCache(string Key, object Obj)
4603 {
4604 this.remoteComponents[Key] = Obj;
4605 }
4606
4612 internal bool RemoveItemFromCache(string Key)
4613 {
4614 return this.remoteComponents.Remove(Key);
4615 }
4616
4617 private async Task CanSignAsHandler(object Sender, IqEventArgs e)
4618 {
4619 try
4620 {
4621 CaseInsensitiveString ReferenceId = XML.Attribute(e.Query, "referenceId");
4622 if (CaseInsensitiveString.IsNullOrEmpty(ReferenceId))
4623 {
4624 await e.IqErrorBadRequest(e.To, "No Reference ID provided.", "en");
4625 return;
4626 }
4627
4628 CaseInsensitiveString SignatoryId = XML.Attribute(e.Query, "signatoryId");
4629 if (CaseInsensitiveString.IsNullOrEmpty(SignatoryId))
4630 {
4631 await e.IqErrorBadRequest(e.To, "No Signatory ID provided.", "en");
4632 return;
4633 }
4634
4635 XmppAddress ReferenceAddress = new XmppAddress(ReferenceId);
4636 XmppAddress SignatoryAddress = new XmppAddress(SignatoryId);
4637
4638 if (ReferenceAddress.Domain != SignatoryAddress.Domain)
4639 {
4640 await e.IqErrorBadRequest(e.To, "Reference and Signatory domain mismatch.", "en");
4641 return;
4642 }
4643
4644 bool IncludeDetailedErrorMessage = e.From.IsDomain;
4645
4646 if (!this.IsComponentDomain(ReferenceAddress.Domain, true))
4647 {
4648 await e.IqErrorBadRequest(e.To, IncludeDetailedErrorMessage ? "Reference identity not hosted by broker." : string.Empty, "en");
4649 return;
4650 }
4651
4652 LegalIdentity ReferenceIdentity = await GetLocalLegalIdentity(ReferenceId);
4653 if (ReferenceIdentity is null)
4654 {
4655 await e.IqErrorItemNotFound(e.To, IncludeDetailedErrorMessage ? "Reference ID not found." : string.Empty, "en");
4656 return;
4657 }
4658
4659 switch (ReferenceIdentity.State)
4660 {
4661 case Legal.Identity.IdentityState.Created:
4662 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID never approved." : string.Empty, "en");
4663 return;
4664
4665 case Legal.Identity.IdentityState.Approved:
4666 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID still approved." : string.Empty, "en");
4667 return;
4668 }
4669
4670 CaseInsensitiveString BareJid = ReferenceIdentity[PersonalInformation.JidTag];
4672 {
4673 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded JID." : string.Empty, "en");
4674 return;
4675 }
4676
4677 CaseInsensitiveString PersonalNumber = ReferenceIdentity[PersonalInformation.PersonalNumberTag];
4678 if (CaseInsensitiveString.IsNullOrEmpty(PersonalNumber))
4679 {
4680 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded Personal Number." : string.Empty, "en");
4681 return;
4682 }
4683
4684 CaseInsensitiveString Country = ReferenceIdentity[PersonalInformation.CountryTag];
4686 {
4687 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded Country." : string.Empty, "en");
4688 return;
4689 }
4690
4691 LegalIdentity SignatoryIdentity = await GetLocalLegalIdentity(SignatoryId);
4692 if (SignatoryIdentity is null)
4693 {
4694 await e.IqErrorItemNotFound(e.To, IncludeDetailedErrorMessage ? "Signatory ID not found." : string.Empty, "en");
4695 return;
4696 }
4697
4698 if (SignatoryIdentity.State != Legal.Identity.IdentityState.Approved)
4699 {
4700 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Signatory ID not in an approved state." : string.Empty, "en");
4701 return;
4702 }
4703
4704 if (SignatoryIdentity[PersonalInformation.JidTag] != BareJid)
4705 {
4706 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "JID mismatch." : string.Empty, "en");
4707 return;
4708 }
4709
4710 if (SignatoryIdentity[PersonalInformation.PersonalNumberTag] != PersonalNumber)
4711 {
4712 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Personal Number mismatch." : string.Empty, "en");
4713 return;
4714 }
4715
4716 if (SignatoryIdentity[PersonalInformation.CountryTag] != Country)
4717 {
4718 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Country mismatch." : string.Empty, "en");
4719 return;
4720 }
4721
4722 await e.IqResult(string.Empty, e.To);
4723 }
4724 catch (Exception ex)
4725 {
4726 await e.IqError(ex, e.To);
4727 }
4728 }
4729
4737 internal async Task<bool> CanSignAs(CaseInsensitiveString ReferenceId, CaseInsensitiveString SignatoryId)
4738 {
4739 XmppAddress ReferenceAddress = new XmppAddress(ReferenceId);
4740 XmppAddress SignatoryAddress = new XmppAddress(SignatoryId);
4741
4742 if (ReferenceAddress.Domain != SignatoryAddress.Domain)
4743 return false;
4744
4745 if (this.IsComponentDomain(ReferenceAddress.Domain, true))
4746 {
4747 LegalIdentity ReferenceIdentity = await GetLocalLegalIdentity(ReferenceId);
4748 if (ReferenceIdentity is null)
4749 return false;
4750
4751 switch (ReferenceIdentity.State)
4752 {
4753 case Legal.Identity.IdentityState.Created:
4754 case Legal.Identity.IdentityState.Approved:
4755 return false; // Reference must no longer be valid.
4756 }
4757
4758 CaseInsensitiveString BareJid = ReferenceIdentity[PersonalInformation.JidTag];
4759 CaseInsensitiveString PersonalNumber = ReferenceIdentity[PersonalInformation.PersonalNumberTag];
4760 CaseInsensitiveString Country = ReferenceIdentity[PersonalInformation.CountryTag];
4761
4762 if (CaseInsensitiveString.IsNullOrEmpty(BareJid) ||
4763 CaseInsensitiveString.IsNullOrEmpty(PersonalNumber) ||
4765 {
4766 return false;
4767 }
4768
4769 LegalIdentity SignatoryIdentity = await GetLocalLegalIdentity(SignatoryId);
4770 if (SignatoryIdentity is null)
4771 return false;
4772
4773 if (SignatoryIdentity.State != Legal.Identity.IdentityState.Approved)
4774 return false;
4775
4776 if (SignatoryIdentity[PersonalInformation.JidTag] != BareJid ||
4777 SignatoryIdentity[PersonalInformation.PersonalNumberTag] != PersonalNumber ||
4778 SignatoryIdentity[PersonalInformation.CountryTag] != Country)
4779 {
4780 return false;
4781 }
4782
4783 return true;
4784 }
4785 else
4786 {
4787 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
4788 StringBuilder Xml = new StringBuilder();
4789 string Namespace = NamespaceLegalIdentity(NamespaceSet.Current);
4790
4791 Xml.Append("<canSignAs xmlns='");
4792 Xml.Append(Namespace);
4793 Xml.Append("' referenceId='");
4794 Xml.Append(XML.Encode(ReferenceId));
4795 Xml.Append("' signatoryId='");
4796 Xml.Append(XML.Encode(SignatoryId));
4797 Xml.Append("'/>");
4798
4799 await this.Server.SendIqRequest("get", this.MainDomain.Address, ReferenceAddress.Domain, string.Empty, Xml.ToString(), true, (sender2, e2) =>
4800 {
4801 Result.TrySetResult(e2.Ok);
4802 return Task.CompletedTask;
4803 }, null);
4804
4805 return await Result.Task;
4806 }
4807 }
4808
4809 private async Task AuthorizeAccessToIdHandler(object Sender, IqEventArgs e)
4810 {
4811 try
4812 {
4813 if (!this.Server.IsServerDomain(e.From.Domain, true))
4814 {
4815 await e.IqErrorForbidden(e.To, "Service only available to local accounts.", "en");
4816 return;
4817 }
4818
4820
4822 {
4823 await e.IqErrorBadRequest(e.To, "No Legal ID specified.", "en");
4824 return;
4825 }
4826
4827 XmppAddress IdAddress = new XmppAddress(Id);
4828
4829 if (!IdAddress.IsBareJID)
4830 {
4831 await e.IqErrorBadRequest(e.To, "Invalid Legal ID.", "en");
4832 return;
4833 }
4834
4835 if (!this.IsComponentDomain(IdAddress.Domain, true))
4836 {
4837 await e.IqErrorBadRequest(e.To, "Not a local Legal ID.", "en");
4838 return;
4839 }
4840
4841 CaseInsensitiveString RemoteId = XML.Attribute(e.Query, "remoteId");
4842 bool Authorized = XML.Attribute(e.Query, "auth", true);
4843
4845 {
4846 await e.IqErrorBadRequest(e.To, "No Remote ID specified.", "en");
4847 return;
4848 }
4849
4850 XmppAddress RemoteAddress = new XmppAddress(RemoteId);
4851 if (!RemoteAddress.IsBareJID)
4852 {
4853 await e.IqErrorBadRequest(e.To, "Invalid Remote ID.", "en");
4854 return;
4855 }
4856
4857 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase);
4858 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
4859 if (Identity is null)
4860 {
4861 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
4862 return;
4863 }
4864
4865 if (e.From.Account != Identity.Account)
4866 {
4867 await e.IqErrorForbidden(e.To, "Not your identity.", "en");
4868 return;
4869 }
4870
4871 ClientInformation ClientInfo = await this.GetNetworkIdentity(RemoteId, true, true, Identity.Version);
4872 CaseInsensitiveString RemoteJid = ClientInfo.Jid;
4873 this.IdentityAuthorization(RemoteJid, e.From.BareJid, Id, Authorized);
4874
4875 await e.IqResult(string.Empty, e.To);
4876 }
4877 catch (Exception ex)
4878 {
4879 await e.IqError(ex, e.To);
4880 }
4881 }
4882
4883 private async Task ReadyForApprovalHandler(object Sender, IqEventArgs e)
4884 {
4885 try
4886 {
4888 using Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase);
4889
4890 IAccount Account = await XmppServerModule.GetAccountAsync(e.From.Account);
4891 if (Account is null)
4892 {
4893 await e.IqErrorForbidden(e.To, "Account not found.", "en");
4894 return;
4895 }
4896
4897 if (!Account.Enabled)
4898 {
4899 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
4900 return;
4901 }
4902
4903 KeyValuePair<LegalIdentity, bool> P = await GetLocalLegalIdentity(Id, true);
4904 LegalIdentity Identity = P.Key;
4905 bool Preview = P.Value;
4906
4907 if (Identity is null)
4908 {
4909 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
4910 return;
4911 }
4912
4913 if (e.From.Account != Identity.Account || !this.Server.IsServerDomain(e.From.Domain, true))
4914 {
4915 await e.IqErrorForbidden(e.To, "You can only mark your own identity applications for approval.", "en");
4916 return;
4917 }
4918
4919 await e.IqResult(string.Empty, e.To);
4920
4921 this.CheckAuthenticityOfIdentity(Identity, Identity.GetTags(), e.To, e.From,
4922 Account as Account, Preview, (IdentityReviewXml, Identity2) =>
4923 {
4924 // Store in cache, as a means to pre-approve attachment that might
4925 // be added to the Identity, albeit in an approved state when request
4926 // arrives.
4927 this.remoteComponents?.Add(Identity2.Id + "|identityReview", IdentityReviewXml);
4928
4929 return Task.FromResult(Identity2);
4930 });
4931 }
4932 catch (Exception ex)
4933 {
4934 await e.IqError(ex, e.To);
4935 }
4936 }
4937
4938 internal async void CheckAuthenticityOfIdentity(LegalIdentity Identity,
4939 KeyValuePair<string, object>[] Claims, XmppAddress ComponentAddress,
4940 XmppAddress ClientJid, Account Account, bool Preview,
4941 Func<string, LegalIdentity, Task<LegalIdentity>> IdentityReviewCallback)
4942 {
4943 try
4944 {
4945 KeyValuePair<IPhoto[], XmlDocument[]> P = await GetPhotosAndDocuments(Identity, Preview);
4946 IdentityApplication Application = new IdentityApplication(Identity.Id,
4947 NamespaceLegalIdentity(Identity.Version), Preview,
4948 GetPersonalInformation(Identity), Claims,
4949 P.Key, P.Value, Account);
4950
4951 foreach (KeyValuePair<string, object> Claim in Claims)
4952 {
4953 switch (Claim.Key)
4954 {
4956 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string EMail) &&
4957 EMail == Account.EMail &&
4958 Account.EMailVerified.HasValue &&
4959 DateTime.UtcNow.Subtract(Account.EMailVerified.Value.ToUniversalTime()).TotalDays < 3 * 365.2)
4960 {
4961 Application.ClaimValid(Claim.Key, this);
4962 }
4963 break;
4964
4966 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string Phone) &&
4967 Phone == Account.PhoneNr &&
4968 Account.PhoneNrVerified.HasValue &&
4969 DateTime.UtcNow.Subtract(Account.PhoneNrVerified.Value.ToUniversalTime()).TotalDays < 3 * 365.25)
4970 {
4971 Application.ClaimValid(Claim.Key, this);
4972 }
4973 break;
4974
4975 case "ID":
4976 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string Id))
4977 {
4978 if (Id == Identity.Id)
4979 Application.ClaimValid(Claim.Key, this);
4980 else
4981 {
4982 Application.ClaimInvalid(Claim.Key,
4983 "Identifier does not correspond to created identifier.",
4984 "en", "IdMismatch", this);
4985 }
4986 }
4987 break;
4988
4990 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string Jid))
4991 {
4992 if (Jid == ClientJid.BareJid)
4993 Application.ClaimValid(Claim.Key, this);
4994 else
4995 {
4996 Application.ClaimInvalid(Claim.Key,
4997 "JID does not correspond to client JID.",
4998 "en", "JidMismatch", this);
4999 }
5000 }
5001 break;
5002
5006 Application.ClaimValid(Claim.Key, this);
5007 break;
5008
5009 case "Account":
5010 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string AccountName))
5011 {
5012 if (AccountName == Account.UserName)
5013 Application.ClaimValid(Claim.Key, this);
5014 else
5015 {
5016 Application.ClaimInvalid(Claim.Key,
5017 "Account name does not match account.",
5018 "en", "AccountMismatch", this);
5019 }
5020 }
5021 break;
5022
5023 case "Provider":
5024 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out string Provider))
5025 {
5026 if (this.IsComponentDomain(Provider, true))
5027 Application.ClaimValid(Claim.Key, this);
5028 else
5029 {
5030 Application.ClaimInvalid(Claim.Key,
5031 "Provider does not match legal component address.",
5032 "en", "ProviderMismatch", this);
5033 }
5034 }
5035 break;
5036
5037 case "State":
5038 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out Legal.Identity.IdentityState State))
5039 {
5040 if (State == Identity.State)
5041 Application.ClaimValid(Claim.Key, this);
5042 else
5043 {
5044 Application.ClaimInvalid(Claim.Key,
5045 "State does not match identity state.",
5046 "en", "StateMismatch", this);
5047 }
5048 }
5049 break;
5050
5051 case "Created":
5052 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out DateTime Created))
5053 {
5054 if (Created == Identity.Created)
5055 Application.ClaimValid(Claim.Key, this);
5056 else
5057 {
5058 Application.ClaimInvalid(Claim.Key,
5059 "Created does not match identity creation timestamp.",
5060 "en", "CreatedMismatch", this);
5061 }
5062 }
5063 break;
5064
5065 case "Updated":
5066 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out DateTime Updated))
5067 {
5068 if (Updated == Identity.Updated)
5069 Application.ClaimValid(Claim.Key, this);
5070 else
5071 {
5072 Application.ClaimInvalid(Claim.Key,
5073 "Updated does not match identity update timestamp.",
5074 "en", "UpdatedMismatch", this);
5075 }
5076 }
5077 break;
5078
5079 case "From":
5080 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out DateTime From))
5081 {
5082 if (From == Identity.From)
5083 Application.ClaimValid(Claim.Key, this);
5084 else
5085 {
5086 Application.ClaimInvalid(Claim.Key,
5087 "From does not match identity creation timestamp.",
5088 "en", "FromMismatch", this);
5089 }
5090 }
5091 break;
5092
5093 case "To":
5094 if (Application.TryGetValue(Claim.Key, Claim.Value, this, out DateTime To))
5095 {
5096 if (To == Identity.To)
5097 Application.ClaimValid(Claim.Key, this);
5098 else
5099 {
5100 Application.ClaimInvalid(Claim.Key,
5101 "To does not match identity creation timestamp.",
5102 "en", "ToMismatch", this);
5103 }
5104 }
5105 break;
5106 }
5107 }
5108
5109 IIdentityAuthenticatorService[] Authenticators = Types.FindSupport<IIdentityAuthenticatorService, IIdentityApplication>(Application);
5110 if (Authenticators.Length == 0 && !Application.IsValid.HasValue)
5111 return;
5112
5113 foreach (IIdentityAuthenticatorService Authenticator in Authenticators)
5114 {
5115 if (Application.IsValid.HasValue)
5116 break;
5117
5118 try
5119 {
5120 await Authenticator.Validate(Application);
5121 await RuntimeCounters.IncrementCounter("KyC." + Application.GetType().FullName);
5122 }
5123 catch (Exception ex)
5124 {
5125 Application.ReportError(ex.Message, string.Empty, string.Empty,
5126 ValidationErrorType.Service, Authenticator);
5127 }
5128 }
5129
5130 if (!Application.Preview)
5131 Application.LogResults();
5132
5133 if ((!Application.IsValid.HasValue &&
5134 (Application.HasValidatedClaims || Application.HasValidatedPhotos)) ||
5135 (Application.IsValid.HasValue && Application.IsValid.Value))
5136 {
5137 string Xml = Application.GetIdentityReviewXml();
5138
5139 Identity = await IdentityReviewCallback(Xml, Identity);
5140
5141 await this.Server.SendMessage(string.Empty, string.Empty, ComponentAddress,
5142 ClientJid.ToBareJID(), string.Empty, Xml);
5143 }
5144
5145 if (!Application.IsValid.HasValue)
5146 {
5147 string Xml = Application.GetClientErrorMessageXml(out string Language,
5148 "Unable to validate application automatically. The application needs to be validated manually, or by peer review.",
5149 "ManualReview", "en");
5150
5151 await this.Server.SendMessage(string.Empty, string.Empty, ComponentAddress,
5152 ClientJid.ToBareJID(), Language, Xml);
5153 }
5154 else
5155 {
5156 KeyValuePair<string, object>[] Tags = Application.Tags;
5157 string Url = Gateway.GetUrl("/LegalIdentity.md?Id=" + Identity.Id);
5158 StringBuilder Name = new StringBuilder();
5160 int i = 0;
5161 int c = Application.NrServices;
5162
5163 foreach (object Service in Application.Services)
5164 {
5165 if (i > 0)
5166 {
5167 if (i == c - 1)
5168 Name.Append(" and ");
5169 else
5170 Name.Append(", ");
5171 }
5172
5173 if (Service is global::Paiwise.IServiceProvider ServiceProvider)
5174 {
5175 Name.Append("**");
5177 Name.Append("**");
5178 }
5179 else
5180 {
5181 Name.Append('`');
5182 Name.Append(Service.GetType().FullName);
5183 Name.Append('`');
5184 }
5185
5186 Actors.Add(Service.GetType().FullName);
5187 }
5188
5189 if (Application.IsValid.Value)
5190 {
5191 // Valid application
5192
5193 if (Preview)
5194 {
5195 LoginAuditor.Success("Legal identity preview application has been automatically approved.", ClientJid.Account.Value,
5196 ClientJid.BareJid.Value, "XMPP", Tags);
5197 }
5198 else
5199 {
5200 LoginAuditor.Success("Legal identity application has been automatically approved.", ClientJid.Account.Value,
5201 ClientJid.BareJid.Value, "XMPP", Tags);
5202 }
5203
5204 await this.UpdateState(Identity, Legal.Identity.IdentityState.Approved,
5205 Actors.ToArray(), Account, Preview, true, Application);
5206
5207 if (!Application.Preview &&
5209 {
5210 StringBuilder Markdown = new StringBuilder();
5211
5212 Markdown.Append("Legal identity application has been automatically approved: [`");
5213 Markdown.Append(Identity.Id);
5214 Markdown.Append("`](");
5215 Markdown.Append(Url);
5216 Markdown.Append(") (by ");
5217 Markdown.Append(Name.ToString());
5218 Markdown.AppendLine(")");
5219 Markdown.AppendLine();
5220 Output(Markdown, Tags);
5221
5222 await Gateway.SendNotification(Markdown.ToString());
5223 }
5224 }
5225 else
5226 {
5227 // Invalid application
5228
5229 if (!Application.Preview)
5230 {
5231 if (Application.HasErrors)
5232 {
5233 foreach (ValidationError Error in Application.Errors)
5234 {
5235 switch (Error.ErrorType)
5236 {
5237 case ValidationErrorType.Server:
5238 case ValidationErrorType.Service:
5239 Log.Error(Error.ErrorMessage, string.Empty,
5240 Error.Service.GetType().Namespace, Error.ErrorCode,
5241 Error.Tags);
5242 break;
5243
5244 case ValidationErrorType.Client:
5245 Log.Warning(Error.ErrorMessage, string.Empty,
5246 Error.Service.GetType().Namespace, Error.ErrorCode,
5247 Error.Tags);
5248 break;
5249 }
5250 }
5251 }
5252 }
5253
5254 await this.UpdateState(Identity, Legal.Identity.IdentityState.Rejected,
5255 Actors.ToArray(), Account, Preview, true, Application);
5256
5257 string Xml = Application.GetClientErrorMessageXml(out string Language,
5258 "Unable to validate application automatically. The application needs to be validated manually, or by peer review.",
5259 "ManualReview", "en");
5260
5261 await this.Server.SendMessage(string.Empty, string.Empty, ComponentAddress,
5262 ClientJid.ToBareJID(), Language, Xml);
5263
5264 LoginAuditor.Fail("Legal identity application has been automatically rejected.", ClientJid.Account.Value,
5265 ClientJid.BareJid.Value, "XMPP", Tags);
5266
5267 if (!Application.Preview &&
5269 {
5270 StringBuilder Markdown = new StringBuilder();
5271
5272 Markdown.Append("Legal identity application has been automatically rejected: [`");
5273 Markdown.Append(Identity.Id);
5274 Markdown.Append("`](");
5275 Markdown.Append(Url);
5276 Markdown.Append(") (by ");
5277 Markdown.Append(Name.ToString());
5278 Markdown.AppendLine(")");
5279 Markdown.AppendLine();
5280 Output(Markdown, Tags);
5281
5282 await Gateway.SendNotification(Markdown.ToString());
5283 }
5284 }
5285 }
5286 }
5287 catch (Exception ex)
5288 {
5289 Log.Exception(ex);
5290 }
5291 }
5292
5293 private async Task GetTrustChainHandler(object Sender, IqEventArgs e)
5294 {
5295 try
5296 {
5297 StringBuilder Xml = new StringBuilder();
5298
5299 Xml.Append("<trustChain xmlns='");
5300 Xml.Append(XML.Encode(e.Query.NamespaceURI));
5301 Xml.Append("'>");
5302
5304 {
5305 foreach (string Broker in await this.GetTrustChainAsync(Gateway.XmppClient.Domain))
5306 {
5307 Xml.Append("<broker domain='");
5308 Xml.Append(XML.Encode(Broker));
5309 Xml.Append("'/>");
5310 }
5311 }
5312 else if (Gateway.XmppClient.Domain != e.To.Domain.Value)
5313 {
5314 Xml.Append("<broker domain='");
5315 Xml.Append(XML.Encode(Gateway.XmppClient.Domain));
5316 Xml.Append("'/>");
5317 }
5318
5319 Xml.Append("<broker domain='");
5320 Xml.Append(XML.Encode(e.To.Domain.Value));
5321 Xml.Append("'/>");
5322
5323 Xml.Append("</trustChain>");
5324
5325 await e.IqResult(Xml.ToString(), e.To);
5326 }
5327 catch (Exception ex)
5328 {
5329 await e.IqError(ex, e.To);
5330 }
5331 }
5332
5338 public async Task<string[]> GetTrustChainAsync(string Domain)
5339 {
5340 string Key = Domain + "|trustChain";
5341
5342 if (this.remoteComponents.TryGetValue(Key, out object Obj) && Obj is string[] CachedResult)
5343 return CachedResult;
5344
5345 if (!Gateway.HasDomain)
5346 return Array.Empty<string>();
5347
5348 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
5349
5350 await this.Server.SendIqRequest("get", Gateway.Domain.Value, Domain, null,
5351 "<getTrustChain xmlns='" + NamespaceLegalIdentity(NamespaceSet.Current) + "'/>",
5352 (sender2, e2) =>
5353 {
5354 XmlElement E;
5355
5356 if (!e2.Ok)
5357 Result.TrySetException(XmppClient.GetExceptionObject(e2.ErrorElement));
5358 else if ((E = e2.FirstElement) is null ||
5359 E.LocalName != "trustChain" ||
5360 !IsNamespaceLegalIdentity(E.NamespaceURI))
5361 {
5362 Result.TrySetException(new Exception("Unable to get the trust chain from " +
5363 Domain + ". Invalid response returned."));
5364 }
5365 else
5366 {
5367 ChunkedList<string> Brokers = new ChunkedList<string>();
5368
5369 foreach (XmlNode N in E.ChildNodes)
5370 {
5371 if (N is XmlElement E2 &&
5372 E2.LocalName == "broker" &&
5373 IsNamespaceLegalIdentity(E2.NamespaceURI))
5374 {
5375 Brokers.Add(XML.Attribute(E2, "domain"));
5376 }
5377 }
5378
5379 CachedResult = Brokers.ToArray();
5380 this.remoteComponents[Key] = CachedResult;
5381 Result.TrySetResult(CachedResult);
5382 }
5383
5384 return Task.CompletedTask;
5385 }, null);
5386
5387 return await Result.Task;
5388 }
5389
5390 public async Task<string> GetTrustAnchor(string Domain)
5391 {
5392 string[] Chain = await this.GetTrustChainAsync(Domain);
5393 if ((Chain?.Length ?? 0) == 0)
5394 return null;
5395 else
5396 return Chain[0];
5397 }
5398
5406 public async Task<bool> InSameTreeOfTrust(string Domain)
5407 {
5408 if (!Gateway.HasDomain)
5409 return Gateway.IsDomain(Domain, true);
5410
5411 string Anchor1 = await this.GetTrustAnchor(Gateway.Domain.Value);
5412 string Anchor2 = await this.GetTrustAnchor(Domain);
5413
5414 if (Anchor1 is null || Anchor2 is null)
5415 return false;
5416 else
5417 return Anchor1 == Anchor2;
5418 }
5419
5420 #endregion
5421
5422 #region Legal Identity references
5423
5424 internal static async Task CheckLegalIdentityReferences()
5425 {
5426 string[] CollectionNames = await Database.GetCollections();
5427
5428 if (Array.IndexOf(CollectionNames, "LegalIdentityReferences") < 0)
5429 {
5430 foreach (LegalIdentity Identity in await Database.Find<LegalIdentity>(
5431 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved)))
5432 {
5433 await UpdateLegalIdentityReference(Identity);
5434 }
5435 }
5436 }
5437
5438 internal static async Task AddLegalIdentityReference(LegalIdentity Identity)
5439 {
5441 {
5442 Country = Identity[PersonalInformation.CountryTag],
5443 PNr = Identity[PersonalInformation.PersonalNumberTag],
5444 LegalId = Identity.Id,
5445 Provider = Identity.Provider,
5446 ArchiveDays = CalcRefArchiveDays(Identity)
5447 });
5448 }
5449
5456 public static async Task<LegalIdentityReference> FindLegalIdentityReference(string LegalId)
5457 {
5458 return await Database.FindFirstDeleteRest<LegalIdentityReference>(
5459 new FilterFieldEqualTo("LegalId", LegalId));
5460 }
5461
5471 public static async Task<IEnumerable<LegalIdentityReference>> FindLegalIdentityReferences(string Country, string PNr)
5472 {
5474 new FilterFieldEqualTo("Country", Country),
5475 new FilterFieldEqualTo("PNr", PNr)));
5476 }
5477
5478 private static async Task UpdateLegalIdentityReference(LegalIdentity Identity)
5479 {
5480 LegalIdentityReference Ref = await FindLegalIdentityReference(Identity.Id);
5481
5482 if (Ref is null)
5483 await AddLegalIdentityReference(Identity);
5484 else
5485 {
5486 string s = Identity[PersonalInformation.CountryTag];
5487 bool Updated = false;
5488
5489 if (Ref.Country != s)
5490 {
5491 Ref.Country = s;
5492 Updated = true;
5493 }
5494
5496 if (Ref.PNr != s)
5497 {
5498 Ref.PNr = s;
5499 Updated = true;
5500 }
5501
5502 if (Ref.Provider != Identity.Provider)
5503 {
5504 Ref.Provider = Identity.Provider;
5505 Updated = true;
5506 }
5507
5508 if (Updated)
5509 await Database.Update(Ref);
5510 }
5511 }
5512
5513 private static Task DeleteLegalIdentityReference(string LegalId)
5514 {
5515 return Database.Delete<LegalIdentityReference>(new FilterFieldEqualTo("LegalId", LegalId));
5516 }
5517
5518 private static int CalcRefArchiveDays(LegalIdentity Identity)
5519 {
5520 DateTime Expires = Identity.To;
5521 if (Expires.Year >= 9999)
5522 return int.MaxValue;
5523
5524 TimeSpan Span = Expires - DateTime.Now;
5525 double Days = Math.Ceiling(Span.TotalDays);
5526
5527 if (Days < 0)
5528 return 0;
5529 else if (Days > int.MaxValue)
5530 return int.MaxValue;
5531 else
5532 return (int)Days;
5533 }
5534
5535 #endregion
5536
5537 #region Remote Legal Identity References
5538
5539 private async Task LegalIdReferenceAddedHandler(object Sender, IqEventArgs e)
5540 {
5541 if (!e.From.IsDomain)
5542 {
5543 await e.IqErrorForbidden(e.To, "Not a broker.", "en");
5544 return;
5545 }
5546
5547 string LegalId = XML.Attribute(e.Query, "legalId");
5548 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
5549
5550 if (Identity is null)
5551 {
5552 await e.IqErrorItemNotFound(e.To, "Legal Identity not found.", "en");
5553 return;
5554 }
5555
5557 {
5558 LegalId = LegalId,
5559 RemoteDomain = e.From.Domain,
5560 Account = Identity.Account
5561 };
5562
5563 await Database.Insert(Ref);
5564
5565 await e.IqResult(string.Empty, e.To);
5566 }
5567
5568 private async Task LegalIdReferenceRemovedHandler(object Sender, IqEventArgs e)
5569 {
5570 if (!e.From.IsDomain)
5571 {
5572 await e.IqErrorForbidden(e.To, "Not a broker.", "en");
5573 return;
5574 }
5575
5576 string LegalId = XML.Attribute(e.Query, "legalId");
5577 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
5578
5579 if (Identity is null)
5580 {
5581 await e.IqErrorItemNotFound(e.To, "Legal Identity not found.", "en");
5582 return;
5583 }
5584
5585 int Count = await Database.Delete<RemoteIdentityReference>(new FilterAnd(
5586 new FilterFieldEqualTo("Account", Identity.Account),
5587 new FilterFieldEqualTo("RemoteDomain", e.From.Domain),
5588 new FilterFieldEqualTo("LegalId", LegalId)));
5589
5590 if (Count > 0)
5591 await e.IqResult(string.Empty, e.To);
5592 else
5593 await e.IqErrorItemNotFound(e.To, "Referene not found.", "en");
5594 }
5595
5596 private async Task User_UpdatingUserLegalId(object Sender, Security.Users.UpdatingLegalIdEventArgs e)
5597 {
5598 if (!string.IsNullOrEmpty(e.OldLegalId))
5599 {
5600 XmppAddress Addr = new XmppAddress(e.OldLegalId);
5601 StringBuilder Xml = new StringBuilder();
5602
5603 Xml.Append("<legalIdReferenceRemoved xmlns='");
5605 Xml.Append("' legalId='");
5606 Xml.Append(XML.Encode(e.OldLegalId));
5607 Xml.Append("'/>");
5608
5609 await this.Server.SendMessage(string.Empty, string.Empty, Gateway.Domain,
5610 Addr.Domain, string.Empty, Xml.ToString());
5611 }
5612
5613 if (!string.IsNullOrEmpty(e.NewLegalId))
5614 {
5615 XmppAddress Addr = new XmppAddress(e.NewLegalId);
5616 StringBuilder Xml = new StringBuilder();
5617
5618 Xml.Append("<legalIdReferenceAdded xmlns='");
5620 Xml.Append("' legalId='");
5621 Xml.Append(XML.Encode(e.NewLegalId));
5622 Xml.Append("'/>");
5623
5624 await this.Server.SendMessage(string.Empty, string.Empty, Gateway.Domain,
5625 Addr.Domain, string.Empty, Xml.ToString());
5626 }
5627 }
5628
5629 #endregion
5630
5631 #region Smart Contracts
5632
5633 internal async Task<KeyValuePair<Contract, IqResultEventArgs>> GetContract(CaseInsensitiveString ContractId)
5634 {
5635 TaskCompletionSource<IqResultEventArgs> T = new TaskCompletionSource<IqResultEventArgs>();
5636 XmppAddress ContractAddress = new XmppAddress(ContractId);
5637
5638 if (this.Server.IsServerDomain(ContractAddress.Domain, true) ||
5639 this.IsComponentDomain(ContractAddress.Domain, true))
5640 {
5641 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
5642 return new KeyValuePair<Contract, IqResultEventArgs>(Contract, null);
5643 }
5644 else
5645 {
5646 if (!await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(ContractAddress.Domain), string.Empty,
5647 "<getContract xmlns='" + NamespaceSmartContracts(NamespaceSet.Current) + "' id='" + XML.Encode(ContractId) + "'/>", true,
5648 (sender2, e2) =>
5649 {
5650 T.TrySetResult(e2);
5651 return Task.CompletedTask;
5652 }, null))
5653 {
5654 return new KeyValuePair<Contract, IqResultEventArgs>(null, null);
5655 }
5656
5657 await T.Task;
5658
5659 IqResultEventArgs e3 = T.Task.Result;
5660 if (!e3.Ok || e3.FirstElement is null)
5661 return new KeyValuePair<Contract, IqResultEventArgs>(null, e3);
5662
5663 ParsedContract Parsed = await Contract.Parse(e3.FirstElement, this);
5664 return new KeyValuePair<Contract, IqResultEventArgs>(Parsed?.Contract, e3);
5665 }
5666 }
5667
5668 private async Task PetitionContractHandler(object Sender, IqEventArgs e)
5669 {
5670 try
5671 {
5672 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
5673 string PetitionId = XML.Attribute(e.Query, "pid");
5674 string Purpose = XML.Attribute(e.Query, "purpose");
5675 string Nonce = XML.Attribute(e.Query, "nonce");
5676 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
5677 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + ContractId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase);
5678
5679 if (!TryGetContext(e.Query, out XmlElement ContextXml, out string _,
5680 out string[] Properties, out string[] Attachments))
5681 {
5682 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
5683 return;
5684 }
5685
5686 (LegalIdentity ReqIdentity, Dictionary<string, string> ReqAttachmentUrls) = await this.ValidateSenderSignature(
5687 e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, null); // TODO: Authorize access to requestor attachments
5688
5689 if (ReqIdentity is null)
5690 return;
5691
5692 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
5693 if (Contract is null)
5694 {
5695 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
5696 return;
5697 }
5698
5699 StringBuilder Msg = new StringBuilder();
5700
5701 Msg.Append("<petitionContractMsg id=\"");
5702 Msg.Append(XML.Encode(ContractId));
5703 Msg.Append("\" pid=\"");
5704 Msg.Append(XML.Encode(PetitionId));
5705 Msg.Append("\" from=\"");
5706 Msg.Append(XML.Encode(e.From.Address.Value));
5707 Msg.Append("\" purpose=\"");
5708 Msg.Append(XML.Encode(Purpose));
5709
5710 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
5711 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
5712 {
5713 Msg.Append("\" clientEp=\"");
5714 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
5715 }
5716 else
5717 {
5718 ClientInformation ClientInfo = await this.GetNetworkIdentity(ReqIdentity.Id, true, false, Contract.Version);
5719 if (!(ClientInfo is null))
5720 {
5721 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
5722
5723 if (!string.IsNullOrEmpty(ClientEndpoint))
5724 {
5725 Msg.Append("\" clientEp=\"");
5726 Msg.Append(XML.Encode(ClientEndpoint));
5727 }
5728 }
5729 }
5730
5731 Msg.Append("\" xmlns=\"");
5732 Msg.Append(NamespaceSmartContracts(Contract.Version));
5733 Msg.Append("\">");
5734
5735 this.Append(Msg, Properties, Attachments, ContextXml,
5736 null, ReqIdentity, Contract.Version, ReqAttachmentUrls);
5737
5738 Msg.Append("</petitionContractMsg>");
5739
5740 string Xml = Msg.ToString();
5741
5742 if (!(Contract.ClientSignatures is null))
5743 {
5744 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
5745 {
5746 await this.Server.SendMessage(string.Empty, string.Empty, e.To, new XmppAddress(ClientSignature.BareJid),
5747 string.Empty, Xml);
5748 }
5749 }
5750
5751 await e.IqResult(string.Empty, e.To);
5752 }
5753 catch (Exception ex)
5754 {
5755 await e.IqError(ex, e.To);
5756 }
5757 }
5758
5759 private async Task PetitionContractResponseHandler(object Sender, IqEventArgs e)
5760 {
5761 try
5762 {
5763 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
5764 string PetitionId = XML.Attribute(e.Query, "pid");
5765 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
5766 bool Response = XML.Attribute(e.Query, "response", false);
5768 XmlElement ContextXml = null;
5769
5770 foreach (XmlNode N in e.Query)
5771 {
5772 if (!(N is XmlElement E))
5773 continue;
5774
5775 if (ContextXml is null)
5776 ContextXml = E;
5777 else
5778 {
5779 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
5780 return;
5781 }
5782 }
5783
5784 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
5785 if (Contract is null)
5786 {
5787 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
5788 return;
5789 }
5790
5791 this.ContractAuthorization(RequestorFullJid.BareJid, e.From.BareJid, ContractId, Response);
5792
5793 StringBuilder Msg = new StringBuilder();
5794
5795 Msg.Append("<petitionContractResponseMsg pid=\"");
5796 Msg.Append(XML.Encode(PetitionId));
5797 Msg.Append("\" response=\"");
5798 Msg.Append(CommonTypes.Encode(Response));
5799
5800 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
5801 !string.IsNullOrEmpty(Connection.RemoteEndPoint))
5802 {
5803 Msg.Append("\" clientEp=\"");
5804 Msg.Append(XML.Encode(Connection.RemoteEndPoint));
5805 }
5806
5807 Msg.Append("\" xmlns=\"");
5808 Msg.Append(NamespaceSmartContracts(Contract.Version));
5809 Msg.Append("\">");
5810
5811 if (!(Contract is null))
5812 await Contract.Serialize(Msg, false, true, true, true, true, true, true, null, this);
5813
5814 if (!(ContextXml is null))
5815 Msg.Append(ContextXml.OuterXml);
5816
5817 Msg.Append("</petitionContractResponseMsg>");
5818
5819 await this.Server.SendMessage(string.Empty, string.Empty, e.To, RequestorFullJid, string.Empty, Msg.ToString());
5820
5821 await e.IqResult(string.Empty, e.To);
5822 }
5823 catch (Exception ex)
5824 {
5825 await e.IqError(ex, e.To);
5826 }
5827 }
5828
5829 internal void ContractAuthorization(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
5830 CaseInsensitiveString ContractId, bool Authorized)
5831 {
5832 this.Authorization("C:", ToBareJid, FromBareJid, ContractId, Authorized ? 1 : 0);
5833 }
5834
5835 internal bool IsAccessToContractAuthorized(CaseInsensitiveString BareJid, CaseInsensitiveString ContractId)
5836 {
5837 CaseInsensitiveString Key = "C:" + ContractId + ":" + BareJid;
5838 return (this.petitions?.TryGetValue(Key, out int i) ?? false) && (i > 0);
5839 }
5840
5841 private async Task CreateContractHandler(object Sender, IqEventArgs e)
5842 {
5843 Profiler Profiler = new Profiler();
5844 ProfilerThread CreateContractThread = Profiler.CreateThread("CreateContractHandler", ProfilerThreadType.StateMachine);
5845 ProfilerThread ParametersThread = Profiler.CreateThread("Parameters", ProfilerThreadType.StateMachine);
5846 Contract Contract = null;
5847
5848 Profiler.Start();
5849 CreateContractThread.Start();
5850 ParametersThread.Start();
5851 try
5852 {
5853 CreateContractThread.NewState("Parsing");
5854
5855 if (!this.Server.IsServerDomain(e.From.Domain, true))
5856 {
5857 await e.IqErrorForbidden(e.To, "Only accounts on the broker can create new contracts.", "en");
5858 return;
5859 }
5860
5861 LinkedList<ClientSignature> SignaturesToTransfer = null;
5862 XmlElement E = null;
5863 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
5864 Dictionary<CaseInsensitiveString, Parameter> Parameters = new Dictionary<CaseInsensitiveString, Parameter>();
5865 LinkedList<CaseInsensitiveString> ParameterOrder = new LinkedList<CaseInsensitiveString>();
5866 Dictionary<CaseInsensitiveString, Parameter> TransientParameters = null;
5867 bool ParametersChecked = false;
5868 bool HasTransientParameters = false;
5869
5870 foreach (XmlNode N in e.Query.ChildNodes)
5871 {
5872 E = N as XmlElement;
5873 if (E is null)
5874 continue;
5875
5876 switch (E.LocalName ?? string.Empty)
5877 {
5878 case "contract":
5879 ParsedContract Parsed = await Contract.Parse(E, this);
5880 Contract = Parsed?.Contract;
5881
5882 if (Contract is null)
5883 {
5884 await e.IqErrorBadRequest(e.To, "Invalid contract.", "en");
5885 return;
5886 }
5887
5888 bool HasStatus = Parsed.HasStatus;
5889 bool ParametersValid = Parsed.ParametersValid;
5890
5891 if (HasStatus)
5892 {
5893 await e.IqErrorBadRequest(e.To, "Status element not permitted when creating new contract.", "en");
5894 return;
5895 }
5896
5898 {
5899 await e.IqErrorBadRequest(e.To, "id attribute must not be set by client.", "en");
5900 return;
5901 }
5902
5903 if (!(Contract.ClientSignatures is null) && Contract.ClientSignatures.Length > 0)
5904 {
5905 await e.IqErrorBadRequest(e.To, "Predefined signatures not permitted.", "en");
5906 return;
5907 }
5908
5909 if (!(Contract.ServerSignature is null))
5910 {
5911 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
5912 return;
5913 }
5914
5915 if (Contract.PartsMode == ContractParts.ExplicitlyDefined && (Contract.Parts is null || Contract.Parts.Length == 0))
5916 {
5917 await e.IqErrorBadRequest(e.To, "No explicit parts defined.", "en");
5918 return;
5919 }
5920
5921 if (!ParametersValid && Contract.PartsMode != ContractParts.TemplateOnly)
5922 {
5923 await e.IqErrorBadRequest(e.To, "Parameter " + Parsed.FirstParameterErrorName +
5924 " has invalid value: " + Parsed.FirstParameterError, "en");
5925 return;
5926 }
5927
5928 ParametersChecked = true;
5929 Contract.State = ContractState.Proposed;
5930 break;
5931
5932 case "template":
5933 CaseInsensitiveString TemplateId = XML.Attribute(E, "id");
5934
5935 if (CaseInsensitiveString.IsNullOrEmpty(TemplateId) || TemplateId.IndexOf('@') < 0)
5936 {
5937 await e.IqErrorBadRequest(e.To, "Invalid contract template identity.", "en");
5938 return;
5939 }
5940
5941 KeyValuePair<Contract, IqResultEventArgs> P2 = await this.GetContract(TemplateId);
5942 Contract = P2.Key;
5943 if (Contract is null)
5944 {
5945 if (string.IsNullOrEmpty(P2.Value?.ErrorText))
5946 {
5947 await e.IqErrorItemNotFound(e.To, "Contract template not found: " +
5948 TemplateId, "en");
5949 }
5950 else
5951 {
5952 await e.IqErrorItemNotFound(e.To, "Unable to get contract template " +
5953 TemplateId + ": " + P2.Value.ErrorText, "en");
5954 }
5955
5956 return;
5957 }
5958
5959 if (!Contract.CanActAsTemplate)
5960 {
5961 await e.IqErrorNotAcceptable(e.To, "Referenced contract cannot be used as a template.", "en");
5962 return;
5963 }
5964
5965 if (Contract.State == ContractState.Obsoleted ||
5966 Contract.State == ContractState.Deleted ||
5967 Contract.State == ContractState.Proposed ||
5968 Contract.State == ContractState.Rejected ||
5969 Contract.State == ContractState.Failed)
5970 {
5971 await e.IqErrorNotAcceptable(e.To, "Referenced contract not in an acceptable state.", "en");
5972 return;
5973 }
5974
5975 if (!await Contract.CanRead(e.From, this.Server, this))
5976 {
5977 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
5978 return;
5979 }
5980
5981 if (!(Contract.ClientSignatures is null))
5982 {
5983 foreach (ClientSignature Signature in Contract.ClientSignatures)
5984 {
5985 if (Signature.Transferable)
5986 {
5987 SignaturesToTransfer ??= new LinkedList<ClientSignature>();
5988 SignaturesToTransfer.AddLast(Signature);
5989 }
5990 }
5991 }
5992
5993 Contract = new Contract()
5994 {
5995 Version = QueryVersion,
5996 ForMachines = Contract.ForMachines,
5997 ForMachinesLocalName = Contract.ForMachinesLocalName,
5998 ForMachinesNamespace = Contract.ForMachinesNamespace,
5999 ForHumans = (HumanReadableText[])Contract.ForHumans?.Clone(),
6000 Roles = (Role[])Contract.Roles?.Clone(),
6001 Parameters = (Parameter[])Contract.Parameters?.Clone(),
6002 State = ContractState.Approved,
6003 TemplateId = CaseInsensitiveString.IsNullOrEmpty(Contract.TemplateId) ? TemplateId : Contract.TemplateId,
6004 SignAfter = Contract.SignAfter,
6005 SignBefore = Contract.SignBefore,
6006 Nonce = null
6007 };
6008
6009 if (HasSecondaryName(Contract, out string SecondaryName,
6010 out string SecondaryNamespace, out SecondaryNameType NameType))
6011 {
6012 Contract.SecondaryType = NameType;
6013 Contract.SecondaryLocalName = SecondaryName;
6014 Contract.SecondaryNamespace = SecondaryNamespace;
6015 }
6016 else
6017 {
6018 Contract.SecondaryType = SecondaryNameType.None;
6019 Contract.SecondaryLocalName = null;
6020 Contract.SecondaryNamespace = null;
6021 }
6022
6023 bool HasVisibility = false;
6024 bool HasDuration = false;
6025 bool HasArchiveReq = false;
6026 bool HasArchiveOpt = false;
6027 bool WellDefined = true;
6028
6029 foreach (XmlAttribute Attr in E.Attributes)
6030 {
6031 switch (Attr.Name)
6032 {
6033 case "visibility":
6034 if (Enum.TryParse<ContractVisibility>(Attr.Value, out ContractVisibility Visibility))
6035 {
6036 Contract.Visibility = Visibility;
6037 HasVisibility = true;
6038 }
6039 else
6040 WellDefined = false;
6041 break;
6042
6043 case "duration":
6044 if (Duration.TryParse(Attr.Value, out Duration D))
6045 {
6046 Contract.Duration = D;
6047 HasDuration = true;
6048 }
6049 else
6050 WellDefined = false;
6051 break;
6052
6053 case "archiveReq":
6054 if (Duration.TryParse(Attr.Value, out D))
6055 {
6056 Contract.ArchiveRequired = D;
6057 HasArchiveReq = true;
6058 }
6059 else
6060 WellDefined = false;
6061 break;
6062
6063 case "archiveOpt":
6064 if (Duration.TryParse(Attr.Value, out D))
6065 {
6066 Contract.ArchiveOptional = D;
6067 HasArchiveOpt = true;
6068 }
6069 else
6070 WellDefined = false;
6071 break;
6072
6073 case "signAfter":
6074 if (XML.TryParse(Attr.Value, out DateTime TP))
6075 Contract.SignAfter = TP;
6076 else
6077 WellDefined = false;
6078 break;
6079
6080 case "signBefore":
6081 if (XML.TryParse(Attr.Value, out TP))
6082 Contract.SignBefore = TP;
6083 else
6084 WellDefined = false;
6085 break;
6086
6087 case "canActAsTemplate":
6088 if (CommonTypes.TryParse(Attr.Value, out bool b))
6089 {
6090 Contract.CanActAsTemplate = b;
6091 HasArchiveOpt = true;
6092 }
6093 else
6094 WellDefined = false;
6095 break;
6096
6097 case "nonce":
6098 try
6099 {
6100 Contract.Nonce = Convert.FromBase64String(Attr.Value);
6101 }
6102 catch (Exception)
6103 {
6104 await e.IqErrorBadRequest(e.To, "Invalid nonce value.", "en");
6105 return;
6106 }
6107 break;
6108
6109 case "xmlns":
6110 case "id":
6111 break;
6112
6113 default:
6114 if (Attr.Prefix != "xmlns")
6115 WellDefined = false;
6116 break;
6117 }
6118 }
6119
6120 if (!(WellDefined && HasVisibility && HasDuration && HasArchiveReq && HasArchiveOpt))
6121 {
6122 await e.IqErrorBadRequest(e.To, "Invalid request, missing attributes.", "en");
6123 return;
6124 }
6125
6126 if (Contract.SignBefore <= Contract.SignAfter)
6127 {
6128 await e.IqErrorBadRequest(e.To, "Signature timepoints invalid.", "en");
6129 return;
6130 }
6131
6132 foreach (XmlNode N2 in E.ChildNodes)
6133 {
6134 if (N2 is XmlElement E2)
6135 {
6136 switch (E2.LocalName)
6137 {
6138 case "parts":
6139 List<Part> Parts = null;
6140 ContractParts? Mode = null;
6141
6142 foreach (XmlNode N3 in E2.ChildNodes)
6143 {
6144 if (N3 is XmlElement E3)
6145 {
6146 switch (E3.LocalName)
6147 {
6148 case "open":
6149 if (Mode.HasValue)
6150 {
6151 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
6152 return;
6153 }
6154
6155 Mode = ContractParts.Open;
6156 break;
6157
6158 case "templateOnly":
6159 if (Mode.HasValue)
6160 {
6161 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
6162 return;
6163 }
6164
6165 Mode = ContractParts.TemplateOnly;
6166 break;
6167
6168 case "part":
6169 if (Mode.HasValue)
6170 {
6171 if (Mode.Value != ContractParts.ExplicitlyDefined)
6172 {
6173 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
6174 return;
6175 }
6176 }
6177 else
6178 Mode = ContractParts.ExplicitlyDefined;
6179
6180 CaseInsensitiveString LegalId = null;
6181 CaseInsensitiveString RoleRef = null;
6182
6183 foreach (XmlAttribute Attr in E3.Attributes)
6184 {
6185 switch (Attr.Name)
6186 {
6187 case "legalId":
6188 LegalId = Attr.Value;
6189 break;
6190
6191 case "role":
6192 RoleRef = Attr.Value;
6193 break;
6194
6195 case "xmlns":
6196 break;
6197
6198 default:
6199 if (Attr.Prefix != "xmlns")
6200 WellDefined = false;
6201 break;
6202 }
6203 }
6204
6206 {
6207 await e.IqErrorBadRequest(e.To, "Invalid part definition.", "en");
6208 return;
6209 }
6210
6211 bool RoleFound = false;
6212
6213 if (!(Contract.Roles is null))
6214 {
6215 foreach (Role Role2 in Contract.Roles)
6216 {
6217 if (Role2.Name == RoleRef)
6218 {
6219 RoleFound = true;
6220 break;
6221 }
6222 }
6223 }
6224
6225 if (!RoleFound)
6226 {
6227 await e.IqErrorBadRequest(e.To, "Undefined role.", "en");
6228 return;
6229 }
6230
6231 Parts ??= new List<Part>();
6232 Parts.Add(new Part()
6233 {
6234 LegalId = LegalId,
6235 Role = RoleRef
6236 });
6237
6238 break;
6239
6240 default:
6241 WellDefined = false;
6242 break;
6243 }
6244 }
6245 }
6246
6247 if (!WellDefined || !Mode.HasValue)
6248 {
6249 await e.IqErrorBadRequest(e.To, "Parts not well-defined.", "en");
6250 return;
6251 }
6252
6253 Contract.PartsMode = Mode.Value;
6254 Contract.Parts = Parts?.ToArray();
6255 break;
6256
6257 case "parameters":
6258 if (Contract.Parameters is null)
6259 {
6260 await e.IqErrorBadRequest(e.To, "Referenced contract does not have parameters.", "en");
6261 return;
6262 }
6263
6264 foreach (Parameter P3 in Contract.Parameters)
6265 {
6266 if (!Parameters.ContainsKey(P3.Name))
6267 {
6268 Parameters[P3.Name] = P3;
6269 ParameterOrder.AddLast(P3.Name);
6270 }
6271 }
6272
6273 foreach (XmlNode N3 in E2.ChildNodes)
6274 {
6275 if (N3 is XmlElement E3)
6276 {
6277 CaseInsensitiveString Name = XML.Attribute(E3, "name");
6279 {
6280 await e.IqErrorBadRequest(e.To, "Missing parameter name.", "en");
6281 return;
6282 }
6283
6284 if (!Parameters.TryGetValue(Name, out Parameter OrgParameter))
6285 {
6286 await e.IqErrorBadRequest(e.To, "Parameter does not exist in original contract: " + Name, "en");
6287 return;
6288 }
6289
6290 Parameter ParameterDefinition = await this.TryParseParameter(E3, e, Name, OrgParameter);
6291 if (ParameterDefinition is null)
6292 return;
6293
6294 Parameters[Name] = ParameterDefinition;
6295 }
6296 }
6297
6299 {
6300 CreateContractThread.NewState("Parameters");
6301
6302 ParametersChecked = true;
6303 KeyValuePair<Parameter, string> P3 = await Contract.CheckParameters(Parameters.Values,
6304 Contract.Duration, this, true, Contract.FirstSignatureAt, ParametersThread);
6305
6306 Parameter FailingParameter = P3.Key;
6307 if (!(FailingParameter is null))
6308 {
6309 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter.Name + " contains invalid value: " + P3.Value, "en");
6310 return;
6311 }
6312 }
6313
6314 int c = Parameters.Count;
6315 int i = 0;
6316 Parameter[] NewParameters = new Parameter[c];
6317
6318 foreach (string Name in ParameterOrder)
6319 NewParameters[i++] = Parameters[Name];
6320
6321 Contract.Parameters = NewParameters;
6322 break;
6323
6324 default:
6325 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
6326 return;
6327 }
6328 }
6329 }
6330 break;
6331
6332 case "transient":
6333 if (Contract.Parameters is null)
6334 {
6335 await e.IqErrorBadRequest(e.To, "Referenced contract does not have parameters.", "en");
6336 return;
6337 }
6338
6339 if (ParameterOrder.First is null)
6340 {
6341 foreach (Parameter P3 in Contract.Parameters)
6342 {
6343 if (!Parameters.ContainsKey(P3.Name))
6344 {
6345 Parameters[P3.Name] = P3;
6346 ParameterOrder.AddLast(P3.Name);
6347 }
6348 }
6349 }
6350
6351 foreach (XmlNode N3 in E.ChildNodes)
6352 {
6353 if (N3 is XmlElement E3)
6354 {
6355 CaseInsensitiveString Name = XML.Attribute(E3, "name");
6357 {
6358 await e.IqErrorBadRequest(e.To, "Missing parameter name.", "en");
6359 return;
6360 }
6361
6362 if (!Parameters.TryGetValue(Name, out Parameter OrgParameter))
6363 {
6364 await e.IqErrorBadRequest(e.To, "Parameter does not exist in original contract: " + Name, "en");
6365 return;
6366 }
6367
6368 Parameter ParameterDefinition = await this.TryParseParameter(E3, e, Name, OrgParameter);
6369 if (ParameterDefinition is null)
6370 return;
6371
6372 TransientParameters ??= new Dictionary<CaseInsensitiveString, Parameter>();
6373 TransientParameters[Name] = ParameterDefinition;
6374 }
6375 }
6376
6377 if (!(TransientParameters is null))
6378 {
6379 foreach (Parameter TransientParameter in TransientParameters.Values)
6380 {
6381 if (Contract.TryGetParameter(TransientParameter.Name, out Parameter ContractParamteter))
6382 {
6383 ContractParamteter.StringValue = TransientParameter.StringValue;
6384 ContractParamteter.ProtectedValue = TransientParameter.ProtectedValue;
6385 }
6386 }
6387 }
6388
6389 if (Contract.PartsMode != ContractParts.TemplateOnly)
6390 {
6391 CreateContractThread.NewState("Parameters");
6392
6393 KeyValuePair<Parameter, string> P4 = await Contract.CheckParameters(Parameters.Values,
6394 Contract.Duration, this, true, Contract.FirstSignatureAt, ParametersThread);
6395
6396 Parameter FailingParameter2 = P4.Key;
6397 if (!(FailingParameter2 is null))
6398 {
6399 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter2.Name + " contains invalid value: " + P4.Value, "en");
6400 return;
6401 }
6402 }
6403
6404 ParametersChecked = true;
6405 HasTransientParameters = true;
6406 break;
6407
6408 default:
6409 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
6410 return;
6411 }
6412 }
6413
6414 if (Contract is null)
6415 {
6416 await e.IqErrorBadRequest(e.To, "No contract provided.", "en");
6417 return;
6418 }
6419
6420 if (Contract.Duration <= Duration.Zero)
6421 {
6422 await e.IqErrorBadRequest(e.To, "Contract duration must be positive.", "en");
6423 return;
6424 }
6425
6426 if (!ParametersChecked)
6427 {
6428 CreateContractThread.NewState("Parameters");
6429
6430 KeyValuePair<Parameter, string> P5 = await Contract.CheckParameters(Parameters.Values, Contract.Duration,
6431 this, true, Contract.FirstSignatureAt, ParametersThread);
6432
6433 Parameter FailingParameter3 = P5.Key;
6434 if (!(FailingParameter3 is null))
6435 {
6436 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter3.Name + " contains invalid value: " + P5.Value, "en");
6437 return;
6438 }
6439 }
6440
6441 Contract.Provider = e.To.Address;
6442 Contract.Account = e.From.Account;
6443 Contract.Created = UtcNowSecond;
6444 Contract.Updated = DateTime.MinValue;
6445
6446 CreateContractThread.NewState("Integrity");
6447
6448 KeyValuePair<ContentIntegrity, string> P = await Contract.CheckContentIntegrity(this);
6449 string IntegrityParameter = P.Value;
6450
6451 switch (P.Key)
6452 {
6453 case ContentIntegrity.Ok:
6454 break;
6455
6456 case ContentIntegrity.RolesNotDefined:
6457 await e.IqErrorBadRequest(e.To, "No roles have been defined.", "en");
6458 return;
6459
6460 case ContentIntegrity.PartsNotDefined:
6461 await e.IqErrorBadRequest(e.To, "Part definition expected.", "en");
6462 return;
6463
6464 case ContentIntegrity.RoleCountsMismatch:
6465 await e.IqErrorBadRequest(e.To, "Role counts are incorrect for role " + IntegrityParameter, "en");
6466 return;
6467
6468 case ContentIntegrity.DuplicateRoleDefinition:
6469 await e.IqErrorBadRequest(e.To, "Duplicate role definition for role " + IntegrityParameter, "en");
6470 return;
6471
6472 case ContentIntegrity.DuplicateParameterDefinition:
6473 await e.IqErrorBadRequest(e.To, "Duplicate parameter definition for parameter " + IntegrityParameter, "en");
6474 return;
6475
6476 case ContentIntegrity.InvalidLegalIdReference:
6477 await e.IqErrorBadRequest(e.To, "Invalid Legal ID reference: " + IntegrityParameter, "en");
6478 return;
6479
6480 case ContentIntegrity.InvalidRoleReference:
6481 await e.IqErrorBadRequest(e.To, "Invalid role reference: " + IntegrityParameter, "en");
6482 return;
6483
6484 case ContentIntegrity.TooFewPartsOfRole:
6485 await e.IqErrorBadRequest(e.To, "Too few parts for role " + IntegrityParameter, "en");
6486 return;
6487
6488 case ContentIntegrity.TooManyPartsOfRole:
6489 await e.IqErrorBadRequest(e.To, "Too many parts for role " + IntegrityParameter, "en");
6490 return;
6491
6492 case ContentIntegrity.InvalidRoleIndex:
6493 await e.IqErrorBadRequest(e.To, "Role index is invalid for role reference parameter " + IntegrityParameter, "en");
6494 return;
6495
6496 case ContentIntegrity.MissingRoleProperty:
6497 await e.IqErrorBadRequest(e.To, "Missing property reference for role reference parameter " + IntegrityParameter, "en");
6498 return;
6499
6500 case ContentIntegrity.InvalidMachineReadable:
6501 await e.IqErrorBadRequest(e.To, "Machine-readable information is missing or is invalid.", "en");
6502 return;
6503
6504 case ContentIntegrity.MissingRoleHumanReadable:
6505 await e.IqErrorBadRequest(e.To, "Missing human-readable information for role " + IntegrityParameter, "en");
6506 return;
6507
6508 case ContentIntegrity.MissingParameterHumanReadable:
6509 await e.IqErrorBadRequest(e.To, "Missing human-readable information for parameter " + IntegrityParameter, "en");
6510 return;
6511
6512 case ContentIntegrity.ParameterValidationExpressionError:
6513 await e.IqErrorBadRequest(e.To, "Validation expression error for parameter " + IntegrityParameter, "en");
6514 return;
6515
6516 case ContentIntegrity.InvalidParameterReference:
6517 await e.IqErrorBadRequest(e.To, "An undefined parameter is referenced: " + IntegrityParameter, "en");
6518 return;
6519
6520 case ContentIntegrity.MissingHumanReadable:
6521 await e.IqErrorBadRequest(e.To, "Missing human-readable information.", "en");
6522 return;
6523
6524 case ContentIntegrity.DuplicateLocalization:
6525 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for contract: " + IntegrityParameter, "en");
6526 return;
6527
6528 case ContentIntegrity.DuplicateRoleLocalization:
6529 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for role: " + IntegrityParameter, "en");
6530 return;
6531
6532 case ContentIntegrity.DuplicateParameterLocalization:
6533 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for parameter: " + IntegrityParameter, "en");
6534 return;
6535
6536 default:
6537 await e.IqErrorServiceUnavailable(e.To, "Contract integrity check failed for unknown reasons.", "en");
6538 return;
6539 }
6540
6541 CreateContractThread.NewState("Legal ID");
6542
6543 LegalIdentity Identity = await this.GetCurrentApprovedLegalIdentityAsync(e.From.Account);
6544 if (Identity is null)
6545 {
6546 await e.IqErrorForbidden(e.To, "No current approved legal identity found for account.", "en");
6547 return;
6548 }
6549
6550 CreateContractThread.NewState("Content");
6551
6552 ProfilerThread SchemaProfiler = Profiler?.CreateThread("Schemas", ProfilerThreadType.StateMachine);
6553 SchemaProfiler?.Start();
6554
6555 try
6556 {
6557 string Errors = await this.ValidateContent(Contract, SchemaProfiler);
6558 if (!string.IsNullOrEmpty(Errors))
6559 {
6560 await e.IqErrorBadRequest(e.To, Errors, "en");
6561 return;
6562 }
6563 }
6564 finally
6565 {
6566 SchemaProfiler?.Idle();
6567 SchemaProfiler?.Stop();
6568 }
6569
6570 StringBuilder Xml = new StringBuilder();
6571
6572 if (!(SignaturesToTransfer is null))
6573 {
6574 CreateContractThread.NewState("Signature Transfer");
6575
6576 Dictionary<string, int> RoleCounters = new Dictionary<string, int>();
6577 List<ClientSignature> Transfered = null;
6578
6579 await Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
6580 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
6581
6582 foreach (ClientSignature Signature in SignaturesToTransfer)
6583 {
6584 Dictionary<string, string> AttachmentUrls;
6585
6586 (Identity, AttachmentUrls) = await this.ValidateSenderSignature(
6587 new XmppAddress(Signature.BareJid), null, Signature.Timestamp,
6588 Data, Signature.DigitalSignature, null); // TODO: Authorize access to requestor attachments
6589
6590 if (!(Identity is null))
6591 {
6592 if (!RoleCounters.TryGetValue(Signature.Role, out int RoleIndex))
6593 RoleIndex = 0;
6594
6595 RoleIndex++;
6596 if (await Contract.TrySetRoleParameters(Signature.Role, RoleIndex,
6598 AttachmentUrls, this) is null)
6599 {
6600 Transfered ??= new List<ClientSignature>();
6601 Transfered.Add(Signature);
6602 RoleCounters[Signature.Role] = RoleIndex;
6603 }
6604 }
6605 }
6606
6607 Contract.ClientSignatures = Transfered?.ToArray();
6608
6609 Xml.Clear();
6610 }
6611
6612 if (HasTransientParameters)
6613 {
6614 CreateContractThread.NewState("Transient Parameters");
6616 }
6617
6618 CreateContractThread.NewState("Object ID");
6619
6620 await Database.Insert(Contract);
6621
6622 CreateContractThread.NewState("Sign");
6623
6624 Contract.ContractId = Contract.ObjectId + "@" + e.To.Address;
6625 await Contract.Sign(this);
6626
6627 if (Contract.CanActAsTemplate)
6628 await RuntimeCounters.IncrementCounter("Legal.Template." + Contract.State.ToString());
6629 else
6630 await RuntimeCounters.IncrementCounter("Legal.Contract." + Contract.State.ToString());
6631
6632 CreateContractThread.NewState("Store");
6633
6634 await Database.Update(Contract);
6635
6636 if (HasTransientParameters)
6637 {
6638 CreateContractThread.NewState("Transient");
6639 this.AddTransientParameters(Contract.ContractId, TransientParameters);
6640 }
6641
6642 CreateContractThread.NewState("Tags");
6643
6644 KeyValuePair<string, object>[] Tags = Contract.GetTags();
6645
6646 switch (Contract.State)
6647 {
6648 case ContractState.Proposed:
6649 Log.Informational("Contract proposal registered.",
6650 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
6651 break;
6652
6653 case ContractState.Approved:
6654 Log.Informational("Contract registered and automatically approved.",
6655 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
6656 break;
6657
6658 default:
6659 Log.Informational("Contract registered.",
6660 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
6661 break;
6662 }
6663
6664 CreateContractThread.NewState("Return Response");
6665
6666 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Contract.ContractId, true);
6667
6668 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
6669 string ContractXml = Xml.ToString();
6670
6671 await e.IqResult(ContractXml, e.To);
6672
6673 if (Contract.State == ContractState.Proposed &&
6675 {
6676 CreateContractThread.NewState("Notifications");
6677
6678 StringBuilder Markdown = new StringBuilder();
6679
6680 Markdown.Append("Contract proposal received: [");
6681 Markdown.Append(MarkdownDocument.Encode(Contract.ContractId));
6682 Markdown.Append("](");
6683 Markdown.Append(Gateway.GetUrl("/Contract.md?ID=" + Contract.ContractId));
6684 Markdown.AppendLine(")");
6685 Markdown.AppendLine();
6686 Output(Markdown, Tags);
6687
6688 await Gateway.SendNotification(Markdown.ToString());
6689 }
6690
6691 CreateContractThread.NewState("Event");
6692
6693 await this.SendContractUpdatedEvent(Contract, true);
6694 }
6695 catch (Exception ex)
6696 {
6697 CreateContractThread.Exception(ex);
6698 await e.IqError(ex, e.To);
6699 }
6700 finally
6701 {
6702 CreateContractThread.Stop();
6703 ParametersThread.Stop();
6704 Profiler.Stop();
6705
6706 if (Profiler.ElapsedSeconds > 2)
6707 {
6708 string Uml = Profiler.ExportPlantUml(TimeUnit.Seconds);
6709
6710 Log.Debug("Slow contract creation:\r\n\r\n```uml\r\n" + Uml + "```",
6711 new KeyValuePair<string, object>("From", e.From.Address),
6712 new KeyValuePair<string, object>("To", e.To.Address),
6713 new KeyValuePair<string, object>("ContractId", Contract?.ContractId),
6714 new KeyValuePair<string, object>("TemplateId", Contract?.TemplateId));
6715 }
6716 }
6717 }
6718
6727 internal static bool HasSecondaryName(Contract Contract, out string LocalName,
6728 out string Namespace, out SecondaryNameType NameType)
6729 {
6730 LocalName = null;
6731 Namespace = null;
6732 NameType = SecondaryNameType.None;
6733
6734 if (Contract.ForMachinesLocalName != "Create" ||
6735 Contract.ForMachinesNamespace != NeuroFeaturesProcessor.NeuroFeaturesNamespace ||
6736 Contract.ForMachinesParsed.DocumentElement is null)
6737 {
6738 return false;
6739 }
6740
6741 try
6742 {
6743 foreach (XmlNode N in Contract.ForMachinesParsed.DocumentElement.ChildNodes)
6744 {
6745 if (N is XmlElement E &&
6746 E.LocalName == "Definition" &&
6748 {
6749 foreach (XmlNode N2 in E.ChildNodes)
6750 {
6751 if (N2 is XmlElement E2)
6752 {
6753 LocalName = E2.LocalName;
6754 Namespace = E2.NamespaceURI;
6755 NameType = SecondaryNameType.TokenCreation;
6756 return true;
6757 }
6758 }
6759
6760 return false;
6761 }
6762 }
6763 }
6764 catch (Exception ex)
6765 {
6766 Log.Exception(ex);
6767 }
6768
6769 return false;
6770 }
6771
6772 internal void AddTransientParameters(CaseInsensitiveString ContractId,
6773 Dictionary<CaseInsensitiveString, Parameter> TransientParameters)
6774 {
6775 this.transientParameters?.Add(ContractId, TransientParameters);
6776 }
6777
6778 private async Task<Parameter> TryParseParameter(XmlElement E3, IqEventArgs e, CaseInsensitiveString Name, Parameter OrgParameter)
6779 {
6780 byte[] ProtectedValue = null;
6781
6782 if (E3.HasAttribute("guide") && XML.Attribute(E3, "guide") != OrgParameter.Guide)
6783 {
6784 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter guide strings.", "en");
6785 return null;
6786 }
6787
6788 if (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression)
6789 {
6790 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6791 return null;
6792 }
6793
6794 if (E3.HasAttribute("protection"))
6795 {
6796 if (XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OrgParameter.Protection)
6797 {
6798 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter protection rules.", "en");
6799 return null;
6800 }
6801 }
6802
6803 if (E3.HasAttribute("protected"))
6804 {
6805 try
6806 {
6807 ProtectedValue = Convert.FromBase64String(XML.Attribute(E3, "protected"));
6808 }
6809 catch (Exception)
6810 {
6811 await e.IqErrorBadRequest(e.To, "Invalid base64-encoded protected value.", "en");
6812 return null;
6813 }
6814 }
6815 else if (OrgParameter.Protection == ProtectionLevel.Transient)
6816 ProtectedValue = OrgParameter.ProtectedValue;
6817
6818 switch (E3.LocalName)
6819 {
6820 case "stringParameter":
6821 if (!(OrgParameter is StringParameter OldStringParameter))
6822 {
6823 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6824 return null;
6825 }
6826
6827 if ((E3.HasAttribute("regEx") && XML.Attribute(E3, "regEx") != OldStringParameter.RegEx) ||
6828 (E3.HasAttribute("min") && XML.Attribute(E3, "min") != OldStringParameter.Min) ||
6829 (E3.HasAttribute("max") && XML.Attribute(E3, "max") != OldStringParameter.Max) ||
6830 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldStringParameter.MinIncluded) ||
6831 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldStringParameter.MaxIncluded) ||
6832 (E3.HasAttribute("minLength") && XML.Attribute(E3, "minLength", 0) != OldStringParameter.MinLength) ||
6833 (E3.HasAttribute("maxLength") && XML.Attribute(E3, "maxLength", 0) != OldStringParameter.MaxLength) ||
6834 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldStringParameter.Protection) ||
6835 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6836 {
6837 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6838 return null;
6839 }
6840
6841 return new StringParameter()
6842 {
6843 Name = Name,
6844 Value = XML.Attribute(E3, "value"),
6845 Guide = OrgParameter.Guide,
6846 Expression = OrgParameter.Expression,
6847 RegEx = OldStringParameter.RegEx,
6848 Min = OldStringParameter.Min,
6849 Max = OldStringParameter.Max,
6850 MinIncluded = OldStringParameter.MinIncluded,
6851 MaxIncluded = OldStringParameter.MaxIncluded,
6852 MinLength = OldStringParameter.MinLength,
6853 MaxLength = OldStringParameter.MaxLength,
6854 Descriptions = OrgParameter.Descriptions,
6855 Protection = OrgParameter.Protection,
6856 ProtectedValue = ProtectedValue
6857 };
6858
6859 case "numericalParameter":
6860 if (!(OrgParameter is NumericalParameter OldNumericalParameter))
6861 {
6862 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6863 return null;
6864 }
6865
6866 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", 0.0m) != OldNumericalParameter.Min) ||
6867 (E3.HasAttribute("max") && XML.Attribute(E3, "max", 0.0m) != OldNumericalParameter.Max) ||
6868 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldNumericalParameter.MinIncluded) ||
6869 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldNumericalParameter.MaxIncluded) ||
6870 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldNumericalParameter.Protection) ||
6871 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6872 {
6873 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6874 return null;
6875 }
6876
6877 return new NumericalParameter()
6878 {
6879 Name = Name,
6880 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", 0.0m) : (decimal?)null,
6881 Guide = OrgParameter.Guide,
6882 Expression = OrgParameter.Expression,
6883 Min = OldNumericalParameter.Min,
6884 Max = OldNumericalParameter.Max,
6885 MinIncluded = OldNumericalParameter.MinIncluded,
6886 MaxIncluded = OldNumericalParameter.MaxIncluded,
6887 Descriptions = OrgParameter.Descriptions,
6888 Protection = OrgParameter.Protection,
6889 ProtectedValue = ProtectedValue
6890 };
6891
6892 case "booleanParameter":
6893 if (!(OrgParameter is BooleanParameter OldBooleanParameter))
6894 {
6895 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6896 return null;
6897 }
6898
6899 if ((E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldBooleanParameter.Protection) ||
6900 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6901 {
6902 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6903 return null;
6904 }
6905
6906 return new BooleanParameter()
6907 {
6908 Name = Name,
6909 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", false) : (bool?)null,
6910 Guide = OrgParameter.Guide,
6911 Expression = OrgParameter.Expression,
6912 Descriptions = OrgParameter.Descriptions,
6913 Protection = OrgParameter.Protection,
6914 ProtectedValue = ProtectedValue
6915 };
6916
6917 case "dateParameter":
6918 if (!(OrgParameter is DateParameter OldDateParameter))
6919 {
6920 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6921 return null;
6922 }
6923
6924 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", DateTime.MinValue) != OldDateParameter.Min) ||
6925 (E3.HasAttribute("max") && XML.Attribute(E3, "max", DateTime.MinValue) != OldDateParameter.Max) ||
6926 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDateParameter.MinIncluded) ||
6927 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDateParameter.MaxIncluded) ||
6928 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDateParameter.Protection) ||
6929 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6930 {
6931 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6932 return null;
6933 }
6934
6935 return new DateParameter()
6936 {
6937 Name = Name,
6938 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", DateTime.MinValue).Date : (DateTime?)null,
6939 Guide = OrgParameter.Guide,
6940 Expression = OrgParameter.Expression,
6941 Min = OldDateParameter.Min,
6942 Max = OldDateParameter.Max,
6943 MinIncluded = OldDateParameter.MinIncluded,
6944 MaxIncluded = OldDateParameter.MaxIncluded,
6945 Descriptions = OrgParameter.Descriptions,
6946 Protection = OrgParameter.Protection,
6947 ProtectedValue = ProtectedValue
6948 };
6949
6950 case "dateTimeParameter":
6951 if (!(OrgParameter is DateTimeParameter OldDateTimeParameter))
6952 {
6953 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6954 return null;
6955 }
6956
6957 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", DateTime.MinValue) != OldDateTimeParameter.Min) ||
6958 (E3.HasAttribute("max") && XML.Attribute(E3, "max", DateTime.MinValue) != OldDateTimeParameter.Max) ||
6959 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDateTimeParameter.MinIncluded) ||
6960 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDateTimeParameter.MaxIncluded) ||
6961 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDateTimeParameter.Protection) ||
6962 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6963 {
6964 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6965 return null;
6966 }
6967
6968 return new DateTimeParameter()
6969 {
6970 Name = Name,
6971 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", DateTime.MinValue) : (DateTime?)null,
6972 Guide = OrgParameter.Guide,
6973 Expression = OrgParameter.Expression,
6974 Min = OldDateTimeParameter.Min,
6975 Max = OldDateTimeParameter.Max,
6976 MinIncluded = OldDateTimeParameter.MinIncluded,
6977 MaxIncluded = OldDateTimeParameter.MaxIncluded,
6978 Descriptions = OrgParameter.Descriptions,
6979 Protection = OrgParameter.Protection,
6980 ProtectedValue = ProtectedValue
6981 };
6982
6983 case "timeParameter":
6984 if (!(OrgParameter is TimeParameter OldTimeParameter))
6985 {
6986 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
6987 return null;
6988 }
6989
6990 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", TimeSpan.Zero) != OldTimeParameter.Min) ||
6991 (E3.HasAttribute("max") && XML.Attribute(E3, "max", TimeSpan.Zero) != OldTimeParameter.Max) ||
6992 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldTimeParameter.MinIncluded) ||
6993 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldTimeParameter.MaxIncluded) ||
6994 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldTimeParameter.Protection) ||
6995 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
6996 {
6997 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
6998 return null;
6999 }
7000
7001 return new TimeParameter()
7002 {
7003 Name = Name,
7004 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", TimeSpan.Zero) : (TimeSpan?)null,
7005 Guide = OrgParameter.Guide,
7006 Expression = OrgParameter.Expression,
7007 Min = OldTimeParameter.Min,
7008 Max = OldTimeParameter.Max,
7009 MinIncluded = OldTimeParameter.MinIncluded,
7010 MaxIncluded = OldTimeParameter.MaxIncluded,
7011 Descriptions = OrgParameter.Descriptions,
7012 Protection = OrgParameter.Protection,
7013 ProtectedValue = ProtectedValue
7014 };
7015
7016 case "durationParameter":
7017 if (!(OrgParameter is DurationParameter OldDurationParameter))
7018 {
7019 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7020 return null;
7021 }
7022
7023 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", Duration.Zero) != OldDurationParameter.Min) ||
7024 (E3.HasAttribute("max") && XML.Attribute(E3, "max", Duration.Zero) != OldDurationParameter.Max) ||
7025 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDurationParameter.MinIncluded) ||
7026 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDurationParameter.MaxIncluded) ||
7027 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDurationParameter.Protection) ||
7028 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
7029 {
7030 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7031 return null;
7032 }
7033
7034 return new DurationParameter()
7035 {
7036 Name = Name,
7037 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", Duration.Zero) : (Duration?)null,
7038 Guide = OrgParameter.Guide,
7039 Expression = OrgParameter.Expression,
7040 Min = OldDurationParameter.Min,
7041 Max = OldDurationParameter.Max,
7042 MinIncluded = OldDurationParameter.MinIncluded,
7043 MaxIncluded = OldDurationParameter.MaxIncluded,
7044 Descriptions = OrgParameter.Descriptions,
7045 Protection = OrgParameter.Protection,
7046 ProtectedValue = ProtectedValue
7047 };
7048
7049 case "geoParameter":
7050 if (!(OrgParameter is GeoParameter OldGeoParameter))
7051 {
7052 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7053 return null;
7054 }
7055
7056 if ((E3.HasAttribute("min") && GeoParameter.GeoPositionAttribute(E3, "min") != OldGeoParameter.Min) ||
7057 (E3.HasAttribute("max") && GeoParameter.GeoPositionAttribute(E3, "max") != OldGeoParameter.Max) ||
7058 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldGeoParameter.MinIncluded) ||
7059 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldGeoParameter.MaxIncluded) ||
7060 (E3.HasAttribute("altitude") && XML.Attribute(E3, "altitude", AltitudeUse.Optional) != OldGeoParameter.Altitude) ||
7061 (E3.HasAttribute("contractLocation") && XML.Attribute(E3, "contractLocation", false) != OldGeoParameter.ContractLocation) ||
7062 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldGeoParameter.Protection) ||
7063 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
7064 {
7065 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7066 return null;
7067 }
7068
7069 return new GeoParameter()
7070 {
7071 Name = Name,
7072 Value = GeoParameter.GeoPositionAttribute(E3, "value"),
7073 Guide = OrgParameter.Guide,
7074 Expression = OrgParameter.Expression,
7075 Min = OldGeoParameter.Min,
7076 Max = OldGeoParameter.Max,
7077 MinIncluded = OldGeoParameter.MinIncluded,
7078 MaxIncluded = OldGeoParameter.MaxIncluded,
7079 Altitude = OldGeoParameter.Altitude,
7080 ContractLocation = OldGeoParameter.ContractLocation,
7081 Descriptions = OrgParameter.Descriptions,
7082 Protection = OrgParameter.Protection,
7083 ProtectedValue = ProtectedValue
7084 };
7085
7086 case "calcParameter":
7087 if (!(OrgParameter is CalcParameter))
7088 {
7089 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7090 return null;
7091 }
7092
7093 if (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression)
7094 {
7095 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7096 return null;
7097 }
7098
7099 return new CalcParameter()
7100 {
7101 Name = Name,
7102 Guide = OrgParameter.Guide,
7103 Expression = OrgParameter.Expression,
7104 Descriptions = OrgParameter.Descriptions,
7105 Protection = OrgParameter.Protection,
7106 ProtectedValue = ProtectedValue
7107 };
7108
7109 case "roleParameter":
7110 if (!(OrgParameter is RoleParameter OldRoleParameter))
7111 {
7112 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7113 return null;
7114 }
7115
7116 if ((E3.HasAttribute("role") && XML.Attribute(E3, "role") != OldRoleParameter.Role) ||
7117 (E3.HasAttribute("index") && XML.Attribute(E3, "index", 0) != OldRoleParameter.Index) ||
7118 (E3.HasAttribute("property") && XML.Attribute(E3, "property") != OldRoleParameter.Property) ||
7119 (E3.HasAttribute("required") && XML.Attribute(E3, "required", false) != OldRoleParameter.Required) ||
7120 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldRoleParameter.Protection) ||
7121 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
7122 {
7123 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7124 return null;
7125 }
7126
7127 return new RoleParameter()
7128 {
7129 Name = Name,
7130 Role = OldRoleParameter.Role,
7131 Index = OldRoleParameter.Index,
7132 Property = OldRoleParameter.Property,
7133 Required = OldRoleParameter.Required,
7134 Guide = OrgParameter.Guide,
7135 Expression = OrgParameter.Expression,
7136 Descriptions = OrgParameter.Descriptions,
7137 Protection = OrgParameter.Protection,
7138 ProtectedValue = ProtectedValue
7139 };
7140
7141 case "contractReferenceParameter":
7142 if (!(OrgParameter is ContractReferenceParameter OldContractReferenceParameter))
7143 {
7144 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7145 return null;
7146 }
7147
7148 if ((E3.HasAttribute("localName") && XML.Attribute(E3, "localName") != OldContractReferenceParameter.LocalName) ||
7149 (E3.HasAttribute("namespace") && XML.Attribute(E3, "namespace") != OldContractReferenceParameter.Namespace) ||
7150 (E3.HasAttribute("templateId") && XML.Attribute(E3, "templateId") != OldContractReferenceParameter.TemplateId) ||
7151 (E3.HasAttribute("provider") && XML.Attribute(E3, "provider") != OldContractReferenceParameter.Provider) ||
7152 (E3.HasAttribute("creatorRole") && XML.Attribute(E3, "creatorRole") != OldContractReferenceParameter.CreatorRole) ||
7153 (E3.HasAttribute("required") && XML.Attribute(E3, "required", false) != OldContractReferenceParameter.Required) ||
7154 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldContractReferenceParameter.Protection) ||
7155 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
7156 {
7157 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7158 return null;
7159 }
7160
7161 return new ContractReferenceParameter()
7162 {
7163 Name = Name,
7164 Value = XML.Attribute(E3, "value"),
7165 Guide = OrgParameter.Guide,
7166 Expression = OrgParameter.Expression,
7167 Descriptions = OrgParameter.Descriptions,
7168 Labels = OldContractReferenceParameter.Labels,
7169 LocalName = OldContractReferenceParameter.LocalName,
7170 Namespace = OldContractReferenceParameter.Namespace,
7171 TemplateId = OldContractReferenceParameter.TemplateId,
7172 Provider = OldContractReferenceParameter.Provider,
7173 CreatorRole = OldContractReferenceParameter.CreatorRole,
7174 Required = OldContractReferenceParameter.Required,
7175 Protection = OrgParameter.Protection,
7176 ProtectedValue = ProtectedValue
7177 };
7178
7179 case "attachmentParameter":
7180 if (!(OrgParameter is AttachmentParameter OldAttachmentParameter))
7181 {
7182 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
7183 return null;
7184 }
7185
7186 if ((E3.HasAttribute("required") && XML.Attribute(E3, "required", false) != OldAttachmentParameter.Required) ||
7187 (E3.HasAttribute("contentType") && XML.Attribute(E3, "contentType") != OldAttachmentParameter.ContentType) ||
7188 (E3.HasAttribute("minSize") && XML.Attribute(E3, "minSize", 0) != OldAttachmentParameter.MinSize) ||
7189 (E3.HasAttribute("maxSize") && XML.Attribute(E3, "maxSize", 0) != OldAttachmentParameter.MaxSize) ||
7190 (E3.HasAttribute("minWidth") && XML.Attribute(E3, "minWidth", 0) != OldAttachmentParameter.MinWidth) ||
7191 (E3.HasAttribute("maxWidth") && XML.Attribute(E3, "maxWidth", 0) != OldAttachmentParameter.MaxWidth) ||
7192 (E3.HasAttribute("minHeight") && XML.Attribute(E3, "minHeight", 0) != OldAttachmentParameter.MinHeight) ||
7193 (E3.HasAttribute("maxHeight") && XML.Attribute(E3, "maxHeight", 0) != OldAttachmentParameter.MaxHeight) ||
7194 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldAttachmentParameter.Protection) ||
7195 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
7196 {
7197 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
7198 return null;
7199 }
7200
7201 return new AttachmentParameter()
7202 {
7203 Name = Name,
7204 Value = XML.Attribute(E3, "value"),
7205 Guide = OrgParameter.Guide,
7206 Expression = OrgParameter.Expression,
7207 Required = OldAttachmentParameter.Required,
7208 ContentType = OldAttachmentParameter.ContentType,
7209 MinSize = OldAttachmentParameter.MinSize,
7210 MaxSize = OldAttachmentParameter.MaxSize,
7211 MinWidth = OldAttachmentParameter.MinWidth,
7212 MaxWidth = OldAttachmentParameter.MaxWidth,
7213 MinHeight = OldAttachmentParameter.MinHeight,
7214 MaxHeight = OldAttachmentParameter.MaxHeight,
7215 Descriptions = OrgParameter.Descriptions,
7216 Protection = OrgParameter.Protection,
7217 ProtectedValue = ProtectedValue
7218 };
7219
7220 default:
7221 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
7222 return null;
7223 }
7224 }
7225
7226 internal async Task<string> CheckContentIntegrity(Contract Contract)
7227 {
7228 KeyValuePair<ContentIntegrity, string> P = await Contract.CheckContentIntegrity(this);
7229 string IntegrityParameter = P.Value;
7230
7231 return P.Key switch
7232 {
7233 ContentIntegrity.Ok => null,
7234 ContentIntegrity.RolesNotDefined => "No roles have been defined.",
7235 ContentIntegrity.PartsNotDefined => "Part definition expected.",
7236 ContentIntegrity.RoleCountsMismatch => "Role counts are incorrect for role " + IntegrityParameter,
7237 ContentIntegrity.DuplicateRoleDefinition => "Duplicate role definition for role " + IntegrityParameter,
7238 ContentIntegrity.DuplicateParameterDefinition => "Duplicate parameter definition for parameter " + IntegrityParameter,
7239 ContentIntegrity.InvalidLegalIdReference => "Invalid Legal ID reference: " + IntegrityParameter,
7240 ContentIntegrity.InvalidRoleReference => "Invalid role reference: " + IntegrityParameter,
7241 ContentIntegrity.TooFewPartsOfRole => "Too few parts for role " + IntegrityParameter,
7242 ContentIntegrity.TooManyPartsOfRole => "Too many parts for role " + IntegrityParameter,
7243 ContentIntegrity.InvalidRoleIndex => "Role index is invalid for role reference parameter " + IntegrityParameter,
7244 ContentIntegrity.MissingRoleProperty => "Missing property reference for role reference parameter " + IntegrityParameter,
7245 ContentIntegrity.InvalidMachineReadable => "Machine-readable information is missing or is invalid.",
7246 ContentIntegrity.MissingRoleHumanReadable => "Missing human-readable information for role " + IntegrityParameter,
7247 ContentIntegrity.MissingParameterHumanReadable => "Missing human-readable information for parameter " + IntegrityParameter,
7248 ContentIntegrity.ParameterValidationExpressionError => "Validation expression error for parameter " + IntegrityParameter,
7249 ContentIntegrity.InvalidParameterReference => "An undefined parameter is referenced: " + IntegrityParameter,
7250 ContentIntegrity.MissingHumanReadable => "Missing human-readable information.",
7251 ContentIntegrity.DuplicateLocalization => "Duplication language reference for human-readable text for contract: " + IntegrityParameter,
7252 ContentIntegrity.DuplicateRoleLocalization => "Duplication language reference for human-readable text for role: " + IntegrityParameter,
7253 ContentIntegrity.DuplicateParameterLocalization => "Duplication language reference for human-readable text for parameter: " + IntegrityParameter,
7254 _ => "Contract integrity check failed for unknown reasons.",
7255 };
7256 }
7257
7258 internal async Task<string> ValidateContent(Contract Contract, ProfilerThread Profiler)
7259 {
7260 Contract.ContentSchemaDigest = null;
7261 Contract.ContentSchemaHashFunction = HashFunction.SHA256;
7262
7263 if (string.IsNullOrEmpty(Contract.ForMachines))
7264 return "No machine-readable content.";
7265
7266 XmlDocument Doc;
7267
7268 try
7269 {
7270 Doc = Contract.ForMachinesParsed;
7271
7272 if (Doc.DocumentElement is null)
7273 return "Invalid machine-readable XML: No root element.";
7274 }
7275 catch (XmlException ex)
7276 {
7277 ex = XML.AnnotateException(ex, Contract.ForMachines);
7278 return "Invalid machine-readable XML: " + ex.Message;
7279 }
7280 catch (Exception ex)
7281 {
7282 return "Invalid machine-readable XML: " + ex.Message;
7283 }
7284
7285 (string ErrorMessage, Dictionary<string, ValidationSchema> Schemas) = await this.ValidateContent(Doc, Profiler);
7286 if (!string.IsNullOrEmpty(ErrorMessage))
7287 return ErrorMessage;
7288
7289 int NrSchemas = Schemas.Count;
7290 if (NrSchemas == 0 || !Schemas.ContainsKey(Contract.ForMachinesNamespace))
7291 return "Machine-readable content not defined by schemas.";
7292
7293 int i = 0;
7294 int c = Schemas.Count;
7295 SchemaReference[] References = new SchemaReference[c];
7296
7297 foreach (ValidationSchema Schema2 in Schemas.Values)
7298 {
7299 References[i++] = new SchemaReference()
7300 {
7301 Namespace = Schema2.Namespace,
7302 Digest = Convert.FromBase64String(Schema2.HashBase64),
7303 Algorithm = Schema2.Function
7304 };
7305 }
7306
7307 Contract.SchemaReferences = References;
7308
7309 if (Contract.TryGetSchemaReference(Contract.ForMachinesNamespace, out SchemaReference Ref))
7310 {
7311 Contract.ContentSchemaDigest = Ref.Digest;
7312 Contract.ContentSchemaHashFunction = Ref.Algorithm;
7313 }
7314 else
7315 return "M2M content schema not found.";
7316
7317 return null;
7318 }
7319
7320 internal async Task<(string, Dictionary<string, ValidationSchema>)> ValidateContent(XmlDocument Doc, ProfilerThread Profiler)
7321 {
7322 Profiler?.NewState("Checking");
7323
7324 Dictionary<string, ValidationSchema> Schemas = new Dictionary<string, ValidationSchema>();
7326 {
7327 Doc.DocumentElement
7328 };
7329 XmlElement E;
7330 string LastNamespace = null;
7331 string Namespace;
7332
7333 while (ToCheck.HasFirstItem)
7334 {
7335 E = ToCheck.RemoveFirst();
7336 Namespace = E.NamespaceURI;
7337 if (!string.IsNullOrEmpty(Namespace) && Namespace != LastNamespace)
7338 {
7339 Schemas[Namespace] = null;
7340 LastNamespace = Namespace;
7341 }
7342
7343 if (E.HasAttributes)
7344 {
7345 foreach (XmlAttribute Attr in E.Attributes)
7346 {
7347 Namespace = Attr.NamespaceURI;
7348
7349 if (!string.IsNullOrEmpty(Namespace) &&
7350 Namespace != LastNamespace &&
7351 Namespace != "http://www.w3.org/XML/1998/namespace" &&
7352 Namespace != "http://www.w3.org/2000/xmlns/")
7353 {
7354 Schemas[Namespace] = null; // Only change LastNamespace when element namespaces change.
7355 }
7356 }
7357 }
7358
7359 foreach (XmlNode N in E.ChildNodes)
7360 {
7361 if (N is XmlElement E2)
7362 ToCheck.Add(E2);
7363 }
7364 }
7365
7366 int NrSchemas = Schemas.Count;
7367 ValidationSchema Schema;
7368 string ErrorMsg;
7369 Dictionary<string, bool> Loaded = new Dictionary<string, bool>();
7370 string[] Namespaces;
7371 bool Added;
7372
7373 do
7374 {
7375 Added = false;
7376
7377 Namespaces = new string[NrSchemas];
7378 Schemas.Keys.CopyTo(Namespaces, 0);
7379
7380 foreach (string Namespace2 in Namespaces)
7381 {
7382 if (Loaded.ContainsKey(Namespace2))
7383 continue;
7384
7385 Profiler?.NewState(Namespace2);
7386
7387 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(new FilterFieldEqualTo("Namespace", Namespace2), "-Created");
7388
7389 if (Schema is null)
7390 {
7391 (Schema, ErrorMsg) = await this.LoadSchema(Namespace2, null);
7392 if (!string.IsNullOrEmpty(ErrorMsg))
7393 return (ErrorMsg, null);
7394
7395 Loaded[Namespace2] = true;
7396 }
7397
7398 Schemas[Namespace2] = Schema;
7399
7400 string[] Imports = GetImports(Schema.Parsed);
7401
7402 foreach (string Import in Imports)
7403 {
7404 if (!Schemas.ContainsKey(Import))
7405 {
7406 Schemas[Import] = null;
7407 NrSchemas++;
7408 Added = true;
7409 }
7410 }
7411 }
7412 }
7413 while (Added);
7414
7415 Profiler?.NewState("Validating");
7416
7417 ErrorMsg = this.ValidateXml(Schemas.Values, Doc);
7418 if (!string.IsNullOrEmpty(ErrorMsg))
7419 {
7420 bool Changed = false;
7421
7422 foreach (string Namespace2 in Namespaces)
7423 {
7424 if (Loaded.ContainsKey(Namespace2))
7425 continue;
7426
7427 Schema = Schemas[Namespace2];
7428 (ValidationSchema Schema2, string ErrorMsg2) = await this.LoadSchema(Namespace2, Schema);
7429 if (!string.IsNullOrEmpty(ErrorMsg2))
7430 return (ErrorMsg2, null);
7431
7432 if (Schema2 != Schema)
7433 {
7434 Schemas[Namespace2] = Schema2;
7435 Changed = true;
7436 }
7437 }
7438
7439 if (!Changed)
7440 return (ErrorMsg, null);
7441
7442 ErrorMsg = this.ValidateXml(Schemas.Values, Doc);
7443 if (!string.IsNullOrEmpty(ErrorMsg))
7444 return (ErrorMsg, null);
7445 }
7446
7447 return (null, Schemas);
7448 }
7449
7450 private static string[] GetImports(XmlSchema Schema)
7451 {
7452 Dictionary<string, bool> Result = null;
7454
7455 foreach (XmlSchemaObject Obj in Schema.Includes)
7456 ToCheck.Add(Obj);
7457
7458 while (ToCheck.HasFirstItem)
7459 {
7460 XmlSchemaObject Obj = ToCheck.RemoveFirst();
7461
7462 if (Obj is XmlSchemaExternal External)
7463 {
7464 if (!(External.Schema is null))
7465 {
7466 foreach (XmlSchemaObject Obj2 in External.Schema.Includes)
7467 ToCheck.Add(Obj2);
7468 }
7469
7470 if (Obj is XmlSchemaImport Import)
7471 {
7472 Result ??= new Dictionary<string, bool>();
7473 Result[Import.Namespace] = true;
7474 }
7475 }
7476 }
7477
7478 if (Result is null)
7479 return Array.Empty<string>();
7480
7481 string[] Result2 = new string[Result.Count];
7482 Result.Keys.CopyTo(Result2, 0);
7483
7484 return Result2;
7485 }
7486
7487 private async Task<(ValidationSchema, string)> LoadSchema(string Namespace, ValidationSchema PrevSchema)
7488 {
7489 Stream File;
7490 string ContentType;
7491
7492 switch (Namespace)
7493 {
7494 case "http://www.w3.org/XML/1998/namespace":
7495 Type T = typeof(Networking.XMPP.Contracts.ContractsClient);
7496 Assembly A = T.Assembly;
7497 File = A.GetManifestResourceStream(T.Namespace + ".Schema.Xml.xsd");
7499 break;
7500
7501 case "http://www.w3.org/2000/xmlns/":
7502 T = typeof(Networking.XMPP.Contracts.ContractsClient);
7503 A = T.Assembly;
7504 File = A.GetManifestResourceStream(T.Namespace + ".Schema.Xmlns.xsd");
7506 break;
7507
7508 default:
7509 Uri Uri = new Uri(Namespace);
7510
7511 if (!InternetContent.CanGet(Uri, out Grade _, out IContentGetter Getter))
7512 return (null, "Schema not downloadable: " + Namespace);
7513
7514 try
7515 {
7516 ContentStreamResponse P = await Getter.GetTempStreamAsync(Uri, null, null, 10000,
7517 new KeyValuePair<string, string>("Accept", "application/xml, text/xml"),
7518 new KeyValuePair<string, string>("Accept-Charset", "utf-8"));
7519
7520 if (P.HasError)
7521 return (null, "Unable to get schema file " + Namespace + ". The following error was reported: " + P.Error.Message);
7522
7523 File = P.Encoded;
7524 ContentType = P.ContentType;
7525 }
7526 catch (Exception ex)
7527 {
7528 return (null, "Unable to get schema file " + Namespace + ". The following error was reported: " + ex.Message);
7529 }
7530 break;
7531 }
7532
7533 try
7534 {
7535 if (File.Length > int.MaxValue)
7536 throw new OutOfMemoryException("Schema file too large.");
7537
7538 File.Position = 0;
7539
7540 byte[] Bin = await File.ReadAllAsync();
7541
7542 string Digest = Convert.ToBase64String(Hashes.ComputeHash(HashFunction.SHA384, Bin));
7543
7544 if (!(PrevSchema is null) && Digest == PrevSchema.HashBase64)
7545 return (PrevSchema, null);
7546
7547 ValidationSchema Schema = new ValidationSchema()
7548 {
7549 Namespace = Namespace,
7550 Function = HashFunction.SHA384,
7551 HashBase64 = Digest,
7552 Created = UtcNowSecond,
7553 XmlSchema = Bin,
7555 };
7556
7557 await Database.Insert(Schema);
7558
7559 return (Schema, null);
7560 }
7561 finally
7562 {
7563 File.Dispose();
7564 }
7565 }
7566
7567 private string ValidateXml(IEnumerable<ValidationSchema> Schemas, XmlDocument Xml)
7568 {
7569 StringBuilder Errors = null;
7570 StringBuilder Warnings = null;
7571
7572 try
7573 {
7574 if (Xml.Schemas.Count > 0)
7575 {
7576 LinkedList<XmlSchema> ToRemove = new LinkedList<XmlSchema>();
7577
7578 foreach (XmlSchema Schema in Xml.Schemas.Schemas())
7579 ToRemove.AddLast(Schema);
7580
7581 foreach (XmlSchema Schema in ToRemove)
7582 Xml.Schemas.Remove(Schema);
7583 }
7584
7585 foreach (ValidationSchema Schema in Schemas)
7586 Xml.Schemas.Add(Schema.Parsed);
7587
7588 Xml.Validate((sender2, e2) =>
7589 {
7590 switch (e2.Severity)
7591 {
7592 case XmlSeverityType.Error:
7593 Errors ??= new StringBuilder();
7594 Errors.AppendLine(e2.Message);
7595 break;
7596
7597 case XmlSeverityType.Warning:
7598 Warnings ??= new StringBuilder();
7599 Warnings.AppendLine(e2.Message);
7600 break;
7601 }
7602 });
7603 }
7604 catch (Exception ex)
7605 {
7606 Errors ??= new StringBuilder();
7607 Errors.AppendLine(ex.Message);
7608 }
7609
7610 if (!(Errors is null) || !(Warnings is null))
7611 {
7612 StringBuilder Report = new StringBuilder();
7613
7614 if (!(Errors is null))
7615 {
7616 Report.AppendLine("Errors found during validation:");
7617 Report.AppendLine(new string('-', 40));
7618 Report.Append(Errors.ToString());
7619
7620 if (!(Warnings is null))
7621 Report.AppendLine();
7622 }
7623
7624 if (!(Warnings is null))
7625 {
7626 Report.AppendLine("Warnings found during validation:");
7627 Report.AppendLine(new string('-', 40));
7628 Report.Append(Warnings.ToString());
7629 }
7630
7631 return Report.ToString();
7632 }
7633
7634 return null;
7635 }
7636
7642 public static async Task<Tuple<byte[], XmlSchema>> LoadSchema(string Url)
7643 {
7644 try
7645 {
7646 HttpRequestMessage Request = null;
7647 HttpResponseMessage Response = null;
7648 HttpClient Client = new HttpClient();
7649
7650 try
7651 {
7652 Client.Timeout = TimeSpan.FromMilliseconds(30000);
7653 Client.DefaultRequestHeaders.ExpectContinue = false;
7654
7655 Uri Uri = new Uri(Url);
7656 Request = new HttpRequestMessage(HttpMethod.Head, Uri);
7657 Request.Headers.Add("Accept", "application/xml, text/xml");
7658 Request.Headers.Add("Accept-Charset", "utf-8");
7659
7660 Response = await Client.SendAsync(Request);
7661 if (!Response.IsSuccessStatusCode)
7662 return null;
7663
7664 Request.Dispose();
7665 Response.Dispose();
7666
7667 Request = new HttpRequestMessage(HttpMethod.Get, Uri);
7668 Request.Headers.Add("Accept", "application/xml, text/xml");
7669 Request.Headers.Add("Accept-Charset", "utf-8");
7670
7671 Response = await Client.SendAsync(Request);
7672 if (!Response.IsSuccessStatusCode)
7673 return null;
7674
7675 string ContentType = null;
7676
7677 if (Response.Headers.TryGetValues("Content-Type", out IEnumerable<string> Values))
7678 {
7679 foreach (string s in Values)
7680 {
7681 ContentType = s;
7682 break;
7683 }
7684 }
7685
7686 ContentType ??= XmlCodec.DefaultContentType;
7687
7688 byte[] Data = await Response.Content.ReadAsByteArrayAsync();
7689 ContentResponse Decoded = await InternetContent.DecodeAsync(ContentType, Data, Uri);
7690
7691 if (Decoded.HasError)
7692 {
7693 Log.Exception(Decoded.Error);
7694 return null;
7695 }
7696
7697 if (!(Decoded.Decoded is XmlDocument Doc))
7698 return null;
7699
7700 using XmlReader r = XmlReader.Create(new MemoryStream(Data));
7701 XmlSchema Schema = XmlSchema.Read(r, null);
7702 return new Tuple<byte[], XmlSchema>(Data, Schema);
7703 }
7704 finally
7705 {
7706 Request?.Dispose();
7707 Response?.Dispose();
7708 Client.Dispose();
7709 }
7710 }
7711 catch (Exception ex)
7712 {
7713 Log.Exception(ex);
7714 }
7715
7716 return null;
7717 }
7718
7719 private async Task GetCreatedContractsHandler(object Sender, IqEventArgs e)
7720 {
7721 try
7722 {
7723 if (!this.Server.IsServerDomain(e.From.Domain, true))
7724 {
7725 await e.IqErrorForbidden(e.To, "Not an account on the broker.", "en");
7726 return;
7727 }
7728
7729 int Offset = XML.Attribute(e.Query, "offset", 0);
7730 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
7731 bool References = XML.Attribute(e.Query, "references", true);
7732 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
7733
7734 IEnumerable<Contract> Contracts = await Database.Find<Contract>(Offset, MaxCount,
7735 new FilterFieldEqualTo("Account", e.From.Account), "Created");
7736
7737 string Xml = await this.SerializeContractReferences(Contracts, References, QueryVersion);
7738
7739 await e.IqResult(Xml, e.To);
7740 }
7741 catch (Exception ex)
7742 {
7743 await e.IqError(ex, e.To);
7744 }
7745 }
7746
7747 internal async Task<string> SerializeContractReferences(IEnumerable<IContractReference> Contracts, bool References, NamespaceSet Version)
7748 {
7749 StringBuilder Xml = new StringBuilder();
7750
7751 if (References)
7752 Xml.Append("<contractReferences xmlns='");
7753 else
7754 Xml.Append("<contracts xmlns='");
7755
7756 Xml.Append(NamespaceSmartContracts(Version));
7757 Xml.Append("'>");
7758
7759 foreach (IContractReference Contract in Contracts)
7760 {
7761 if (References)
7762 {
7763 Xml.Append("<ref id='");
7764 Xml.Append(XML.Encode(Contract.ContractId));
7765 Xml.Append("'/>");
7766 }
7767 else if (Contract is Contract Contract2)
7768 await Contract2.Serialize(Xml, Contract2.Version != Version, true, true, true, true, true, true, null, this);
7769 else
7770 {
7771 KeyValuePair<Contract, IqResultEventArgs> P = await this.GetContract(Contract.ContractId);
7772 Contract2 = P.Key;
7773
7774 if (Contract2 is null)
7775 {
7776 Xml.Append("<ref id='");
7777 Xml.Append(XML.Encode(Contract.ContractId));
7778 Xml.Append("'/>");
7779 }
7780 else
7781 await Contract2.Serialize(Xml, Contract2.Version != Version, true, true, true, true, true, true, null, this);
7782 }
7783 }
7784
7785 if (References)
7786 Xml.Append("</contractReferences>");
7787 else
7788 Xml.Append("</contracts>");
7789
7790 return Xml.ToString();
7791 }
7792
7793 public async Task<CaseInsensitiveString> GetComponent(CaseInsensitiveString ServerDomain, string Feature)
7794 {
7795 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(ServerDomain, new string[] { Feature });
7796 return P.Key;
7797 }
7798
7799 public async Task<KeyValuePair<CaseInsensitiveString, string>> GetComponent(CaseInsensitiveString ServerDomain, params string[] Features)
7800 {
7801 if (!Gateway.HasDomain && !(Gateway.XmppClient is null) &&
7802 !XmppServer.IsRemoteDomainRegistered(ServerDomain)) // S2S not possible in developer environment, use C2S.
7803 {
7804 KeyValuePair<string, string> P = await Gateway.XmppClient.FindComponentAsync(ServerDomain, Features);
7805 return new KeyValuePair<CaseInsensitiveString, string>(P.Key, P.Value);
7806 }
7807
7808 foreach (string Feature in Features)
7809 {
7810 string Key = ServerDomain + "#" + Feature;
7811
7812 if (this.remoteComponents.TryGetValue(Key, out object Obj) &&
7813 Obj is CaseInsensitiveString JID)
7814 {
7815 return new KeyValuePair<CaseInsensitiveString, string>(JID, Feature);
7816 }
7817 }
7818
7819 TaskCompletionSource<KeyValuePair<CaseInsensitiveString, string>> Result =
7820 new TaskCompletionSource<KeyValuePair<CaseInsensitiveString, string>>();
7821
7822 await this.Server.SendIqRequest("get", new XmppAddress(this.Server.Domain), new XmppAddress(ServerDomain), string.Empty,
7823 "<query xmlns='http://jabber.org/protocol/disco#items'/>", true, async (Sender, e) =>
7824 {
7825 if (e.Ok && !(e.FirstElement is null))
7826 {
7827 List<CaseInsensitiveString> JIDs = new List<CaseInsensitiveString>() { ServerDomain };
7828 object SynchObject = new object();
7829
7830 foreach (XmlNode N in e.FirstElement)
7831 {
7832 if (N is XmlElement E && E.LocalName == "item")
7833 {
7834 CaseInsensitiveString JID = XML.Attribute(E, "jid");
7836 JIDs.Add(JID);
7837 }
7838 }
7839
7840 int Count = JIDs.Count;
7841
7842 foreach (CaseInsensitiveString JID2 in JIDs)
7843 {
7844 await this.Server.SendIqRequest("get", new XmppAddress(this.Server.Domain), new XmppAddress(JID2), string.Empty,
7845 "<query xmlns='http://jabber.org/protocol/disco#info'/>", true, (sender2, e2) =>
7846 {
7847 if (e2.Ok)
7848 {
7849 foreach (XmlNode N2 in e2.FirstElement)
7850 {
7851 if (!(N2 is XmlElement E2) || E2.LocalName != "feature")
7852 continue;
7853
7854 string Var = XML.Attribute(E2, "var");
7855
7856 foreach (string Feature in Features)
7857 {
7858 if (Var == Feature)
7859 {
7860 lock (SynchObject)
7861 {
7862 if (Count > 0)
7863 Count = -1;
7864 else
7865 break;
7866 }
7867
7868 CaseInsensitiveString s = (CaseInsensitiveString)e2.State;
7869 string Key = ServerDomain + "#" + Feature;
7870
7871 this.remoteComponents[Key] = s;
7872 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(s, Feature));
7873
7874 return Task.CompletedTask;
7875 }
7876 }
7877 }
7878 }
7879
7880 lock (SynchObject)
7881 {
7882 Count--;
7883 if (Count != 0)
7884 return Task.CompletedTask;
7885 }
7886
7887 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(null, null));
7888
7889 return Task.CompletedTask;
7890 }, JID2);
7891 }
7892 }
7893 else
7894 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(null, null));
7895 }, null);
7896
7897 return await Result.Task;
7898 }
7899
7900 private async Task SignContractHandler(object Sender, IqEventArgs e)
7901 {
7902 try
7903 {
7904 CaseInsensitiveString ContractId = null;
7906 byte[] Signature = null;
7907 bool Transferable = false;
7908
7909 foreach (XmlAttribute Attr in e.Query.Attributes)
7910 {
7911 switch (Attr.Name)
7912 {
7913 case "id":
7914 ContractId = Attr.Value;
7915 break;
7916
7917 case "role":
7918 Role = Attr.Value;
7919 break;
7920
7921 case "s":
7922 Signature = Convert.FromBase64String(Attr.Value);
7923 break;
7924
7925 case "transferable":
7926 if (CommonTypes.TryParse(Attr.Value, out bool b))
7927 Transferable = b;
7928 else
7929 {
7930 await e.IqErrorBadRequest(e.To, "Invalid transferable value.", "en");
7931 return;
7932 }
7933 break;
7934 }
7935 }
7936
7937 if (ContractId is null || Role is null || Signature is null)
7938 {
7939 await e.IqErrorBadRequest(e.To, "Attributes missing.", "en");
7940 return;
7941 }
7942
7943 using Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase);
7944 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7945 if (Contract is null)
7946 {
7947 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7948 return;
7949 }
7950
7951 if (Contract.PartsMode == ContractParts.TemplateOnly)
7952 {
7953 await e.IqErrorForbidden(e.To, "Contract is a template only, and cannot be signed.", "en");
7954 return;
7955 }
7956
7957 switch (Contract.State)
7958 {
7959 case ContractState.Obsoleted:
7960 await e.IqErrorForbidden(e.To, "Contract is obsoleted.", "en");
7961 return;
7962
7963 case ContractState.Deleted:
7964 await e.IqErrorForbidden(e.To, "Contract is deleted.", "en");
7965 return;
7966
7967 case ContractState.Proposed:
7968 await e.IqErrorForbidden(e.To, "Contract is not yet approved to be signed.", "en");
7969 return;
7970
7971 case ContractState.Rejected:
7972 await e.IqErrorForbidden(e.To, "Contract proposal has been rejected.", "en");
7973 return;
7974
7975 case ContractState.Failed:
7976 await e.IqErrorForbidden(e.To, "Contract proposal has failed.", "en");
7977 return;
7978 }
7979
7980 DateTime UtcNow = DateTime.UtcNow;
7981 if (Contract.SignAfter.HasValue && UtcNow < Contract.SignAfter.Value.ToUniversalTime())
7982 {
7983 await e.IqErrorForbidden(e.To, "Contract will be open for signatures after " + Contract.SignAfter.Value.ToUniversalTime().ToString() + " (UTC)", "en");
7984 return;
7985 }
7986
7987 if (Contract.SignBefore.HasValue && UtcNow > Contract.SignBefore.Value.ToUniversalTime())
7988 {
7989 await e.IqErrorForbidden(e.To, "Contract closed for signatures as of " + Contract.SignBefore.Value.ToUniversalTime().ToString() + " (UTC)", "en");
7990 return;
7991 }
7992
7994 {
7995 KeyValuePair<Parameter, string> P = await Contract.CheckParameters(this, false);
7996 Parameter FailingParameter = P.Key;
7997
7998 if (!(FailingParameter is null))
7999 {
8000 await e.IqErrorResourceConstraint(e.To, "Contract parameter " + FailingParameter.Name + " contains invalid value: " + P.Value, "en");
8001 return;
8002 }
8003 }
8004
8005 int NrSignaturesForRole = 0;
8006 bool RoleFound = false;
8007
8008 if (!(Contract.ClientSignatures is null))
8009 {
8010 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8011 {
8012 if (ClientSignature.Role == Role)
8013 NrSignaturesForRole++;
8014 }
8015 }
8016
8017 if (!(Contract.Roles is null))
8018 {
8019 foreach (Role Role2 in Contract.Roles)
8020 {
8021 if (Role2.Name == Role)
8022 {
8023 if (NrSignaturesForRole >= Role2.MaxCount)
8024 {
8025 await e.IqErrorForbidden(e.To, "No more signatures of specified role allowed.", "en");
8026 return;
8027 }
8028
8029 RoleFound = true;
8030 break;
8031 }
8032 }
8033 }
8034
8035 if (!RoleFound)
8036 {
8037 await e.IqErrorForbidden(e.To, "No such role defined in contract.", "en");
8038 return;
8039 }
8040
8041 StringBuilder Xml = new StringBuilder();
8042 await Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
8043 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
8044
8045 (LegalIdentity Identity, Dictionary<string, string> AttachmentUrls) =
8046 await this.ValidateSenderSignature(e.From, new ExternalRequest(e),
8047 UtcNow, Data, Signature, null); // TODO: Authorize access to requestor attachments
8048
8049 if (Identity is null)
8050 return;
8051
8052 if (Contract.PartsMode == ContractParts.ExplicitlyDefined)
8053 {
8054 bool InList = false;
8055
8056 if (!(Contract.Parts is null))
8057 {
8058 foreach (Part Part in Contract.Parts)
8059 {
8060 if (Part.LegalId == Identity.Id && Part.Role == Role)
8061 {
8062 InList = true;
8063 break;
8064 }
8065 }
8066
8067 if (!InList)
8068 {
8069 XmppAddress SignatoryId = new XmppAddress(Identity.Id);
8070
8071 foreach (Part Part in Contract.Parts)
8072 {
8073 if (Part.Role != Role)
8074 continue;
8075
8076 XmppAddress PartId = new XmppAddress(Part.LegalId);
8077 if (SignatoryId.Domain != PartId.Domain)
8078 continue;
8079
8080 bool HasSigned = false;
8081
8082 if (!(Contract.ClientSignatures is null))
8083 {
8084 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8085 {
8087 {
8088 HasSigned = true;
8089 break;
8090 }
8091 }
8092 }
8093
8094 if (HasSigned)
8095 continue;
8096
8097 if (!await this.CanSignAs(Part.LegalId, Identity.Id))
8098 continue;
8099
8100 InList = true;
8101 break;
8102 }
8103 }
8104 }
8105
8106 if (!InList)
8107 {
8108 await e.IqErrorForbidden(e.To, "Legal identity not in explicitly defined list of parts in contract.", "en");
8109 return;
8110 }
8111 }
8112
8113 if (!(Contract.ClientSignatures is null))
8114 {
8115 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8116 {
8117 if (ClientSignature.LegalId == Identity.Id && ClientSignature.Role == Role)
8118 {
8119 // Contract already signed. Return successful response without altering the contract.
8120
8121 Xml.Clear();
8122 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
8123 await e.IqResult(Xml.ToString(), e.To);
8124
8125 return;
8126 }
8127 }
8128 }
8129
8130 DateTime SignatureTimestamp = DateTime.UtcNow;
8131 string s = await Contract.TrySetRoleParameters(Role, NrSignaturesForRole + 1,
8132 Identity, SignatureTimestamp, Signature, AttachmentUrls, this);
8133
8134 if (!string.IsNullOrEmpty(s))
8135 {
8136 await e.IqErrorForbidden(e.To, "Legal identity does not have required property " + s + " defined.", "en");
8137 return;
8138 }
8139
8140 int c = (Contract.ClientSignatures?.Length ?? 0) + 1;
8141 ClientSignature[] Signatures = new ClientSignature[c];
8142
8143 Contract.ClientSignatures?.CopyTo(Signatures, 0);
8144 Signatures[c - 1] = new ClientSignature()
8145 {
8146 BareJid = e.From.BareJid,
8147 LegalId = Identity.Id,
8148 Role = Role,
8149 DigitalSignature = Signature,
8150 Timestamp = SignatureTimestamp,
8151 Transferable = Transferable
8152 };
8153
8154 bool GeoPublish = false;
8155 bool Signed = false;
8156
8157 Contract.ClientSignatures = Signatures;
8158
8159 if (Contract.From == DateTime.MinValue)
8160 {
8161 if (await Contract.IsLegallyBinding(false, true, this))
8162 {
8163 Contract.State = ContractState.Signed;
8164 Contract.From = Contract.Created;
8165
8166 if (Contract.Duration.HasValue)
8167 Contract.To = Contract.From + Contract.Duration.Value;
8168 else
8169 Contract.To = Contract.From;
8170
8171 GeoPublish = true;
8172 Signed = true;
8173 }
8174 else
8175 Contract.State = ContractState.BeingSigned;
8176 }
8177
8178 await Contract.Sign(this);
8179
8180 await Database.Update(Contract);
8181
8182 if (Contract.CanActAsTemplate)
8183 await RuntimeCounters.IncrementCounter("Legal.Template." + Contract.State.ToString());
8184 else
8185 await RuntimeCounters.IncrementCounter("Legal.Contract." + Contract.State.ToString());
8186
8188 {
8189 ContractId = Contract.ContractId,
8190 LegalId = Identity.Id,
8191 BareJid = e.From.BareJid
8192 };
8193
8195
8196 Log.Informational("Contract signed as " + Role.Value + ".",
8197 Contract.ContractId.Value, Identity.Id.Value, "ContractSigned",
8198 new KeyValuePair<string, object>("BareJid", e.From.BareJid.Value),
8199 new KeyValuePair<string, object>("Role", Role.Value),
8200 new KeyValuePair<string, object>("Transferable", Transferable));
8201
8202 Xml.Clear();
8203 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
8204 string ContractXml = Xml.ToString();
8205 await e.IqResult(ContractXml, e.To);
8206
8207 if (GeoPublish && !(this.geo is null))
8208 await this.geo.Publish(Contract);
8209
8210 await this.SendContractSignedNotification(Contract, Identity, Role, Signed, e.From, e.To);
8211
8212 await StateMachineProcessor.ContractSignature(Contract, Role, Identity.Id);
8213
8214 if (Signed)
8215 await StateMachineProcessor.ContractSigned(Contract);
8216
8217 try
8218 {
8219 await this.ContractSigned(Contract, true, this.eDaler, this.pubsub);
8220 }
8221 catch (Exception ex)
8222 {
8224 }
8225 }
8226 catch (Exception ex)
8227 {
8228 await e.IqError(ex, e.To);
8229 }
8230 }
8231
8232 internal async Task<Contract> SignContract(Contract Contract, bool ContractIsLocked, CaseInsensitiveString Role, bool Transferable,
8233 byte[] Signature, XmppAddress SignerJid)
8234 {
8235 if (Contract.PartsMode == ContractParts.TemplateOnly)
8236 throw new Exception("Contract is a template only, and cannot be signed.");
8237
8238 switch (Contract.State)
8239 {
8240 case ContractState.Obsoleted:
8241 throw new Exception("Contract is obsoleted.");
8242
8243 case ContractState.Deleted:
8244 throw new Exception("Contract is deleted.");
8245
8246 case ContractState.Proposed:
8247 throw new Exception("Contract is not yet approved to be signed.");
8248
8249 case ContractState.Rejected:
8250 throw new Exception("Contract proposal has been rejected.");
8251
8252 case ContractState.Failed:
8253 throw new Exception("Contract proposal has failed.");
8254 }
8255
8256 DateTime UtcNow = DateTime.UtcNow;
8257 if (Contract.SignAfter.HasValue && UtcNow < Contract.SignAfter.Value.ToUniversalTime())
8258 throw new Exception("Contract will be open for signatures after " + Contract.SignAfter.Value.ToUniversalTime().ToString() + " (UTC)");
8259
8260 if (Contract.SignBefore.HasValue && UtcNow > Contract.SignBefore.Value.ToUniversalTime())
8261 throw new Exception("Contract closed for signatures as of " + Contract.SignBefore.Value.ToUniversalTime().ToString() + " (UTC)");
8262
8263 KeyValuePair<Parameter, string> P = await Contract.CheckParameters(this, false);
8264 Parameter FailingParameter = P.Key;
8265
8266 if (!(FailingParameter is null))
8267 throw new Exception("Contract parameter " + FailingParameter.Name + " contains invalid value: " + P.Value);
8268
8269 int NrSignaturesForRole = 0;
8270 bool RoleFound = false;
8271
8272 if (!(Contract.ClientSignatures is null))
8273 {
8274 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8275 {
8276 if (ClientSignature.Role == Role)
8277 NrSignaturesForRole++;
8278 }
8279 }
8280
8281 if (!(Contract.Roles is null))
8282 {
8283 foreach (Role Role2 in Contract.Roles)
8284 {
8285 if (Role2.Name == Role)
8286 {
8287 if (NrSignaturesForRole >= Role2.MaxCount)
8288 throw new Exception("No more signatures of specified role allowed.");
8289
8290 RoleFound = true;
8291 break;
8292 }
8293 }
8294 }
8295
8296 if (!RoleFound)
8297 throw new Exception("No such role defined in contract.");
8298
8299 StringBuilder Xml = new StringBuilder();
8300 await Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
8301 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
8302
8303 InternalProcessing Request = new InternalProcessing(string.Empty);
8304 (LegalIdentity Identity, Dictionary<string, string> AttachmentUrls) =
8305 await this.ValidateSenderSignature(SignerJid, Request, UtcNow, Data,
8306 Signature, null); // TODO: Authorize access to requestor attachments
8307
8308 if (Identity is null)
8309 {
8310 string Msg = Request.ErrorMessage;
8311 if (string.IsNullOrEmpty(Msg))
8312 Msg = "Unable to validate sender signature.";
8313
8314 throw new Exception(Msg);
8315 }
8316
8317 if (Contract.PartsMode == ContractParts.ExplicitlyDefined)
8318 {
8319 bool InList = false;
8320
8321 if (!(Contract.Parts is null))
8322 {
8323 foreach (Part Part in Contract.Parts)
8324 {
8325 if (Part.LegalId == Identity.Id && Part.Role == Role)
8326 {
8327 InList = true;
8328 break;
8329 }
8330 }
8331 }
8332
8333 if (!InList)
8334 throw new Exception("Legal identity not in explicitly defined list of parts in contract.");
8335 }
8336
8337 if (!(Contract.ClientSignatures is null))
8338 {
8339 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8340 {
8341 if (ClientSignature.LegalId == Identity.Id && ClientSignature.Role == Role)
8342 return Contract; // Contract already signed. Return successful response without altering the contract.
8343 }
8344 }
8345
8346 DateTime SignatureTimestamp = DateTime.UtcNow;
8347 string s = await Contract.TrySetRoleParameters(Role, NrSignaturesForRole + 1,
8348 Identity, SignatureTimestamp, Signature, AttachmentUrls, this);
8349
8350 if (!string.IsNullOrEmpty(s))
8351 throw new Exception("Legal identity does not have required property " + s + " defined.");
8352
8353 int c = (Contract.ClientSignatures?.Length ?? 0) + 1;
8354 ClientSignature[] Signatures = new ClientSignature[c];
8355
8356 Contract.ClientSignatures?.CopyTo(Signatures, 0);
8357 Signatures[c - 1] = new ClientSignature()
8358 {
8359 BareJid = SignerJid.BareJid,
8360 LegalId = Identity.Id,
8361 Role = Role,
8362 DigitalSignature = Signature,
8363 Timestamp = SignatureTimestamp,
8364 Transferable = Transferable
8365 };
8366
8367 Contract.ClientSignatures = Signatures;
8368
8369 bool GeoPublish = false;
8370 bool Signed = false;
8371
8372 if (Contract.From == DateTime.MinValue)
8373 {
8374 if (await Contract.IsLegallyBinding(false, true, this))
8375 {
8376 Contract.State = ContractState.Signed;
8377 Contract.From = Contract.Created;
8378
8379 if (Contract.Duration.HasValue)
8380 Contract.To = Contract.From + Contract.Duration.Value;
8381 else
8382 Contract.To = Contract.From;
8383
8384 GeoPublish = true;
8385 Signed = true;
8386 }
8387 else
8388 Contract.State = ContractState.BeingSigned;
8389 }
8390
8391 await Contract.Sign(this);
8392
8393 await Database.Update(Contract);
8394
8395 if (Contract.CanActAsTemplate)
8396 await RuntimeCounters.IncrementCounter("Legal.Template." + Contract.State.ToString());
8397 else
8398 await RuntimeCounters.IncrementCounter("Legal.Contract." + Contract.State.ToString());
8399
8401 {
8402 ContractId = Contract.ContractId,
8403 LegalId = Identity.Id,
8404 BareJid = SignerJid.BareJid
8405 };
8406
8408
8409 Log.Informational("Contract signed as " + Role.Value + ".",
8410 Contract.ContractId.Value, Identity.Id.Value, "ContractSigned",
8411 new KeyValuePair<string, object>("BareJid", SignerJid.BareJid.Value),
8412 new KeyValuePair<string, object>("Role", Role.Value),
8413 new KeyValuePair<string, object>("Transferable", Transferable));
8414
8415 if (GeoPublish && !(this.geo is null))
8416 await this.geo.Publish(Contract);
8417
8418 await this.SendContractSignedNotification(Contract, Identity, Role, Signed, SignerJid, this.MainDomain);
8419
8420 await StateMachineProcessor.ContractSignature(Contract, Role, Identity.Id);
8421
8422 if (Signed)
8423 await StateMachineProcessor.ContractSigned(Contract);
8424
8425 try
8426 {
8427 await this.ContractSigned(Contract, ContractIsLocked, this.eDaler, this.pubsub);
8428 }
8429 catch (Exception ex)
8430 {
8432 }
8433
8434 return Contract;
8435 }
8436
8437 private async Task SendContractSignedNotification(Contract Contract, LegalIdentity Identity,
8438 string Role, bool Signed, XmppAddress SignerJid, XmppAddress Sender)
8439 {
8440 try
8441 {
8442 string ContractNamespace = NamespaceSmartContracts(Contract.Version);
8443 Dictionary<CaseInsensitiveString, KeyValuePair<string, bool>> StatusRecipients = new Dictionary<CaseInsensitiveString, KeyValuePair<string, bool>>()
8444 {
8445 { Contract.Account + "@" + this.Server.Domain, new KeyValuePair<string, bool>(ContractNamespace, false) }
8446 };
8447
8448 XmppAddress Addr = new XmppAddress(Contract.TemplateId);
8449
8450 if (!this.Server.IsServerDomain(Addr.Domain, true))
8451 {
8452 KeyValuePair<CaseInsensitiveString, string> P2 = await this.GetComponent(Addr.Domain, NamespacesSmartContracts);
8455 StatusRecipients[Component] = new KeyValuePair<string, bool>(P2.Value, true);
8456 }
8457
8458 if (!(Contract.Parameters is null))
8459 {
8460 foreach (Parameter Parameter in Contract.Parameters)
8461 {
8464 {
8466
8467 if (!this.Server.IsServerDomain(Addr.Domain, true))
8468 {
8469 KeyValuePair<CaseInsensitiveString, string> P2 = await this.GetComponent(Addr.Domain, NamespacesSmartContracts);
8472 StatusRecipients[Component] = new KeyValuePair<string, bool>(P2.Value, true);
8473 }
8474 }
8475 }
8476 }
8477
8478 if (!(Contract.ClientSignatures is null))
8479 {
8480 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
8481 {
8482 StatusRecipients[ClientSignature.BareJid] = new KeyValuePair<string, bool>(ContractNamespace, false);
8483
8485
8486 if (!this.Server.IsServerDomain(Addr.Domain, true))
8487 {
8488 KeyValuePair<CaseInsensitiveString, string> P2 = await this.GetComponent(Addr.Domain, NamespacesSmartContracts);
8491 StatusRecipients[Component] = new KeyValuePair<string, bool>(P2.Value, true);
8492 }
8493 }
8494 }
8495
8496 if (StatusRecipients.Count > 0)
8497 {
8498 StringBuilder Xml = new StringBuilder();
8499 await Contract.Serialize(Xml, false, true, true, true, true, true, true, null, this);
8500 string ContractXml = Xml.ToString();
8501
8502 foreach (KeyValuePair<CaseInsensitiveString, KeyValuePair<string, bool>> P3 in StatusRecipients)
8503 {
8504 CaseInsensitiveString StatusRecipient = P3.Key;
8505 string Namespace = P3.Value.Key;
8506 bool IsComponent = P3.Value.Value;
8507 Xml.Clear();
8508
8509 Xml.Append("<contractSigned xmlns='");
8510 Xml.Append(Namespace);
8511 Xml.Append("' contractId='");
8512 Xml.Append(XML.Encode(Contract.ContractId));
8513 Xml.Append("' legalId='");
8514 Xml.Append(XML.Encode(Identity.Id));
8515 Xml.Append("' role='");
8516 Xml.Append(XML.Encode(Role));
8517
8518 if (Signed)
8519 Xml.Append("' signed='true");
8520
8521 Xml.Append("'>");
8522 Xml.Append(ContractXml);
8523 Xml.Append("</contractSigned>");
8524
8525 string Event = Xml.ToString();
8526
8527 try
8528 {
8529 if (IsComponent)
8530 {
8531 await this.Server.SendIqRequest("set", Sender, new XmppAddress(StatusRecipient),
8532 string.Empty, Event, false, (_, e) =>
8533 {
8534 if (e.Ok &&
8535 !(e.FirstElement is null) &&
8536 e.FirstElement.LocalName == "authorizeJid" &&
8537 IsNamespaceSmartContract(e.FirstElement.NamespaceURI))
8538 {
8539 string Jid = XML.Attribute(e.FirstElement, "jid");
8540
8541 if (!string.IsNullOrEmpty(Jid))
8542 {
8543 this.ContractAuthorization(Jid, SignerJid.BareJid,
8544 Contract.ContractId, true);
8545
8546 if (!(Contract.ClientSignatures is null))
8547 {
8548 foreach (ClientSignature Signature in Contract.ClientSignatures)
8549 {
8550 XmppAddress Addr = new XmppAddress(Signature.LegalId);
8551
8552 if (this.IsComponentDomain(Addr.Domain, true))
8553 this.IdentityAuthorization(Jid, SignerJid.BareJid, Signature.LegalId, true);
8554 }
8555 }
8556 }
8557 }
8558
8559 return Task.CompletedTask;
8560 }, null);
8561 }
8562 else
8563 {
8564 this.ContractAuthorization(StatusRecipient, SignerJid.BareJid,
8565 Contract.ContractId, true);
8566
8567 if (!(Contract.ClientSignatures is null))
8568 {
8569 foreach (ClientSignature Signature in Contract.ClientSignatures)
8570 {
8571 XmppAddress Addr2 = new XmppAddress(Signature.LegalId);
8572
8573 if (this.IsComponentDomain(Addr2.Domain, true))
8574 this.IdentityAuthorization(StatusRecipient, SignerJid.BareJid, Signature.LegalId, true);
8575 }
8576 }
8577
8578 await this.Server.SendMessage(string.Empty, string.Empty, Sender, new XmppAddress(StatusRecipient),
8579 string.Empty, Event);
8580 }
8581 }
8582 catch (Exception ex)
8583 {
8585 }
8586 }
8587 }
8588 }
8589 catch (Exception ex)
8590 {
8592 }
8593 }
8594
8602 private async Task ContractSigned(Contract Contract, bool ContractIsLocked,
8604 {
8605 if (!(this.transientParameters?.TryGetValue(Contract.ContractId,
8606 out Dictionary<CaseInsensitiveString, Parameter> TransientParameters) ?? false))
8607 {
8608 TransientParameters = null;
8609 }
8610
8611 switch (Contract.ForMachinesNamespace)
8612 {
8614 await MarketplaceProcessor.ContractSigned(Contract, ContractIsLocked,
8615 TransientParameters, this, EDaler, PubSub);
8616 break;
8617
8619 await NeuroFeaturesProcessor.ContractSigned(Contract, ContractIsLocked,
8620 TransientParameters, this, EDaler);
8621 break;
8622
8624 await PaiwiseProcessor.ContractSigned(Contract, ContractIsLocked,
8625 TransientParameters, this, EDaler);
8626 break;
8627 }
8628
8629 if (Contract.State == ContractState.Signed)
8630 {
8631 foreach (Payment Payment in await Database.Find<Payment>(
8632 new FilterFieldEqualTo("ConditionContractId", Contract.ContractId)))
8633 {
8634 if (!Payment.Processed.HasValue)
8635 PaiwiseProcessor.QueueForProcessing(Payment, EDaler);
8636 }
8637 }
8638 }
8639
8640 internal async Task<LegalIdentity> ValidateLocalSenderSignature(XmppAddress Sender, IqEventArgs e, DateTime Timestamp, Stream Data, byte[] Signature)
8641 {
8642 if (this.Server.IsServerDomain(Sender.Domain, true))
8643 {
8644 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
8645 new FilterFieldEqualTo("Account", Sender.Account),
8646 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
8647 new FilterFieldLesserOrEqualTo("From", Timestamp),
8648 new FilterFieldGreaterOrEqualTo("To", Timestamp)), "-Created"))
8649 {
8650 if (ID.ValidateSignature(Data, Signature))
8651 return ID;
8652 }
8653
8654 e?.IqErrorForbidden(e.To, "Signature proving access to private keys not correct.", "en");
8655 }
8656 else
8657 e?.IqErrorForbidden(e.To, "Sender must correspond to an account on the local machine.", "en");
8658
8659 return null;
8660 }
8661
8662 internal async Task<(LegalIdentity, Dictionary<string, string>)> ValidateSenderSignature(XmppAddress Sender,
8663 EDalerUriState Request, DateTime Timestamp, byte[] Data, byte[] Signature, string ForBareJid)
8664 {
8665 LegalIdentity Identity = null;
8666 Dictionary<string, string> AttachmentUrls = null;
8667
8668 if (this.Server.IsServerDomain(Sender.Domain, true))
8669 {
8670 bool IdFound = false;
8671
8672 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
8673 new FilterFieldEqualTo("Account", Sender.Account),
8674 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
8675 new FilterFieldLesserOrEqualTo("From", Timestamp.ToUniversalTime()),
8676 new FilterFieldGreaterOrEqualTo("To", Timestamp.ToUniversalTime())), "-Created"))
8677 {
8678 IdFound = true;
8679
8680 if (ID.ValidateSignature(Data, Signature))
8681 {
8682 if (!string.IsNullOrEmpty(ForBareJid))
8683 this.IdentityAuthorization(ForBareJid, Sender.BareJid, ID.Id, true);
8684
8685 Identity = ID;
8686 AttachmentUrls = this.GetAttachmentUrls(ID);
8687 break;
8688 }
8689 }
8690
8691 if (!IdFound)
8692 {
8693 Request?.Error(EDalerUriErrorType.ServiceUnavailable, "No approved legal identities found for " +
8694 Sender.BareJid + ".", false);
8695
8696 return (null, null);
8697 }
8698 }
8699 else
8700 {
8701 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(Sender.Domain, NamespacesLegalIdentity);
8702 CaseInsensitiveString RemoteLegalComponent = P.Key;
8703 string RemoteNamespace = P.Value;
8704 NamespaceSet RemoteVersion = XmppServerModule.GetVersion(RemoteNamespace);
8705
8706 if (CaseInsensitiveString.IsNullOrEmpty(RemoteLegalComponent))
8707 {
8708 Request?.Error(EDalerUriErrorType.ServiceUnavailable, this.MainDomain.Address + " could not find legal component of " + Sender.Domain + ", to validate signature.", false);
8709 return (null, null);
8710 }
8711
8712 if (!Gateway.HasDomain && !(Gateway.ContractsClient is null)) // Development mode. S2S connections not available. Need to perform validation using C2S.
8713 {
8714 Networking.XMPP.Contracts.LegalIdentity Identity2 = await Gateway.ContractsClient.ValidateSignatureAsync(string.Empty, Data, Signature);
8715 if (Identity2 is null)
8716 {
8717 Request?.Error(EDalerUriErrorType.Forbidden, "Signature does not match any of the approved legal identities of " +
8718 Sender.BareJid + ".", false);
8719 }
8720
8721 StringBuilder sb = new StringBuilder();
8722 Identity2.Serialize(sb, true, true, true, true, true, true, true);
8723
8724 XmlDocument Doc = XML.ParseXml(sb.ToString(), true);
8725
8726 Identity = LegalIdentity.Parse(Doc.DocumentElement, out _, out AttachmentUrls);
8727 }
8728 else
8729 {
8730 IqResultEventArgs e2 = await this.ValidateRemoteSignature(RemoteLegalComponent, Sender.BareJid, Data, Signature, ForBareJid, RemoteVersion);
8731 if (e2 is null)
8732 {
8733 Request?.Error(EDalerUriErrorType.ServiceUnavailable, this.MainDomain.Address + " was unable to validate remote signature.", false);
8734 return (null, null);
8735 }
8736
8737 if (!e2.Ok || e2.FirstElement is null)
8738 {
8739 Request?.Error(e2.ErrorTypeString, e2.ErrorElement?.OuterXml ?? string.Empty, e2.ErrorText);
8740 return (null, null);
8741 }
8742
8743 Identity = LegalIdentity.Parse(e2.FirstElement, out bool _, out AttachmentUrls);
8744 }
8745 }
8746
8747 if (Identity is null)
8748 {
8749 Request?.Error(EDalerUriErrorType.Forbidden, "Signature does not match any of the approved legal identities of " +
8750 Sender.BareJid + ".", false);
8751 }
8752
8753 return (Identity, AttachmentUrls);
8754 }
8755
8756 internal Dictionary<string, string> GetAttachmentUrls(LegalIdentity Identity)
8757 {
8758 if (Identity.Attachments is null)
8759 return null;
8760
8761 Dictionary<string, string> Urls = new Dictionary<string, string>();
8762
8763 foreach (AttachmentReference Ref in Identity.Attachments)
8764 Urls[Ref.Id] = Gateway.GetUrl("/Attachments/" + Ref.Id.Value, this.HttpServer);
8765
8766 return Urls;
8767 }
8768
8769 internal async Task<(LegalIdentity, Dictionary<string, string>)> ValidateSignature(XmppAddress Signatory, DateTime Timestamp, byte[] Data,
8770 byte[] Signature)
8771 {
8772 LegalIdentity Identity = null;
8773 Dictionary<string, string> AttachmentUrls;
8774
8775 if (this.Server.IsServerDomain(Signatory.Domain, true))
8776 {
8777 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
8778 new FilterFieldEqualTo("Account", Signatory.Account),
8779 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Approved),
8780 new FilterFieldLesserOrEqualTo("From", Timestamp),
8781 new FilterFieldGreaterOrEqualTo("To", Timestamp)), "-Created"))
8782 {
8783 if (ID.ValidateSignature(Data, Signature))
8784 {
8785 Identity = ID;
8786 AttachmentUrls = this.GetAttachmentUrls(ID);
8787 break;
8788 }
8789 }
8790
8791 AttachmentUrls = null;
8792 }
8793 else
8794 {
8795 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(Signatory.Domain, NamespacesLegalIdentity);
8796 CaseInsensitiveString RemoteLegalComponent = P.Key;
8797
8798 if (CaseInsensitiveString.IsNullOrEmpty(RemoteLegalComponent))
8799 return (null, null);
8800
8801 NamespaceSet RemoteVersion = XmppServerModule.GetVersion(RemoteLegalComponent);
8802
8803 IqResultEventArgs e2 = await this.ValidateRemoteSignature(RemoteLegalComponent, Signatory.BareJid, Data, Signature, null, RemoteVersion);
8804 if (e2 is null || !e2.Ok || e2.FirstElement is null)
8805 return (null, null);
8806
8807 Identity = LegalIdentity.Parse(e2.FirstElement, out _, out AttachmentUrls);
8808 }
8809
8810 return (Identity, AttachmentUrls);
8811 }
8812
8813 private async Task<IqResultEventArgs> ValidateRemoteSignature(string RemoteLegalComponent, string BareJid, byte[] Data, byte[] Signature,
8814 string ForBareJid, NamespaceSet Version)
8815 {
8816 TaskCompletionSource<IqResultEventArgs> T = new TaskCompletionSource<IqResultEventArgs>();
8817 StringBuilder Xml = new StringBuilder();
8818
8819 Xml.Clear();
8820 Xml.Append("<validateSignature data=\"");
8821 Xml.Append(Convert.ToBase64String(Data));
8822 Xml.Append("\" s=\"");
8823 Xml.Append(Convert.ToBase64String(Signature));
8824 Xml.Append("\" bareJid=\"");
8825 Xml.Append(XML.Encode(BareJid));
8826
8827 if (!string.IsNullOrEmpty(ForBareJid))
8828 {
8829 Xml.Append("\" for=\"");
8830 Xml.Append(XML.Encode(ForBareJid));
8831 }
8832
8833 Xml.Append("\" xmlns=\"");
8834 Xml.Append(NamespaceLegalIdentity(Version));
8835 Xml.Append("\"/>");
8836
8837 if (!await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(RemoteLegalComponent), string.Empty,
8838 Xml.ToString(), true, (sender2, e2) =>
8839 {
8840 T.TrySetResult(e2);
8841 return Task.CompletedTask;
8842 }, null))
8843 {
8844 return null;
8845 }
8846
8847 return await T.Task;
8848 }
8849
8850 private async Task ContractSignedHandler(object Sender, IqEventArgs e)
8851 {
8852 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
8853 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "legalId");
8854 string Role = XML.Attribute(e.Query, "role");
8855 bool Signed = XML.Attribute(e.Query, "signed", false);
8856
8857 XmppAddress ContractAddress = new XmppAddress(ContractId);
8858
8859 if (ContractAddress.Domain != e.From.Address)
8860 {
8861 await e.IqErrorForbidden(e.To, "Sender not responsible for contract.", "en");
8862 return;
8863 }
8864
8865 XmppAddress LegalAddress = new XmppAddress(LegalId);
8866 bool LocalLegalId = this.IsComponentDomain(LegalAddress.Domain, true);
8867
8868 Contract Contract = null;
8869
8870 foreach (XmlNode N in e.Query.ChildNodes)
8871 {
8872 if (N is XmlElement E && E.LocalName == "contract" && IsNamespaceSmartContract(E.NamespaceURI))
8873 {
8874 ParsedContract Parsed = await Contract.Parse(E, this);
8875 Contract = Parsed?.Contract;
8876
8877 if (Contract is null)
8878 {
8879 await e.IqErrorBadRequest(e.To, "Unable to parse embedded contract.", "en");
8880 return;
8881 }
8882
8883 break;
8884 }
8885 }
8886
8887 if (Contract is null)
8888 {
8889 KeyValuePair<Contract, IqResultEventArgs> P = await this.GetContract(ContractId);
8890 Contract = P.Key;
8891
8892 if (Contract is null)
8893 {
8894 if (string.IsNullOrEmpty(P.Value?.ErrorText))
8895 await e.IqErrorItemNotFound(e.To, "Unable to get contract.", "en");
8896 else
8897 await e.IqErrorItemNotFound(e.To, "Unable to get contract: " + P.Value.ErrorText, "en");
8898
8899 return;
8900 }
8901 }
8902
8903 if (Contract.ClientSignatures is null)
8904 {
8905 await e.IqErrorBadRequest(e.To, "No recorded client signatures in contract.", "en");
8906 return;
8907 }
8908
8909 CaseInsensitiveString BareJid = null;
8910
8911 foreach (ClientSignature Signature in Contract.ClientSignatures)
8912 {
8913 if (Signature.LegalId == LegalId)
8914 {
8915 BareJid = Signature.BareJid;
8916 break;
8917 }
8918 }
8919
8920 if (BareJid is null)
8921 {
8922 await e.IqErrorForbidden(e.To, "Legal Identity not in list of client signatures.", "en");
8923 return;
8924 }
8925
8926 if (Gateway.ContractsClient is null)
8927 {
8928 if (!await this.ValidateServerSignature(Contract))
8929 {
8930 await e.IqErrorForbidden(e.To, "Server signature not valid.", "en");
8931 return;
8932 }
8933
8934 await e.IqResult(string.Empty, e.To);
8935 await this.ProcessContractSignature(LocalLegalId, Contract, LegalId, BareJid,
8936 Role, Signed);
8937 }
8938 else
8939 {
8940 Networking.XMPP.Contracts.Contract ClientContract = await this.ToClientContract(Contract);
8941 if (ClientContract is null)
8942 {
8943 await e.IqErrorBadRequest(e.To, "Unable to prepare embedded contract for validation.", "en");
8944 return;
8945 }
8946
8947 StringBuilder sb = new StringBuilder();
8948
8949 sb.Append("<authorizeJid jid='");
8950 sb.Append(XML.Encode(Gateway.XmppClient.BareJID));
8951 sb.Append("' xmlns='");
8952 sb.Append(XML.Encode(e.Query.NamespaceURI));
8953 sb.Append("'/>");
8954
8955 await e.IqResult(sb.ToString(), e.To);
8956
8957 this.ContractSignedHandlerValidateContract(LocalLegalId, Contract, LegalId,
8958 BareJid, Role, Signed, ClientContract);
8959 }
8960 }
8961
8962 private async Task ProcessContractSignature(bool LocalLegalId, Contract Contract,
8963 CaseInsensitiveString LegalId, CaseInsensitiveString BareJid, string Role,
8964 bool Signed)
8965 {
8966 if (LocalLegalId)
8967 {
8969 {
8970 ContractId = Contract.ContractId,
8971 LegalId = LegalId,
8972 BareJid = BareJid
8973 };
8974
8976 }
8977
8978 await StateMachineProcessor.ContractSignature(Contract, Role, LegalId);
8979
8980 if (Signed)
8981 await StateMachineProcessor.ContractSigned(Contract);
8982 }
8983
8984 private async void ContractSignedHandlerValidateContract(bool LocalLegalId,
8986 string Role, bool Signed, Networking.XMPP.Contracts.Contract ClientContract)
8987 {
8988 try
8989 {
8992 int DelayMs = 5000;
8993 bool First = true;
8994
8995 do
8996 {
8997 if (First)
8998 First = false;
8999 else
9000 {
9001 await Task.Delay(DelayMs);
9002
9003 if (DelayMs < 1000 * 60 * 60) // 1h
9004 DelayMs <<= 1; // Max will be 1.422222... h
9005 }
9006
9007 e2 = await Gateway.ContractsClient.ValidateAsync(ClientContract);
9008 Status = e2.Status;
9009 }
9010 while (Status == Networking.XMPP.Contracts.ContractStatus.NoResponse);
9011
9012 switch (Status)
9013 {
9014 case Networking.XMPP.Contracts.ContractStatus.ContractUndefined:
9015 case Networking.XMPP.Contracts.ContractStatus.NotApproved:
9016 case Networking.XMPP.Contracts.ContractStatus.NotValidYet:
9017 case Networking.XMPP.Contracts.ContractStatus.NotValidAnymore:
9018 case Networking.XMPP.Contracts.ContractStatus.TemplateOnly:
9019 case Networking.XMPP.Contracts.ContractStatus.HumanReadableNotWellDefined:
9020 case Networking.XMPP.Contracts.ContractStatus.ParameterValuesNotValid:
9021 case Networking.XMPP.Contracts.ContractStatus.MachineReadableNotWellDefined:
9022 case Networking.XMPP.Contracts.ContractStatus.NoSchemaAccess:
9023 case Networking.XMPP.Contracts.ContractStatus.CorruptSchema:
9024 case Networking.XMPP.Contracts.ContractStatus.FraudulentSchema:
9025 case Networking.XMPP.Contracts.ContractStatus.FraudulentMachineReadable:
9026 case Networking.XMPP.Contracts.ContractStatus.NoClientSignatures:
9027 case Networking.XMPP.Contracts.ContractStatus.ClientSignatureInvalid:
9028 case Networking.XMPP.Contracts.ContractStatus.ClientSignatureNotValidated:
9029 case Networking.XMPP.Contracts.ContractStatus.ClientIdentityInvalid:
9030 case Networking.XMPP.Contracts.ContractStatus.AttachmentLacksUrl:
9031 case Networking.XMPP.Contracts.ContractStatus.AttachmentUnavailable:
9032 case Networking.XMPP.Contracts.ContractStatus.AttachmentInconsistency:
9033 case Networking.XMPP.Contracts.ContractStatus.AttachmentSignatureInvalid:
9034 case Networking.XMPP.Contracts.ContractStatus.NoTrustProvider:
9035 case Networking.XMPP.Contracts.ContractStatus.NoProviderPublicKey:
9036 case Networking.XMPP.Contracts.ContractStatus.NoProviderSignature:
9037 case Networking.XMPP.Contracts.ContractStatus.ProviderSignatureInvalid:
9038 case Networking.XMPP.Contracts.ContractStatus.ProviderKeyNotRecognized:
9039 case Networking.XMPP.Contracts.ContractStatus.NoResponse:
9040 Log.Warning("Client signature message ignored. Contract validation failed.",
9041 e2.Tags.Join(
9042 new KeyValuePair<string, object>("Status", Status),
9043 new KeyValuePair<string, object>("ContractId", Contract.ContractId.Value),
9044 new KeyValuePair<string, object>("LegalId", LegalId.Value),
9045 new KeyValuePair<string, object>("LocalLegalId", LocalLegalId),
9046 new KeyValuePair<string, object>("BareJid", BareJid.Value),
9047 new KeyValuePair<string, object>("Role", Role),
9048 new KeyValuePair<string, object>("Signed", Signed)));
9049 return;
9050
9051 case Networking.XMPP.Contracts.ContractStatus.NotLegallyBinding:
9052 if (Signed)
9053 {
9054 Log.Warning("Client signature message ignored. Contract validation failed.",
9055 e2.Tags.Join(
9056 new KeyValuePair<string, object>("Status", Status),
9057 new KeyValuePair<string, object>("ContractId", Contract.ContractId.Value),
9058 new KeyValuePair<string, object>("LegalId", LegalId.Value),
9059 new KeyValuePair<string, object>("LocalLegalId", LocalLegalId),
9060 new KeyValuePair<string, object>("BareJid", BareJid.Value),
9061 new KeyValuePair<string, object>("Role", Role),
9062 new KeyValuePair<string, object>("Signed", Signed)));
9063 return;
9064 }
9065 break;
9066
9067 case Networking.XMPP.Contracts.ContractStatus.Valid:
9068 await this.ProcessContractSignature(LocalLegalId, Contract, LegalId, BareJid,
9069 Role, Signed);
9070 break;
9071 }
9072 }
9073 catch (Exception ex)
9074 {
9075 Log.Warning("Client signature message ignored. Contract validation failed: " + ex.Message);
9076 return;
9077 }
9078 }
9079
9080 private async Task FailContractHandler(object Sender, MessageEventArgs e)
9081 {
9082 CaseInsensitiveString ContractId = XML.Attribute(e.Content, "contractId");
9083 string Reason = XML.Attribute(e.Content, "reason");
9084
9085 if (!e.From.IsDomain)
9086 {
9087 Log.Warning("Contract failure message ignored. Sender not a broker.");
9088 return;
9089 }
9090
9091 XmppAddress ContractAddress = new XmppAddress(ContractId);
9092
9093 if (!this.IsComponentDomain(ContractAddress.Domain, true))
9094 {
9095 Log.Warning("Contract failure message ignored. Contract not hosted on broker.");
9096 return;
9097 }
9098
9099 KeyValuePair<Contract, IqResultEventArgs> P = await this.GetContract(ContractId);
9100 Contract Contract = P.Key;
9101 if (Contract is null)
9102 {
9103 if (string.IsNullOrEmpty(P.Value?.ErrorText))
9104 Log.Warning("Contract failure message ignored. Contract not found.");
9105 else
9106 Log.Warning("Contract failure message ignored: " + P.Value.ErrorText);
9107
9108 return;
9109 }
9110
9111 bool LegitimateSender = false;
9112 XmppAddress Addr;
9113
9115 {
9116 Addr = new XmppAddress(Contract.TemplateId.Value);
9117 LegitimateSender = Addr.Domain == e.From.Address;
9118 }
9119
9120 if (!LegitimateSender && !(Contract.Parameters is null))
9121 {
9122 foreach (Parameter Parameter in Contract.Parameters)
9123 {
9125 {
9127 if (Addr.Domain == e.From.Address)
9128 {
9129 LegitimateSender = true;
9130 break;
9131 }
9132 }
9133 }
9134 }
9135
9136 if (!LegitimateSender && !(Contract.ClientSignatures is null))
9137 {
9138 foreach (ClientSignature Signature in Contract.ClientSignatures)
9139 {
9140 Addr = new XmppAddress(Signature.LegalId);
9141 if (Addr.Domain == e.From.Address)
9142 {
9143 LegitimateSender = true;
9144 break;
9145 }
9146 }
9147 }
9148
9149 if (!LegitimateSender)
9150 {
9151 Log.Warning("Contract failure message ignored. Not a legitimate sender.");
9152 return;
9153 }
9154
9155 await NeuroFeaturesProcessor.RejectContract(Contract, false, Reason, true, this, this.eDaler);
9156 }
9157
9163 internal async Task<Networking.XMPP.Contracts.Contract> ToClientContract(Contract Contract)
9164 {
9165 StringBuilder sb = new StringBuilder();
9166 await Contract.Serialize(sb, true, true, true, true, true, true, true, null, this);
9167
9168 string ContractXml = sb.ToString();
9169 XmlDocument Xml = XML.ParseXml(ContractXml, true);
9170
9171 // Note: Call XmlElement overload to avoid XSL validation and namespace version conflicts.
9172 Networking.XMPP.Contracts.ParsedContract Parsed = await Networking.XMPP.Contracts.Contract.Parse(Xml.DocumentElement, Gateway.ContractsClient, true);
9173
9174 return Parsed.Contract;
9175 }
9176
9177 private async Task<bool> ValidateServerSignature(Contract Contract)
9178 {
9179 if (Contract.ServerSignature is null ||
9180 Contract.ServerSignature.DigitalSignature is null)
9181 {
9182 return false;
9183 }
9184
9185 StringBuilder Xml = new StringBuilder();
9186 await Contract.Serialize(Xml, false, true, true, true, true, false, false, null, this);
9187 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
9188 XmppAddress ContractAddress = new XmppAddress(Contract.ContractId);
9189
9190 return await this.ValidateServerSignature(ContractAddress.Domain, Data,
9191 Contract.ServerSignature.DigitalSignature,
9192 Contract.ServerSignature.Timestamp);
9193 }
9194
9195 public async Task<bool> ValidateServerSignature(CaseInsensitiveString ServerDomain,
9196 byte[] Data, byte[] Signature, DateTime Timestamp)
9197 {
9198 if (this.IsComponentDomain(ServerDomain, true))
9200 else
9201 {
9202 IE2eEndpoint PubKey = await this.GetPublicKey(ServerDomain, Timestamp);
9203 return PubKey.Verify(Data, Signature);
9204 }
9205 }
9206
9207 public async Task<IE2eEndpoint> GetPublicKey(string ServerDomain, DateTime Timestamp)
9208 {
9209 string Key = ServerDomain + "|pub";
9210
9211 if (this.remoteComponents.TryGetValue(Key, out object Obj) &&
9212 Obj is PublicKeyRecords Records &&
9213 Records.TryGetRecord(Timestamp, out IE2eEndpoint PubKey))
9214 {
9215 return PubKey;
9216 }
9217
9218 TaskCompletionSource<IE2eEndpoint> Result = new TaskCompletionSource<IE2eEndpoint>();
9219 string Namespace = NamespaceLegalIdentity(NamespaceSet.Current);
9220 StringBuilder sb = new StringBuilder();
9221
9222 sb.Append("<getPublicKey xmlns=\"");
9223 sb.Append(Namespace);
9224 sb.Append("\" ts=\"");
9225 sb.Append(XML.Encode(Timestamp));
9226 sb.Append("\"/>");
9227
9228 await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(ServerDomain), string.Empty,
9229 sb.ToString(), true, (Sender, e) =>
9230 {
9231 IE2eEndpoint ServerKey = null;
9232 XmlElement E;
9233
9234 if (e.Ok &&
9235 !((E = e.FirstElement) is null) &&
9236 E.LocalName == "publicKey" &&
9237 E.NamespaceURI == Namespace)
9238 {
9239 DateTime From = XML.Attribute(E, "from", DateTime.MinValue);
9240 DateTime? To = E.HasAttribute("to") ?
9241 XML.Attribute(E, "to", DateTime.MaxValue) : (DateTime?)null;
9242
9243 foreach (XmlNode N in E.ChildNodes)
9244 {
9245 if (N is XmlElement E2)
9246 {
9247 ServerKey = EndpointSecurity.ParseE2eKey(E2);
9248 if (!(ServerKey is null))
9249 {
9250 if (!this.remoteComponents.TryGetValue(Key, out object Obj) ||
9251 !(Obj is PublicKeyRecords Records))
9252 {
9253 Records = new PublicKeyRecords();
9254 this.remoteComponents[Key] = Records;
9255 }
9256
9257 Records.Add(From, To ?? DateTime.UtcNow, ServerKey);
9258
9259 Result.TrySetResult(ServerKey);
9260 return Task.CompletedTask;
9261 }
9262 }
9263 }
9264 }
9265
9266 Result.TrySetResult(null);
9267 return Task.CompletedTask;
9268 }, null);
9269
9270 await Result.Task;
9271
9272 PubKey = Result.Task.Result;
9273
9274 return PubKey;
9275 }
9276
9277 private async Task GetSignedContractsHandler(object Sender, IqEventArgs e)
9278 {
9279 try
9280 {
9281 int Offset = XML.Attribute(e.Query, "offset", 0);
9282 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
9283 bool References = XML.Attribute(e.Query, "references", true);
9284 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
9285
9286 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(Offset, MaxCount,
9287 new FilterFieldEqualTo("BareJid", e.From.BareJid), "LegalId", "ContractId");
9288
9289 string Xml = await this.SerializeContractReferences(Signatures, References, QueryVersion);
9290
9291 await e.IqResult(Xml, e.To);
9292 }
9293 catch (Exception ex)
9294 {
9295 await e.IqError(ex, e.To);
9296 }
9297 }
9298
9299 private async Task GetContractHandler(object Sender, IqEventArgs e)
9300 {
9301 try
9302 {
9303 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
9304
9305 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
9306 {
9307 await e.IqErrorBadRequest(e.To, "No Contract ID specified.", "en");
9308 return;
9309 }
9310
9311 // Note: Do not log the contract for reading for simply trying to get
9312 // the current state. While the contract is locked for writing
9313 // while being signed, external parties may need to get the contract
9314 // to validate signatures, for example, for validating signatures
9315 // perfor approving payments.
9316
9317 //using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
9318 {
9319 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9320 if (Contract is null)
9321 {
9322 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9323 return;
9324 }
9325
9326 if (!await Contract.CanRead(e.From, this.Server, this))
9327 {
9328 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, ContractId, false);
9329 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
9330 return;
9331 }
9332
9333 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, ContractId, true);
9334
9335 StringBuilder Xml = new StringBuilder();
9336 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9337 await e.IqResult(Xml.ToString(), e.To);
9338 }
9339 }
9340 catch (Exception ex)
9341 {
9342 await e.IqError(ex, e.To);
9343 }
9344 }
9345
9346 private async Task GetContractsHandler(object Sender, IqEventArgs e)
9347 {
9348 try
9349 {
9350 StringBuilder Xml = new StringBuilder();
9351
9352 Xml.Append("<contracts xmlns='");
9353 Xml.Append(e.Query.NamespaceURI);
9354 Xml.Append("'>");
9355
9356 foreach (XmlNode N in e.Query.ChildNodes)
9357 {
9358 if (N is XmlElement E && E.LocalName == "ref" && E.NamespaceURI == e.Query.NamespaceURI)
9359 {
9360 CaseInsensitiveString Id = XML.Attribute(E, "id");
9361 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", Id), "Created");
9362
9363 if (!(Contract is null) &&
9364 await Contract.CanRead(e.From, this.Server, this))
9365 {
9366 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Id, true);
9367 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9368 }
9369 else
9370 {
9371 if (!(Contract is null))
9372 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Id, false);
9373
9374 Xml.Append("<ref id='");
9375 Xml.Append(XML.Encode(Id));
9376 Xml.Append("'/>");
9377 }
9378 }
9379 }
9380
9381 Xml.Append("</contracts>");
9382
9383 await e.IqResult(Xml.ToString(), e.To);
9384 }
9385 catch (Exception ex)
9386 {
9387 await e.IqError(ex, e.To);
9388 }
9389 }
9390
9391 private async Task IsPartHandler(object Sender, IqEventArgs e)
9392 {
9393 try
9394 {
9395 CaseInsensitiveString BareJid = XML.Attribute(e.Query, "bareJid");
9396
9398 {
9399 await e.IqErrorBadRequest(e.To, "No Bare JID specified.", "en");
9400 return;
9401 }
9402
9403 XmppAddress BareAddress = new XmppAddress(BareJid);
9404 if (!BareAddress.IsBareJID)
9405 {
9406 await e.IqErrorBadRequest(e.To, "Invalid Bare JID.", "en");
9407 return;
9408 }
9409
9410 if (!this.Server.IsServerDomain(BareAddress.Domain, true))
9411 {
9412 await e.IqErrorBadRequest(e.To, "Bare JID not registered on server.", "en");
9413 return;
9414 }
9415
9416 if (!e.From.IsDomain)
9417 {
9418 await e.IqErrorForbidden(e.To, "Not authorized to execute request.", "en");
9419 return;
9420 }
9421
9422 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
9423 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(e.From.Address, NamespacesLegalIdentity);
9425 string Namespace = P.Value;
9426
9427 if (Component != e.From.Address)
9428 {
9429 await e.IqErrorForbidden(e.To, "Not authorized to execute request.", "en");
9430 return;
9431 }
9432
9433 foreach (XmlNode N in e.Query.ChildNodes)
9434 {
9435 if (N is XmlElement E && E.LocalName == "idRef")
9436 {
9437 CaseInsensitiveString LegalId = XML.Attribute(E, "id");
9439 continue;
9440
9441 XmppAddress LegalIdAddress = new XmppAddress(LegalId);
9442 if (!this.IsComponentDomain(LegalIdAddress.Domain, true))
9443 continue;
9444
9445 using Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + LegalId.LowerCase);
9446 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
9447 if (Identity is null)
9448 continue;
9449
9450 if (Identity.Account == BareAddress.Account)
9451 {
9452 await e.IqResult("<part xmlns='" + e.Query.NamespaceURI + "'>true</part>", e.To);
9453 return;
9454 }
9455 }
9456 }
9457
9458 await e.IqResult("<part xmlns='" + e.Query.NamespaceURI + "'>false</part>", e.To);
9459 }
9460 catch (Exception ex)
9461 {
9462 await e.IqError(ex, e.To);
9463 }
9464 }
9465
9466 private async Task ObsoleteContractHandler(object Sender, IqEventArgs e)
9467 {
9468 try
9469 {
9470 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
9471
9472 using Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase);
9473 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9474 if (Contract is null)
9475 {
9476 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9477 return;
9478 }
9479
9480 switch (await Contract.CanRevoke(e.From, this.Server, this))
9481 {
9482 case 0: // OK, creator, contract not legally biding.
9483 case 1: // OK, role dictates part can revoke contract/consent.
9484 break;
9485
9486 case 2:
9487 await e.IqErrorForbidden(e.To, "Contract is legally binding and cannot be obsoleted.", "en");
9488 return;
9489
9490 case 3:
9491 await e.IqErrorForbidden(e.To, "Only the creator can obsolete a contract.", "en");
9492 return;
9493
9494 case 4:
9495 await e.IqErrorForbidden(e.To, "Not a part in the contract.", "en");
9496 return;
9497
9498 case 5:
9499 await e.IqErrorForbidden(e.To, "Your role cannot revoke signature/consent.", "en");
9500 return;
9501 }
9502
9503 Contract.State = ContractState.Obsoleted;
9504 await Contract.Sign(this);
9505
9506 await Database.Update(Contract);
9507
9508 if (Contract.CanActAsTemplate)
9509 await RuntimeCounters.IncrementCounter("Legal.Template." + Contract.State.ToString());
9510 else
9511 await RuntimeCounters.IncrementCounter("Legal.Contract." + Contract.State.ToString());
9512
9513 Log.Informational("Contract obsoleted.", Contract.ContractId.Value, e.From.Address.Value, "ContractObsoleted");
9514
9515 StringBuilder Xml = new StringBuilder();
9516 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9517 await e.IqResult(Xml.ToString(), e.To);
9518
9519 await this.SendContractUpdatedEvent(Contract, false);
9520
9521 if (!(this.geo is null))
9522 await this.geo.Delete(Contract);
9523 }
9524 catch (Exception ex)
9525 {
9526 await e.IqError(ex, e.To);
9527 }
9528 }
9529
9530 internal async Task SendContractUpdatedEvent(Contract Contract, bool Created)
9531 {
9532 StringBuilder Xml = new StringBuilder();
9533
9534 Xml.Append("<contract");
9535 Xml.Append(Created ? "Created" : "Updated");
9536 Xml.Append(" xmlns='");
9537 Xml.Append(NamespaceSmartContracts(Contract.Version));
9538 Xml.Append("' contractId='");
9539 Xml.Append(XML.Encode(Contract.ContractId));
9540 Xml.Append("'/>");
9541
9542 string Event = Xml.ToString();
9543
9544 foreach (string StatusRecipient in Contract.GetStakeholders(this.Server))
9545 await this.Server.SendMessage(string.Empty, string.Empty, Contract.Provider, StatusRecipient, string.Empty, Event);
9546 }
9547
9548 private async Task DeleteContractHandler(object Sender, IqEventArgs e)
9549 {
9550 try
9551 {
9552 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
9553
9554 using Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase);
9555 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9556 if (Contract is null)
9557 {
9558 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9559 return;
9560 }
9561
9562 if (!Contract.IsCreator(e.From, this.Server))
9563 {
9564 await e.IqErrorForbidden(e.To, "Only the creator can delete a contract.", "en");
9565 return;
9566 }
9567
9568 if (await Contract.IsLegallyBinding(true, false, this))
9569 {
9570 await e.IqErrorForbidden(e.To, "Contract is legally binding and cannot be deleted.", "en");
9571 return;
9572 }
9573
9574 if (await Contract.IsLegallyBinding(false, false, this) && DateTime.Now < Contract.To + Contract.ArchiveRequired)
9575 {
9576 await e.IqErrorForbidden(e.To, "Contract cannot be deleted before its required archivation period expires.", "en");
9577 return;
9578 }
9579
9580 Contract.State = ContractState.Deleted;
9581 await Contract.Sign(this);
9582
9583 await Database.Delete(Contract);
9584 await this.DeleteAttachments(Contract.Attachments);
9585 Contract.Attachments = null;
9586
9587 Log.Informational("Contract deleted.", Contract.ContractId.Value, e.From.Address.Value, "ContractDeleted");
9588
9589 if (Contract.CanActAsTemplate)
9590 await RuntimeCounters.IncrementCounter("Legal.Template." + Contract.State.ToString());
9591 else
9592 await RuntimeCounters.IncrementCounter("Legal.Contract." + Contract.State.ToString());
9593
9594 StringBuilder Xml = new StringBuilder();
9595 await Contract.Serialize(Xml, true, true, true, false, true, true, false, null, this);
9596 await e.IqResult(Xml.ToString(), e.To);
9597
9598 await this.SendContractDeletedEvent(Contract);
9599
9600 if (!(this.geo is null))
9601 await this.geo.Delete(Contract);
9602 }
9603 catch (Exception ex)
9604 {
9605 await e.IqError(ex, e.To);
9606 }
9607 }
9608
9609 private async Task DeleteAttachments(params AttachmentReference[] AttachmentReferences)
9610 {
9611 if (!(AttachmentReferences is null))
9612 {
9613 foreach (AttachmentReference Ref in AttachmentReferences)
9614 {
9615 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
9616 if (!(Attachment is null))
9617 {
9618 await Database.Delete(Attachment);
9619
9620 if (File.Exists(Attachment.LocalFileName))
9621 {
9622 try
9623 {
9624 File.Delete(Attachment.LocalFileName);
9625 }
9626 catch (Exception ex)
9627 {
9628 Log.Exception(ex);
9629 }
9630 }
9631 }
9632 }
9633 }
9634 }
9635
9636 private async Task SendContractDeletedEvent(Contract Contract)
9637 {
9638 StringBuilder Xml = new StringBuilder();
9639
9640 Xml.Append("<contractDeleted xmlns='");
9641 Xml.Append(NamespaceSmartContracts(Contract.Version));
9642 Xml.Append("' contractId='");
9643 Xml.Append(XML.Encode(Contract.ContractId));
9644 Xml.Append("'/>");
9645
9646 string Event = Xml.ToString();
9647
9648 foreach (string StatusRecipient in Contract.GetStakeholders(this.Server))
9649 await this.Server.SendMessage(string.Empty, string.Empty, Contract.Provider, StatusRecipient, string.Empty, Event);
9650 }
9651
9652 private async Task UpdateContractHandler(object Sender, IqEventArgs e)
9653 {
9654 try
9655 {
9656 if (!this.Server.IsServerDomain(e.From.Domain, true))
9657 {
9658 await e.IqErrorForbidden(e.To, "Only accounts on the broker can manage contracts on the broker.", "en");
9659 return;
9660 }
9661
9662 Contract UpdatedContract = null;
9663 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
9664 string Namespace = NamespaceSmartContracts(QueryVersion);
9665
9666 foreach (XmlNode N in e.Query.ChildNodes)
9667 {
9668 if (N is XmlElement E && E.LocalName == "contract" && IsNamespaceSmartContract(E.NamespaceURI))
9669 {
9670 ParsedContract Parsed = await Contract.Parse(E, this);
9671 UpdatedContract = Parsed?.Contract;
9672
9673 if (UpdatedContract is null)
9674 {
9675 await e.IqErrorBadRequest(e.To, "Invalid contract.", "en");
9676 return;
9677 }
9678
9679 bool HasStatus = Parsed.HasStatus;
9680 bool ParametersValid = Parsed.ParametersValid;
9681
9682 if (HasStatus)
9683 {
9684 await e.IqErrorBadRequest(e.To, "Status element not permitted when updating contract.", "en");
9685 return;
9686 }
9687
9688 if (!ParametersValid)
9689 {
9690 await e.IqErrorBadRequest(e.To, "Contract parameter " + Parsed.FirstParameterErrorName +
9691 " has invalid value: " + Parsed.FirstParameterError, "en");
9692 return;
9693 }
9694
9695 break;
9696 }
9697 }
9698
9699 if (UpdatedContract is null)
9700 {
9701 await e.IqErrorBadRequest(e.To, "No contract.", "en");
9702 return;
9703 }
9704
9705 CaseInsensitiveString ContractId = UpdatedContract.ContractId;
9706 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
9707 {
9708 await e.IqErrorBadRequest(e.To, "id attribute must be specified by client.", "en");
9709 return;
9710 }
9711
9712 if (!(UpdatedContract.ClientSignatures is null) && UpdatedContract.ClientSignatures.Length > 0)
9713 {
9714 await e.IqErrorBadRequest(e.To, "Cannot update a contract with signatures.", "en");
9715 return;
9716 }
9717
9718 if (!(UpdatedContract.ServerSignature is null))
9719 {
9720 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
9721 return;
9722 }
9723
9724 using Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase);
9725 Contract PrevContract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9726 if (PrevContract is null)
9727 {
9728 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9729 return;
9730 }
9731
9732 if (!PrevContract.IsCreator(e.From, this.Server))
9733 {
9734 await e.IqErrorForbidden(e.To, "Only the creator can update the contract.", "en");
9735 return;
9736 }
9737
9738 if (!(PrevContract.ClientSignatures is null) && PrevContract.ClientSignatures.Length > 0)
9739 {
9740 await e.IqErrorBadRequest(e.To, "Cannot update a contract with signatures.", "en");
9741 return;
9742 }
9743
9744 switch (PrevContract.State)
9745 {
9746 case ContractState.Proposed:
9747 case ContractState.Rejected:
9748 case ContractState.Approved:
9749 case ContractState.Obsoleted:
9750 break;
9751
9752 case ContractState.BeingSigned:
9753 case ContractState.Deleted:
9754 case ContractState.Signed:
9755 case ContractState.Failed:
9756 default:
9757 await e.IqErrorForbidden(e.To, "Current state of the contract does not allow updates.", "en");
9758 return;
9759 }
9760
9761 LegalIdentity Identity = await this.GetCurrentApprovedLegalIdentityAsync(e.From.Account);
9762 if (Identity is null)
9763 {
9764 await e.IqErrorForbidden(e.To, "No current approved legal identity found for account.", "en");
9765 return;
9766 }
9767
9768 string Errors = await this.ValidateContent(UpdatedContract, null);
9769 if (!string.IsNullOrEmpty(Errors))
9770 {
9771 await e.IqErrorBadRequest(e.To, Errors, "en");
9772 return;
9773 }
9774
9775 UpdatedContract.ObjectId = PrevContract.ObjectId;
9776 UpdatedContract.TemplateId = PrevContract.TemplateId;
9777 UpdatedContract.Provider = PrevContract.Provider;
9778 UpdatedContract.Account = PrevContract.Account;
9779 UpdatedContract.Created = PrevContract.Created;
9780 UpdatedContract.Nonce = PrevContract.Nonce;
9781 UpdatedContract.Updated = UtcNowSecond;
9782 UpdatedContract.Version = QueryVersion;
9783
9784 if (PrevContract.State == ContractState.Approved && !PrevContract.UpdateRequiresReview(UpdatedContract))
9785 UpdatedContract.State = ContractState.Approved;
9786 else
9787 UpdatedContract.State = ContractState.Proposed;
9788
9789 await UpdatedContract.Sign(this);
9790
9791 await Database.Update(UpdatedContract);
9792
9793 Log.Informational("Contract updated.", UpdatedContract.ContractId.Value, e.From.Address.Value, "ContractUpdated");
9794
9795 StringBuilder Xml = new StringBuilder();
9796 await UpdatedContract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9797 await e.IqResult(Xml.ToString(), e.To);
9798
9799 await this.SendContractUpdatedEvent(UpdatedContract, false);
9800 }
9801 catch (Exception ex)
9802 {
9803 await e.IqError(ex, e.To);
9804 }
9805 }
9806
9807 private async Task GetSchemasHandler(object Sender, IqEventArgs e)
9808 {
9809 try
9810 {
9811 StringBuilder Xml = new StringBuilder();
9812 CaseInsensitiveString LastNamespace = null;
9813
9814 Xml.Append("<schemas xmlns='");
9815 Xml.Append(e.Query.NamespaceURI);
9816 Xml.Append("'>");
9817
9818 foreach (ValidationSchema Schema in await Database.Find<ValidationSchema>("Namespace", "HashBase64"))
9819 {
9820 if (Schema.Namespace != LastNamespace)
9821 {
9822 if (!(LastNamespace is null))
9823 Xml.Append("</schemaRef>");
9824
9825 LastNamespace = Schema.Namespace;
9826
9827 Xml.Append("<schemaRef namespace='");
9828 Xml.Append(XML.Encode(LastNamespace));
9829 Xml.Append("'>");
9830 }
9831
9832 Xml.Append("<digest function='");
9833 Xml.Append(Schema.Function.ToString());
9834 Xml.Append("'>");
9835 Xml.Append(Schema.HashBase64);
9836 Xml.Append("</digest>");
9837 }
9838
9839 if (!(LastNamespace is null))
9840 Xml.Append("</schemaRef>");
9841
9842 Xml.Append("</schemas>");
9843
9844 await e.IqResult(Xml.ToString(), e.To);
9845 }
9846 catch (Exception ex)
9847 {
9848 await e.IqError(ex, e.To);
9849 }
9850 }
9851
9852 private async Task GetSchemaHandler(object Sender, IqEventArgs e)
9853 {
9854 try
9855 {
9856 HashFunction Function = HashFunction.SHA256;
9857 string DigestBase64 = null;
9858 CaseInsensitiveString Namespace = XML.Attribute(e.Query, "namespace");
9859
9860 if (CaseInsensitiveString.IsNullOrEmpty(Namespace))
9861 {
9862 await e.IqErrorBadRequest(e.To, "No namespace provided.", "en");
9863 return;
9864 }
9865
9866 foreach (XmlNode N in e.Query.ChildNodes)
9867 {
9868 if (N is XmlElement E && E.LocalName == "digest" && E.NamespaceURI == e.Query.NamespaceURI)
9869 {
9870 DigestBase64 = E.InnerText;
9871 if (!Enum.TryParse(XML.Attribute(E, "function"), out Function))
9872 {
9873 await e.IqErrorBadRequest(e.To, "Invalid hash function.", "en");
9874 return;
9875 }
9876 }
9877 }
9878
9879 ValidationSchema Schema;
9880
9881 if (DigestBase64 is null)
9882 {
9883 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(
9884 new FilterFieldEqualTo("Namespace", Namespace), "-Created");
9885 }
9886 else
9887 {
9888 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(new FilterAnd(
9889 new FilterFieldEqualTo("Namespace", Namespace),
9890 new FilterFieldEqualTo("HashBase64", DigestBase64),
9891 new FilterFieldEqualTo("Function", Function)), "-Created");
9892 }
9893
9894 if (Schema is null)
9895 {
9896 string ErrorMsg;
9897
9898 (Schema, ErrorMsg) = await this.LoadSchema(Namespace, null);
9899 if (!string.IsNullOrEmpty(ErrorMsg))
9900 await e.IqErrorBadRequest(e.To, "Unable to download schema: " + ErrorMsg, "en");
9901
9902 if (Schema is null)
9903 {
9904 await e.IqErrorItemNotFound(e.To, "Schema not found.", "en");
9905 return;
9906 }
9907 }
9908
9909 StringBuilder Xml = new StringBuilder();
9910
9911 Xml.Append("<schema xmlns='");
9912 Xml.Append(e.Query.NamespaceURI);
9913 Xml.Append("'>");
9914 Xml.Append(Convert.ToBase64String(Schema.XmlSchema));
9915 Xml.Append("</schema>");
9916
9917 await e.IqResult(Xml.ToString(), e.To);
9918 }
9919 catch (Exception ex)
9920 {
9921 await e.IqError(ex, e.To);
9922 }
9923 }
9924
9925 private async Task GetLegalIdentitiesOfContractHandler(object Sender, IqEventArgs e)
9926 {
9927 try
9928 {
9929 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
9930 bool Current = XML.Attribute(e.Query, "current", false);
9931 bool Historic = XML.Attribute(e.Query, "historic", true);
9932
9933 if (!(Current || Historic))
9934 {
9935 await e.IqErrorBadRequest(e.To, "Both current and historic attributes cannot both be false.", "en");
9936 return;
9937 }
9938
9939 XmppAddress ContractAddress = new XmppAddress(ContractId);
9940 List<LegalIdentity> Identities = new List<LegalIdentity>();
9941 List<Dictionary<string, string>> AttachmentUrlss = new List<Dictionary<string, string>>();
9942 LegalIdentity Identity, CurrentIdentity;
9943 DateTime UtcNow = DateTime.UtcNow;
9944
9945 if (this.IsComponentDomain(ContractAddress.Domain, true))
9946 {
9948
9949 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
9950 {
9951 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9952 if (Contract is null)
9953 {
9954 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9955 return;
9956 }
9957
9958 if (!await Contract.CanRead(e.From, this.Server, this))
9959 {
9960 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
9961 return;
9962 }
9963 }
9964
9965 if (!(Contract.ClientSignatures is null))
9966 {
9967 Dictionary<string, bool> Domains = null;
9968
9969 foreach (ClientSignature Signature in Contract.ClientSignatures)
9970 {
9971 XmppAddress LegalId = new XmppAddress(Signature.LegalId);
9972 if (this.IsComponentDomain(LegalId.Domain, true))
9973 {
9974 Identity = await GetLocalLegalIdentity(Signature.LegalId);
9975
9976 if (Historic)
9977 {
9978 Identities.Add(Identity);
9979 AttachmentUrlss.Add(null);
9980 }
9981
9982 if (Current)
9983 {
9984 CurrentIdentity = await this.GetApprovedLegalIdentityAsync(Identity.Account, UtcNow);
9985 if (CurrentIdentity.Id != Identity.Id)
9986 {
9987 Identities.Add(CurrentIdentity);
9988 AttachmentUrlss.Add(null);
9989 }
9990 }
9991 }
9992 else
9993 {
9994 Domains ??= new Dictionary<string, bool>();
9995 Domains[LegalId.Domain] = true;
9996 }
9997 }
9998
9999 if (!(Domains is null))
10000 {
10001 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
10002 object SynchObj = new object();
10003 int Count = Domains.Count;
10004 StringBuilder Xml = new StringBuilder();
10005 string ContractsNamespace = NamespaceSmartContracts(Contract.Version);
10006
10007 Xml.Append("<getLegalIdentities xmlns='");
10008 Xml.Append(ContractsNamespace);
10009 Xml.Append("' contractId='");
10010 Xml.Append(XML.Encode(ContractId));
10011 Xml.Append("' current='");
10012 Xml.Append(CommonTypes.Encode(Current));
10013 Xml.Append("' historic='");
10014 Xml.Append(CommonTypes.Encode(Historic));
10015 Xml.Append("'/>");
10016
10017 string Request = Xml.ToString();
10018 bool Responded = false;
10019
10020 foreach (KeyValuePair<string, bool> ByDomain in Domains)
10021 {
10022 await this.Server.SendIqRequest("get", this.MainDomain,
10023 new XmppAddress(ByDomain.Key), string.Empty, Request, false, async (sender2, e2) =>
10024 {
10025 bool Last;
10026
10027 lock (SynchObj)
10028 {
10029 Last = --Count == 0;
10030 }
10031
10032 if (e2.Ok)
10033 {
10034 XmlElement E = e2.FirstElement;
10035
10036 if (!(E is null) && E.LocalName == "identities")
10037 {
10038 foreach (XmlNode N in E.ChildNodes)
10039 {
10040 if (N is XmlElement E2 && E2.LocalName == "identity")
10041 {
10042 Identity = LegalIdentity.Parse(E2, out bool HasStatus, out Dictionary<string, string> AttachmentUrls);
10043
10044 lock (Identities)
10045 {
10046 Identities.Add(Identity);
10047 AttachmentUrlss.Add(AttachmentUrls);
10048 }
10049 }
10050 }
10051 }
10052
10053 Result.TrySetResult(true);
10054 }
10055 else
10056 {
10057 if (Result.TrySetResult(false) && !Responded)
10058 {
10059 Responded = true;
10060 await e.IqError(e2.ErrorTypeString, e2.ErrorElement?.OuterXml ?? string.Empty, e2.To, e2.ErrorText, string.Empty);
10061 }
10062 }
10063 }, null);
10064 }
10065
10066 if (!await Result.Task || Responded)
10067 return;
10068 }
10069 }
10070 }
10071 else
10072 {
10073 if (e.From.Address != ContractAddress.Domain)
10074 {
10075 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
10076 return;
10077 }
10078
10079 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(
10080 new FilterFieldEqualTo("ContractId", ContractId), "LegalId");
10081
10082 foreach (ContractSignature Signature in Signatures)
10083 {
10084 Identity = await GetLocalLegalIdentity(Signature.LegalId);
10085
10086 if (Historic)
10087 {
10088 Identities.Add(Identity);
10089 AttachmentUrlss.Add(null);
10090 }
10091
10092 if (Current)
10093 {
10094 CurrentIdentity = await this.GetApprovedLegalIdentityAsync(Identity.Account, UtcNow);
10095 if (CurrentIdentity.Id != Identity.Id)
10096 {
10097 Identities.Add(CurrentIdentity);
10098 AttachmentUrlss.Add(null);
10099 }
10100 }
10101 }
10102 }
10103
10104 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
10105 string Response;
10106
10107 lock (Identities)
10108 {
10109 Response = this.SerializeIdentities(Identities, AttachmentUrlss, QueryVersion);
10110 }
10111
10112 await e.IqResult(Response, e.To);
10113 }
10114 catch (Exception ex)
10115 {
10116 await e.IqError(ex, e.To);
10117 }
10118 }
10119
10120 private async Task GetNetworkIdentitiesHandler(object Sender, IqEventArgs e)
10121 {
10122 try
10123 {
10124 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
10125
10126 XmppAddress ContractAddress = new XmppAddress(ContractId);
10127 List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> Identities = new List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>>();
10128
10129 if (this.IsComponentDomain(ContractAddress.Domain, true))
10130 {
10132
10133 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
10134 {
10135 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
10136 if (Contract is null)
10137 {
10138 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
10139 return;
10140 }
10141
10142 if (!await Contract.CanRead(e.From, this.Server, this))
10143 {
10144 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
10145 return;
10146 }
10147 }
10148
10149 IqResultEventArgs ErrorResponse = await this.GetNetworkIdentities(Contract, Identities);
10150 if (!(ErrorResponse is null))
10151 {
10152 await e.IqError(ErrorResponse.ErrorTypeString, ErrorResponse.ErrorElement?.OuterXml ?? string.Empty,
10153 ErrorResponse.To, ErrorResponse.ErrorText, string.Empty);
10154 return;
10155 }
10156 }
10157 else
10158 {
10159 if (e.From.Address != ContractAddress.Domain)
10160 {
10161 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
10162 return;
10163 }
10164
10165 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(
10166 new FilterFieldEqualTo("ContractId", ContractId), "LegalId");
10167
10168 foreach (ContractSignature Signature in Signatures)
10169 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(Signature.LegalId, Signature.BareJid));
10170 }
10171
10172 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
10173 StringBuilder Response = new StringBuilder();
10174
10175 Response.Append("<networkIdentities xmlns='");
10176 Response.Append(NamespaceSmartContracts(QueryVersion));
10177 Response.Append("'>");
10178
10179 lock (Identities)
10180 {
10181 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in Identities)
10182 {
10183 Response.Append("<networkIdentity bareJid='");
10184 Response.Append(XML.Encode(P.Value.Value));
10185 Response.Append("' legalId='");
10186 Response.Append(XML.Encode(P.Key.Value));
10187 Response.Append("'/>");
10188 }
10189 }
10190
10191 Response.Append("</networkIdentities>");
10192
10193 await e.IqResult(Response.ToString(), e.To);
10194 }
10195 catch (Exception ex)
10196 {
10197 await e.IqError(ex, e.To);
10198 }
10199 }
10200
10201 internal async Task<IqResultEventArgs> GetNetworkIdentities(Contract Contract,
10202 List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> Identities)
10203 {
10204 if (!(Contract.ClientSignatures is null))
10205 {
10206 Dictionary<string, bool> Domains = null;
10207
10208 foreach (ClientSignature Signature in Contract.ClientSignatures)
10209 {
10210 XmppAddress LegalId = new XmppAddress(Signature.LegalId);
10211 if (this.IsComponentDomain(LegalId.Domain, true))
10212 {
10213 int i = LegalId.Domain.IndexOf('.');
10214 string JidDomain = i < 0 ? this.Server.Domain : LegalId.Domain.Substring(i + 1);
10215 LegalIdentity Identity = await GetLocalLegalIdentity(Signature.LegalId);
10216 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(Identity.Id, Identity.Account + "@" + JidDomain));
10217 }
10218 else
10219 {
10220 Domains ??= new Dictionary<string, bool>();
10221 Domains[LegalId.Domain] = true;
10222 }
10223 }
10224
10225 if (!(Domains is null))
10226 {
10227 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
10228 object SynchObj = new object();
10229 int Count = Domains.Count;
10230 StringBuilder Xml = new StringBuilder();
10231
10232 Xml.Append("<getNetworkIdentities xmlns='");
10233 Xml.Append(NamespaceSmartContracts(Contract.Version));
10234 Xml.Append("' contractId='");
10235 Xml.Append(XML.Encode(Contract.ContractId));
10236 Xml.Append("'/>");
10237
10238 string Request = Xml.ToString();
10239 IqResultEventArgs ErrorResponse = null;
10240
10241 foreach (KeyValuePair<string, bool> ByDomain in Domains)
10242 {
10243 await this.Server.SendIqRequest("get", this.MainDomain,
10244 new XmppAddress(ByDomain.Key), string.Empty, Request, false, (sender2, e2) =>
10245 {
10246 bool Last;
10247
10248 lock (SynchObj)
10249 {
10250 Last = --Count == 0;
10251 }
10252
10253 if (e2.Ok)
10254 {
10255 XmlElement E = e2.FirstElement;
10256
10257 if (!(E is null) && E.LocalName == "networkIdentities")
10258 {
10259 foreach (XmlNode N in E.ChildNodes)
10260 {
10261 if (N is XmlElement E2 && E2.LocalName == "networkIdentity")
10262 {
10263 lock (Identities)
10264 {
10265 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(
10266 XML.Attribute(E2, "legalId"), XML.Attribute(E2, "bareJid")));
10267 }
10268 }
10269 }
10270 }
10271
10272 Result.TrySetResult(true);
10273 }
10274 else
10275 {
10276 if (ErrorResponse is null)
10277 {
10278 ErrorResponse = e2;
10279 Result.TrySetResult(false);
10280 }
10281 }
10282
10283 return Task.CompletedTask;
10284 }, null);
10285 }
10286
10287 if (!await Result.Task)
10288 return ErrorResponse;
10289 }
10290 }
10291
10292 return null;
10293 }
10294
10295 private async Task SearchPublicContractsHandler(object Sender, IqEventArgs e)
10296 {
10297 try
10298 {
10299 int Offset = XML.Attribute(e.Query, "offset", 0);
10300 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
10301 List<CustomFilter> CustomFilters = new List<CustomFilter>();
10302 List<Filter> Filters = new List<Filter>()
10303 {
10304 new FilterFieldEqualTo("Visibility", ContractVisibility.PublicSearchable)
10305 };
10306
10307 foreach (XmlNode N in e.Query.ChildNodes)
10308 {
10309 if (N is XmlElement E)
10310 {
10311 switch (E.LocalName)
10312 {
10313 case "localName":
10314 if (!await this.AddStrFilter(E, e, Filters, "ForMachinesLocalName"))
10315 return;
10316 break;
10317
10318 case "namespace":
10319 if (!await this.AddStrFilter(E, e, Filters, "ForMachinesNamespace"))
10320 return;
10321 break;
10322
10323 case "template":
10324 if (!await this.AddStrFilter(E, e, Filters, "TemplateId"))
10325 return;
10326 break;
10327
10328 case "role":
10329 if (!await this.AddRoleFilter(E, e, CustomFilters))
10330 return;
10331 break;
10332
10333 case "parameter":
10334 if (!await this.AddParameterFilter(E, e, CustomFilters, XML.Attribute(E, "name")))
10335 return;
10336 break;
10337
10338 case "created":
10339 if (!await this.AddDateTimeFilter(E, e, Filters, "Created"))
10340 return;
10341 break;
10342
10343 case "updated":
10344 if (!await this.AddDateTimeFilter(E, e, Filters, "Updated"))
10345 return;
10346 break;
10347
10348 case "from":
10349 if (!await this.AddDateTimeFilter(E, e, Filters, "From"))
10350 return;
10351 break;
10352
10353 case "to":
10354 if (!await this.AddDateTimeFilter(E, e, Filters, "To"))
10355 return;
10356 break;
10357
10358 case "duration":
10359 if (!await this.AddDurationFilter(E, e, CustomFilters))
10360 return;
10361 break;
10362
10363 default:
10364 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10365 return;
10366 }
10367 }
10368 }
10369
10370 List<Contract> Result = new List<Contract>();
10371 Filter SearchFilter = Filters.Count == 1 ? Filters[0] : new FilterAnd(Filters.ToArray());
10372 bool HasCustomFilters = CustomFilters.Count > 0;
10373 IEnumerable<Contract> Contracts;
10374 bool More = false;
10375
10376 if (HasCustomFilters)
10377 Contracts = await Database.Find<Contract>(SearchFilter, "ContractId");
10378 else
10379 {
10380 Contracts = await Database.Find<Contract>(Offset, MaxCount == int.MaxValue ? MaxCount : MaxCount + 1, SearchFilter, "ContractId");
10381 Offset = 0;
10382 }
10383
10384 foreach (Contract Contract in Contracts)
10385 {
10386 if (HasCustomFilters)
10387 {
10388 bool Included = true;
10389
10390 foreach (CustomFilter Filter in CustomFilters)
10391 {
10392 if (!Filter.IsIncluded(Contract))
10393 {
10394 Included = false;
10395 break;
10396 }
10397 }
10398
10399 if (!Included)
10400 continue;
10401 }
10402
10403 if (Offset > 0)
10404 {
10405 Offset--;
10406 continue;
10407 }
10408
10409 if (MaxCount > 0)
10410 MaxCount--;
10411 else
10412 {
10413 More = true;
10414 break;
10415 }
10416
10417 Result.Add(Contract);
10418 }
10419
10420 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
10421 StringBuilder Xml = new StringBuilder();
10422
10423 Xml.Append("<searchResult xmlns='");
10424 Xml.Append(NamespaceSmartContracts(QueryVersion));
10425 Xml.Append("' more='");
10426 Xml.Append(CommonTypes.Encode(More));
10427 Xml.Append("'>");
10428
10429 foreach (IContractReference Contract in Result)
10430 {
10431 Xml.Append("<ref id='");
10432 Xml.Append(XML.Encode(Contract.ContractId));
10433 Xml.Append("'/>");
10434 }
10435
10436 Xml.Append("</searchResult>");
10437
10438 await e.IqResult(Xml.ToString(), e.To);
10439 }
10440 catch (Exception ex)
10441 {
10442 await e.IqError(ex, e.To);
10443 }
10444 }
10445
10446 private async Task<bool> AddStrFilter(XmlElement E, IqEventArgs e, List<Filter> Filters, string Field)
10447 {
10448 foreach (XmlNode N2 in E.ChildNodes)
10449 {
10450 if (N2 is XmlElement E2)
10451 {
10452 switch (E2.LocalName)
10453 {
10454 case "eq":
10455 Filters.Add(new FilterFieldEqualTo(Field, E2.InnerText));
10456 break;
10457
10458 case "like":
10459 Filters.Add(new FilterFieldLikeRegEx(Field, E2.InnerText));
10460 break;
10461
10462 default:
10463 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10464 return false;
10465 }
10466 }
10467 }
10468
10469 return true;
10470 }
10471
10472 private async Task<bool> AddRoleFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters)
10473 {
10474 foreach (XmlNode N2 in E.ChildNodes)
10475 {
10476 if (N2 is XmlElement E2)
10477 {
10478 switch (E2.LocalName)
10479 {
10480 case "eq":
10481 Filters.Add(new RoleEqFilter()
10482 {
10483 Value = E2.InnerText
10484 });
10485 break;
10486
10487 case "like":
10488 Filters.Add(new RoleLikeFilter()
10489 {
10490 Value = E2.InnerText
10491 });
10492 break;
10493
10494 default:
10495 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10496 return false;
10497 }
10498 }
10499 }
10500
10501 return true;
10502 }
10503
10504 private async Task<bool> AddParameterFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters, string Name)
10505 {
10506 foreach (XmlNode N2 in E.ChildNodes)
10507 {
10508 if (N2 is XmlElement E2)
10509 {
10510 switch (E2.LocalName)
10511 {
10512 case "eqStr":
10513 Filters.Add(new StringParameterEqFilter()
10514 {
10515 Name = Name,
10516 Value = E2.InnerText
10517 });
10518 break;
10519
10520 case "neqStr":
10521 Filters.Add(new StringParameterNEqFilter()
10522 {
10523 Name = Name,
10524 Value = E2.InnerText
10525 });
10526 break;
10527
10528 case "gtStr":
10529 Filters.Add(new StringParameterGtFilter()
10530 {
10531 Name = Name,
10532 Value = E2.InnerText
10533 });
10534 break;
10535
10536 case "gteStr":
10537 Filters.Add(new StringParameterGteFilter()
10538 {
10539 Name = Name,
10540 Value = E2.InnerText
10541 });
10542 break;
10543
10544 case "ltStr":
10545 Filters.Add(new StringParameterLtFilter()
10546 {
10547 Name = Name,
10548 Value = E2.InnerText
10549 });
10550 break;
10551
10552 case "lteStr":
10553 Filters.Add(new StringParameterLteFilter()
10554 {
10555 Name = Name,
10556 Value = E2.InnerText
10557 });
10558 break;
10559
10560 case "like":
10561 Filters.Add(new StringParameterLikeFilter()
10562 {
10563 Name = Name,
10564 Value = E2.InnerText
10565 });
10566 break;
10567
10568 case "eqNum":
10569 if (!CommonTypes.TryParse(E2.InnerText, out decimal NumValue))
10570 {
10571 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10572 return false;
10573 }
10574
10575 Filters.Add(new NumericalParameterEqFilter()
10576 {
10577 Name = Name,
10578 Value = NumValue
10579 });
10580 break;
10581
10582 case "neqNum":
10583 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
10584 {
10585 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10586 return false;
10587 }
10588
10589 Filters.Add(new NumericalParameterNEqFilter()
10590 {
10591 Name = Name,
10592 Value = NumValue
10593 });
10594 break;
10595
10596 case "gtNum":
10597 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
10598 {
10599 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10600 return false;
10601 }
10602
10603 Filters.Add(new NumericalParameterGtFilter()
10604 {
10605 Name = Name,
10606 Value = NumValue
10607 });
10608 break;
10609
10610 case "gteNum":
10611 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
10612 {
10613 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10614 return false;
10615 }
10616
10617 Filters.Add(new NumericalParameterGteFilter()
10618 {
10619 Name = Name,
10620 Value = NumValue
10621 });
10622 break;
10623
10624 case "ltNum":
10625 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
10626 {
10627 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10628 return false;
10629 }
10630
10631 Filters.Add(new NumericalParameterLtFilter()
10632 {
10633 Name = Name,
10634 Value = NumValue
10635 });
10636 break;
10637
10638 case "lteNum":
10639 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
10640 {
10641 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10642 return false;
10643 }
10644
10645 Filters.Add(new NumericalParameterLteFilter()
10646 {
10647 Name = Name,
10648 Value = NumValue
10649 });
10650 break;
10651
10652 case "eqB":
10653 if (!CommonTypes.TryParse(E2.InnerText, out bool BoolValue))
10654 {
10655 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10656 return false;
10657 }
10658
10659 Filters.Add(new BooleanParameterEqFilter()
10660 {
10661 Name = Name,
10662 Value = BoolValue
10663 });
10664 break;
10665
10666 case "neqB":
10667 if (!CommonTypes.TryParse(E2.InnerText, out BoolValue))
10668 {
10669 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10670 return false;
10671 }
10672
10673 Filters.Add(new BooleanParameterNEqFilter()
10674 {
10675 Name = Name,
10676 Value = BoolValue
10677 });
10678 break;
10679
10680
10681 case "eqD":
10682 if (!XML.TryParse(E2.InnerText, out DateTime TP) || TP.TimeOfDay != TimeSpan.Zero)
10683 {
10684 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10685 return false;
10686 }
10687
10688 Filters.Add(new DateParameterEqFilter()
10689 {
10690 Name = Name,
10691 Value = TP
10692 });
10693 break;
10694
10695 case "neqD":
10696 if (!XML.TryParse(E2.InnerText, out TP))
10697 {
10698 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10699 return false;
10700 }
10701
10702 Filters.Add(new DateParameterNEqFilter()
10703 {
10704 Name = Name,
10705 Value = TP
10706 });
10707 break;
10708
10709 case "gtD":
10710 if (!XML.TryParse(E2.InnerText, out TP))
10711 {
10712 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10713 return false;
10714 }
10715
10716 Filters.Add(new DateParameterGtFilter()
10717 {
10718 Name = Name,
10719 Value = TP
10720 });
10721 break;
10722
10723 case "gteD":
10724 if (!XML.TryParse(E2.InnerText, out TP))
10725 {
10726 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10727 return false;
10728 }
10729
10730 Filters.Add(new DateParameterGteFilter()
10731 {
10732 Name = Name,
10733 Value = TP
10734 });
10735 break;
10736
10737 case "ltD":
10738 if (!XML.TryParse(E2.InnerText, out TP))
10739 {
10740 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10741 return false;
10742 }
10743
10744 Filters.Add(new DateParameterLtFilter()
10745 {
10746 Name = Name,
10747 Value = TP
10748 });
10749 break;
10750
10751 case "lteD":
10752 if (!XML.TryParse(E2.InnerText, out TP))
10753 {
10754 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10755 return false;
10756 }
10757
10758 Filters.Add(new DateParameterLteFilter()
10759 {
10760 Name = Name,
10761 Value = TP
10762 });
10763 break;
10764
10765 case "eqDT":
10766 if (!XML.TryParse(E2.InnerText, out TP))
10767 {
10768 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10769 return false;
10770 }
10771
10772 Filters.Add(new DateTimeParameterEqFilter()
10773 {
10774 Name = Name,
10775 Value = TP
10776 });
10777 break;
10778
10779 case "neqDT":
10780 if (!XML.TryParse(E2.InnerText, out TP))
10781 {
10782 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10783 return false;
10784 }
10785
10786 Filters.Add(new DateTimeParameterNEqFilter()
10787 {
10788 Name = Name,
10789 Value = TP
10790 });
10791 break;
10792
10793 case "gtDT":
10794 if (!XML.TryParse(E2.InnerText, out TP))
10795 {
10796 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10797 return false;
10798 }
10799
10800 Filters.Add(new DateTimeParameterGtFilter()
10801 {
10802 Name = Name,
10803 Value = TP
10804 });
10805 break;
10806
10807 case "gteDT":
10808 if (!XML.TryParse(E2.InnerText, out TP))
10809 {
10810 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10811 return false;
10812 }
10813
10814 Filters.Add(new DateTimeParameterGteFilter()
10815 {
10816 Name = Name,
10817 Value = TP
10818 });
10819 break;
10820
10821 case "ltDT":
10822 if (!XML.TryParse(E2.InnerText, out TP))
10823 {
10824 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10825 return false;
10826 }
10827
10828 Filters.Add(new DateTimeParameterLtFilter()
10829 {
10830 Name = Name,
10831 Value = TP
10832 });
10833 break;
10834
10835 case "lteDT":
10836 if (!XML.TryParse(E2.InnerText, out TP))
10837 {
10838 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10839 return false;
10840 }
10841
10842 Filters.Add(new DateTimeParameterLteFilter()
10843 {
10844 Name = Name,
10845 Value = TP
10846 });
10847 break;
10848
10849 case "eqT":
10850 if (!TimeSpan.TryParse(E2.InnerText, out TimeSpan TS))
10851 {
10852 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10853 return false;
10854 }
10855
10856 Filters.Add(new TimeParameterEqFilter()
10857 {
10858 Name = Name,
10859 Value = TS
10860 });
10861 break;
10862
10863 case "neqT":
10864 if (!TimeSpan.TryParse(E2.InnerText, out TS))
10865 {
10866 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10867 return false;
10868 }
10869
10870 Filters.Add(new TimeParameterNEqFilter()
10871 {
10872 Name = Name,
10873 Value = TS
10874 });
10875 break;
10876
10877 case "gtT":
10878 if (!TimeSpan.TryParse(E2.InnerText, out TS))
10879 {
10880 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10881 return false;
10882 }
10883
10884 Filters.Add(new TimeParameterGtFilter()
10885 {
10886 Name = Name,
10887 Value = TS
10888 });
10889 break;
10890
10891 case "gteT":
10892 if (!TimeSpan.TryParse(E2.InnerText, out TS))
10893 {
10894 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10895 return false;
10896 }
10897
10898 Filters.Add(new TimeParameterGteFilter()
10899 {
10900 Name = Name,
10901 Value = TS
10902 });
10903 break;
10904
10905 case "ltT":
10906 if (!TimeSpan.TryParse(E2.InnerText, out TS))
10907 {
10908 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10909 return false;
10910 }
10911
10912 Filters.Add(new TimeParameterLtFilter()
10913 {
10914 Name = Name,
10915 Value = TS
10916 });
10917 break;
10918
10919 case "lteT":
10920 if (!TimeSpan.TryParse(E2.InnerText, out TS))
10921 {
10922 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10923 return false;
10924 }
10925
10926 Filters.Add(new TimeParameterLteFilter()
10927 {
10928 Name = Name,
10929 Value = TS
10930 });
10931 break;
10932
10933 case "eqDr":
10934 if (!Duration.TryParse(E2.InnerText, out Duration Dr))
10935 {
10936 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10937 return false;
10938 }
10939
10940 Filters.Add(new DurationParameterEqFilter()
10941 {
10942 Name = Name,
10943 Value = Dr
10944 });
10945 break;
10946
10947 case "neqDr":
10948 if (!Duration.TryParse(E2.InnerText, out Dr))
10949 {
10950 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10951 return false;
10952 }
10953
10954 Filters.Add(new DurationParameterNEqFilter()
10955 {
10956 Name = Name,
10957 Value = Dr
10958 });
10959 break;
10960
10961 case "gtDr":
10962 if (!Duration.TryParse(E2.InnerText, out Dr))
10963 {
10964 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10965 return false;
10966 }
10967
10968 Filters.Add(new DurationParameterGtFilter()
10969 {
10970 Name = Name,
10971 Value = Dr
10972 });
10973 break;
10974
10975 case "gteDr":
10976 if (!Duration.TryParse(E2.InnerText, out Dr))
10977 {
10978 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10979 return false;
10980 }
10981
10982 Filters.Add(new DurationParameterGteFilter()
10983 {
10984 Name = Name,
10985 Value = Dr
10986 });
10987 break;
10988
10989 case "ltDr":
10990 if (!Duration.TryParse(E2.InnerText, out Dr))
10991 {
10992 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
10993 return false;
10994 }
10995
10996 Filters.Add(new DurationParameterLtFilter()
10997 {
10998 Name = Name,
10999 Value = Dr
11000 });
11001 break;
11002
11003 case "lteDr":
11004 if (!Duration.TryParse(E2.InnerText, out Dr))
11005 {
11006 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11007 return false;
11008 }
11009
11010 Filters.Add(new DurationParameterLteFilter()
11011 {
11012 Name = Name,
11013 Value = Dr
11014 });
11015 break;
11016
11017 default:
11018 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11019 return false;
11020 }
11021 }
11022 }
11023
11024 return true;
11025 }
11026
11027 private async Task<bool> AddDateTimeFilter(XmlElement E, IqEventArgs e, List<Filter> Filters, string Field)
11028 {
11029 foreach (XmlNode N2 in E.ChildNodes)
11030 {
11031 if (N2 is XmlElement E2)
11032 {
11033 if (!XML.TryParse(E2.InnerText, out DateTime TP))
11034 {
11035 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11036 return false;
11037 }
11038
11039 switch (E2.LocalName)
11040 {
11041 case "eq":
11042 Filters.Add(new FilterFieldEqualTo(Field, TP));
11043 break;
11044
11045 case "neq":
11046 Filters.Add(new FilterFieldNotEqualTo(Field, TP));
11047 break;
11048
11049 case "gt":
11050 Filters.Add(new FilterFieldGreaterThan(Field, TP));
11051 break;
11052
11053 case "gte":
11054 Filters.Add(new FilterFieldGreaterOrEqualTo(Field, TP));
11055 break;
11056
11057 case "lt":
11058 Filters.Add(new FilterFieldLesserThan(Field, TP));
11059 break;
11060
11061 case "lte":
11062 Filters.Add(new FilterFieldLesserOrEqualTo(Field, TP));
11063 break;
11064
11065 default:
11066 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11067 return false;
11068 }
11069 }
11070 }
11071
11072 return true;
11073 }
11074
11075 private async Task<bool> AddDurationFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters)
11076 {
11077 foreach (XmlNode N2 in E.ChildNodes)
11078 {
11079 if (N2 is XmlElement E2)
11080 {
11081 if (!Duration.TryParse(E2.InnerText, out Duration Value))
11082 {
11083 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11084 return false;
11085 }
11086
11087 switch (E2.LocalName)
11088 {
11089 case "eq":
11090 Filters.Add(new DurationEqFilter()
11091 {
11092 Value = Value
11093 });
11094 break;
11095
11096 case "neq":
11097 Filters.Add(new DurationNEqFilter()
11098 {
11099 Value = Value
11100 });
11101 break;
11102
11103 case "gt":
11104 Filters.Add(new DurationGtFilter()
11105 {
11106 Value = Value
11107 });
11108 break;
11109
11110 case "gte":
11111 Filters.Add(new DurationGteFilter()
11112 {
11113 Value = Value
11114 });
11115 break;
11116
11117 case "lt":
11118 Filters.Add(new DurationLtFilter()
11119 {
11120 Value = Value
11121 });
11122 break;
11123
11124 case "lte":
11125 Filters.Add(new DurationLteFilter()
11126 {
11127 Value = Value
11128 });
11129 break;
11130
11131 default:
11132 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
11133 return false;
11134 }
11135 }
11136 }
11137
11138 return true;
11139 }
11140
11141 private async Task AddContractAttachmentHandler(object Sender, IqEventArgs e)
11142 {
11143 try
11144 {
11145 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
11146 string GetUrl = XML.Attribute(e.Query, "getUrl");
11147 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
11148
11149 if (!this.Server.IsServerDomain(e.From.Domain, true))
11150 {
11151 await e.IqErrorForbidden(e.To, "Only accounts on the broker can add attachments.", "en");
11152 return;
11153 }
11154
11155 if (!Uri.TryCreate(GetUrl, UriKind.Absolute, out Uri GetUri))
11156 {
11157 await e.IqErrorBadRequest(e.To, "Invalid Get URL.", "en");
11158 return;
11159 }
11160
11161 IAccount Account = await XmppServerModule.GetAccountAsync(e.From.Account);
11162 if (Account is null)
11163 {
11164 await e.IqErrorForbidden(e.To, "Account not found.", "en");
11165 return;
11166 }
11167
11168 if (!Account.Enabled)
11169 {
11170 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
11171 return;
11172 }
11173
11174 if (!(Account is Account Account2))
11175 {
11176 await e.IqErrorForbidden(e.To, "Forbidden to add attachments.", "en");
11177 return;
11178 }
11179
11180 using Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase);
11181 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
11182 if (Contract is null)
11183 {
11184 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
11185 return;
11186 }
11187
11188 if (!Contract.IsCreator(e.From, this.Server))
11189 {
11190 await e.IqErrorForbidden(e.To, "Only allowed to add attachments to your own contracts.", "en");
11191 return;
11192 }
11193
11194 if (Contract.State != ContractState.Proposed && Contract.State != ContractState.Approved)
11195 {
11196 await e.IqErrorForbidden(e.To, "Attachments can only be added to proposed or approved contracts, before they are signed.", "en");
11197 return;
11198 }
11199
11200 if (!(Contract.Attachments is null))
11201 {
11202 string s = Convert.ToBase64String(Signature);
11203
11204 foreach (AttachmentReference Ref in Contract.Attachments)
11205 {
11206 if (Convert.ToBase64String(Ref.Signature) == s)
11207 {
11208 await e.IqErrorForbidden(e.To, "Attachment already assigned to contract.", "en");
11209 return;
11210 }
11211 }
11212 }
11213
11215
11216 if (P.HasError)
11217 {
11218 await e.IqError(P.Error, e.To);
11219 return;
11220 }
11221
11222 string ContentType = P.ContentType;
11223
11224 using TemporaryStream File = P.Encoded;
11225 File.Position = 0;
11226
11227 LegalIdentity UploadingIdentity = await this.ValidateLocalSenderSignature(e.From, null, DateTime.Now, File, Signature);
11228 if (UploadingIdentity is null)
11229 {
11230 await e.IqErrorForbidden(e.To, "Attachment signature is invalid.", "en");
11231 return;
11232 }
11233
11234 KeyValuePair<Attachment, AttachmentReference> A = await this.CreateAttachment(GetUri,
11235 UploadingIdentity, Signature, File, ContentType, Account2, ContractId,
11236 Contract.Expires.ToUniversalTime(), false);
11237 List<AttachmentReference> References = new List<AttachmentReference>();
11238
11239 if (!(Contract.Attachments is null))
11240 References.AddRange(Contract.Attachments);
11241
11242 References.Add(A.Value);
11243
11244 Contract.Attachments = References.ToArray();
11245 await Contract.Sign(this);
11246
11247 await Database.Update(Contract);
11248
11249 StringBuilder Xml = new StringBuilder();
11250 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
11251
11252 await e.IqResult(Xml.ToString(), e.To);
11253 }
11254 catch (Exception ex)
11255 {
11256 await e.IqError(ex, e.To);
11257 }
11258 }
11259
11260 private async Task RemoveContractAttachmentHandler(object Sender, IqEventArgs e)
11261 {
11262 try
11263 {
11264 CaseInsensitiveString AttachmentId = XML.Attribute(e.Query, "attachmentId");
11265
11266 if (!this.Server.IsServerDomain(e.From.Domain, true))
11267 {
11268 await e.IqErrorForbidden(e.To, "Only accounts on the broker can remove attachments.", "en");
11269 return;
11270 }
11271
11272 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", AttachmentId));
11273 if (Attachment is null)
11274 {
11275 await e.IqErrorItemNotFound(e.To, "Attachment not found.", "en");
11276 return;
11277 }
11278
11280 {
11281 await e.IqErrorItemNotFound(e.To, "Attachment not assigned to contract.", "en");
11282 return;
11283 }
11284
11286 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", Attachment.ContractId), "Created");
11287 if (Contract is null)
11288 {
11289 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
11290 return;
11291 }
11292
11293 if (!Contract.IsCreator(e.From, this.Server))
11294 {
11295 await e.IqErrorForbidden(e.To, "Only allowed to remove attachments to your own contracts.", "en");
11296 return;
11297 }
11298
11299 if (Contract.State != ContractState.Proposed && Contract.State != ContractState.Approved)
11300 {
11301 await e.IqErrorForbidden(e.To, "Attachments can only be removed to proposed or approved contracts, before they are signed.", "en");
11302 return;
11303 }
11304
11305 if (File.Exists(Attachment.LocalFileName))
11306 File.Delete(Attachment.LocalFileName);
11307
11308 if (!(Contract.Attachments is null))
11309 {
11310 List<AttachmentReference> Attachments = new List<AttachmentReference>();
11311
11312 foreach (AttachmentReference Ref in Contract.Attachments)
11313 {
11314 if (Ref.Id != AttachmentId)
11315 Attachments.Add(Ref);
11316 }
11317
11318 Contract.Attachments = Attachments.ToArray();
11319 await Contract.Sign(this);
11320
11321 await Database.Update(Contract);
11322 }
11323
11324 await Database.Delete(Attachment);
11325
11326 StringBuilder Xml = new StringBuilder();
11327 await Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
11328
11329 await e.IqResult(Xml.ToString(), e.To);
11330 }
11331 catch (Exception ex)
11332 {
11333 await e.IqError(ex, e.To);
11334 }
11335 }
11336
11337 private async Task AuthorizeAccessToContractHandler(object Sender, IqEventArgs e)
11338 {
11339 try
11340 {
11341 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
11342
11343 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
11344 {
11345 await e.IqErrorBadRequest(e.To, "No Contract ID specified.", "en");
11346 return;
11347 }
11348
11349 XmppAddress ContractIdAddress = new XmppAddress(ContractId);
11350
11351 if (!ContractIdAddress.IsBareJID)
11352 {
11353 await e.IqErrorBadRequest(e.To, "Invalid Contract ID.", "en");
11354 return;
11355 }
11356
11357 if (!this.IsComponentDomain(ContractIdAddress.Domain, true))
11358 {
11359 await e.IqErrorBadRequest(e.To, "Not a local Contract ID.", "en");
11360 return;
11361 }
11362
11363 CaseInsensitiveString RemoteId = XML.Attribute(e.Query, "remoteId");
11364 bool Authorized = XML.Attribute(e.Query, "auth", true);
11365
11367 {
11368 await e.IqErrorBadRequest(e.To, "No Remote ID specified.", "en");
11369 return;
11370 }
11371
11372 XmppAddress RemoteAddress = new XmppAddress(RemoteId);
11373 if (!RemoteAddress.IsBareJID)
11374 {
11375 await e.IqErrorBadRequest(e.To, "Invalid Remote ID.", "en");
11376 return;
11377 }
11378
11379 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
11380 {
11381 KeyValuePair<Contract, IqResultEventArgs> P = await this.GetContract(ContractId);
11382 Contract Contract = P.Key;
11383
11384 if (Contract is null)
11385 {
11386 if (string.IsNullOrEmpty(P.Value?.ErrorText))
11387 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
11388 else
11389 {
11390 await e.IqErrorItemNotFound(e.To, "Unable to get contract: " +
11391 P.Value.ErrorText, "en");
11392 }
11393
11394 return;
11395 }
11396
11397 if (!await Contract.CanRead(e.From, this.Server, this))
11398 {
11399 await e.IqErrorForbidden(e.To, "You do not have access rights to contract.", "en");
11400 return;
11401 }
11402
11403 ClientInformation ClientInfo = await this.GetNetworkIdentity(RemoteId, true, true, Contract.Version);
11404 CaseInsensitiveString RemoteJid = ClientInfo.Jid;
11405
11406 this.ContractAuthorization(RemoteJid, e.From.BareJid, ContractId, Authorized);
11407 }
11408
11409 await e.IqResult(string.Empty, e.To);
11410 }
11411 catch (Exception ex)
11412 {
11413 await e.IqError(ex, e.To);
11414 }
11415 }
11416
11417 #endregion
11418
11419 #region Third-party peer reviews
11420
11421 private async Task GetReviewIdProvidersHandler(object Sender, IqEventArgs e)
11422 {
11423 if (!this.Server.IsServerDomain(e.From.Domain, true) || !e.From.HasAccount)
11424 {
11425 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
11426 return;
11427 }
11428
11429 CaseInsensitiveString AccountName = e.From.Account;
11430 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
11431 if (Account is null)
11432 {
11433 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
11434 return;
11435 }
11436
11437 if (!Account.Enabled)
11438 {
11439 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
11440 return;
11441 }
11442
11443 LegalIdentity IdentityApplication = await Database.FindFirstIgnoreRest<LegalIdentity>(new FilterAnd(
11444 new FilterFieldEqualTo("Account", AccountName),
11445 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Created)), "-Created");
11446
11447 if (IdentityApplication is null)
11448 {
11449 await e.IqErrorForbidden(e.To, "No Identity Application found.", "en");
11450 return;
11451 }
11452
11453 KeyValuePair<string, object>[] Tags = IdentityApplication.GetTags();
11454 StringBuilder Xml = new StringBuilder();
11455
11456 Xml.Append("<providers xmlns='");
11457 Xml.Append(e.Query.NamespaceURI);
11458 Xml.Append("'>");
11459
11460 foreach (IPeerReviewService Service in await this.GetPeerReviewServices(Tags, true, true))
11461 {
11462 Xml.Append("<provider id='");
11463 Xml.Append(XML.Encode(Service.Id));
11464 Xml.Append("' type='");
11465 Xml.Append(XML.Encode(Service.PeerReviewServiceProvider.GetType().FullName));
11466 Xml.Append("' name='");
11467 Xml.Append(XML.Encode(Service.Name));
11468 Xml.Append("' legalId='");
11469 Xml.Append(XML.Encode(Service.PeerReviewerLegalId));
11470 Xml.Append("' external='");
11471 Xml.Append(CommonTypes.Encode(Service.External));
11472
11473 if (!string.IsNullOrEmpty(Service.IconUrl))
11474 {
11475 Xml.Append("' iconUrl='");
11476 Xml.Append(XML.Encode(Service.IconUrl));
11477 Xml.Append("' iconWidth='");
11478 Xml.Append(Service.IconWidth.ToString());
11479 Xml.Append("' iconHeight='");
11480 Xml.Append(Service.IconHeight.ToString());
11481 }
11482
11483 Xml.Append("'/>");
11484 }
11485
11486 Xml.Append("</providers>");
11487
11488 await e.IqResult(Xml.ToString(), e.To);
11489 }
11490
11491 private async Task SelectReviewServiceHandler(object Sender, IqEventArgs e)
11492 {
11493 if (!this.Server.IsServerDomain(e.From.Domain, true) || !e.From.HasAccount)
11494 {
11495 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
11496 return;
11497 }
11498
11499 CaseInsensitiveString AccountName = e.From.Account;
11500 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
11501 if (Account is null)
11502 {
11503 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
11504 return;
11505 }
11506
11507 if (!Account.Enabled)
11508 {
11509 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
11510 return;
11511 }
11512
11513 string ServiceProvider = XML.Attribute(e.Query, "provider");
11514 string ServiceId = XML.Attribute(e.Query, "serviceId");
11515
11516 Type T = Types.GetType(ServiceProvider);
11517
11518 if (T is null)
11519 {
11520 await e.IqErrorItemNotFound(e.To, "Service provider not found.", "en");
11521 return;
11522 }
11523
11524 if (!typeof(IPeerReviewServiceProvider).IsAssignableFrom(T))
11525 {
11526 await e.IqErrorBadRequest(e.To, "Invalid service provider.", "en");
11527 return;
11528 }
11529
11530 LegalIdentity IdentityApplication = await Database.FindFirstIgnoreRest<LegalIdentity>(new FilterAnd(
11531 new FilterFieldEqualTo("Account", AccountName),
11532 new FilterFieldEqualTo("State", Legal.Identity.IdentityState.Created)), "-Created");
11533
11534 if (IdentityApplication is null)
11535 {
11536 await e.IqErrorForbidden(e.To, "No Identity Application found.", "en");
11537 return;
11538 }
11539
11540 KeyValuePair<string, object>[] Tags = IdentityApplication.GetTags();
11542 IPeerReviewService Service = await Provider.GetServiceForPeerReview(ServiceId, Tags);
11543
11544 if (Service is null)
11545 {
11546 await e.IqErrorItemNotFound(e.To, "Peer-review service not found.", "en");
11547 return;
11548 }
11549
11550 this.SelectServiceProvider(AccountName, Service);
11551
11552 await e.IqResult(string.Empty, e.To);
11553 }
11554
11555 internal void SelectServiceProvider(string AccountName, IPeerReviewService Service)
11556 {
11557 lock (this.selectedServicePerAccount)
11558 {
11559 this.selectedServicePerAccount[AccountName] = Service;
11560 }
11561 }
11562
11563 private readonly Dictionary<CaseInsensitiveString, IPeerReviewService> selectedServicePerAccount = new Dictionary<CaseInsensitiveString, IPeerReviewService>();
11564
11565 internal async Task<IPeerReviewService[]> GetPeerReviewServices(KeyValuePair<string, object>[] Tags, bool IncludeInternal, bool IncludeExternal)
11566 {
11567 List<IPeerReviewService> Result = new List<IPeerReviewService>();
11568 Type[] ServiceTypes = Types.GetTypesImplementingInterface(typeof(IPeerReviewServiceProvider));
11569
11570 foreach (Type T in ServiceTypes)
11571 {
11572 ConstructorInfo CI = Types.GetDefaultConstructor(T);
11573 if (CI is null)
11574 continue;
11575
11577 IPeerReviewService[] Services = await ServiceProvider.GetServicesForPeerReview(Tags);
11578
11579 foreach (IPeerReviewService Service in Services)
11580 {
11581 if (Service.External)
11582 {
11583 if (IncludeExternal)
11584 Result.Add(Service);
11585 }
11586 else
11587 {
11588 if (IncludeInternal)
11589 Result.Add(Service);
11590 }
11591 }
11592 }
11593
11594 return Result.ToArray();
11595 }
11596
11597 private async Task ContractsClient_PetitionForPeerReviewIDReceived(object Sender,
11598 Networking.XMPP.Contracts.EventArguments.SignaturePetitionEventArgs e)
11599 {
11600 try
11601 {
11602 if (Gateway.ContractsClient is null)
11603 return;
11604
11605 KeyValuePair<string, object>[] Tags = e.RequestorIdentity.GetTags();
11606 XmppAddress From = new XmppAddress(e.RequestorFullJid);
11607
11608 if (!From.HasAccount || !this.Server.IsServerDomain(From.Domain, true))
11609 {
11610 Log.Warning("Peer-review request denied. Access to service providers only granted to accounts on broker.",
11611 e.RequestorIdentity.Id, e.From, Tags);
11612
11613 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11614 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11615
11616 return;
11617 }
11618
11619 CaseInsensitiveString AccountName = From.Account;
11620 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
11621
11622 if (Account is null)
11623 {
11624 Log.Warning("Peer-review request denied. Access to service providers only granted to accounts on broker.",
11625 e.RequestorIdentity.Id, e.From, Tags);
11626
11627 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11628 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11629
11630 return;
11631 }
11632
11633 if (!Account.Enabled)
11634 {
11635 Log.Warning("Peer-review request denied. Account has been disabled.",
11636 e.RequestorIdentity.Id, e.From, Tags);
11637
11638 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11639 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11640
11641 return;
11642 }
11643
11644 XmppAddress RequestorIdentityId = new XmppAddress(e.RequestorIdentity.Id);
11645 if (!this.IsComponentDomain(RequestorIdentityId.Domain, true))
11646 {
11647 Log.Warning("Peer-review request denied. Requestor legal identity not on the server.",
11648 e.RequestorIdentity.Id, e.From, Tags);
11649
11650 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11651 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11652
11653 return;
11654 }
11655
11656 LegalIdentity LocalRequestorIdentity = await Database.FindFirstIgnoreRest<LegalIdentity>(
11657 new FilterFieldEqualTo("Id", RequestorIdentityId));
11658
11659 if (LocalRequestorIdentity is null)
11660 {
11661 Log.Warning("Peer-review request denied. Proposed identity did not exist on server.",
11662 e.RequestorIdentity.Id, e.From, Tags);
11663
11664 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11665 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11666
11667 return;
11668 }
11669
11670 StringBuilder Xml1 = new StringBuilder();
11671 StringBuilder Xml2 = new StringBuilder();
11672
11673 LocalRequestorIdentity.Serialize(Xml1, true, true, true, true, true, true, false, null, this);
11674 e.RequestorIdentity.Serialize(Xml2, true, true, true, true, true, true, false);
11675
11676 if (Xml1.ToString() != Xml2.ToString())
11677 {
11678 Log.Warning("Peer-review request denied. Requestor legal identity representation not the same as local version.",
11679 e.RequestorIdentity.Id, e.From, Tags);
11680
11681 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11682 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11683
11684 return;
11685 }
11686
11687 IPeerReviewService[] PeerReviewServices = await this.GetPeerReviewServices(Tags, true, false);
11688
11689 if (PeerReviewServices.Length == 0)
11690 {
11691 Log.Warning("Peer-review request denied. No local peer review services found.",
11692 e.RequestorIdentity.Id, e.From, Tags);
11693
11694 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11695 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11696
11697 return;
11698 }
11699
11700 IPeerReviewService PeerReviewService;
11701
11702 lock (this.selectedServicePerAccount)
11703 {
11704 if (!this.selectedServicePerAccount.TryGetValue(AccountName, out PeerReviewService))
11705 PeerReviewService = null;
11706 }
11707
11708 if (PeerReviewService is null)
11709 {
11710 Log.Warning("Peer-review request denied. No selected peer review service provider.",
11711 e.RequestorIdentity.Id, e.From, Tags);
11712
11713 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11714 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11715
11716 return;
11717 }
11718
11719 string Namespace = NamespaceLegalIdentity(LocalRequestorIdentity.Version);
11720 KeyValuePair<IPhoto[], XmlDocument[]> P = await GetPhotosAndDocuments(LocalRequestorIdentity, false);
11722 e.RequestorIdentity.Id, Namespace, false,
11723 GetPersonalInformation(LocalRequestorIdentity),
11724 LocalRequestorIdentity.GetTags(),
11725 P.Key, P.Value, Account, e.Purpose, e.ClientEndpoint);
11726
11727 try
11728 {
11729 await PeerReviewService.Validate(Application, async (sender, e2) =>
11730 {
11731 StringBuilder Message = new StringBuilder();
11732
11733 Message.Append("<petitionClientUrl xmlns='");
11734 Message.Append(Namespace);
11735 Message.Append("' pid='");
11736 Message.Append(XML.Encode(e.PetitionId));
11737 Message.Append("' url='");
11738 Message.Append(XML.Encode(e2.Url));
11739 Message.Append("'/>");
11740
11741 await this.Server.SendMessage(string.Empty, string.Empty, e.To, From.BareJid, string.Empty, Message.ToString());
11742 // Note: Client may have a new XMPP connection at this point.
11743 }, null);
11744 }
11745 catch (Exception ex)
11746 {
11747 Application.ReportError(ex.Message, string.Empty, string.Empty,
11748 ValidationErrorType.Service, PeerReviewService);
11749 }
11750
11751 if (!Application.IsValid.HasValue)
11752 {
11753 string Xml = Application.GetClientErrorMessageXml(out string Language,
11754 "Unable to validate the review.", "UnableReview", "en");
11755
11756 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
11757 e.RequestorFullJid, Language, Xml);
11758 }
11759 else
11760 {
11761 if (Application.IsValid.Value)
11762 {
11763 // Valid application
11764
11765 byte[] Signature = await Gateway.ContractsClient.SignAsync(e.ContentToSign,
11766 Networking.XMPP.Contracts.SignWith.LatestApprovedId);
11767
11768 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11769 Signature, e.PetitionId, e.RequestorFullJid, true);
11770 }
11771 else
11772 {
11773 // Invalid application
11774
11775 if (Application.HasErrors)
11776 {
11777 foreach (ValidationError Error in Application.Errors)
11778 {
11779 switch (Error.ErrorType)
11780 {
11781 case ValidationErrorType.Server:
11782 case ValidationErrorType.Service:
11783 Log.Error(Error.ErrorMessage, string.Empty,
11784 Error.Service.GetType().Namespace, Error.ErrorCode,
11785 Error.Tags);
11786 break;
11787
11788 case ValidationErrorType.Client:
11789 Log.Warning(Error.ErrorMessage, string.Empty,
11790 Error.Service.GetType().Namespace, Error.ErrorCode,
11791 Error.Tags);
11792 break;
11793 }
11794 }
11795 }
11796
11797 string Xml = Application.GetClientErrorMessageXml(out string Language,
11798 "Unable to validate the review.", "UnableReview", "en");
11799
11800 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
11801 e.RequestorFullJid, Language, Xml);
11802
11803 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
11804 Array.Empty<byte>(), e.PetitionId, e.RequestorFullJid, false);
11805 }
11806 }
11807 }
11808 catch (Exception ex)
11809 {
11810 Log.Exception(ex);
11811 }
11812 }
11813
11814 #endregion
11815
11816 // TODO: Delete Account => Delete bareJid-portion of legal identities registered for
11817 // account, and delete legal identities not referenced in contracts.
11818 }
11819}
Contains information about a legal identity application.
Contains information about a legal identity application, including its current state.
Contains information about a legal identity application.
Contains personal information found in a legal identity.
const string DeviceIdTag
DEVICE_ID
const string OrganizationAddressTag
ORGADDR
CaseInsensitiveString Country
Country
const string NationalityTag
NATIONALITY
CaseInsensitiveString FirstName
First name
CaseInsensitiveString OrgDepartment
Organization Department
CaseInsensitiveString OrgArea
Organization Area
bool HasOrg
If identity has organization information
CaseInsensitiveString OrgNumber
Organization Number
CaseInsensitiveString OrgPostalCode
Organization Postal Code
const string OrganizationNameTag
ORGNAME
CaseInsensitiveString Area
Area
const string OrganizationAddress2Tag
ORGADDR2
CaseInsensitiveString OrgRole
Role in Organization
CaseInsensitiveString OrgName
Organization Name
const string OrganizationNumberTag
ORGNR
const string MiddleNamesTag
MIDDLE
const string OrganizationCountryTag
ORGCOUNTRY
CaseInsensitiveString Address
Address
CaseInsensitiveString OrgRegion
Organization Region
CaseInsensitiveString City
City
CaseInsensitiveString LastNames
Last names
const string CountryTag
COUNTRY
const string OrganizationCityTag
ORGCITY
CaseInsensitiveString MiddleNames
Middle names
CaseInsensitiveString OrgCity
Organization City
const string OrganizationDepartmentTag
ORGDEPT
const string OrganizationRegionTag
ORGREGION
CaseInsensitiveString OrgCountry
Organization Country
const string AgeAboveTag
AGEABOVE
CaseInsensitiveString FullName
FullName
CaseInsensitiveString Nationality
Nationality
CaseInsensitiveString PostalCode
Postal Code
CaseInsensitiveString PersonalNumber
Personal Number
CaseInsensitiveString Region
Region
const string OrganizationRoleTag
ORGROLE
CaseInsensitiveString OrgAddress
Organization Address
const string OrganizationAreaTag
ORGAREA
const string BirthMonthTag
BMONTH
const string OrganizationPostalCodeTag
ORGZIP
const string FullNameTag
FULLNAME
bool HasBirthDate
If identity has birth date
Represents a photo in an identity application.
Definition: Photo.cs:11
Contains information about a service provider.
string Name
Displayable name of service provider.
Contains information about a validation error.
string ErrorCode
Machine-readable error code (service-specific).
string ErrorMessage
Error message that can be sent to origin
object Service
Service reporting the error.
KeyValuePair< string, object >[] Tags
Tags annotating the error message.
ValidationErrorType ErrorType
Type of error.
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:596
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
Contains information about a stream response to a content request.
override string ToString()
Definition: HtmlElement.cs:230
Image encoder/decoder.
Definition: ImageCodec.cs:14
static readonly string[] ImageContentTypes
Image content types.
Definition: ImageCodec.cs:126
Static class managing encoding and decoding of internet content.
static bool CanGet(Uri Uri, out Grade Grade, out IContentGetter Getter)
If a resource can be gotten, given its URI.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:20
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
XML encoder/decoder.
Definition: XmlCodec.cs:19
static readonly string[] XmlContentTypes
XML content types.
Definition: XmlCodec.cs:40
const string DefaultContentType
Default content type for XML documents.
Definition: XmlCodec.cs:30
const string SchemaContentType
Default content type for XML schema documents.
Definition: XmlCodec.cs:35
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Definition: XML.cs:892
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
Definition: XML.cs:1762
static string NormalizeXml(string Xml)
Normalizes XML in string form.
Definition: XML.cs:1561
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
static bool IsValidXml(string Xml)
Checks if a string is valid XML
Definition: XML.cs:1397
Class representing an event.
Definition: Event.cs:11
override string ToString()
Definition: Event.cs:170
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:576
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:692
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:344
static void Debug(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a debug event.
Definition: Log.cs:228
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:3087
static bool IsDomain(string DomainOrHost, bool IncludeAlternativeDomains)
If a domain or host name represents the gateway.
Definition: Gateway.cs:5174
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3860
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:4677
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:5079
static ContractsClient ContractsClient
XMPP Contracts Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:5299
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:4038
static bool HasDomain
If a domain name is configured.
Definition: Gateway.cs:3093
Static class that gives access to local content published by the gateway, without having to perform r...
Definition: LocalContent.cs:17
static Task< ContentStreamResponse > GetTempStreamAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, given its URI.
DateTime Created
When the object was created.
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< IPAddress[]> TryLookupIP4Addresses(string DomainName)
Tries to look up the IPv4 addresses related to a given domain name.
Definition: DnsResolver.cs:684
static Task< IPAddress[]> TryLookupIP6Addresses(string DomainName)
Tries to look up the IPv6 addresses related to a given domain name.
Definition: DnsResolver.cs:730
Implements an HTTP server.
Definition: HttpServer.cs:41
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Contains the definition of a contract
Definition: Contract.cs:22
KeyValuePair< string, object >[] Tags
Associated tags with more information.
Contains information about a parsed contract.
Abstract base class for XMPP client connections
abstract string RemoteEndPoint
Remote endpoint
Base class for components.
Definition: Component.cs:17
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
Definition: Component.cs:298
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
Definition: Component.cs:162
CaseInsensitiveString Subdomain
Subdomain name.
Definition: Component.cs:77
CaseInsensitiveString SubdomainSuffixed
Subdomain name, suffixed with a period (.).
Definition: Component.cs:82
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: Component.cs:150
XmppAddress MainDomain
Main/principal domain address
Definition: Component.cs:87
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
Definition: Component.cs:124
XmppServer Server
XMPP Server.
Definition: Component.cs:97
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: Component.cs:250
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Definition: Component.cs:263
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
Definition: Component.cs:191
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
XmppAddress From
From address attribute
Definition: IqEventArgs.cs:93
Task IqErrorNotAcceptable(XmppAddress From, string ErrorText, string Language)
Returns a not-acceptable error.
Definition: IqEventArgs.cs:248
Task IqResult(string Xml, string From)
Returns a response to the current request.
Definition: IqEventArgs.cs:113
Task IqErrorResourceConstraint(XmppAddress From, string ErrorText, string Language)
Returns a resource-constraint error.
Definition: IqEventArgs.cs:178
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
Definition: IqEventArgs.cs:206
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:70
XmppAddress To
To address attribute
Definition: IqEventArgs.cs:88
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Definition: IqEventArgs.cs:139
Task IqErrorServiceUnavailable(XmppAddress From, string ErrorText, string Language)
Returns a service-unavailable error.
Definition: IqEventArgs.cs:220
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:164
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Definition: IqEventArgs.cs:234
Event arguments for responses to IQ queries.
string ErrorTypeString
Error Type XML attribute string
XmlElement FirstElement
First child element of the Response element.
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppAddress From
From address attribute
XmlElement Content
Content element, if found, null otherwise.
Presence information event arguments.
XmlElement Content
Content element, if found, null otherwise.
Task PresenceErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Task PresenceErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
XmlElement StanzaElement
Stanza element.
Definition: Stanza.cs:113
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
override string ToString()
object.ToString()
Definition: XmppAddress.cs:190
bool IsBareJID
If the address is a Bare JID.
Definition: XmppAddress.cs:159
bool HasAccount
If the address has an account part.
Definition: XmppAddress.cs:167
bool IsEmpty
If the address is empty.
Definition: XmppAddress.cs:183
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
CaseInsensitiveString Address
XMPP Address
Definition: XmppAddress.cs:37
bool IsDomain
If the Address is a domain.
Definition: XmppAddress.cs:175
XmppAddress ToBareJID()
Returns the Bare JID as an XmppAddress object.
Definition: XmppAddress.cs:215
CaseInsensitiveString BareJid
Bare JID
Definition: XmppAddress.cs:45
static readonly XmppAddress Empty
Empty address.
Definition: XmppAddress.cs:31
CaseInsensitiveString Account
Account
Definition: XmppAddress.cs:124
bool IsFullJID
If the Address is a Full JID.
Definition: XmppAddress.cs:151
Manages an XMPP server-to-server connection.
static bool IsRemoteDomainRegistered(CaseInsensitiveString RemoteDomain)
Checks if a remote domain is registered.
Definition: XmppServer.cs:2367
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Definition: XmppServer.cs:3862
bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
Tries to get available connections for a given client.
Definition: XmppServer.cs:855
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:844
Task< bool > SendIqRequest(string Type, string From, string To, string Language, string ContentXml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
Definition: XmppServer.cs:3668
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
Definition: XmppServer.cs:898
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: XmppServer.cs:1756
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1691
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:922
async Task< string > FindComponentAsync(string Jid, string Feature)
Finds a component having a specific feature, servicing a JID.
Definition: XmppClient.cs:7682
string Domain
Current Domain.
Definition: XmppClient.cs:3492
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
int Length
Gets the number of characters in the current CaseInsensitiveString object.
string LowerCase
Lower-case representation of the case-insensitive string.
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
bool StartsWith(CaseInsensitiveString value)
Determines whether the beginning of this string instance matches the specified string.
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
Definition: Database.cs:1442
static Task< string[]> GetCollections()
Gets an array of available collections.
Definition: Database.cs:2355
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
Definition: Database.cs:1838
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field greater or equal to a given value.
This filter selects objects that have a named field greater than a given value.
This filter selects objects that have a named field lesser or equal to a given value.
This filter selects objects that have a named field lesser than a given value.
This filter selects objects that have a named field matching a given regular expression.
This filter selects objects that have a named field not equal to a given value.
Base class for all filter classes.
Definition: Filter.cs:15
Implements an in-memory cache.
Definition: Cache.cs:17
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:99
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:616
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:311
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:446
Event arguments for cache item removal events.
KeyType Key
Key of item that was removed.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
bool HasFirstItem
If there is a first item in the collection
Definition: ChunkedList.cs:778
T RemoveFirst()
Removes the first item in the collection.
Definition: ChunkedList.cs:876
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
T[] ToArray()
Returns an array containing all elements of the collection.
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:42
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1454
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1742
Class that keeps track of events and timing.
Definition: Profiler.cs:68
void Stop()
Stops measuring time.
Definition: Profiler.cs:227
ProfilerThread CreateThread(string Name, ProfilerThreadType Type)
Creates a new profiler thread.
Definition: Profiler.cs:128
void NewState(string State)
Main Thread changes state.
Definition: Profiler.cs:267
string ExportPlantUml(TimeUnit TimeUnit)
Exports events to PlantUML.
Definition: Profiler.cs:530
double ElapsedSeconds
Elapsed seconds since start.
Definition: Profiler.cs:241
void Start()
Starts measuring time.
Definition: Profiler.cs:217
Class that keeps track of events and timing for one thread.
void Exception(System.Exception Exception)
Exception occurred
void NewState(string State)
Thread changes state.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
Class managing the contents of a temporary file. When the class is disposed, the temporary file is de...
Manages a temporary stream. Contents is kept in-memory, if below a memory threshold,...
override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
Asynchronously reads the bytes from the current stream and writes them to another stream,...
override long Length
When overridden in a derived class, gets the length in bytes of the stream.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginRead(string Key)
Waits until the semaphore identified by Key is ready for reading. Each call to BeginRead must be fol...
Definition: Semaphores.cs:54
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static string ComputeSHA256HashString(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:449
static byte[] ComputeHash(HashFunction Function, byte[] Data)
Computes a hash of a block of binary data.
Definition: Hashes.cs:212
Static class containing predefined JWT claim names.
Definition: JwtClaims.cs:10
const string Issuer
Issuer of the JWT
Definition: JwtClaims.cs:14
const string IssueTime
Time at which the JWT was issued; can be used to determine age of the JWT
Definition: JwtClaims.cs:39
const string JwtId
Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only...
Definition: JwtClaims.cs:44
const string Subject
Subject of the JWT (the user)
Definition: JwtClaims.cs:19
const string ExpirationTime
Time after which the JWT expires
Definition: JwtClaims.cs:29
string Create(params KeyValuePair< string, object >[] Claims)
Creates a new JWT token.
Definition: JwtFactory.cs:379
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
async Task< DateTime?> GetEarliestLoginOpportunity(string RemoteEndPoint, string Protocol)
Checks when a remote endpoint can login.
static async void Success(string Message, string UserName, string RemoteEndPoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
static void Fail(string Message, string UserName, string RemoteEndPoint, string Protocol)
Handles a failed login attempt.
Contains information about a broker account.
Definition: Account.cs:41
DateTime? PhoneNrVerified
When Phone Number was verified.
Definition: Account.cs:344
CaseInsensitiveString EMail
E-mail address associated with account.
Definition: Account.cs:176
CaseInsensitiveString UserName
User Name of account
Definition: Account.cs:141
bool Enabled
If account is enabled
Definition: Account.cs:354
DateTime? EMailVerified
When e-Mail was verified.
Definition: Account.cs:335
CaseInsensitiveString PhoneNr
Phone number associated with account.
Definition: Account.cs:185
CaseInsensitiveString LatestIdentity
Last Legal Identity approved for account. Note: Identity object might not be approved any longer.
Definition: Account.cs:196
Manages eDaler on accounts connected to the broker.
virtual void Error(EDalerUriErrorType ErrorType, string ErrorMessage, bool LogAsNotice)
Reports an error with the URI
string ErrorMessage
Error message, or null if no error.
Current state of URI from external source
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
const string MarketplaceNamespace
https://paiwise.tagroot.io/Schema/Marketplace.xsd
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
const string NeuroFeaturesNamespace
https://paiwise.tagroot.io/Schema/NeuroFeatures.xsd
Paiwise processor, processing payment instructions defined in smart contracts.
const string PaymentInstructionsNamespace
https://paiwise.tagroot.io/Schema/PaymentInstructions.xsd
Contains information about a payment instruction.
Definition: Payment.cs:18
DateTime? Processed
When object was successfully processed.
Definition: Payment.cs:93
PubSub component, as defined in XEP-0060. https://xmpp.org/extensions/xep-0060.html
Provides the user with options to control notifications from the Broker.
static BrokerNotificationConfiguration Instance
Current instance of configuration.
bool PeerReviewApproved
If a notification should be sent when a peer review of a Legal ID has been approved.
bool OtherLegalIds
If a notification should be sent for every existing valid Legal ID that exists when a new Legal ID ap...
bool LegalIdAutoRejected
If a notification should be sent when a Legal ID application has been automatically rejected.
bool LegalIdAutoApproved
If a notification should be sent when a Legal ID application has been automatically approved.
bool LegalIdReceived
If a notification should be sent when a new a Legal ID application is received.
bool ContractProposalReceived
If a notification should be sent when a contract proposal has been received.
Provides the user configuration options regarding peer-review of new legal identities.
bool AllowPeerReview
If peer-review is allowed on the broker.
bool RequireFirstName
If first name is required in applications for them to be peer-reviewable.
int NrReviewersToApprove
Number of peers required to review and approve a legal identity application before it can be approved...
bool RequireBirthDate
If birth date is required in applications for them to be peer-reviewable.
int NrPhotosRequired
Number of photos required in an application for it to be peer-reviewable.
bool RequirePostalCode
If postal code is required in applications for them to be peer-reviewable.
bool IsReviewerAllowed(string Jid, string LegalId)
Checks if a reviewer is allowed to perform peer review, based on the white-list, if configured.
bool RequireCity
If city is required in applications for them to be peer-reviewable.
static PeerReviewConfiguration Instance
Current instance of configuration.
bool RequireNationality
If nationality is required in applications for them to be peer-reviewable.
bool RequireMiddleName
If middle name(s) is/are required in applications for them to be peer-reviewable.
bool RequireRegion
If region is required in applications for them to be peer-reviewable.
bool RequireIso3166Compliance
If country codes are required to be ISO 3166 compliant in applications for them to be peer-reviewable...
bool RequireCountry
If country is required in applications for them to be peer-reviewable.
bool RequirePersonalNumber
If personal number is required in applications for them to be peer-reviewable.
bool RequireAddress
If address is required in applications for them to be peer-reviewable.
bool RequireLastName
If last names is/are is required in applications for them to be peer-reviewable.
bool RequireArea
If area is required in applications for them to be peer-reviewable.
bool RequireGender
If gender is required in applications for them to be peer-reviewable.
Class representing the current state of a state machine.
Definition: CurrentState.cs:20
Abstract base class for agent resources
Service Module hosting the XMPP broker and its components.
static NamespaceSet GetVersion(string Namespace)
Gets the namespace set version corresponding to a given a namespace.
static async Task< Stream > EncodeBlob(Stream Data)
Encodes a variable-length BLOB by prefixing it with its length. This permits the BLOB to be safely en...
static Task SaveEncryptedFile(string FileName, byte[] Salt, byte[] Data)
Saves an encrypted file.
static Task< byte[]> LoadEncryptedFile(string FileName, byte[] Salt)
Loads an encrypted file.
static async Task< Stream > DecodeBlob(Stream Data)
Decodes a variable-length BLOB encoded by the EncodeBlob method. The length prefix is read first,...
static bool Verify(byte[] Data, byte[] Signature)
Verifies a digital signature, supposedly made by the ledger.
static byte[] Sign(byte[] Data)
Signs data with the private key of the ledger.
static LedgerConfiguration Instance
Current instance of configuration.
byte[] PublicKey
Public key used to validate signatures.
Interface for identity applications.
Interface for identity applications.
Interface for identity application authenticator services.
Task Validate(IIdentityApplication Application)
Validates an identity application.
Interface for stateful identity application services.
Task ApplicationUpdated(IIdentityApplicationState Application)
Called when an Identity application state has been updated.
Interface for currency converter service providers
Task Validate(IIdentityReviewApplication Application, EventHandlerAsync< ClientUrlEventArgs > ClientUrlCallback, object State)
Checks the veracity of identity claims.
IPeerReviewServiceProvider PeerReviewServiceProvider
Reference to service provider.
bool External
If the PeerReviewerLegalId is an external legal identity (true), or represents the neuron itself (fal...
string PeerReviewerLegalId
Legal ID of peer reviewer
Interface for peer-review service providers.
Task< IPeerReviewService > GetServiceForPeerReview(string ServiceId, KeyValuePair< string, object >[] Identity)
Gets a peer-review service.
Interface for information about a service provider.
string Id
ID of service provider.
int IconWidth
Width of icon, if available.
string IconUrl
Optional URL to icon of service provider.
int IconHeight
Height of icon, if available.
string Name
Displayable name of service provider.
Basic interface for Internet Content getters. A class implementing this interface and having a defaul...
Interface for asynchronously disposable objects.
string RemoteEndPoint
Remote endpoint.
Abstract base class for End-to-End encryption schemes.
Definition: IE2eEndpoint.cs:13
bool Verify(byte[] Data, byte[] Signature)
Verifies a signature.
Interface for XMPP user accounts.
Definition: IAccount.cs:9
PresenceEventArgs LastPresence
Last presence received.
bool CheckLive()
Checks if the connection is live.
Definition: ImplTypes.g.cs:58
ValidationErrorType
Type of validation error.
Gender
Gender of a person.
Definition: Gender.cs:7
ContractStatus
Validation Status of smart contract
RemovedReason
Reason for removing the item.
Grade
Grade enumeration
Definition: Grade.cs:7
TimeUnit
Options for presenting time in reports.
Definition: Profiler.cs:17
ProfilerThreadType
Type of profiler thread.
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11
Reason
Reason a token is not valid.
Definition: JwtFactory.cs:15
HashFunction
Hash method enumeration.
Definition: Hashes.cs:26
NamespaceSet
Namespace versions
Definition: NamespaceSet.cs:7
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:14
static bool TryParse(string s, out Duration Result)
Tries to parse a duration value.
Definition: Duration.cs:86
static readonly Duration Zero
Zero value
Definition: Duration.cs:577