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;
3using System.Collections.Generic;
4using System.IO;
5using System.Net.Http;
6using System.Reflection;
7using System.Security.Cryptography;
8using System.Text;
9using System.Threading.Tasks;
10using System.Xml;
11using System.Xml.Schema;
12using Waher.Content;
16using Waher.Events;
29using Waher.Security;
48
50{
55 {
59 public const string NamespaceLegalIdentityIeeeV1 = "urn:ieee:iot:leg:id:1.0";
60
64 public const string NamespaceLegalIdentityNeuroFoundationV1 = "urn:nf:iot:leg:id:1.0";
65
69 public static readonly string[] NamespacesLegalIdentity = new string[]
70 {
73 };
74
78 public const string NamespaceSmartContractsIeeeV1 = "urn:ieee:iot:leg:sc:1.0";
79
83 public const string NamespaceSmartContractsNeuroFoundationV1 = "urn:nf:iot:leg:sc:1.0";
84
88 public static readonly string[] NamespacesSmartContracts = new string[]
89 {
92 };
93
97 public const string NamespaceE2EIeeeV1 = "urn:ieee:iot:e2e:1.0";
98
102 public const string NamespaceE2ENeuroFoundationV1 = "urn:nf:iot:e2e:1.0";
103
109 public static string NamespaceLegalIdentity(NamespaceSet Version)
110 {
111 switch (Version)
112 {
113 case NamespaceSet.XsfV0:
114 case NamespaceSet.IeeeV1: return NamespaceLegalIdentityIeeeV1;
115 default:
116 case NamespaceSet.NeuroFoundationV1: return NamespaceLegalIdentityNeuroFoundationV1;
117 }
118 }
119
125 public static string NamespaceSmartContracts(NamespaceSet Version)
126 {
127 switch (Version)
128 {
129 case NamespaceSet.XsfV0:
131 default:
132 case NamespaceSet.NeuroFoundationV1: return NamespaceSmartContractsNeuroFoundationV1;
133 }
134 }
135
141 public static string NamespaceE2E(NamespaceSet Version)
142 {
143 switch (Version)
144 {
145 case NamespaceSet.XsfV0:
146 case NamespaceSet.IeeeV1: return NamespaceE2EIeeeV1;
147 default:
148 case NamespaceSet.NeuroFoundationV1: return NamespaceE2ENeuroFoundationV1;
149 }
150 }
151
152 private Cache<CaseInsensitiveString, object> remoteComponents = new Cache<CaseInsensitiveString, object>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromHours(1));
153 private Cache<CaseInsensitiveString, int> petitions = new Cache<CaseInsensitiveString, int>(int.MaxValue, TimeSpan.FromHours(1), TimeSpan.FromHours(1), true);
154 private Cache<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Parameter>> transientParameters = new Cache<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Parameter>>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromHours(1), true);
155 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, CaseInsensitiveString>> petitionsByBareJid = new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, CaseInsensitiveString>>();
156 private readonly AttachmentsResource attachmentsResource;
157 private readonly HttpServer httpServer;
158 private readonly string attachmentsFolder;
159 private readonly PubSubComponent pubsub;
160 private EDalerComponent eDaler;
161
173 : base(Server, Subdomain, Name)
174 {
175 this.httpServer = HttpServer;
176 this.attachmentsFolder = AttachmentsFolder;
177 this.eDaler = EDaler;
178 this.pubsub = PubSub;
179
180 if (!Directory.Exists(this.attachmentsFolder))
181 Directory.CreateDirectory(this.attachmentsFolder);
182
183 this.attachmentsResource = new AttachmentsResource(this);
184 this.httpServer.Register(this.attachmentsResource);
185
186 #region Neuro-Foundation V1 handlers
187
188 this.RegisterIqGetHandler("getPublicKey", NamespaceLegalIdentityNeuroFoundationV1, this.GetPublicKeyHandler, true);
189 this.RegisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityNeuroFoundationV1, this.IdApplicationAttributesHandler, false); ;
190 this.RegisterIqSetHandler("apply", NamespaceLegalIdentityNeuroFoundationV1, this.ApplyHandler, false);
191 this.RegisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentitiesHandler, false);
192 this.RegisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentityHandler, false);
193 this.RegisterIqGetHandler("validateSignature", NamespaceLegalIdentityNeuroFoundationV1, this.ValidateSignatureHandler, false);
194 this.RegisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.ObsoleteLegalIdentityHandler, false);
195 this.RegisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.CompromiseLegalIdentityHandler, false);
196 this.RegisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityHandler, false);
197 this.RegisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityResponseHandler, false);
198 this.RegisterIqSetHandler("addAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.AddLegalIdAttachmentHandler, false);
199 this.RegisterIqSetHandler("removeAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.RemoveLegalIdAttachmentHandler, false);
200 this.RegisterIqSetHandler("petitionSignature", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureHandler, false);
201 this.RegisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureResponseHandler, false);
202 this.RegisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityNeuroFoundationV1, this.AuthorizeAccessToIdHandler, false);
203 this.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkLegalIdentityHandler, false);
204 this.RegisterIqGetHandler("canSignAs", NamespaceLegalIdentityNeuroFoundationV1, this.CanSignAsHandler, false);
205 this.RegisterIqSetHandler("readyForApproval", NamespaceLegalIdentityNeuroFoundationV1, this.ReadyForApprovalHandler, false);
206 this.RegisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityNeuroFoundationV1, this.GetReviewIdProvidersHandler, false);
207 this.RegisterIqSetHandler("selectReviewService", NamespaceLegalIdentityNeuroFoundationV1, this.SelectReviewServiceHandler, false);
208
209 this.Server.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkXmppIdentityHandler, false);
210
211 this.RegisterIqSetHandler("createContract", NamespaceSmartContractsNeuroFoundationV1, this.CreateContractHandler, true);
212 this.RegisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetCreatedContractsHandler, false);
213 this.RegisterIqSetHandler("signContract", NamespaceSmartContractsNeuroFoundationV1, this.SignContractHandler, false);
214 this.RegisterIqGetHandler("getSignedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetSignedContractsHandler, false);
215 this.RegisterIqGetHandler("getContract", NamespaceSmartContractsNeuroFoundationV1, this.GetContractHandler, false);
216 this.RegisterIqGetHandler("getContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetContractsHandler, false);
217 this.RegisterIqGetHandler("isPart", NamespaceSmartContractsNeuroFoundationV1, this.IsPartHandler, false);
218 this.RegisterIqSetHandler("obsoleteContract", NamespaceSmartContractsNeuroFoundationV1, this.ObsoleteContractHandler, false);
219 this.RegisterIqSetHandler("deleteContract", NamespaceSmartContractsNeuroFoundationV1, this.DeleteContractHandler, false);
220 this.RegisterIqSetHandler("updateContract", NamespaceSmartContractsNeuroFoundationV1, this.UpdateContractHandler, false);
221 this.RegisterIqGetHandler("getSchemas", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemasHandler, false);
222 this.RegisterIqGetHandler("getSchema", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemaHandler, false);
223 this.RegisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetLegalIdentitiesOfContractHandler, false);
224 this.RegisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetNetworkIdentitiesHandler, false);
225 this.RegisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsNeuroFoundationV1, this.SearchPublicContractsHandler, false);
226 this.RegisterIqSetHandler("petitionContract", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractHandler, false);
227 this.RegisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseHandler, false);
228 this.RegisterIqSetHandler("addAttachment", NamespaceSmartContractsNeuroFoundationV1, this.AddContractAttachmentHandler, false);
229 this.RegisterIqSetHandler("removeAttachment", NamespaceSmartContractsNeuroFoundationV1, this.RemoveContractAttachmentHandler, false);
230 this.RegisterIqSetHandler("authorizeAccess", NamespaceSmartContractsNeuroFoundationV1, this.AuthorizeAccessToContractHandler, false);
231 this.RegisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedHandler, false);
232
233 #endregion
234
235 #region IEEE V1 handlers
236
237 this.RegisterIqGetHandler("getPublicKey", NamespaceLegalIdentityIeeeV1, this.GetPublicKeyHandler, true);
238 this.RegisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityIeeeV1, this.IdApplicationAttributesHandler, false); ;
239 this.RegisterIqSetHandler("apply", NamespaceLegalIdentityIeeeV1, this.ApplyHandler, false);
240 this.RegisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentitiesHandler, false);
241 this.RegisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentityHandler, false);
242 this.RegisterIqGetHandler("validateSignature", NamespaceLegalIdentityIeeeV1, this.ValidateSignatureHandler, false);
243 this.RegisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityIeeeV1, this.ObsoleteLegalIdentityHandler, false);
244 this.RegisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityIeeeV1, this.CompromiseLegalIdentityHandler, false);
245 this.RegisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityHandler, false);
246 this.RegisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityResponseHandler, false);
247 this.RegisterIqSetHandler("addAttachment", NamespaceLegalIdentityIeeeV1, this.AddLegalIdAttachmentHandler, false);
248 this.RegisterIqSetHandler("removeAttachment", NamespaceLegalIdentityIeeeV1, this.RemoveLegalIdAttachmentHandler, false);
249 this.RegisterIqSetHandler("petitionSignature", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureHandler, false);
250 this.RegisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureResponseHandler, false);
251 this.RegisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityIeeeV1, this.AuthorizeAccessToIdHandler, false);
252 this.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkLegalIdentityHandler, false);
253 this.RegisterIqGetHandler("canSignAs", NamespaceLegalIdentityIeeeV1, this.CanSignAsHandler, false);
254 this.RegisterIqSetHandler("readyForApproval", NamespaceLegalIdentityIeeeV1, this.ReadyForApprovalHandler, false);
255 this.RegisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityIeeeV1, this.GetReviewIdProvidersHandler, false);
256 this.RegisterIqSetHandler("selectReviewService", NamespaceLegalIdentityIeeeV1, this.SelectReviewServiceHandler, false);
257
258 this.Server.RegisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkXmppIdentityHandler, false);
259
260 this.RegisterIqSetHandler("createContract", NamespaceSmartContractsIeeeV1, this.CreateContractHandler, true);
261 this.RegisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsIeeeV1, this.GetCreatedContractsHandler, false);
262 this.RegisterIqSetHandler("signContract", NamespaceSmartContractsIeeeV1, this.SignContractHandler, false);
263 this.RegisterIqGetHandler("getSignedContracts", NamespaceSmartContractsIeeeV1, this.GetSignedContractsHandler, false);
264 this.RegisterIqGetHandler("getContract", NamespaceSmartContractsIeeeV1, this.GetContractHandler, false);
265 this.RegisterIqGetHandler("getContracts", NamespaceSmartContractsIeeeV1, this.GetContractsHandler, false);
266 this.RegisterIqGetHandler("isPart", NamespaceSmartContractsIeeeV1, this.IsPartHandler, false);
267 this.RegisterIqSetHandler("obsoleteContract", NamespaceSmartContractsIeeeV1, this.ObsoleteContractHandler, false);
268 this.RegisterIqSetHandler("deleteContract", NamespaceSmartContractsIeeeV1, this.DeleteContractHandler, false);
269 this.RegisterIqSetHandler("updateContract", NamespaceSmartContractsIeeeV1, this.UpdateContractHandler, false);
270 this.RegisterIqGetHandler("getSchemas", NamespaceSmartContractsIeeeV1, this.GetSchemasHandler, false);
271 this.RegisterIqGetHandler("getSchema", NamespaceSmartContractsIeeeV1, this.GetSchemaHandler, false);
272 this.RegisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsIeeeV1, this.GetLegalIdentitiesOfContractHandler, false);
273 this.RegisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsIeeeV1, this.GetNetworkIdentitiesHandler, false);
274 this.RegisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsIeeeV1, this.SearchPublicContractsHandler, false);
275 this.RegisterIqSetHandler("petitionContract", NamespaceSmartContractsIeeeV1, this.PetitionContractHandler, false);
276 this.RegisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseHandler, false);
277 this.RegisterIqSetHandler("addAttachment", NamespaceSmartContractsIeeeV1, this.AddContractAttachmentHandler, false);
278 this.RegisterIqSetHandler("removeAttachment", NamespaceSmartContractsIeeeV1, this.RemoveContractAttachmentHandler, false);
279 this.RegisterIqSetHandler("authorizeAccess", NamespaceSmartContractsIeeeV1, this.AuthorizeAccessToContractHandler, false);
280 this.RegisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedHandler, false);
281
282 #endregion
283
284 this.Server.OnPresenceLocalSender += this.Server_OnPresenceLocalSender;
285 this.petitions.Removed += this.Petitions_Removed;
286
288 NeuroFeaturesProcessor.RegisterHandlers(this);
289 }
290
294 public override void Dispose()
295 {
296 this.Server.OnPresenceLocalSender -= this.Server_OnPresenceLocalSender;
297
298 if (!(this.petitions is null))
299 {
300 this.petitions.Removed -= this.Petitions_Removed;
301 this.petitions.Dispose();
302 this.petitions = null;
303 }
304
305 this.transientParameters?.Dispose();
306 this.transientParameters = null;
307
308 this.httpServer.Unregister(this.attachmentsResource);
309
310 #region Neuro-Foundation V1 handlers
311
312 this.UnregisterIqGetHandler("getPublicKey", NamespaceLegalIdentityNeuroFoundationV1, this.GetPublicKeyHandler, true);
313 this.UnregisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityNeuroFoundationV1, this.IdApplicationAttributesHandler, false); ;
314 this.UnregisterIqSetHandler("apply", NamespaceLegalIdentityNeuroFoundationV1, this.ApplyHandler, false);
315 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentitiesHandler, false);
316 this.UnregisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetLegalIdentityHandler, false);
317 this.UnregisterIqGetHandler("validateSignature", NamespaceLegalIdentityNeuroFoundationV1, this.ValidateSignatureHandler, false);
318 this.UnregisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.ObsoleteLegalIdentityHandler, false);
319 this.UnregisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.CompromiseLegalIdentityHandler, false);
320 this.UnregisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityHandler, false);
321 this.UnregisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionIdentityResponseHandler, false);
322 this.UnregisterIqSetHandler("addAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.AddLegalIdAttachmentHandler, false);
323 this.UnregisterIqSetHandler("removeAttachment", NamespaceLegalIdentityNeuroFoundationV1, this.RemoveLegalIdAttachmentHandler, false);
324 this.UnregisterIqSetHandler("petitionSignature", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureHandler, false);
325 this.UnregisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityNeuroFoundationV1, this.PetitionSignatureResponseHandler, false);
326 this.UnregisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityNeuroFoundationV1, this.AuthorizeAccessToIdHandler, false);
327 this.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkLegalIdentityHandler, false);
328 this.UnregisterIqGetHandler("canSignAs", NamespaceLegalIdentityNeuroFoundationV1, this.CanSignAsHandler, false);
329 this.UnregisterIqSetHandler("readyForApproval", NamespaceLegalIdentityNeuroFoundationV1, this.ReadyForApprovalHandler, false);
330 this.UnregisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityNeuroFoundationV1, this.GetReviewIdProvidersHandler, false);
331 this.UnregisterIqSetHandler("selectReviewService", NamespaceLegalIdentityNeuroFoundationV1, this.SelectReviewServiceHandler, false);
332
333 this.Server.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityNeuroFoundationV1, this.GetNetworkXmppIdentityHandler, false);
334
335 this.UnregisterIqSetHandler("createContract", NamespaceSmartContractsNeuroFoundationV1, this.CreateContractHandler, true);
336 this.UnregisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetCreatedContractsHandler, false);
337 this.UnregisterIqSetHandler("signContract", NamespaceSmartContractsNeuroFoundationV1, this.SignContractHandler, false);
338 this.UnregisterIqGetHandler("getSignedContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetSignedContractsHandler, false);
339 this.UnregisterIqGetHandler("getContract", NamespaceSmartContractsNeuroFoundationV1, this.GetContractHandler, false);
340 this.UnregisterIqGetHandler("getContracts", NamespaceSmartContractsNeuroFoundationV1, this.GetContractsHandler, false);
341 this.UnregisterIqGetHandler("isPart", NamespaceSmartContractsNeuroFoundationV1, this.IsPartHandler, false);
342 this.UnregisterIqSetHandler("obsoleteContract", NamespaceSmartContractsNeuroFoundationV1, this.ObsoleteContractHandler, false);
343 this.UnregisterIqSetHandler("deleteContract", NamespaceSmartContractsNeuroFoundationV1, this.DeleteContractHandler, false);
344 this.UnregisterIqSetHandler("updateContract", NamespaceSmartContractsNeuroFoundationV1, this.UpdateContractHandler, false);
345 this.UnregisterIqGetHandler("getSchemas", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemasHandler, false);
346 this.UnregisterIqGetHandler("getSchema", NamespaceSmartContractsNeuroFoundationV1, this.GetSchemaHandler, false);
347 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetLegalIdentitiesOfContractHandler, false);
348 this.UnregisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsNeuroFoundationV1, this.GetNetworkIdentitiesHandler, false);
349 this.UnregisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsNeuroFoundationV1, this.SearchPublicContractsHandler, false);
350 this.UnregisterIqSetHandler("petitionContract", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractHandler, false);
351 this.UnregisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseHandler, false);
352 this.UnregisterIqSetHandler("addAttachment", NamespaceSmartContractsNeuroFoundationV1, this.AddContractAttachmentHandler, false);
353 this.UnregisterIqSetHandler("removeAttachment", NamespaceSmartContractsNeuroFoundationV1, this.RemoveContractAttachmentHandler, false);
354 this.UnregisterIqSetHandler("authorizeAccess", NamespaceSmartContractsNeuroFoundationV1, this.AuthorizeAccessToContractHandler, false);
355 this.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedHandler, false);
356
357 #endregion
358
359 #region IEEE V1 handlers
360
361 this.UnregisterIqGetHandler("getPublicKey", NamespaceLegalIdentityIeeeV1, this.GetPublicKeyHandler, true);
362 this.UnregisterIqGetHandler("applicationAttributes", NamespaceLegalIdentityIeeeV1, this.IdApplicationAttributesHandler, false); ;
363 this.UnregisterIqSetHandler("apply", NamespaceLegalIdentityIeeeV1, this.ApplyHandler, false);
364 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentitiesHandler, false);
365 this.UnregisterIqGetHandler("getLegalIdentity", NamespaceLegalIdentityIeeeV1, this.GetLegalIdentityHandler, false);
366 this.UnregisterIqGetHandler("validateSignature", NamespaceLegalIdentityIeeeV1, this.ValidateSignatureHandler, false);
367 this.UnregisterIqSetHandler("obsoleteLegalIdentity", NamespaceLegalIdentityIeeeV1, this.ObsoleteLegalIdentityHandler, false);
368 this.UnregisterIqSetHandler("compromisedLegalIdentity", NamespaceLegalIdentityIeeeV1, this.CompromiseLegalIdentityHandler, false);
369 this.UnregisterIqSetHandler("petitionIdentity", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityHandler, false);
370 this.UnregisterIqSetHandler("petitionIdentityResponse", NamespaceLegalIdentityIeeeV1, this.PetitionIdentityResponseHandler, false);
371 this.UnregisterIqSetHandler("addAttachment", NamespaceLegalIdentityIeeeV1, this.AddLegalIdAttachmentHandler, false);
372 this.UnregisterIqSetHandler("removeAttachment", NamespaceLegalIdentityIeeeV1, this.RemoveLegalIdAttachmentHandler, false);
373 this.UnregisterIqSetHandler("petitionSignature", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureHandler, false);
374 this.UnregisterIqSetHandler("petitionSignatureResponse", NamespaceLegalIdentityIeeeV1, this.PetitionSignatureResponseHandler, false);
375 this.UnregisterIqSetHandler("authorizeAccess", NamespaceLegalIdentityIeeeV1, this.AuthorizeAccessToIdHandler, false);
376 this.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkLegalIdentityHandler, false);
377 this.UnregisterIqGetHandler("canSignAs", NamespaceLegalIdentityIeeeV1, this.CanSignAsHandler, false);
378 this.UnregisterIqSetHandler("readyForApproval", NamespaceLegalIdentityIeeeV1, this.ReadyForApprovalHandler, false);
379 this.UnregisterIqGetHandler("reviewIdProviders", NamespaceLegalIdentityIeeeV1, this.GetReviewIdProvidersHandler, false);
380 this.UnregisterIqSetHandler("selectReviewService", NamespaceLegalIdentityIeeeV1, this.SelectReviewServiceHandler, false);
381
382 this.Server.UnregisterIqGetHandler("getNetworkIdentity", NamespaceLegalIdentityIeeeV1, this.GetNetworkXmppIdentityHandler, false);
383
384 this.UnregisterIqSetHandler("createContract", NamespaceSmartContractsIeeeV1, this.CreateContractHandler, true);
385 this.UnregisterIqGetHandler("getCreatedContracts", NamespaceSmartContractsIeeeV1, this.GetCreatedContractsHandler, false);
386 this.UnregisterIqSetHandler("signContract", NamespaceSmartContractsIeeeV1, this.SignContractHandler, false);
387 this.UnregisterIqGetHandler("getSignedContracts", NamespaceSmartContractsIeeeV1, this.GetSignedContractsHandler, false);
388 this.UnregisterIqGetHandler("getContract", NamespaceSmartContractsIeeeV1, this.GetContractHandler, false);
389 this.UnregisterIqGetHandler("getContracts", NamespaceSmartContractsIeeeV1, this.GetContractsHandler, false);
390 this.UnregisterIqGetHandler("isPart", NamespaceSmartContractsIeeeV1, this.IsPartHandler, false);
391 this.UnregisterIqSetHandler("obsoleteContract", NamespaceSmartContractsIeeeV1, this.ObsoleteContractHandler, false);
392 this.UnregisterIqSetHandler("deleteContract", NamespaceSmartContractsIeeeV1, this.DeleteContractHandler, false);
393 this.UnregisterIqSetHandler("updateContract", NamespaceSmartContractsIeeeV1, this.UpdateContractHandler, false);
394 this.UnregisterIqGetHandler("getSchemas", NamespaceSmartContractsIeeeV1, this.GetSchemasHandler, false);
395 this.UnregisterIqGetHandler("getSchema", NamespaceSmartContractsIeeeV1, this.GetSchemaHandler, false);
396 this.UnregisterIqGetHandler("getLegalIdentities", NamespaceSmartContractsIeeeV1, this.GetLegalIdentitiesOfContractHandler, false);
397 this.UnregisterIqGetHandler("getNetworkIdentities", NamespaceSmartContractsIeeeV1, this.GetNetworkIdentitiesHandler, false);
398 this.UnregisterIqGetHandler("searchPublicContracts", NamespaceSmartContractsIeeeV1, this.SearchPublicContractsHandler, false);
399 this.UnregisterIqSetHandler("petitionContract", NamespaceSmartContractsIeeeV1, this.PetitionContractHandler, false);
400 this.UnregisterIqSetHandler("petitionContractResponse", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseHandler, false);
401 this.UnregisterIqSetHandler("addAttachment", NamespaceSmartContractsIeeeV1, this.AddContractAttachmentHandler, false);
402 this.UnregisterIqSetHandler("removeAttachment", NamespaceSmartContractsIeeeV1, this.RemoveContractAttachmentHandler, false);
403 this.UnregisterIqSetHandler("authorizeAccess", NamespaceSmartContractsIeeeV1, this.AuthorizeAccessToContractHandler, false);
404 this.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedHandler, false);
405
406 #endregion
407
408 this.remoteComponents?.Dispose();
409 this.remoteComponents = null;
410
412 NeuroFeaturesProcessor.UnregisterHandlers(this);
413 }
414
419 {
420 if (!(Gateway.ContractsClient is null))
421 Gateway.ContractsClient.PetitionForPeerReviewIDReceived += this.ContractsClient_PetitionForPeerReviewIDReceived;
422 }
423
428 {
429 if (!(Gateway.ContractsClient is null))
430 Gateway.ContractsClient.PetitionForPeerReviewIDReceived -= this.ContractsClient_PetitionForPeerReviewIDReceived;
431 }
432
437 public override bool SupportsAccounts => false;
438
442 internal HttpServer HttpServer => this.httpServer;
443
448 {
449 get => this.eDaler;
450 set
451 {
452 if (this.eDaler is null)
453 this.eDaler = value;
454 else if (this.eDaler != value)
455 throw new InvalidOperationException("Not allowed to change component reference.");
456 }
457 }
458
462 public string AttachmentsFolder => this.attachmentsFolder;
463
464 internal byte[] Sign(byte[] Data)
465 {
466 return LedgerConfiguration.Sign(Data);
467 }
468
469 internal byte[] Sign(Stream Data)
470 {
471 return LedgerConfiguration.Sign(Data);
472 }
473
474 internal bool Verify(byte[] Data, byte[] Signature)
475 {
477 }
478
483 public static Task<string> GetOnboardingNeuronDomainName()
484 {
485 return RuntimeSettings.GetAsync("Onboarding.DomainName", "id.tagroot.io");
486 }
487
488 #region Legal Identities
489
490 private Task GetPublicKeyHandler(object Sender, IqEventArgs e)
491 {
492 StringBuilder Xml = new StringBuilder();
493 NamespaceSet Version = XmppServerModule.GetVersion(e.Query.NamespaceURI);
494
495 Xml.Append("<publicKey xmlns='");
496 Xml.Append(NamespaceLegalIdentity(Version));
497 Xml.Append("'><ed448 pub='");
498 Xml.Append(Convert.ToBase64String(LedgerConfiguration.Instance.PublicKey));
499 Xml.Append("' xmlns='");
500 Xml.Append(NamespaceE2E(Version));
501 Xml.Append("'/></publicKey>");
502
503 e.IqResult(Xml.ToString(), e.To);
504
505 return Task.CompletedTask;
506 }
507
508 private Task IdApplicationAttributesHandler(object Sender, IqEventArgs e)
509 {
510 StringBuilder Xml = new StringBuilder();
511 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
512
513 Xml.Append("<idApplicationAttributes xmlns='");
514 Xml.Append(NamespaceLegalIdentity(QueryVersion));
515
516 if (PeerReviewConfiguration.Instance?.AllowPeerReview ?? false)
517 {
518 Xml.Append("' peerReview='true' nrReviewers='");
519 Xml.Append(PeerReviewConfiguration.Instance.NrReviewersToApprove.ToString());
520 Xml.Append("' nrPhotos='");
521 Xml.Append(PeerReviewConfiguration.Instance.NrPhotosRequired.ToString());
522 Xml.Append("' iso3166='");
523 Xml.Append(CommonTypes.Encode(PeerReviewConfiguration.Instance.RequireIso3166Compliance));
524 Xml.Append("'>");
525
526 if (PeerReviewConfiguration.Instance.RequireFirstName)
527 Xml.Append("<required>FIRST</required>");
528
529 if (PeerReviewConfiguration.Instance.RequireMiddleName)
530 Xml.Append("<required>MIDDLE</required>");
531
532 if (PeerReviewConfiguration.Instance.RequireLastName)
533 Xml.Append("<required>LAST</required>");
534
535 if (PeerReviewConfiguration.Instance.RequirePersonalNumber)
536 Xml.Append("<required>PNR</required>");
537
538 if (PeerReviewConfiguration.Instance.RequireAddress)
539 Xml.Append("<required>ADDR</required>");
540
541 if (PeerReviewConfiguration.Instance.RequirePostalCode)
542 Xml.Append("<required>ZIP</required>");
543
544 if (PeerReviewConfiguration.Instance.RequireArea)
545 Xml.Append("<required>AREA</required>");
546
547 if (PeerReviewConfiguration.Instance.RequireCity)
548 Xml.Append("<required>CITY</required>");
549
550 if (PeerReviewConfiguration.Instance.RequireRegion)
551 Xml.Append("<required>REGION</required>");
552
553 if (PeerReviewConfiguration.Instance.RequireCountry)
554 Xml.Append("<required>COUNTRY</required>");
555
556 if (PeerReviewConfiguration.Instance.RequireNationality)
557 Xml.Append("<required>NATIONALITY</required>");
558
559 if (PeerReviewConfiguration.Instance.RequireGender)
560 Xml.Append("<required>GENDER</required>");
561
562 if (PeerReviewConfiguration.Instance.RequireBirthDate)
563 {
564 Xml.Append("<required>BDAY</required>");
565 Xml.Append("<required>BMONTH</required>");
566 Xml.Append("<required>BYEAR</required>");
567 }
568
569 Xml.Append("</idApplicationAttributes>");
570 }
571 else
572 Xml.Append("' peerReview='false'/>");
573
574 e.IqResult(Xml.ToString(), e.To);
575
576 return Task.CompletedTask;
577 }
578
579 private async Task ApplyHandler(object Sender, IqEventArgs e)
580 {
581 try
582 {
583 if (!this.Server.IsServerDomain(e.From.Domain, true))
584 {
585 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
586 return;
587 }
588
589 LegalIdentity Identity = null;
590
591 foreach (XmlNode N in e.Query.ChildNodes)
592 {
593 if (N is XmlElement E && E.LocalName == "identity" && E.NamespaceURI == e.Query.NamespaceURI)
594 {
595 Identity = LegalIdentity.Parse(E, out bool HasStatus, out _);
596 if (HasStatus)
597 {
598 await e.IqErrorBadRequest(e.To, "Status element not permitted when applying for a legal identity.", "en");
599 return;
600 }
601 }
602 }
603
604 if (Identity is null)
605 {
606 await e.IqErrorBadRequest(e.To, "Identity missing.", "en");
607 return;
608 }
609
610 if (!CaseInsensitiveString.IsNullOrEmpty(Identity.Id))
611 {
612 await e.IqErrorBadRequest(e.To, "id attribute must not be set by client.", "en");
613 return;
614 }
615
616 if (!Identity.HasClientPublicKey)
617 {
618 await e.IqErrorBadRequest(e.To, "Client public key missing.", "en");
619 return;
620 }
621
622 if (!Identity.HasClientSignature)
623 {
624 await e.IqErrorBadRequest(e.To, "Client signature missing.", "en");
625 return;
626 }
627
628 if (!(Identity.ServerSignature is null))
629 {
630 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
631 return;
632 }
633
634 if (!Identity.ValidateClientSignature(this))
635 {
636 await e.IqErrorBadRequest(e.To, "Invalid client signature.", "en");
637 return;
638 }
639
640 if (!CaseInsensitiveString.IsNullOrEmpty(Identity["AGENT"]))
641 {
642 await e.IqErrorBadRequest(e.To, "AGENT property is reserved.", "en");
643 return;
644 }
645
646 string JID = Identity["JID"];
647 if (!string.IsNullOrEmpty(JID) && JID != e.From.BareJid)
648 {
649 await e.IqErrorBadRequest(e.To, "JID does not match sender Bare JID.", "en");
650 return;
651 }
652
653 IAccount Account = await XmppServerModule.GetAccountAsync(e.From.Account);
654 if (Account is null)
655 await e.IqErrorBadRequest(e.To, "Account not found.", "en");
656
657 string EMail = Identity["EMAIL"];
658 if (!string.IsNullOrEmpty(EMail) && !string.IsNullOrEmpty(Account.EMail) && Account.EMail != EMail)
659 {
660 await e.IqErrorBadRequest(e.To, "EMAIL does not match account e-mail.", "en");
661 return;
662 }
663
664 string PhoneNr = Identity["PHONE"];
665 if (!string.IsNullOrEmpty(PhoneNr) && !string.IsNullOrEmpty(Account.PhoneNr) && Account.PhoneNr != PhoneNr)
666 {
667 await e.IqErrorBadRequest(e.To, "PHONE does not match account phone number.", "en");
668 return;
669 }
670
671 Identity.Provider = e.To.Domain;
672 Identity.Account = e.From.Account;
673 Identity.State = IdentityState.Created;
674 Identity.Created = NowSecond;
675 Identity.Updated = DateTime.MinValue;
676 Identity.From = Identity.Created.Date;
677 Identity.To = Identity.From.AddMonths((int)await RuntimeSettings.GetAsync("LegalIdentity.Months", 24));
678
679 foreach (LegalIdentity ToRemove in await Database.FindDelete<LegalIdentity>(new FilterAnd(
680 new FilterFieldEqualTo("Account", e.From.Account),
681 new FilterFieldEqualTo("State", IdentityState.Created))))
682 {
683 Log.Informational("Obsolete Legal Identity Registration deleted.",
684 ToRemove.Id.Value, e.From.BareJid.Value,
685 "LegalIdDeleted", ToRemove.GetTags());
686 }
687
688 await Database.Insert(Identity);
689
690 Identity.Id = Identity.ObjectId + "@" + this.SubdomainSuffixed + e.From.Domain;
691 Identity.Sign(this);
692
693 await Database.Update(Identity);
694
695 KeyValuePair<string, object>[] Tags = Identity.GetTags();
696
697 Log.Informational("Legal Identity application registered.", Identity.Id.Value, e.From.BareJid.Value,
698 "LegalIdRegistered", Tags);
699
700 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Identity.Id, true);
701
702 StringBuilder Xml = new StringBuilder();
703 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
704 string IdentityXml = Xml.ToString();
705
706 await e.IqResult(IdentityXml, e.To);
707
708 StringBuilder Markdown = new StringBuilder();
709
710 Markdown.Append("Legal identity application received: [`");
711 Markdown.Append(Identity.Id);
712 Markdown.Append("`](");
713 Markdown.Append(Gateway.GetUrl("/LegalIdentity.md?Id="));
714 Markdown.Append(Identity.Id);
715 Markdown.AppendLine(")");
716 Markdown.AppendLine();
717 Output(Markdown, Tags);
718
719 await Gateway.SendNotification(Markdown.ToString());
720
721 bool First = true;
722
723 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
724 new FilterFieldEqualTo("Account", Identity.Account),
725 new FilterFieldNotEqualTo("Id", Identity.Id)), "Created"))
726 {
727 switch (ID.State)
728 {
729 case IdentityState.Created:
730 case IdentityState.Approved:
731 if (First)
732 {
733 First = false;
734 Markdown.Clear();
735 Markdown.AppendLine("Other identities registered for the same account:");
736
737 await Gateway.SendNotification(Markdown.ToString());
738 }
739
740 Markdown.Clear();
741
742 Markdown.Append("[`");
743 Markdown.Append(ID.Id);
744 Markdown.Append("`](");
745 Markdown.Append(Gateway.GetUrl("/LegalIdentity.md?Id="));
746 Markdown.Append(ID.Id);
747 Markdown.AppendLine(")");
748 Markdown.AppendLine();
749
750 Output(Markdown, ID.GetTags());
751 await Gateway.SendNotification(Markdown.ToString());
752 break;
753 }
754 }
755
756 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
757 new XmppAddress(e.From.BareJid), string.Empty, IdentityXml);
758 }
759 catch (Exception ex)
760 {
761 await e.IqError(ex, e.To);
762 }
763 }
764
770 internal static PersonalInfo GetPersonalInfo(LegalIdentity Identity)
771 {
772 return GetPersonalInfo(Identity.Properties);
773 }
774
780 internal static PersonalInfo GetPersonalInfo(IEnumerable<Property> Properties)
781 {
782 PersonalInfo Result = new PersonalInfo();
783
784 foreach (Property P in Properties)
785 {
786 switch (P.Name)
787 {
788 case "FIRST":
789 Result.FirstName = P.Value;
790 break;
791
792 case "MIDDLE":
793 Result.MiddleNames = P.Value;
794 break;
795
796 case "LAST":
797 Result.LastNames = P.Value;
798 break;
799
800 case "ADDR":
801 Result.Address = P.Value;
802 break;
803
804 case "ZIP":
805 Result.PostalCode = P.Value;
806 break;
807
808 case "AREA":
809 Result.Area = P.Value;
810 break;
811
812 case "CITY":
813 Result.City = P.Value;
814 break;
815
816 case "REGION":
817 Result.Region = P.Value;
818 break;
819
820 case "COUNTRY":
821 Result.Country = P.Value;
822 break;
823
824 case "NATIONALITY":
825 Result.Nationality = P.Value;
826 break;
827
828 case "GENDER":
829 switch (P.Value.LowerCase)
830 {
831 case "m":
832 Result.Gender = Gender.Male;
833 break;
834
835 case "f":
836 Result.Gender = Gender.Female;
837 break;
838
839 case "x":
840 Result.Gender = Gender.Other;
841 break;
842 }
843 break;
844
845 case "BDAY":
846 if (int.TryParse(P.Value, out int i) && i >= 1 && i <= 31)
847 Result.BirthDay = i;
848 break;
849
850 case "BMONTH":
851 if (int.TryParse(P.Value, out i) && i >= 1 && i <= 12)
852 Result.BirthMonth = i;
853 break;
854
855 case "BYEAR":
856 if (int.TryParse(P.Value, out i) && i >= 1900 && i <= 2100)
857 Result.BirthYear = i;
858 break;
859
860 case "PNR":
861 Result.PersonalNumber = P.Value;
862 break;
863
864 case "ORGNAME":
865 Result.OrgName = P.Value;
866 Result.HasOrg = true;
867 break;
868
869 case "ORGDEPT":
870 Result.OrgDepartment = P.Value;
871 Result.HasOrg = true;
872 break;
873
874 case "ORGROLE":
875 Result.OrgRole = P.Value;
876 Result.HasOrg = true;
877 break;
878
879 case "ORGADDR":
880 Result.OrgAddress = P.Value;
881 Result.HasOrg = true;
882 break;
883
884 case "ORGZIP":
885 Result.OrgPostalCode = P.Value;
886 Result.HasOrg = true;
887 break;
888
889 case "ORGAREA":
890 Result.OrgArea = P.Value;
891 Result.HasOrg = true;
892 break;
893
894 case "ORGCITY":
895 Result.OrgCity = P.Value;
896 Result.HasOrg = true;
897 break;
898
899 case "ORGREGION":
900 Result.OrgRegion = P.Value;
901 Result.HasOrg = true;
902 break;
903
904 case "ORGCOUNTRY":
905 Result.OrgCountry = P.Value;
906 Result.HasOrg = true;
907 break;
908
909 case "ORGNR":
910 Result.OrgNumber = P.Value;
911 Result.HasOrg = true;
912 break;
913 }
914 }
915
916 Result.HasBirthDate =
917 Result.BirthDay.HasValue &&
918 Result.BirthMonth.HasValue &&
919 Result.BirthYear.HasValue &&
920 Result.BirthDay.Value <= DateTime.DaysInMonth(Result.BirthYear.Value, Result.BirthMonth.Value);
921
922 if (!Result.HasBirthDate)
923 {
924 Result.BirthDay = null;
925 Result.BirthMonth = null;
926 Result.BirthYear = null;
927 }
928
929 return Result;
930 }
931
932 internal class PersonalInfo
933 {
934 public CaseInsensitiveString FirstName = null;
935 public CaseInsensitiveString MiddleNames = null;
936 public CaseInsensitiveString LastNames = null;
937 public CaseInsensitiveString Address = null;
938 public CaseInsensitiveString PostalCode = null;
939 public CaseInsensitiveString Area = null;
940 public CaseInsensitiveString City = null;
941 public CaseInsensitiveString Region = null;
942 public CaseInsensitiveString Country = null;
943 public CaseInsensitiveString Nationality = null;
944 public CaseInsensitiveString PersonalNumber = null;
945 public CaseInsensitiveString OrgName = null;
946 public CaseInsensitiveString OrgDepartment = null;
947 public CaseInsensitiveString OrgRole = null;
948 public CaseInsensitiveString OrgAddress = null;
949 public CaseInsensitiveString OrgPostalCode = null;
950 public CaseInsensitiveString OrgArea = null;
951 public CaseInsensitiveString OrgCity = null;
952 public CaseInsensitiveString OrgRegion = null;
953 public CaseInsensitiveString OrgCountry = null;
954 public CaseInsensitiveString OrgNumber = null;
955 public Gender? Gender = null;
956 public int? BirthYear = null;
957 public int? BirthMonth = null;
958 public int? BirthDay = null;
959 public bool HasOrg = false;
960 public bool HasBirthDate = false;
961
962 public DateTime? BirthDate
963 {
964 get
965 {
966 if (this.HasBirthDate)
967 return new DateTime(this.BirthYear.Value, this.BirthMonth.Value, this.BirthDay.Value);
968 else
969 return null;
970 }
971 }
972
973 public int Age => Duration.GetDurationBetween(this.BirthDate.Value, DateTime.Now).Years;
974 }
975
976 internal enum Gender
977 {
978 Male,
979 Female,
980 Other
981 }
982
986 public static DateTime NowSecond
987 {
988 get
989 {
990 DateTime TP = DateTime.Now;
991 TP = new DateTime(TP.Year, TP.Month, TP.Day, TP.Hour, TP.Minute, TP.Second);
992 return TP;
993 }
994 }
995
996 internal static void Output(StringBuilder Markdown, KeyValuePair<string, object>[] Tags)
997 {
998 Output(Markdown, Tags, true);
999 }
1000
1001 internal static void Output(StringBuilder Markdown, KeyValuePair<string, object>[] Tags, bool IncludeHeader)
1002 {
1003 if (IncludeHeader)
1004 {
1005 Markdown.AppendLine("| Key | Value |");
1006 Markdown.AppendLine("|:----|:------|");
1007 }
1008
1009 foreach (KeyValuePair<string, object> Tag in Tags)
1010 {
1011 Markdown.Append("| ");
1012 Markdown.Append(MarkdownDocument.Encode(Tag.Key).Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>"));
1013 Markdown.Append(" | ");
1014 Markdown.Append(MarkdownDocument.Encode(Tag.Value.ToString()).Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>"));
1015 Markdown.AppendLine(" |");
1016 }
1017 }
1018
1019 private async Task GetLegalIdentitiesHandler(object Sender, IqEventArgs e)
1020 {
1021 try
1022 {
1023 if (!this.Server.IsServerDomain(e.From.Domain, true))
1024 {
1025 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
1026 return;
1027 }
1028
1029 NamespaceSet Version = XmppServerModule.GetVersion(e.Query.NamespaceURI);
1030 string Xml = this.SerializeIdentities(await Database.Find<LegalIdentity>(
1031 new FilterFieldEqualTo("Account", e.From.Account)), null, Version);
1032
1033 await e.IqResult(Xml, e.To);
1034 }
1035 catch (Exception ex)
1036 {
1037 await e.IqError(ex, e.To);
1038 }
1039 }
1040
1041 private string SerializeIdentities(IEnumerable<LegalIdentity> Identities, IEnumerable<Dictionary<string, string>> AttachmentUrls,
1042 NamespaceSet Version)
1043 {
1044 StringBuilder Xml = new StringBuilder();
1045 IEnumerator<Dictionary<string, string>> e = AttachmentUrls?.GetEnumerator();
1046
1047 Xml.Append("<identities xmlns='");
1048 Xml.Append(NamespaceLegalIdentity(Version));
1049 Xml.Append("'>");
1050
1051 foreach (LegalIdentity Identity in Identities)
1052 Identity.Serialize(Xml, Identity.Version != Version, true, true, true, true, true, true, (e?.MoveNext() ?? false) ? e.Current : null, this);
1053
1054 Xml.Append("</identities>");
1055
1056 return Xml.ToString();
1057 }
1058
1059 internal static Task<LegalIdentity> GetLocalLegalIdentity(string LegalId)
1060 {
1061 return Database.FindFirstDeleteRest<LegalIdentity>(new FilterFieldEqualTo("Id", LegalId), "Created");
1062 }
1063
1064 private async Task GetLegalIdentityHandler(object Sender, IqEventArgs e)
1065 {
1066 try
1067 {
1069
1070 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase))
1071 {
1072 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
1073 if (Identity is null)
1074 {
1075 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1076 return;
1077 }
1078
1079 if ((e.From.Account == Identity.Account && this.Server.IsServerDomain(e.From.Domain, true)) ||
1080 this.IsAccessToIdentityAuthorized(e.From.BareJid, Identity.Id))
1081 {
1082 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Id, true);
1083
1084 StringBuilder Xml = new StringBuilder();
1085 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
1086
1087 await e.IqResult(Xml.ToString(), e.To);
1088 }
1089 else
1090 {
1091 this.IdentityAuthorization(e.From.BareJid, e.From.BareJid, Id, false);
1092 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");
1093 }
1094 }
1095 }
1096 catch (Exception ex)
1097 {
1098 await e.IqError(ex, e.To);
1099 }
1100 }
1101
1102 private async Task PetitionIdentityHandler(object Sender, IqEventArgs e)
1103 {
1104 try
1105 {
1106 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1107 string PetitionId = XML.Attribute(e.Query, "pid");
1108 string Purpose = XML.Attribute(e.Query, "purpose");
1109 string Nonce = XML.Attribute(e.Query, "nonce");
1110 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
1111 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase);
1112 XmlElement ContextXml = null;
1113
1114 foreach (XmlNode N in e.Query)
1115 {
1116 if (!(N is XmlElement E))
1117 continue;
1118
1119 if (ContextXml is null)
1120 ContextXml = E;
1121 else
1122 {
1123 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1124 return;
1125 }
1126 }
1127
1128 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
1129 if (Identity is null)
1130 {
1131 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1132 return;
1133 }
1134
1135 XmppAddress LegalIdAddr = new XmppAddress(LegalId);
1136 int i = LegalIdAddr.Domain.IndexOf('.');
1137 string JidDomain = i < 0 ? this.Server.Domain : LegalIdAddr.Domain.Substring(i + 1);
1138
1139 (LegalIdentity RequestorIdentity, Dictionary<string, string> RequestorAttachmentUrls) = await this.ValidateSenderSignature(
1140 e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, Identity.Account + "@" + JidDomain);
1141
1142 if (RequestorIdentity is null)
1143 return;
1144
1145 StringBuilder Msg = new StringBuilder();
1146
1147 Msg.Append("<petitionIdentityMsg id=\"");
1148 Msg.Append(XML.Encode(LegalId));
1149 Msg.Append("\" pid=\"");
1150 Msg.Append(XML.Encode(PetitionId));
1151 Msg.Append("\" from=\"");
1152 Msg.Append(XML.Encode(e.From.Address.Value));
1153 Msg.Append("\" purpose=\"");
1154 Msg.Append(XML.Encode(Purpose));
1155
1156 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1157 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
1158 {
1159 Msg.Append("\" clientEp=\"");
1160 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
1161 }
1162 else
1163 {
1164 ClientInformation ClientInfo = await this.GetNetworkIdentity(RequestorIdentity.Id, true, false, Identity.Version);
1165 if (!(ClientInfo is null))
1166 {
1167 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
1168
1169 if (!string.IsNullOrEmpty(ClientEndpoint))
1170 {
1171 Msg.Append("\" clientEp=\"");
1172 Msg.Append(XML.Encode(ClientEndpoint));
1173 }
1174 }
1175 }
1176
1177 Msg.Append("\" xmlns=\"");
1178 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1179 Msg.Append("\">");
1180 RequestorIdentity.Serialize(Msg, RequestorIdentity.Version != Identity.Version, true, true, true, true, true, true, RequestorAttachmentUrls, this);
1181
1182 if (!(ContextXml is null))
1183 Msg.Append(ContextXml.OuterXml);
1184
1185 Msg.Append("</petitionIdentityMsg>");
1186
1187 await this.Server.SendMessage(string.Empty, string.Empty, e.To, new XmppAddress(Identity.Account + "@" + JidDomain),
1188 string.Empty, Msg.ToString());
1189
1190 await e.IqResult(string.Empty, e.To);
1191 }
1192 catch (Exception ex)
1193 {
1194 await e.IqError(ex, e.To);
1195 }
1196 }
1197
1198 private async Task PetitionIdentityResponseHandler(object Sender, IqEventArgs e)
1199 {
1200 try
1201 {
1202 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1203 string PetitionId = XML.Attribute(e.Query, "pid");
1204 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
1205 bool Response = XML.Attribute(e.Query, "response", false);
1206 LegalIdentity Identity;
1207 XmlElement ContextXml = null;
1208
1209 foreach (XmlNode N in e.Query)
1210 {
1211 if (!(N is XmlElement E))
1212 continue;
1213
1214 if (ContextXml is null)
1215 ContextXml = E;
1216 else
1217 {
1218 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1219 return;
1220 }
1221 }
1222
1223 Identity = await GetLocalLegalIdentity(LegalId);
1224 if (Identity is null)
1225 {
1226 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1227 return;
1228 }
1229
1230 StringBuilder Msg = new StringBuilder();
1231
1232 Msg.Append("<petitionIdentityResponseMsg pid=\"");
1233 Msg.Append(XML.Encode(PetitionId));
1234 Msg.Append("\" response=\"");
1235 Msg.Append(CommonTypes.Encode(Response));
1236
1237 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1238 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
1239 {
1240 Msg.Append("\" clientEp=\"");
1241 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
1242 }
1243
1244 Msg.Append("\" xmlns=\"");
1245 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1246 Msg.Append("\">");
1247
1248 if (Response)
1249 {
1250 this.IdentityAuthorization(RequestorFullJid.BareJid, e.From.BareJid, LegalId, Response);
1251
1252 Identity.Serialize(Msg, false, true, true, true, true, true, true, null, this);
1253 }
1254
1255 if (!(ContextXml is null))
1256 Msg.Append(ContextXml.OuterXml);
1257
1258 Msg.Append("</petitionIdentityResponseMsg>");
1259
1260 await this.Server.SendMessage(string.Empty, string.Empty, e.To, RequestorFullJid, string.Empty, Msg.ToString());
1261
1262 await e.IqResult(string.Empty, e.To);
1263 }
1264 catch (Exception ex)
1265 {
1266 await e.IqError(ex, e.To);
1267 }
1268 }
1269
1270 internal void IdentityAuthorization(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1271 CaseInsensitiveString LegalId, bool Authorized)
1272 {
1273 this.Authorization("I:", ToBareJid, FromBareJid, LegalId, Authorized ? 1 : 0);
1274 }
1275
1290 internal void Authorization(CaseInsensitiveString Prefix, CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1291 CaseInsensitiveString Id, int Value)
1292 {
1293 CaseInsensitiveString Key1 = Prefix + Id;
1294 CaseInsensitiveString Key2 = Key1 + ":" + ToBareJid;
1295
1296 if (Value > 0)
1297 {
1298 this.petitions.Add(Key2, Value);
1299
1300 lock (this.petitionsByBareJid)
1301 {
1302 if (!this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
1303 {
1304 ByLegalId = new Dictionary<CaseInsensitiveString, CaseInsensitiveString>();
1305 this.petitionsByBareJid[ToBareJid] = ByLegalId;
1306 }
1307
1308 ByLegalId[Key1] = FromBareJid;
1309 }
1310 }
1311 else
1312 {
1313 this.petitions.Remove(Key2);
1314
1315 lock (this.petitionsByBareJid)
1316 {
1317 if (this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId) &&
1318 ByLegalId.Remove(Key1) &&
1319 ByLegalId.Count == 0)
1320 {
1321 this.petitionsByBareJid.Remove(ToBareJid);
1322 }
1323 }
1324 }
1325 }
1326
1327 private Task Petitions_Removed(object Sender, CacheItemEventArgs<CaseInsensitiveString, int> e)
1328 {
1329 if (e.Reason != RemovedReason.Manual)
1330 {
1331 string Key = e.Key;
1332 int i1 = Key.IndexOf(':');
1333 if (i1 >= 0)
1334 {
1335 int i2 = Key.IndexOf(':', i1 + 1);
1336 if (i2 >= 0)
1337 {
1338 CaseInsensitiveString Key1 = Key.Substring(0, i2);
1339 CaseInsensitiveString ToBareJid = Key.Substring(i2 + 1);
1340
1341 lock (this.petitionsByBareJid)
1342 {
1343 if (this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId) &&
1344 ByLegalId.Remove(Key1) &&
1345 ByLegalId.Count == 0)
1346 {
1347 this.petitionsByBareJid.Remove(ToBareJid);
1348 }
1349 }
1350 }
1351 }
1352 }
1353
1354 return Task.CompletedTask;
1355 }
1356
1357 internal bool IsAccessToIdentityAuthorized(CaseInsensitiveString BareJid, CaseInsensitiveString LegalId)
1358 {
1359 CaseInsensitiveString Key = "I:" + LegalId + ":" + BareJid;
1360 return this.petitions.TryGetValue(Key, out int i) && (i > 0);
1361 }
1362
1363 private async Task Server_OnPresenceLocalSender(object Sender, PresenceEventArgs e)
1364 {
1365 if (!e.To.IsEmpty && !(e.Stanza?.StanzaElement is null))
1366 {
1367 switch (e.Type)
1368 {
1369 case "subscribe":
1370 string Namespace = NamespaceLegalIdentity(XmppServerModule.GetVersion(e.Content?.NamespaceURI ?? string.Empty));
1371
1372 foreach (XmlNode N in e.Stanza.StanzaElement.ChildNodes)
1373 {
1374 if (N is XmlElement E && E.LocalName == "identity" && E.NamespaceURI == Namespace)
1375 {
1376 LegalIdentity Identity = LegalIdentity.Parse(E, out _, out _);
1377 if (!(Identity is null))
1378 {
1379 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Identity.Id.LowerCase))
1380 {
1381 LegalIdentity Identity2 = await GetLocalLegalIdentity(Identity.Id);
1382 if (Identity is null)
1383 {
1384 await e.PresenceErrorItemNotFound(e.To, "Legal identity not found.", "en");
1385 return;
1386 }
1387
1388 if (e.From.Account != Identity2.Account)
1389 {
1390 Log.Warning("Client tried to forward someone else's identity in a presence subscription request.",
1391 Identity.Id.Value, e.From.Address.Value, "IdValidationError",
1392 new KeyValuePair<string, object>("Sender", e.From.BareJid.Value),
1393 new KeyValuePair<string, object>("ID", Identity.Id.Value),
1394 new KeyValuePair<string, object>("ID Account", Identity.Account.Value));
1395
1396 await e.PresenceErrorForbidden(e.To, "A client can only forward its own identity objects.", "en");
1397 return;
1398 }
1399 else
1400 {
1401 StringBuilder Id1 = new StringBuilder();
1402 Identity.Serialize(Id1, true, true, true, true, true, true, false, null, this);
1403 string Id1Xml = Id1.ToString();
1404
1405 StringBuilder Id2 = new StringBuilder();
1406 Identity.Serialize(Id2, true, true, true, true, true, true, false, null, this);
1407 string Id2Xml = Id2.ToString();
1408
1409 if (Id1Xml == Id2Xml)
1410 this.IdentityAuthorization(e.To.BareJid, e.From.BareJid, Identity.Id, true);
1411 else
1412 {
1413 Log.Warning("Client tried to forward invalid ID in a presence subscription request.",
1414 Identity.Id.Value, e.From.Address.Value, "IdValidationError",
1415 new KeyValuePair<string, object>("Sender", e.From.BareJid.Value),
1416 new KeyValuePair<string, object>("ID", Identity.Id.Value),
1417 new KeyValuePair<string, object>("Attempt", Id1Xml),
1418 new KeyValuePair<string, object>("Actual", Id2Xml));
1419
1420 await e.PresenceErrorForbidden(e.To, "Invalid ID representation.", "en");
1421 return;
1422 }
1423 }
1424 }
1425 }
1426 }
1427 }
1428 break;
1429
1430 case "unsubscribed":
1431 LinkedList<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> ToRemove = null;
1432
1433 lock (this.petitionsByBareJid)
1434 {
1435 if (this.petitionsByBareJid.TryGetValue(e.To.BareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
1436 {
1437 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in ByLegalId)
1438 {
1439 if (P.Value == e.From.BareJid && P.Key.Length > 2)
1440 {
1441 if (ToRemove is null)
1442 ToRemove = new LinkedList<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>>();
1443
1444 ToRemove.AddLast(P);
1445 }
1446 }
1447 }
1448 }
1449
1450 if (!(ToRemove is null))
1451 {
1452 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in ToRemove)
1453 this.Authorization(P.Key.Substring(0, 2), e.To.BareJid, P.Value, P.Key.Substring(2), 0);
1454 }
1455 break;
1456 }
1457 }
1458 }
1459
1460 private async Task PetitionSignatureHandler(object Sender, IqEventArgs e)
1461 {
1462 try
1463 {
1464 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1465 string PetitionId = XML.Attribute(e.Query, "pid");
1466 string Purpose = XML.Attribute(e.Query, "purpose");
1467 string Nonce = XML.Attribute(e.Query, "nonce");
1468 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
1469 string ContentStr = null;
1470 XmlElement ContextXml = null;
1471
1472 foreach (XmlNode N in e.Query.ChildNodes)
1473 {
1474 if (!(N is XmlElement E))
1475 continue;
1476
1477 if (E.LocalName == "content" && E.NamespaceURI == e.Query.NamespaceURI)
1478 ContentStr = E.InnerText;
1479 else if (ContextXml is null)
1480 ContextXml = E;
1481 else
1482 {
1483 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1484 return;
1485 }
1486 }
1487
1488 if (ContentStr is null)
1489 {
1490 if (ContextXml is null)
1491 ContentStr = e.Query.InnerText;
1492 else
1493 {
1494 await e.IqErrorBadRequest(e.To, "No content to sign.", "en");
1495 return;
1496 }
1497 }
1498
1499 byte[] Content = Convert.FromBase64String(ContentStr);
1500 string DataStr = PetitionId + ":" + LegalId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase + ":" + ContentStr;
1501 byte[] Data = Encoding.UTF8.GetBytes(DataStr);
1502 string s = Encoding.UTF8.GetString(Content);
1503 LegalIdentity ReqIdentity = null;
1504 Dictionary<string, string> ReqAttachmentUrls = null;
1505 bool PeerReview = false;
1506
1507 if (s.StartsWith("<identity") && s.EndsWith("</identity>"))
1508 {
1509 try
1510 {
1511 XmlDocument Doc = new XmlDocument()
1512 {
1513 PreserveWhitespace = true
1514 };
1515 Doc.LoadXml(s);
1516
1517 if (Doc.DocumentElement.LocalName == "identity")
1518 {
1519 LegalIdentity TempId = LegalIdentity.Parse(Doc.DocumentElement, out bool HasStatus, out _);
1520 CaseInsensitiveString Jid = TempId["JID"];
1521
1522 if (HasStatus &&
1523 TempId.State == IdentityState.Created &&
1524 Jid == e.From.BareJid)
1525 {
1526 ReqIdentity = TempId;
1527 PeerReview = true;
1528
1529 if (!TempId.ValidateSignature(Data, Signature))
1530 {
1531 await e.IqErrorForbidden(e.To, "Signature of identity proving access to private keys not valid.", "en");
1532 return;
1533 }
1534 }
1535 }
1536 }
1537 catch (Exception)
1538 {
1539 // Ignore
1540 }
1541 }
1542
1543 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
1544 if (Identity is null)
1545 {
1546 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1547 return;
1548 }
1549
1550 XmppAddress LegalIdAddr = new XmppAddress(LegalId);
1551 int i = LegalIdAddr.Domain.IndexOf('.');
1552 string JidDomain = i < 0 ? this.Server.Domain : LegalIdAddr.Domain.Substring(i + 1);
1553
1554 if (ReqIdentity is null)
1555 {
1556 (ReqIdentity, ReqAttachmentUrls) = await this.ValidateSenderSignature(e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, Identity.Account + "@" + JidDomain);
1557 if (ReqIdentity is null)
1558 return;
1559 }
1560
1561 StringBuilder Msg = new StringBuilder();
1562
1563 Msg.Append("<petitionSignatureMsg id=\"");
1564 Msg.Append(XML.Encode(LegalId));
1565 Msg.Append("\" pid=\"");
1566 Msg.Append(XML.Encode(PetitionId));
1567 Msg.Append("\" from=\"");
1568 Msg.Append(XML.Encode(e.From.Address.Value));
1569 Msg.Append("\" purpose=\"");
1570 Msg.Append(XML.Encode(Purpose));
1571
1572 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1573 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
1574 {
1575 Msg.Append("\" clientEp=\"");
1576 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
1577 }
1578 else
1579 {
1580 ClientInformation ClientInfo = await this.GetNetworkIdentity(ReqIdentity.Id, true, false, Identity.Version);
1581 if (!(ClientInfo is null))
1582 {
1583 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
1584
1585 if (!string.IsNullOrEmpty(ClientEndpoint))
1586 {
1587 Msg.Append("\" clientEp=\"");
1588 Msg.Append(XML.Encode(ClientEndpoint));
1589 }
1590 }
1591 }
1592
1593 Msg.Append("\" xmlns=\"");
1594 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1595 Msg.Append("\"><content>");
1596 Msg.Append(ContentStr);
1597 Msg.Append("</content>");
1598
1599 if (!PeerReview)
1600 ReqIdentity.Serialize(Msg, ReqIdentity.Version != Identity.Version, true, true, true, true, true, true, ReqAttachmentUrls, this);
1601
1602 XmppAddress ClientBareJid = new XmppAddress(Identity.Account + "@" + JidDomain);
1603
1604 if (!(ContextXml is null))
1605 {
1606 Msg.Append(ContextXml.OuterXml);
1607
1608 if (ContextXml.LocalName == "agentApi" && ContextXml.NamespaceURI == QuickLogin.NamespaceTagSignature)
1609 {
1610 CaseInsensitiveString Domain = XML.Attribute(ContextXml, "domain");
1611 int Timeout = XML.Attribute(ContextXml, "timeout", 3600);
1612
1613 if (Timeout < 1 || Timeout > 3600)
1614 {
1615 await e.IqErrorBadRequest(e.To, "Agent API Timeout out of range.", "en");
1616 return;
1617 }
1618
1620 {
1621 await e.IqErrorBadRequest(e.To, "Missing domain.", "en");
1622 return;
1623 }
1624
1625 CaseInsensitiveString ReqDomain = ReqIdentity["DOMAIN"];
1626 if (CaseInsensitiveString.IsNullOrEmpty(ReqDomain))
1627 {
1628 await e.IqErrorBadRequest(e.To, "Requestor lacks DOMAIN property in its legal identity.", "en");
1629 return;
1630 }
1631
1632 if (ReqDomain != Domain)
1633 {
1634 await e.IqErrorBadRequest(e.To, "Requestor domain does not match stated domain in agent API context.", "en");
1635 return;
1636 }
1637
1638 this.AgentApiLoginCorrelation(e.From.BareJid, ClientBareJid.Address, PetitionId, Timeout);
1639 }
1640 }
1641
1642 Msg.Append("</petitionSignatureMsg>");
1643
1644 await this.Server.SendMessage(string.Empty, string.Empty, e.To, ClientBareJid, string.Empty, Msg.ToString());
1645
1646 await e.IqResult(string.Empty, e.To);
1647 }
1648 catch (Exception ex)
1649 {
1650 await e.IqError(ex, e.To);
1651 }
1652 }
1653
1654 internal void AgentApiLoginCorrelation(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1655 CaseInsensitiveString PetitionId, int Timeout)
1656 {
1657 this.Authorization("A:", ToBareJid, FromBareJid, PetitionId, Timeout);
1658 }
1659
1660 internal bool IsAgentApiLoginCorrelated(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
1661 CaseInsensitiveString PetitionId, out int Timeout)
1662 {
1663 CaseInsensitiveString Key1 = "A:" + PetitionId;
1664 CaseInsensitiveString Key2 = Key1 + ":" + ToBareJid;
1665 if (!this.petitions.TryGetValue(Key2, out Timeout))
1666 {
1667 Timeout = 0;
1668 return false;
1669 }
1670
1671 lock (this.petitionsByBareJid)
1672 {
1673 if (!this.petitionsByBareJid.TryGetValue(ToBareJid, out Dictionary<CaseInsensitiveString, CaseInsensitiveString> ByLegalId))
1674 {
1675 Timeout = 0;
1676 return false;
1677 }
1678
1679 if (!ByLegalId.TryGetValue(Key1, out CaseInsensitiveString FromBareJid2) || FromBareJid != FromBareJid2)
1680 {
1681 Timeout = 0;
1682 return false;
1683 }
1684 }
1685
1686 return true;
1687 }
1688
1693
1694
1695 private async Task PetitionSignatureResponseHandler(object Sender, IqEventArgs e)
1696 {
1697 try
1698 {
1699 CaseInsensitiveString LegalId = XML.Attribute(e.Query, "id");
1700 string PetitionId = XML.Attribute(e.Query, "pid");
1701 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
1702 bool Response = XML.Attribute(e.Query, "response", false);
1703 string ContentStr = null;
1704 string SignatureStr = null;
1705 byte[] Content = null;
1706 byte[] Signature = null;
1707 LegalIdentity Identity;
1708 XmlElement ContextXml = null;
1709
1710 foreach (XmlNode N in e.Query.ChildNodes)
1711 {
1712 if (!(N is XmlElement E))
1713 continue;
1714
1715 if (E.NamespaceURI == e.Query.NamespaceURI)
1716 {
1717 switch (E.LocalName)
1718 {
1719 case "content":
1720 ContentStr = E.InnerText;
1721 Content = Convert.FromBase64String(ContentStr);
1722 break;
1723
1724 case "signature":
1725 SignatureStr = E.InnerText;
1726 Signature = Convert.FromBase64String(SignatureStr);
1727 break;
1728 }
1729 }
1730 else if (ContextXml is null)
1731 ContextXml = E;
1732 else
1733 {
1734 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
1735 return;
1736 }
1737 }
1738
1739 Identity = await GetLocalLegalIdentity(LegalId);
1740 if (Identity is null)
1741 {
1742 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1743 return;
1744 }
1745
1746 StringBuilder Msg = new StringBuilder();
1747
1748 Msg.Append("<petitionSignatureResponseMsg pid=\"");
1749 Msg.Append(XML.Encode(PetitionId));
1750 Msg.Append("\" response=\"");
1751 Msg.Append(CommonTypes.Encode(Response));
1752
1753 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
1754 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
1755 {
1756 Msg.Append("\" clientEp=\"");
1757 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
1758 }
1759
1760 Msg.Append("\" xmlns=\"");
1761 Msg.Append(NamespaceLegalIdentity(Identity.Version));
1762 Msg.Append("\">");
1763
1764 if (Response)
1765 {
1766 if (Content is null)
1767 {
1768 await e.IqErrorBadRequest(e.To, "Content missing.", "en");
1769 return;
1770 }
1771
1772 if (Signature is null)
1773 {
1774 await e.IqErrorBadRequest(e.To, "Signature missing.", "en");
1775 return;
1776 }
1777
1778 if (!Identity.ValidateSignature(Content, Signature))
1779 {
1780 await e.IqErrorBadRequest(e.To, "Signature incorrect.", "en");
1781 return;
1782 }
1783
1784 Msg.Append("<signature>");
1785 Msg.Append(SignatureStr);
1786 Msg.Append("</signature>");
1787 Identity.Serialize(Msg, false, true, true, true, true, true, true, null, this);
1788
1789 this.IdentityAuthorization(RequestorFullJid.BareJid, e.From.BareJid, LegalId, Response);
1790
1791 if (ContextXml is null &&
1792 this.IsAgentApiLoginCorrelated(RequestorFullJid.BareJid, e.From.BareJid, PetitionId, out int Timeout) &&
1793 Timeout > 0 &&
1794 Timeout <= 3600)
1795 {
1796 string RemoteEndpoint;
1797
1800 else if (e.Sender is XmppS2SEndpoint XmppServerConnection)
1801 RemoteEndpoint = XmppServerConnection.RemoteEndpoint;
1802 else
1803 RemoteEndpoint = string.Empty;
1804
1805 if (!string.IsNullOrEmpty(RemoteEndpoint))
1806 LoginAuditor.Success("Successful Agent API account login.", e.From.Account, RemoteEndpoint, "XMPP");
1807
1808 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
1809 int Expires = IssuedAt + Timeout;
1810
1811 string Token = AgentResource.Factory.Create(
1812 new KeyValuePair<string, object>(JwtClaims.JwtId, Convert.ToBase64String(Gateway.NextBytes(32))),
1813 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain?.Value ?? string.Empty),
1814 new KeyValuePair<string, object>(JwtClaims.Subject, e.From.BareJid),
1815 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
1816 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
1817
1818 Msg.Append("<agentApiToken xmlns='");
1820 Msg.Append("'>");
1821 Msg.Append(XML.Encode(Token));
1822 Msg.Append("</agentApiToken>");
1823 }
1824 }
1825
1826 if (!(ContextXml is null))
1827 Msg.Append(ContextXml.OuterXml);
1828
1829 Msg.Append("</petitionSignatureResponseMsg>");
1830
1831 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
1832 RequestorFullJid, string.Empty, Msg.ToString());
1833
1834 await e.IqResult(string.Empty, e.To);
1835 }
1836 catch (Exception ex)
1837 {
1838 await e.IqError(ex, e.To);
1839 }
1840 }
1841
1842 private async Task ValidateSignatureHandler(object Sender, IqEventArgs e)
1843 {
1844 try
1845 {
1846 CaseInsensitiveString Id = null;
1847 CaseInsensitiveString BareJid = null;
1848 byte[] Data = null;
1849 byte[] Signature = null;
1850 string ForBareJid = null;
1851
1852 foreach (XmlAttribute Attr in e.Query.Attributes)
1853 {
1854 switch (Attr.Name)
1855 {
1856 case "id":
1857 Id = Attr.Value;
1858 break;
1859
1860 case "bareJid":
1861 BareJid = Attr.Value;
1862 break;
1863
1864 case "data":
1865 Data = Convert.FromBase64String(Attr.Value);
1866 break;
1867
1868 case "s":
1869 Signature = Convert.FromBase64String(Attr.Value);
1870 break;
1871
1872 case "for":
1873 ForBareJid = Attr.Value;
1874 break;
1875 }
1876 }
1877
1878 if (Data is null || Signature is null)
1879 {
1880 await e.IqErrorBadRequest(e.To, "Request attributes missing.", "en");
1881 return;
1882 }
1883
1885 {
1886 await e.IqErrorBadRequest(e.To, "Both the id and bareJid attributes cannot be specified at the same time.", "en");
1887 return;
1888 }
1889
1890 LegalIdentity Identity = null;
1891
1893 {
1894 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase))
1895 {
1896 Identity = await GetLocalLegalIdentity(Id);
1897 if (Identity is null)
1898 {
1899 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1900 return;
1901 }
1902
1903 if (!Identity.ValidateSignature(Data, Signature))
1904 {
1905 await e.IqErrorForbidden(e.To, "signature not valid.", "en");
1906 return;
1907 }
1908 }
1909 }
1910 else
1911 {
1912 CaseInsensitiveString AccountName;
1913
1915 {
1916 XmppAddress Addr = new XmppAddress(BareJid);
1917 if (!this.Server.IsServerDomain(Addr.Domain, true))
1918 {
1919 await e.IqErrorForbidden(e.To, "Bare JID does not correspond to this Trust Provider.", "en");
1920 return;
1921 }
1922
1923 AccountName = Addr.Account;
1924 }
1925 else
1926 {
1927 AccountName = e.From.Account;
1928 BareJid = e.From.BareJid;
1929 }
1930
1931 DateTime Now = DateTime.Now;
1932
1933 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
1934 new FilterFieldEqualTo("Account", AccountName),
1935 new FilterFieldEqualTo("State", IdentityState.Approved),
1936 new FilterFieldLesserOrEqualTo("From", Now),
1937 new FilterFieldGreaterOrEqualTo("To", Now)), "-Created"))
1938 {
1939 if (ID.ValidateSignature(Data, Signature))
1940 {
1941 Identity = ID;
1942 break;
1943 }
1944 }
1945
1946 if (Identity is null)
1947 {
1948 await e.IqErrorForbidden(e.To, "No matching legal identity found that can validate the signature.", "en");
1949 return;
1950 }
1951 }
1952
1953 if (!string.IsNullOrEmpty(ForBareJid))
1954 this.IdentityAuthorization(ForBareJid, BareJid, Identity.Id, true);
1955
1956 StringBuilder Xml = new StringBuilder();
1957 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
1958
1959 await e.IqResult(Xml.ToString(), e.To);
1960 }
1961 catch (Exception ex)
1962 {
1963 await e.IqError(ex, e.To);
1964 }
1965 }
1966
1967 private Task ObsoleteLegalIdentityHandler(object Sender, IqEventArgs e)
1968 {
1969 return this.ChangeStateLegalIdentityHandler(e, IdentityState.Obsoleted);
1970 }
1971
1972 private Task CompromiseLegalIdentityHandler(object Sender, IqEventArgs e)
1973 {
1974 return this.ChangeStateLegalIdentityHandler(e, IdentityState.Compromised);
1975 }
1976
1977 private async Task ChangeStateLegalIdentityHandler(IqEventArgs e, IdentityState State)
1978 {
1979 try
1980 {
1982
1983 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase))
1984 {
1985 if (!this.Server.IsServerDomain(e.From.Domain, true))
1986 {
1987 await e.IqErrorForbidden(e.To, "Only accounts on the broker can apply for registering legal identities.", "en");
1988 return;
1989 }
1990
1991 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
1992 if (Identity is null)
1993 {
1994 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
1995 return;
1996 }
1997
1998 if (Identity.Account != e.From.Account)
1999 {
2000 await e.IqErrorForbidden(e.To, "Only allowed to obsolete your own legal identities.", "en");
2001 return;
2002 }
2003
2004 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account);
2005 if (Account is null)
2006 {
2007 await e.IqErrorForbidden(e.To, "Account has been removed.", "en");
2008 return;
2009 }
2010
2011 if (!Account.Enabled)
2012 {
2013 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
2014 return;
2015 }
2016
2017 if (Identity.State != State)
2018 {
2019 switch (Identity.State)
2020 {
2021 case IdentityState.Created:
2022 State = IdentityState.Rejected;
2023 break;
2024
2025 case IdentityState.Rejected:
2026 await e.IqErrorForbidden(e.To, "Legal identity has been rejected, and cannot be changed.", "en");
2027 return;
2028
2029 case IdentityState.Compromised:
2030 await e.IqErrorForbidden(e.To, "Legal identity has been flagged as compromised, and cannot be changed.", "en");
2031 return;
2032 }
2033
2034 await this.UpdateState(Identity, State, e.From.Address, Account as Account);
2035 }
2036
2037 StringBuilder Xml = new StringBuilder();
2038 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
2039
2040 await e.IqResult(Xml.ToString(), e.To);
2041 }
2042 }
2043 catch (Exception ex)
2044 {
2045 await e.IqError(ex, e.To);
2046 }
2047 }
2048
2049 internal static async Task CopyPropertiesFromApprovedIdentity(LegalIdentity Identity, Account Account)
2050 {
2052
2053 Account.LatestIdentity = Identity.Id;
2054 Account.FirstName = CaseInsensitiveString.Empty;
2055 Account.MiddleNames = CaseInsensitiveString.Empty;
2056 Account.LastNames = CaseInsensitiveString.Empty;
2057 Account.PersonalNumber = CaseInsensitiveString.Empty;
2058 Account.Country = CaseInsensitiveString.Empty;
2059 Account.OrgName = CaseInsensitiveString.Empty;
2060 Account.OrgNumber = CaseInsensitiveString.Empty;
2061 Account.OrgDepartment = CaseInsensitiveString.Empty;
2062 Account.OrgRole = CaseInsensitiveString.Empty;
2063 Account.OrgCountry = CaseInsensitiveString.Empty;
2064
2065 foreach (Property P in Identity.Properties)
2066 {
2067 s = P.Value;
2069 continue;
2070
2071 switch (P.Name.Value.ToUpper())
2072 {
2073 case "EMAIL":
2074 if (Account.EMail != s)
2075 {
2076 Account.EMail = s;
2077 Account.EMailVerified = null;
2078 }
2079 break;
2080
2081 case "PHONE":
2082 if (Account.PhoneNr != s)
2083 {
2084 Account.PhoneNr = s;
2085 Account.PhoneNrVerified = null;
2086 }
2087 break;
2088
2089 case "FIRST":
2090 Account.FirstName = s;
2091 break;
2092
2093 case "MIDDLE":
2094 Account.MiddleNames = s;
2095 break;
2096
2097 case "LAST":
2098 Account.LastNames = s;
2099 break;
2100
2101 case "PNR":
2102 Account.PersonalNumber = s;
2103 break;
2104
2105 case "COUNTRY":
2106 Account.Country = s;
2107 break;
2108
2109 case "ORGNAME":
2110 Account.OrgName = s;
2111 break;
2112
2113 case "ORGNR":
2114 Account.OrgNumber = s;
2115 break;
2116
2117 case "ORGDEPT":
2118 Account.OrgDepartment = s;
2119 break;
2120
2121 case "ORGROLE":
2122 Account.OrgRole = s;
2123 break;
2124
2125 case "ORGCOUNTRY":
2126 Account.OrgCountry = s;
2127 break;
2128 }
2129 }
2130
2131 await Database.Update(Account);
2132 }
2133
2134 internal async Task UpdateState(LegalIdentity Identity, IdentityState State, string Actor, Account Account)
2135 {
2136 if (Identity.State != State)
2137 {
2138 bool RefBefore = Identity.State == IdentityState.Approved;
2139
2140 Identity.State = State;
2141 Identity.Updated = NowSecond;
2142 Identity.Sign(this);
2143
2144 await Database.Update(Identity);
2145
2146 Log.Informational("Legal Identity registration updated.", Identity.Id.Value, Actor,
2147 "LegalIdUpdated", Identity.GetTags());
2148
2149 bool RefAfter = Identity.State == IdentityState.Approved;
2150
2151 if (RefBefore && RefAfter)
2152 await UpdateLegalIdentityReference(Identity);
2153 else if (RefBefore)
2154 await DeleteLegalIdentityReference(Identity.Id);
2155 else if (RefAfter)
2156 await AddLegalIdentityReference(Identity);
2157
2158 StringBuilder Xml = new StringBuilder();
2159 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
2160
2161 await this.Server.SendMessage(string.Empty, string.Empty, Identity.Provider,
2162 Identity.Account + "@" + Identity.Provider.Substring(this.SubdomainSuffixed.Length), string.Empty, Xml.ToString());
2163
2164 if (State == IdentityState.Approved && !(Account is null))
2165 await CopyPropertiesFromApprovedIdentity(Identity, Account);
2166 }
2167 }
2168
2174 internal Task<LegalIdentity> GetCurrentApprovedLegalIdentityAsync(CaseInsensitiveString Account)
2175 {
2176 return this.GetApprovedLegalIdentityAsync(Account, DateTime.Now);
2177 }
2178
2185 internal async Task<LegalIdentity> GetApprovedLegalIdentityAsync(CaseInsensitiveString Account, DateTime Timestamp)
2186 {
2187 LegalIdentity Result = null;
2188
2189 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
2190 new FilterFieldEqualTo("Account", Account),
2191 new FilterFieldEqualTo("State", IdentityState.Approved))))
2192 {
2193 if (ID.From <= Timestamp && ID.To >= Timestamp && (Result is null || ID.Created > Result.Created))
2194 Result = ID;
2195 }
2196
2197 return Result;
2198 }
2199
2200 private async Task AddLegalIdAttachmentHandler(object Sender, IqEventArgs e)
2201 {
2202 try
2203 {
2205 string GetUrl = XML.Attribute(e.Query, "getUrl");
2206 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
2207 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
2208 string Namespace = NamespaceLegalIdentity(QueryVersion);
2209
2210 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase))
2211 {
2212 if (!this.Server.IsServerDomain(e.From.Domain, true))
2213 {
2214 await e.IqErrorForbidden(e.To, "Only accounts on the broker can add attachments.", "en");
2215 return;
2216 }
2217
2218 if (!Uri.TryCreate(GetUrl, UriKind.Absolute, out Uri GetUri))
2219 {
2220 await e.IqErrorBadRequest(e.To, "Invalid Get URL.", "en");
2221 return;
2222 }
2223
2224 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
2225 if (Identity is null)
2226 {
2227 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
2228 return;
2229 }
2230
2231 if (Identity.Account != e.From.Account)
2232 {
2233 await e.IqErrorForbidden(e.To, "Only allowed to add attachments to your own legal identities.", "en");
2234 return;
2235 }
2236
2237 if (Identity.State != IdentityState.Created)
2238 {
2239 await e.IqErrorForbidden(e.To, "Attachments can only be added to newly created identities before they are approved.", "en");
2240 return;
2241 }
2242
2243 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account);
2244 if (Account is null)
2245 {
2246 await e.IqErrorForbidden(e.To, "Account has been removed.", "en");
2247 return;
2248 }
2249
2250 if (!Account.Enabled)
2251 {
2252 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
2253 return;
2254 }
2255
2256 if (!(Identity.Attachments is null))
2257 {
2258 string s = Convert.ToBase64String(Signature);
2259
2260 foreach (AttachmentReference Ref in Identity.Attachments)
2261 {
2262 if (Convert.ToBase64String(Ref.Signature) == s)
2263 {
2264 await e.IqErrorForbidden(e.To, "Attachment already assigned to identity.", "en");
2265 return;
2266 }
2267 }
2268 }
2269
2270 KeyValuePair<string, TemporaryStream> P = await InternetContent.GetTempStreamAsync(GetUri);
2271 string ContentType = P.Key;
2272
2273 using (TemporaryStream File = P.Value)
2274 {
2275 File.Position = 0;
2276 if (!Identity.ValidateSignature(File, Signature))
2277 {
2278 await e.IqErrorForbidden(e.To, "Attachment signature is invalid.", "en");
2279 return;
2280 }
2281
2282 XmlDocument Doc;
2283 bool IncNrPeerReviews = false;
2284 StringBuilder sb = new StringBuilder();
2285 List<LegalIdentity> Reviewers = null;
2286
2287 if (ContentType.StartsWith(XmlCodec.DefaultContentType) ||
2289 {
2290 File.Position = 0;
2291
2292 Doc = new XmlDocument()
2293 {
2294 PreserveWhitespace = true
2295 };
2296 Doc.Load(File);
2297
2298 if (Doc.DocumentElement.LocalName == "peerReview" && Doc.DocumentElement.NamespaceURI == Namespace)
2299 {
2300 LegalIdentity ReviewedIdentity = null;
2301 LegalIdentity ReviewerIdentity = null;
2304 bool ReviewedHasStatus = false;
2305 bool ReviewerHasStatus = false;
2306 byte[] PeerSignature = Convert.FromBase64String(XML.Attribute(Doc.DocumentElement, "s"));
2307 DateTime TP = XML.Attribute(Doc.DocumentElement, "tp", DateTime.Now);
2308 byte[] SignedIdentity = null;
2309 DateTime Now = DateTime.Now;
2310
2311 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
2312 {
2313 if (!(N is XmlElement E) || N.NamespaceURI != Namespace)
2314 continue;
2315
2316 foreach (XmlNode N2 in E.ChildNodes)
2317 {
2318 if (N2 is XmlElement E2 &&
2319 E2.LocalName == "identity" &&
2320 E2.NamespaceURI == Namespace)
2321 {
2322 switch (E.LocalName)
2323 {
2324 case "reviewed":
2325 ReviewedIdentity = LegalIdentity.Parse(E2, out ReviewedHasStatus,
2326 out Dictionary<string, string> ReviewedIdentityAttachmentsUrls);
2327
2328 sb.Clear();
2329 ReviewedIdentity.Serialize(sb, true, true, true, false, false, false, false, null, this);
2330 string s1 = sb.ToString();
2331
2332 sb.Clear();
2333 Identity.Serialize(sb, true, true, true, false, false, false, false, null, this);
2334 string s2 = sb.ToString();
2335
2336 if (s1 != s2)
2337 {
2338 await e.IqErrorForbidden(e.To, "Reviewed identity mismatch.", "en");
2339 return;
2340 }
2341
2342 sb.Clear();
2343 ReviewedIdentity.Serialize(sb, true, true, true, true, true, true, true, ReviewedIdentityAttachmentsUrls, this);
2344 SignedIdentity = Encoding.UTF8.GetBytes(sb.ToString());
2345 break;
2346
2347 case "reviewer":
2348 ReviewerIdentity = LegalIdentity.Parse(E2, out ReviewerHasStatus,
2349 out Dictionary<string, string> ReviewerIdentityAttachmentsUrls);
2350
2351 sb.Clear();
2352 ReviewerIdentity.Serialize(sb, false, false, false, false, false, false, false, null, this);
2353 byte[] Data = Encoding.UTF8.GetBytes(sb.ToString());
2354 ReviewerJid = ReviewerIdentity["JID"];
2355
2356 if (!CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
2357 {
2358 (ReviewerIdentity, _) = await this.ValidateSignature(new XmppAddress(ReviewerJid), TP, Data,
2359 ReviewerIdentity.ClientSignature);
2360
2361 if (ReviewerIdentity is null || ReviewerJid != ReviewerIdentity["JID"])
2362 {
2363 await e.IqErrorForbidden(e.To, "Peer identity signature invalid.", "en");
2364 return;
2365 }
2366 }
2367
2368 if (ReviewerIdentity.State != IdentityState.Approved)
2369 {
2370 await e.IqErrorForbidden(e.To, "Reviewer identity not approved.", "en");
2371 return;
2372 }
2373
2374 if (Now.Date.AddDays(1) < ReviewerIdentity.From ||
2375 Now.Date.AddDays(-1) > ReviewerIdentity.To)
2376 {
2377 await e.IqErrorForbidden(e.To, "Reviewer identity not valid.", "en");
2378 return;
2379 }
2380
2381 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerIdentity.Provider))
2382 {
2383 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a provider.", "en");
2384 return;
2385 }
2386
2387 if (string.IsNullOrEmpty(ReviewerIdentity.ClientKeyName) ||
2388 ReviewerIdentity.ClientPubKey is null ||
2389 ReviewerIdentity.ClientPubKey.Length == 0)
2390 {
2391 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a key.", "en");
2392 return;
2393 }
2394
2395 if (ReviewerIdentity.ClientSignature is null ||
2396 ReviewerIdentity.ClientSignature.Length == 0)
2397 {
2398 await e.IqErrorForbidden(e.To, "Reviewer identity lacks a signature.", "en");
2399 return;
2400 }
2401
2402 if (CaseInsensitiveString.IsNullOrEmpty(ReviewerJid))
2403 {
2404 await e.IqErrorForbidden(e.To, "Reviewer identity not trusted for reviewing.", "en");
2405 return;
2406 }
2407 break;
2408 }
2409 }
2410 }
2411 }
2412
2413 if (ReviewedIdentity is null || !ReviewedHasStatus ||
2414 ReviewerIdentity is null || !ReviewerHasStatus ||
2415 SignedIdentity is null)
2416 {
2417 await e.IqErrorForbidden(e.To, "Attachment not a correctly formed peer-review document.", "en");
2418 return;
2419 }
2420
2421 CaseInsensitiveString Pnr = ReviewedIdentity["PNR"];
2422
2423 if (Pnr == ReviewerIdentity["PNR"] && !CaseInsensitiveString.IsNullOrEmpty(Pnr))
2424 {
2425 await e.IqErrorForbidden(e.To, "Reviewer cannot be the same person as the reviewed person.", "en");
2426 return;
2427 }
2428
2429 if (!ReviewerIdentity.ValidateSignature(SignedIdentity, PeerSignature))
2430 {
2431 await e.IqErrorForbidden(e.To, "Peer signature invalid.", "en");
2432 return;
2433 }
2434
2435 if (PeerReviewConfiguration.Instance?.AllowPeerReview ?? false)
2436 {
2437 int NrPhotos = 0;
2438
2439 if (!(Identity.Attachments is null))
2440 {
2441 foreach (AttachmentReference Ref in Identity.Attachments)
2442 {
2443 if (Ref.ContentType.StartsWith("image/"))
2444 NrPhotos++;
2445 }
2446 }
2447
2448 if (NrPhotos < PeerReviewConfiguration.Instance.NrPhotosRequired)
2449 {
2450 await e.IqErrorForbidden(e.To, "Peer review not accepted: Identity lacks sufficient photos (" +
2451 PeerReviewConfiguration.Instance.NrPhotosRequired.ToString() + ").", "en");
2452 return;
2453 }
2454
2455 PersonalInfo RI = GetPersonalInfo(ReviewerIdentity);
2456
2457 if (PeerReviewConfiguration.Instance.RequireFirstName && CaseInsensitiveString.IsNullOrEmpty(RI.FirstName))
2458 {
2459 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a First name. Peer review not accepted.", "en");
2460 return;
2461 }
2462
2463 if (PeerReviewConfiguration.Instance.RequireMiddleName && CaseInsensitiveString.IsNullOrEmpty(RI.MiddleNames))
2464 {
2465 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Middle name. Peer review not accepted.", "en");
2466 return;
2467 }
2468
2469 if (PeerReviewConfiguration.Instance.RequireLastName && CaseInsensitiveString.IsNullOrEmpty(RI.LastNames))
2470 {
2471 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Last name. Peer review not accepted.", "en");
2472 return;
2473 }
2474
2475 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(RI.PersonalNumber))
2476 {
2477 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Personal number. Peer review not accepted.", "en");
2478 return;
2479 }
2480
2481 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(RI.Address))
2482 {
2483 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an address. Peer review not accepted.", "en");
2484 return;
2485 }
2486
2487 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(RI.PostalCode))
2488 {
2489 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Postal Code (ZIP). Peer review not accepted.", "en");
2490 return;
2491 }
2492
2494 {
2495 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an Area. Peer review not accepted.", "en");
2496 return;
2497 }
2498
2500 {
2501 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a City. Peer review not accepted.", "en");
2502 return;
2503 }
2504
2506 {
2507 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Region. Peer review not accepted.", "en");
2508 return;
2509 }
2510
2511 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(RI.Country))
2512 {
2513 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Country. Peer review not accepted.", "en");
2514 return;
2515 }
2516
2517 if (!CaseInsensitiveString.IsNullOrEmpty(RI.Country))
2518 {
2519 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(RI.Country, out string _))
2520 {
2521 await e.IqErrorBadRequest(e.To, "Reviewer Country is not an ISO 3166-1 Country Code. Peer review not accepted.", "en");
2522 return;
2523 }
2524
2525 if (!CaseInsensitiveString.IsNullOrEmpty(RI.PersonalNumber))
2526 {
2527 bool? Valid = await PersonalNumberSchemes.IsValid(RI.Country, RI.PersonalNumber);
2528 if (Valid.HasValue && !Valid.Value)
2529 {
2530 await e.IqErrorBadRequest(e.To, "The personal number format used by the reviewer does not comply with regulations.", "en");
2531 return;
2532 }
2533 }
2534 }
2535
2536 if (PeerReviewConfiguration.Instance.RequireNationality && CaseInsensitiveString.IsNullOrEmpty(RI.Nationality))
2537 {
2538 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Nationality. Peer review not accepted.", "en");
2539 return;
2540 }
2541
2542 if (PeerReviewConfiguration.Instance.RequireGender && !RI.Gender.HasValue)
2543 {
2544 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Gender. Peer review not accepted.", "en");
2545 return;
2546 }
2547
2548 if (PeerReviewConfiguration.Instance.RequireBirthDate)
2549 {
2550 if (!RI.HasBirthDate)
2551 {
2552 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a Birth Date. Peer review not accepted.", "en");
2553 return;
2554 }
2555
2556 DateTime BirthDate = RI.BirthDate.Value;
2557
2558 if (RI.Age < 18)
2559 {
2560 await e.IqErrorBadRequest(e.To, "Reviewer identity too young. Peer review not accepted.", "en");
2561 return;
2562 }
2563 }
2564
2565 if (RI.HasOrg)
2566 {
2567 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgName)) // TODO: Configurable
2568 {
2569 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization name. Peer review not accepted.", "en");
2570 return;
2571 }
2572
2573 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgDepartment)) // TODO: Configurable
2574 {
2575 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a department. Peer review not accepted.", "en");
2576 return;
2577 }
2578
2579 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgRole)) // TODO: Configurable
2580 {
2581 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks a role. Peer review not accepted.", "en");
2582 return;
2583 }
2584
2585 if (CaseInsensitiveString.IsNullOrEmpty(RI.OrgNumber)) // TODO: Configurable
2586 {
2587 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization number. Peer review not accepted.", "en");
2588 return;
2589 }
2590
2591 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(RI.OrgAddress))
2592 {
2593 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization address. Peer review not accepted.", "en");
2594 return;
2595 }
2596
2597 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(RI.OrgPostalCode))
2598 {
2599 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Postal Code (ZIP). Peer review not accepted.", "en");
2600 return;
2601 }
2602
2604 {
2605 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Area. Peer review not accepted.", "en");
2606 return;
2607 }
2608
2610 {
2611 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization City. Peer review not accepted.", "en");
2612 return;
2613 }
2614
2615 if (PeerReviewConfiguration.Instance.RequireRegion && CaseInsensitiveString.IsNullOrEmpty(RI.OrgRegion))
2616 {
2617 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Region. Peer review not accepted.", "en");
2618 return;
2619 }
2620
2621 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(RI.OrgCountry))
2622 {
2623 await e.IqErrorBadRequest(e.To, "Reviewer identity lacks an organization Country. Peer review not accepted.", "en");
2624 return;
2625 }
2626
2627 if (!CaseInsensitiveString.IsNullOrEmpty(RI.OrgCountry))
2628 {
2629 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(RI.OrgCountry, out string _))
2630 {
2631 await e.IqErrorBadRequest(e.To, "Reviewer organization Country is not an ISO 3166-1 Country Code. Peer review not accepted.", "en");
2632 return;
2633 }
2634 }
2635 }
2636
2637 if (!await CheckPeerReviewFields(e, ReviewedIdentity))
2638 return;
2639
2640 PersonalInfo PI = GetPersonalInfo(ReviewedIdentity);
2641
2642 if (PI.HasOrg)
2643 {
2644 if (!RI.HasOrg)
2645 {
2646 await e.IqErrorBadRequest(e.To, "Peer reviewer lacks organization information. Peer review not accepted.", "en");
2647 return;
2648 }
2649
2650 if (PI.OrgName != RI.OrgName ||
2651 PI.OrgNumber != RI.OrgNumber ||
2652 PI.OrgCountry != RI.OrgCountry)
2653 {
2654 await e.IqErrorBadRequest(e.To, "Peer reviewer must be from same company. Peer review not accepted.", "en");
2655 return;
2656 }
2657 }
2658
2659 if (!(Identity.Attachments is null))
2660 {
2661 LegalIdentity[] Reviewers2 = await GetPeerReviewers(Identity);
2662
2663 foreach (LegalIdentity ReviewerID in Reviewers2)
2664 {
2665 if (ReviewerID["PNR"] == Pnr)
2666 {
2667 await e.IqErrorBadRequest(e.To, "A reviewer can only review the application once.", "en");
2668 return;
2669 }
2670 }
2671
2672 if (Reviewers is null)
2673 Reviewers = new List<LegalIdentity>();
2674
2675 Reviewers.AddRange(Reviewers2);
2676 }
2677 }
2678
2679 if (Reviewers is null)
2680 Reviewers = new List<LegalIdentity>();
2681
2682 Reviewers.Add(ReviewerIdentity);
2683 IncNrPeerReviews = true;
2684 }
2685 }
2686 else
2687 Doc = null;
2688
2689 KeyValuePair<Attachment, AttachmentReference> A = await this.CreateAttachment(GetUri,
2690 Identity, Signature, File, ContentType, e.From.Account, null);
2691 List<AttachmentReference> References = new List<AttachmentReference>();
2692 bool Approved = false;
2693
2694 if (!(Identity.Attachments is null))
2695 References.AddRange(Identity.Attachments);
2696
2697 References.Add(A.Value);
2698 Identity.Attachments = References.ToArray();
2699 Identity.Updated = NowSecond;
2700
2701 if (IncNrPeerReviews)
2702 {
2703 Identity.NrPeerReviews++;
2704
2705 if ((PeerReviewConfiguration.Instance?.AllowPeerReview ?? true) &&
2706 Identity.NrPeerReviews >= (PeerReviewConfiguration.Instance?.NrReviewersToApprove ?? 2) &&
2707 Identity.State == IdentityState.Created)
2708 {
2709 Identity.State = IdentityState.Approved;
2710 Approved = true;
2711
2712 StringBuilder Markdown = new StringBuilder();
2713
2714 Markdown.AppendLine("Peer reviewed Legal ID approved");
2715 Markdown.AppendLine("===================================");
2716 Markdown.AppendLine();
2717
2718 this.AppendMarkdown(Markdown, Identity, "Applicant");
2719
2720 int Index = 0;
2721
2722 foreach (LegalIdentity Reviewer in Reviewers)
2723 {
2724 Index++;
2725 this.AppendMarkdown(Markdown, Reviewer, "Reviewer " + Index.ToString());
2726 }
2727
2728 await Gateway.SendNotification(Markdown.ToString());
2729 }
2730 }
2731
2732 Identity.Sign(this);
2733 await Database.Update(Identity);
2734
2735 if (Identity.State == IdentityState.Approved)
2736 await CopyPropertiesFromApprovedIdentity(Identity, Account as Account);
2737
2738 Log.Informational("Attachment added to Legal Identity registration.",
2739 Identity.Id.Value, e.From.Address.Value,
2740 "LegalIdUpdated", Identity.GetTags());
2741
2742 sb.Clear();
2743 Identity.Serialize(sb, true, true, true, true, true, true, true, null, this);
2744 string Xml = sb.ToString();
2745
2746 if (Approved)
2747 {
2748 await AddLegalIdentityReference(Identity);
2749
2750 XmppAddress IdentityAddress = new XmppAddress(Identity.Id);
2751 int i = IdentityAddress.Domain.IndexOf('.');
2752 string JidDomain = i < 0 ? this.Server.Domain : IdentityAddress.Domain.Substring(i + 1);
2753
2754 await this.Server.SendMessage(string.Empty, string.Empty, e.To,
2755 new XmppAddress(Identity.Account + "@" + this.Server.Domain), string.Empty, Xml);
2756 }
2757
2758 await e.IqResult(Xml, e.To);
2759 }
2760 }
2761 }
2762 catch (Exception ex)
2763 {
2764 await e.IqError(ex, e.To);
2765 }
2766 }
2767
2768 internal static async Task<bool> CheckPeerReviewFields(IqEventArgs e, LegalIdentity Identity)
2769 {
2770 if (PeerReviewConfiguration.Instance?.AllowPeerReview ?? false)
2771 {
2772 PersonalInfo PI = GetPersonalInfo(Identity);
2773
2774 if (PeerReviewConfiguration.Instance.RequireFirstName && CaseInsensitiveString.IsNullOrEmpty(PI.FirstName))
2775 {
2776 await e.IqErrorBadRequest(e.To, "First name is a required field.", "en");
2777 return false;
2778 }
2779
2780 if (PeerReviewConfiguration.Instance.RequireMiddleName && CaseInsensitiveString.IsNullOrEmpty(PI.MiddleNames))
2781 {
2782 await e.IqErrorBadRequest(e.To, "Middle name is a required field.", "en");
2783 return false;
2784 }
2785
2786 if (PeerReviewConfiguration.Instance.RequireLastName && CaseInsensitiveString.IsNullOrEmpty(PI.LastNames))
2787 {
2788 await e.IqErrorBadRequest(e.To, "Last name is a required field.", "en");
2789 return false;
2790 }
2791
2792 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(PI.PersonalNumber))
2793 {
2794 await e.IqErrorBadRequest(e.To, "Personal Number is a required field.", "en");
2795 return false;
2796 }
2797
2798 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(PI.Address))
2799 {
2800 await e.IqErrorBadRequest(e.To, "Address is a required field.", "en");
2801 return false;
2802 }
2803
2804 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(PI.PostalCode))
2805 {
2806 await e.IqErrorBadRequest(e.To, "Postal Code (ZIP) is a required field.", "en");
2807 return false;
2808 }
2809
2811 {
2812 await e.IqErrorBadRequest(e.To, "Area is a required field.", "en");
2813 return false;
2814 }
2815
2817 {
2818 await e.IqErrorBadRequest(e.To, "City is a required field.", "en");
2819 return false;
2820 }
2821
2823 {
2824 await e.IqErrorBadRequest(e.To, "Region is a required field.", "en");
2825 return false;
2826 }
2827
2828 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(PI.Country))
2829 {
2830 await e.IqErrorBadRequest(e.To, "Country is a required field.", "en");
2831 return false;
2832 }
2833
2834 if (!CaseInsensitiveString.IsNullOrEmpty(PI.Country))
2835 {
2836 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(PI.Country, out string _))
2837 {
2838 await e.IqErrorBadRequest(e.To, "Country must be a ISO 3166-1 Country Code.", "en");
2839 return false;
2840 }
2841
2842 if (!CaseInsensitiveString.IsNullOrEmpty(PI.PersonalNumber))
2843 {
2844 bool? Valid = await PersonalNumberSchemes.IsValid(PI.Country, PI.PersonalNumber);
2845 if (Valid.HasValue && !Valid.Value)
2846 {
2847 await e.IqErrorBadRequest(e.To, "The personal number format does not comply with regulations in your country.", "en");
2848 return false;
2849 }
2850 }
2851 }
2852
2853 if (PeerReviewConfiguration.Instance.RequireNationality && CaseInsensitiveString.IsNullOrEmpty(PI.Nationality))
2854 {
2855 await e.IqErrorBadRequest(e.To, "Nationality is a required field.", "en");
2856 return false;
2857 }
2858
2859 if (PeerReviewConfiguration.Instance.RequireGender && !PI.Gender.HasValue)
2860 {
2861 await e.IqErrorBadRequest(e.To, "Gender is a required field.", "en");
2862 return false;
2863 }
2864
2865 if (PeerReviewConfiguration.Instance.RequireBirthDate && !PI.HasBirthDate)
2866 {
2867 await e.IqErrorBadRequest(e.To, "Birth Date is a required field.", "en");
2868 return false;
2869 }
2870
2871 if (PI.HasOrg)
2872 {
2873 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgName)) // TODO: Make configurable
2874 {
2875 await e.IqErrorBadRequest(e.To, "Organization name is a required field for work identities.", "en");
2876 return false;
2877 }
2878
2879 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgDepartment)) // TODO: Make configurable
2880 {
2881 await e.IqErrorBadRequest(e.To, "Department is a required field for work identities.", "en");
2882 return false;
2883 }
2884
2885 if (CaseInsensitiveString.IsNullOrEmpty(PI.OrgRole)) // TODO: Make configurable
2886 {
2887 await e.IqErrorBadRequest(e.To, "Role is a required field for work identites.", "en");
2888 return false;
2889 }
2890
2891 if (PeerReviewConfiguration.Instance.RequirePersonalNumber && CaseInsensitiveString.IsNullOrEmpty(PI.OrgNumber))
2892 {
2893 await e.IqErrorBadRequest(e.To, "Organization Number is a required field for work identites.", "en");
2894 return false;
2895 }
2896
2897 if (PeerReviewConfiguration.Instance.RequireAddress && CaseInsensitiveString.IsNullOrEmpty(PI.OrgAddress))
2898 {
2899 await e.IqErrorBadRequest(e.To, "Organization Address is a required field for work identites.", "en");
2900 return false;
2901 }
2902
2903 if (PeerReviewConfiguration.Instance.RequirePostalCode && CaseInsensitiveString.IsNullOrEmpty(PI.OrgPostalCode))
2904 {
2905 await e.IqErrorBadRequest(e.To, "Organization Postal Code (ZIP) is a required field for work identites.", "en");
2906 return false;
2907 }
2908
2910 {
2911 await e.IqErrorBadRequest(e.To, "Organization Area is a required field for work identites.", "en");
2912 return false;
2913 }
2914
2916 {
2917 await e.IqErrorBadRequest(e.To, "Organization City is a required field for work identites.", "en");
2918 return false;
2919 }
2920
2921 if (PeerReviewConfiguration.Instance.RequireRegion && CaseInsensitiveString.IsNullOrEmpty(PI.OrgRegion))
2922 {
2923 await e.IqErrorBadRequest(e.To, "Organization Region is a required field for work identites.", "en");
2924 return false;
2925 }
2926
2927 if (PeerReviewConfiguration.Instance.RequireCountry && CaseInsensitiveString.IsNullOrEmpty(PI.OrgCountry))
2928 {
2929 await e.IqErrorBadRequest(e.To, "Organization Country is a required field for work identites.", "en");
2930 return false;
2931 }
2932
2933 if (!CaseInsensitiveString.IsNullOrEmpty(PI.OrgCountry))
2934 {
2935 if (PeerReviewConfiguration.Instance.RequireIso3166Compliance && !Iso3166.TryGetCountry(PI.OrgCountry, out string _))
2936 {
2937 await e.IqErrorBadRequest(e.To, "Organization Country must be a ISO 3166-1 Country Code.", "en");
2938 return false;
2939 }
2940 }
2941 }
2942 }
2943
2944 return true;
2945 }
2946
2947 internal static async Task<LegalIdentity[]> GetPeerReviewers(LegalIdentity Identity)
2948 {
2949 List<LegalIdentity> Result = new List<LegalIdentity>();
2950 string Namespace = NamespaceLegalIdentity(Identity.Version);
2951
2952 if (!(Identity?.Attachments is null))
2953 {
2954 foreach (AttachmentReference Ref in Identity.Attachments)
2955 {
2956 if (Ref.ContentType.StartsWith(XmlCodec.DefaultContentType) ||
2957 Ref.ContentType.StartsWith(XmlCodec.SchemaContentType))
2958 {
2959 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
2960 if (Attachment is null)
2961 continue;
2962
2963 using (FileStream AttachmentFile = File.OpenRead(Attachment.LocalFileName))
2964 {
2965 Aes Aes = Aes.Create();
2966
2967 Aes.BlockSize = 128;
2968 Aes.KeySize = 256;
2969 Aes.Mode = CipherMode.CBC;
2970 Aes.Padding = PaddingMode.Zeros;
2971
2972 byte[] Key = new byte[32];
2973 byte[] IV = new byte[16];
2974
2975 Array.Copy(Attachment.Salt, 0, Key, 0, 32);
2976 Array.Copy(Attachment.Salt, 32, IV, 0, 16);
2977
2978 using (ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV))
2979 {
2980 using (CryptoStream DecryptedAttachmentFile = new CryptoStream(AttachmentFile, Decryptor, CryptoStreamMode.Read))
2981 {
2982 XmlDocument Doc = new XmlDocument()
2983 {
2984 PreserveWhitespace = true
2985 };
2986 Doc.Load(DecryptedAttachmentFile);
2987
2988 if (Doc.DocumentElement.LocalName == "peerReview" &&
2989 Doc.DocumentElement.NamespaceURI == Namespace)
2990 {
2991 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
2992 {
2993 if (N is XmlElement E &&
2994 E.LocalName == "reviewer" &&
2995 E.NamespaceURI == Namespace)
2996 {
2997 foreach (XmlNode N2 in E.ChildNodes)
2998 {
2999 if (N2 is XmlElement E2 &&
3000 E2.LocalName == "identity" &&
3001 E2.NamespaceURI == Namespace)
3002 {
3003 LegalIdentity ReviewerID = LegalIdentity.Parse(E2, out _, out _);
3004 Result.Add(ReviewerID);
3005 }
3006 }
3007 }
3008 }
3009 }
3010 }
3011 }
3012 }
3013 }
3014 }
3015 }
3016
3017 return Result.ToArray();
3018 }
3019
3020 internal void AppendMarkdown(StringBuilder Markdown, LegalIdentity Identity, string Title)
3021 {
3022 Markdown.AppendLine(Title);
3023 Markdown.AppendLine(new string('-', Title.Length + 3));
3024 Markdown.AppendLine();
3025
3026 Markdown.Append("| `");
3027 Markdown.Append(Identity.Id);
3028 Markdown.AppendLine("` ||");
3029 Markdown.AppendLine("|:----|:----|");
3030
3031 foreach (Property P in Identity.Properties)
3032 {
3033 Markdown.Append("| `");
3034 Markdown.Append(P.Name);
3035 Markdown.Append("` | ");
3036 Markdown.Append(MarkdownDocument.Encode(P.Value));
3037 Markdown.AppendLine(" |");
3038 }
3039
3040 Markdown.AppendLine();
3041 }
3042
3043 private Task<KeyValuePair<Attachment, AttachmentReference>> CreateAttachment(Uri GetUri,
3044 LegalIdentity UploadingIdentity, byte[] Signature, TemporaryStream File, string ContentType,
3045 string Account, CaseInsensitiveString ContractId)
3046 {
3047 return CreateAttachment(Path.GetFileName(GetUri.AbsolutePath), UploadingIdentity,
3048 Signature, File, ContentType, Account, ContractId, this.attachmentsFolder);
3049 }
3050
3051 internal static async Task<KeyValuePair<Attachment, AttachmentReference>> CreateAttachment(string RemoteFileName,
3052 LegalIdentity UploadingIdentity, byte[] Signature, TemporaryStream File, string ContentType,
3053 string Account, CaseInsensitiveString ContractId, string AttachmentsFolder)
3054 {
3055 DateTime Timestamp = DateTime.Now;
3056 string Folder = Path.Combine(AttachmentsFolder, Timestamp.Year.ToString("D4"),
3057 Timestamp.Month.ToString("D2"), Timestamp.Day.ToString("D2"));
3058
3059 if (!Directory.Exists(Folder))
3060 Directory.CreateDirectory(Folder);
3061
3062 byte[] Salt = Gateway.NextBytes(48);
3063 byte[] Key = new byte[32];
3064 byte[] IV = new byte[16];
3065 string AttachmentId;
3066 string FileName;
3067
3068 Array.Copy(Salt, 0, Key, 0, 32);
3069 Array.Copy(Salt, 32, IV, 0, 16);
3070
3072 {
3073 Account = Account,
3075 ContractId = ContractId,
3076 RemoteFileName = RemoteFileName,
3078 Timestamp = Timestamp,
3079 UploaderLegalId = UploadingIdentity.Id,
3080 Size = File.Length,
3081 Salt = Salt
3082 };
3083
3084 await Database.Insert(Attachment);
3085
3086 AttachmentId = Attachment.ObjectId;
3087 FileName = Path.Combine(Folder, AttachmentId + ".bin");
3088
3089 Attachment.Id = AttachmentId + "@" + UploadingIdentity.Provider;
3090 Attachment.LocalFileName = FileName;
3091
3092 await Database.Update(Attachment);
3093
3094 File.Position = 0;
3095 using (FileStream AttachmentFile = System.IO.File.Create(FileName))
3096 {
3097 Aes Aes = Aes.Create();
3098
3099 Aes.BlockSize = 128;
3100 Aes.KeySize = 256;
3101 Aes.Mode = CipherMode.CBC;
3102 Aes.Padding = PaddingMode.Zeros;
3103
3104 using (ICryptoTransform Encryptor = Aes.CreateEncryptor(Key, IV))
3105 {
3106 using (CryptoStream EncryptedAttachmentFile = new CryptoStream(AttachmentFile, Encryptor, CryptoStreamMode.Write))
3107 {
3108 await File.CopyToAsync(EncryptedAttachmentFile);
3109 EncryptedAttachmentFile.FlushFinalBlock();
3110 }
3111 }
3112 }
3113
3115 {
3117 FileName = Attachment.RemoteFileName,
3118 Id = Attachment.Id,
3119 LegalId = Attachment.UploaderLegalId,
3121 Timestamp = Timestamp
3122 };
3123
3124 return new KeyValuePair<Attachment, AttachmentReference>(Attachment, Ref);
3125 }
3126
3127 private async Task RemoveLegalIdAttachmentHandler(object Sender, IqEventArgs e)
3128 {
3129 try
3130 {
3131 CaseInsensitiveString AttachmentId = XML.Attribute(e.Query, "attachmentId");
3132
3133 if (!this.Server.IsServerDomain(e.From.Domain, true))
3134 {
3135 await e.IqErrorForbidden(e.To, "Only accounts on the broker can remove attachments.", "en");
3136 return;
3137 }
3138
3139 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", AttachmentId));
3140 if (Attachment is null)
3141 {
3142 await e.IqErrorItemNotFound(e.To, "Attachment not found.", "en");
3143 return;
3144 }
3145
3147 {
3148 await e.IqErrorItemNotFound(e.To, "Attachment not assigned to legal identity.", "en");
3149 return;
3150 }
3151
3153 {
3154 LegalIdentity Identity = await GetLocalLegalIdentity(Attachment.UploaderLegalId);
3155 if (Identity is null)
3156 {
3157 await e.IqErrorItemNotFound(e.To, "Associated Legal identity not found.", "en");
3158 return;
3159 }
3160
3161 if (Identity.Account != e.From.Account)
3162 {
3163 await e.IqErrorForbidden(e.To, "Only allowed to remove attachments from your own legal identities.", "en");
3164 return;
3165 }
3166
3167 if (Identity.State != IdentityState.Created)
3168 {
3169 await e.IqErrorForbidden(e.To, "Attachments can only be removed from newly created identities before they are approved.", "en");
3170 return;
3171 }
3172
3173 if (File.Exists(Attachment.LocalFileName))
3174 File.Delete(Attachment.LocalFileName);
3175
3176 if (!(Identity.Attachments is null))
3177 {
3178 List<AttachmentReference> Attachments = new List<AttachmentReference>();
3179
3180 foreach (AttachmentReference Ref in Identity.Attachments)
3181 {
3182 if (Ref.Id != AttachmentId)
3183 Attachments.Add(Ref);
3184 }
3185
3186 Identity.Attachments = Attachments.ToArray();
3187 Identity.Sign(this);
3188
3189 await Database.Update(Identity);
3190 }
3191
3192 await Database.Delete(Attachment);
3193
3194 StringBuilder Xml = new StringBuilder();
3195 Identity.Serialize(Xml, true, true, true, true, true, true, true, null, this);
3196
3197 await e.IqResult(Xml.ToString(), e.To);
3198 }
3199 }
3200 catch (Exception ex)
3201 {
3202 await e.IqError(ex, e.To);
3203 }
3204 }
3205
3214 internal async Task<ClientInformation> GetNetworkIdentity(CaseInsensitiveString Id, bool CanBeLegal, bool CanBeJid, NamespaceSet Version)
3215 {
3216 XmppAddress Address = new XmppAddress(Id);
3217
3218 if (this.IsComponentDomain(Address.Domain, true))
3219 {
3220 if (!CanBeLegal)
3221 throw new Exception("Expected server domain, not a component domain: " + Address.Domain);
3222
3223 LegalIdentity Identity = await GetLocalLegalIdentity(Id)
3224 ?? throw new Exception("Legal Identity not found: " + Id.Value);
3225
3226 CaseInsensitiveString BareJid = Identity["JID"];
3227
3229 {
3230 if (Address.Domain.StartsWith(this.SubdomainSuffixed, StringComparison.CurrentCultureIgnoreCase))
3231 BareJid = Identity.Account + "@" + Address.Domain.Substring(this.SubdomainSuffixed.Length);
3232 else
3233 BareJid = Identity.Account + "@" + this.Server.Domain;
3234 }
3235
3236 if (!this.Server.TryGetClientConnections(BareJid, out IClientConnection[] Connections))
3237 Connections = null;
3238
3239 return new ClientInformation()
3240 {
3241 Jid = BareJid,
3242 Connections = this.RemoveDisconnected(Connections)
3243 };
3244 }
3245 else if (this.Server.IsServerDomain(Address.Domain, true))
3246 {
3247 if (!CanBeJid)
3248 throw new Exception("Expected legal component domain, not server domain: " + Address.Domain);
3249
3250 IClientConnection[] Connections;
3251
3252 if (Address.IsFullJID)
3253 {
3254 if (this.Server.TryGetClientConnection(Address.Address, out IClientConnection Connection))
3255 Connections = new IClientConnection[] { Connection };
3256 else
3257 Connections = null;
3258 }
3259 else if (Address.IsBareJID)
3260 {
3261 if (!this.Server.TryGetClientConnections(Address.BareJid, out Connections))
3262 Connections = null;
3263 }
3264 else
3265 Connections = null;
3266
3267 return new ClientInformation()
3268 {
3269 Jid = Address.BareJid,
3270 Connections = this.RemoveDisconnected(Connections)
3271 };
3272 }
3273 else
3274 {
3275 TaskCompletionSource<ClientInformation> Result = new TaskCompletionSource<ClientInformation>();
3276 StringBuilder Xml = new StringBuilder();
3277 string Namespace = NamespaceLegalIdentity(Version);
3278
3279 Xml.Append("<getNetworkIdentity xmlns='");
3280 Xml.Append(Namespace);
3281 Xml.Append("' id='");
3282 Xml.Append(XML.Encode(Id));
3283 Xml.Append("'/>");
3284
3285 await this.Server.SendIqRequest("get", this.MainDomain.Address, Address.Domain, string.Empty, Xml.ToString(), (sender2, e2) =>
3286 {
3287 if (e2.Ok)
3288 {
3289 XmlElement E = e2.FirstElement;
3290
3291 if (!(E is null) && E.LocalName == "networkIdentity" && E.NamespaceURI == Namespace)
3292 {
3293 string Jid = XML.Attribute(E, "jid");
3294 List<ClientConnectionInformation> Connections = new List<ClientConnectionInformation>();
3295
3296 foreach (XmlNode N in E.ChildNodes)
3297 {
3298 if (N is XmlElement E2 &&
3299 E2.LocalName == "connection" &&
3300 E2.NamespaceURI == Namespace)
3301 {
3302 Connections.Add(new ClientConnectionInformation()
3303 {
3304 Endpoint = XML.Attribute(E2, "clientEp"),
3305 LastPresenceTimestamp = XML.Attribute(E2, "ts", DateTimeOffset.MinValue)
3306 });
3307 }
3308 }
3309
3310 Result.TrySetResult(new ClientInformation()
3311 {
3312 Jid = Jid,
3313 Connections = Connections.ToArray()
3314 });
3315 }
3316 else
3317 Result.TrySetException(new Exception("Unexpected response received."));
3318 }
3319 else
3320 Result.TrySetException(new Exception(string.IsNullOrEmpty(e2.ErrorText) ? "Unable to get network identity." : e2.ErrorText));
3321
3322 return Task.CompletedTask;
3323 }, null);
3324
3325 return await Result.Task;
3326 }
3327 }
3328
3329 private ClientConnectionInformation[] RemoveDisconnected(IClientConnection[] Connections)
3330 {
3331 if (Connections is null)
3332 return new ClientConnectionInformation[0];
3333
3334 List<ClientConnectionInformation> Result = new List<ClientConnectionInformation>();
3335
3336 foreach (IClientConnection Connection in Connections)
3337 {
3338 if (!Connection.CheckLive())
3339 continue;
3340
3341 if (string.IsNullOrEmpty(Connection.RemoteEndpoint))
3342 continue;
3343
3344 if (Connection.LastPresence is null)
3345 continue;
3346
3347 Result.Add(new ClientConnectionInformation()
3348 {
3349 Endpoint = Connection.RemoteEndpoint,
3350 LastPresenceTimestamp = Connection.LastPresence.Timestamp
3351 });
3352 }
3353
3354 return Result.ToArray();
3355 }
3356
3357 internal class ClientInformation
3358 {
3359 public CaseInsensitiveString Jid { get; set; }
3360 public ClientConnectionInformation[] Connections { get; set; }
3361
3362 public string MostRecentEndpoint
3363 {
3364 get
3365 {
3366 DateTimeOffset Best = DateTimeOffset.MinValue;
3367 string Endpoint = null;
3368
3369 if (!(this.Connections is null))
3370 {
3371 foreach (ClientConnectionInformation Info in this.Connections)
3372 {
3373 if (Info.LastPresenceTimestamp > Best)
3374 {
3375 Best = Info.LastPresenceTimestamp;
3376 Endpoint = Info.Endpoint;
3377 }
3378 }
3379 }
3380
3381 return Endpoint;
3382 }
3383 }
3384 }
3385
3386 internal class ClientConnectionInformation
3387 {
3388 public string Endpoint { get; set; }
3389 public DateTimeOffset LastPresenceTimestamp { get; set; }
3390 }
3391
3392 private Task GetNetworkLegalIdentityHandler(object Sender, IqEventArgs e)
3393 {
3394 return this.GetNetworkIdentityHandler(e, true);
3395 }
3396
3397 private Task GetNetworkXmppIdentityHandler(object Sender, IqEventArgs e)
3398 {
3399 return this.GetNetworkIdentityHandler(e, false);
3400 }
3401
3402 private async Task GetNetworkIdentityHandler(IqEventArgs e, bool ToLegalComponent)
3403 {
3404 try
3405 {
3406 if (!e.From.IsDomain)
3407 {
3408 await e.IqErrorForbidden(e.To, "Service only provided to federated servers.", "en");
3409 return;
3410 }
3411
3414 {
3415 await e.IqErrorBadRequest(e.To, "No ID reference provided.", "en");
3416 return;
3417 }
3418
3419 CaseInsensitiveString NetworkIdentity;
3420 IClientConnection[] Connections;
3421 XmppAddress IdAddress = new XmppAddress(Id);
3422
3423 if (this.IsComponentDomain(IdAddress.Domain, true))
3424 {
3425 if (!ToLegalComponent)
3426 {
3427 await e.IqErrorBadRequest(e.To, "Request must be made to legal component.", "en");
3428 return;
3429 }
3430
3431 if (!IdAddress.IsBareJID)
3432 {
3433 await e.IqErrorBadRequest(e.To, "Invalid Legal ID.", "en");
3434 return;
3435 }
3436
3437 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase))
3438 {
3439 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
3440 if (Identity is null)
3441 {
3442 await e.IqErrorItemNotFound(e.To, "Identity not found.", "en");
3443 return;
3444 }
3445
3446 if (IdAddress.Domain.StartsWith(this.SubdomainSuffixed, StringComparison.CurrentCultureIgnoreCase))
3447 NetworkIdentity = Identity.Account + "@" + IdAddress.Domain.Substring(this.SubdomainSuffixed.Length);
3448 else
3449 NetworkIdentity = Identity.Account + "@" + this.Server.Domain;
3450
3451 if (!this.Server.TryGetClientConnections(NetworkIdentity, out Connections))
3452 Connections = null;
3453 }
3454 }
3455 else if (this.Server.IsServerDomain(IdAddress.Domain, true))
3456 {
3457 if (ToLegalComponent)
3458 {
3459 await e.IqErrorBadRequest(e.To, "Request must be made to server.", "en");
3460 return;
3461 }
3462
3463 if (IdAddress.IsBareJID)
3464 {
3465 NetworkIdentity = IdAddress.Address;
3466
3467 if (!this.Server.TryGetClientConnections(NetworkIdentity, out Connections))
3468 Connections = null;
3469 }
3470 else if (IdAddress.IsFullJID)
3471 {
3472 NetworkIdentity = IdAddress.BareJid;
3473
3474 if (this.Server.TryGetClientConnection(IdAddress.Address, out IClientConnection Connection))
3475 Connections = new IClientConnection[] { Connection };
3476 else
3477 Connections = null;
3478 }
3479 else
3480 {
3481 await e.IqErrorBadRequest(e.To, "Invalid JID.", "en");
3482 return;
3483 }
3484 }
3485 else
3486 {
3487 await e.IqErrorForbidden(e.To, "Identity not hosted by this neuron.", "en");
3488 return;
3489 }
3490
3491 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
3492 StringBuilder Xml = new StringBuilder();
3493
3494 Xml.Append("<networkIdentity xmlns='");
3495 Xml.Append(NamespaceLegalIdentity(QueryVersion));
3496 Xml.Append("' jid='");
3497 Xml.Append(XML.Encode(NetworkIdentity));
3498 Xml.Append("'>");
3499
3500 foreach (ClientConnectionInformation ClientInfo in this.RemoveDisconnected(Connections))
3501 {
3502 Xml.Append("<connection clientEp='");
3503 Xml.Append(XML.Encode(ClientInfo.Endpoint));
3504 Xml.Append("' ts='");
3505 Xml.Append(XML.Encode(ClientInfo.LastPresenceTimestamp));
3506 Xml.Append("'/>");
3507 }
3508
3509 Xml.Append("</networkIdentity>");
3510
3511 await e.IqResult(Xml.ToString(), e.To);
3512 }
3513 catch (Exception ex)
3514 {
3515 await e.IqError(ex, e.To);
3516 }
3517 }
3518
3519 private async Task CanSignAsHandler(object Sender, IqEventArgs e)
3520 {
3521 try
3522 {
3523 CaseInsensitiveString ReferenceId = XML.Attribute(e.Query, "referenceId");
3524 if (CaseInsensitiveString.IsNullOrEmpty(ReferenceId))
3525 {
3526 await e.IqErrorBadRequest(e.To, "No Reference ID provided.", "en");
3527 return;
3528 }
3529
3530 CaseInsensitiveString SignatoryId = XML.Attribute(e.Query, "signatoryId");
3531 if (CaseInsensitiveString.IsNullOrEmpty(SignatoryId))
3532 {
3533 await e.IqErrorBadRequest(e.To, "No Signatory ID provided.", "en");
3534 return;
3535 }
3536
3537 XmppAddress ReferenceAddress = new XmppAddress(ReferenceId);
3538 XmppAddress SignatoryAddress = new XmppAddress(SignatoryId);
3539
3540 if (ReferenceAddress.Domain != SignatoryAddress.Domain)
3541 {
3542 await e.IqErrorBadRequest(e.To, "Reference and Signatory domain mismatch.", "en");
3543 return;
3544 }
3545
3546 bool IncludeDetailedErrorMessage = e.From.IsDomain;
3547
3548 if (!this.IsComponentDomain(ReferenceAddress.Domain, true))
3549 {
3550 await e.IqErrorBadRequest(e.To, IncludeDetailedErrorMessage ? "Reference identity not hosted by broker." : string.Empty, "en");
3551 return;
3552 }
3553
3554 LegalIdentity ReferenceIdentity = await GetLocalLegalIdentity(ReferenceId);
3555 if (ReferenceIdentity is null)
3556 {
3557 await e.IqErrorItemNotFound(e.To, IncludeDetailedErrorMessage ? "Reference ID not found." : string.Empty, "en");
3558 return;
3559 }
3560
3561 switch (ReferenceIdentity.State)
3562 {
3563 case IdentityState.Created:
3564 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID never approved." : string.Empty, "en");
3565 return;
3566
3567 case IdentityState.Approved:
3568 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID still approved." : string.Empty, "en");
3569 return;
3570 }
3571
3572 CaseInsensitiveString BareJid = ReferenceIdentity["JID"];
3574 {
3575 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded JID." : string.Empty, "en");
3576 return;
3577 }
3578
3579 CaseInsensitiveString PersonalNumber = ReferenceIdentity["PNR"];
3580 if (CaseInsensitiveString.IsNullOrEmpty(PersonalNumber))
3581 {
3582 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded Personal Number." : string.Empty, "en");
3583 return;
3584 }
3585
3586 CaseInsensitiveString Country = ReferenceIdentity["COUNTRY"];
3588 {
3589 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Reference ID lacks encoded Country." : string.Empty, "en");
3590 return;
3591 }
3592
3593 LegalIdentity SignatoryIdentity = await GetLocalLegalIdentity(SignatoryId);
3594 if (SignatoryIdentity is null)
3595 {
3596 await e.IqErrorItemNotFound(e.To, IncludeDetailedErrorMessage ? "Signatory ID not found." : string.Empty, "en");
3597 return;
3598 }
3599
3600 if (SignatoryIdentity.State != IdentityState.Approved)
3601 {
3602 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Signatory ID not in an approved state." : string.Empty, "en");
3603 return;
3604 }
3605
3606 if (SignatoryIdentity["JID"] != BareJid)
3607 {
3608 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "JID mismatch." : string.Empty, "en");
3609 return;
3610 }
3611
3612 if (SignatoryIdentity["PNR"] != PersonalNumber)
3613 {
3614 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Personal Number mismatch." : string.Empty, "en");
3615 return;
3616 }
3617
3618 if (SignatoryIdentity["COUNTRY"] != Country)
3619 {
3620 await e.IqErrorForbidden(e.To, IncludeDetailedErrorMessage ? "Country mismatch." : string.Empty, "en");
3621 return;
3622 }
3623
3624 await e.IqResult(string.Empty, e.To);
3625 }
3626 catch (Exception ex)
3627 {
3628 await e.IqError(ex, e.To);
3629 }
3630 }
3631
3639 internal async Task<bool> CanSignAs(CaseInsensitiveString ReferenceId, CaseInsensitiveString SignatoryId)
3640 {
3641 XmppAddress ReferenceAddress = new XmppAddress(ReferenceId);
3642 XmppAddress SignatoryAddress = new XmppAddress(SignatoryId);
3643
3644 if (ReferenceAddress.Domain != SignatoryAddress.Domain)
3645 return false;
3646
3647 if (this.IsComponentDomain(ReferenceAddress.Domain, true))
3648 {
3649 LegalIdentity ReferenceIdentity = await GetLocalLegalIdentity(ReferenceId);
3650 if (ReferenceIdentity is null)
3651 return false;
3652
3653 switch (ReferenceIdentity.State)
3654 {
3655 case IdentityState.Created:
3656 case IdentityState.Approved:
3657 return false; // Reference must no longer be valid.
3658 }
3659
3660 CaseInsensitiveString BareJid = ReferenceIdentity["JID"];
3661 CaseInsensitiveString PersonalNumber = ReferenceIdentity["PNR"];
3662 CaseInsensitiveString Country = ReferenceIdentity["COUNTRY"];
3663
3664 if (CaseInsensitiveString.IsNullOrEmpty(BareJid) ||
3665 CaseInsensitiveString.IsNullOrEmpty(PersonalNumber) ||
3667 {
3668 return false;
3669 }
3670
3671 LegalIdentity SignatoryIdentity = await GetLocalLegalIdentity(SignatoryId);
3672 if (SignatoryIdentity is null)
3673 return false;
3674
3675 if (SignatoryIdentity.State != IdentityState.Approved)
3676 return false;
3677
3678 if (SignatoryIdentity["JID"] != BareJid ||
3679 SignatoryIdentity["PNR"] != PersonalNumber ||
3680 SignatoryIdentity["COUNTRY"] != Country)
3681 {
3682 return false;
3683 }
3684
3685 return true;
3686 }
3687 else
3688 {
3689 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
3690 StringBuilder Xml = new StringBuilder();
3691 string Namespace = NamespaceLegalIdentity(NamespaceSet.Current);
3692
3693 Xml.Append("<canSignAs xmlns='");
3694 Xml.Append(Namespace);
3695 Xml.Append("' referenceId='");
3696 Xml.Append(XML.Encode(ReferenceId));
3697 Xml.Append("' signatoryId='");
3698 Xml.Append(XML.Encode(SignatoryId));
3699 Xml.Append("'/>");
3700
3701 await this.Server.SendIqRequest("get", this.MainDomain.Address, ReferenceAddress.Domain, string.Empty, Xml.ToString(), (sender2, e2) =>
3702 {
3703 Result.TrySetResult(e2.Ok);
3704 return Task.CompletedTask;
3705 }, null);
3706
3707 return await Result.Task;
3708 }
3709 }
3710
3711 private async Task AuthorizeAccessToIdHandler(object Sender, IqEventArgs e)
3712 {
3713 try
3714 {
3715 if (!this.Server.IsServerDomain(e.From.Domain, true))
3716 {
3717 await e.IqErrorForbidden(e.To, "Service only available to local accounts.", "en");
3718 return;
3719 }
3720
3722
3724 {
3725 await e.IqErrorBadRequest(e.To, "No Legal ID specified.", "en");
3726 return;
3727 }
3728
3729 XmppAddress IdAddress = new XmppAddress(Id);
3730
3731 if (!IdAddress.IsBareJID)
3732 {
3733 await e.IqErrorBadRequest(e.To, "Invalid Legal ID.", "en");
3734 return;
3735 }
3736
3737 if (!this.IsComponentDomain(IdAddress.Domain, true))
3738 {
3739 await e.IqErrorBadRequest(e.To, "Not a local Legal ID.", "en");
3740 return;
3741 }
3742
3743 CaseInsensitiveString RemoteId = XML.Attribute(e.Query, "remoteId");
3744 bool Authorized = XML.Attribute(e.Query, "auth", true);
3745
3747 {
3748 await e.IqErrorBadRequest(e.To, "No Remote ID specified.", "en");
3749 return;
3750 }
3751
3752 XmppAddress RemoteAddress = new XmppAddress(RemoteId);
3753 if (!RemoteAddress.IsBareJID)
3754 {
3755 await e.IqErrorBadRequest(e.To, "Invalid Remote ID.", "en");
3756 return;
3757 }
3758
3759 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + Id.LowerCase))
3760 {
3761 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
3762 if (Identity is null)
3763 {
3764 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
3765 return;
3766 }
3767
3768 if (e.From.Account != Identity.Account)
3769 {
3770 await e.IqErrorForbidden(e.To, "Not your identity.", "en");
3771 return;
3772 }
3773
3774 ClientInformation ClientInfo = await this.GetNetworkIdentity(RemoteId, true, true, Identity.Version);
3775 CaseInsensitiveString RemoteJid = ClientInfo.Jid;
3776 this.IdentityAuthorization(RemoteJid, e.From.BareJid, Id, Authorized);
3777
3778 await e.IqResult(string.Empty, e.To);
3779 }
3780 }
3781 catch (Exception ex)
3782 {
3783 await e.IqError(ex, e.To);
3784 }
3785 }
3786
3787 private async Task ReadyForApprovalHandler(object Sender, IqEventArgs e)
3788 {
3789 try
3790 {
3792
3793 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotid:" + Id.LowerCase))
3794 {
3795 LegalIdentity Identity = await GetLocalLegalIdentity(Id);
3796 if (Identity is null)
3797 {
3798 await e.IqErrorItemNotFound(e.To, "Legal identity not found.", "en");
3799 return;
3800 }
3801
3802 if (e.From.Account != Identity.Account || !this.Server.IsServerDomain(e.From.Domain, true))
3803 {
3804 await e.IqErrorForbidden(e.To, "You can only mark your own identity applications for approval.", "en");
3805 return;
3806 }
3807
3808 IAccount Account = await XmppServerModule.GetAccountAsync(Identity.Account);
3809 if (Account is null)
3810 {
3811 await e.IqErrorForbidden(e.To, "Account has been removed.", "en");
3812 return;
3813 }
3814
3815 if (!Account.Enabled)
3816 {
3817 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
3818 return;
3819 }
3820
3821 await e.IqResult(string.Empty, e.To);
3822 this.CheckAuthenticityOfIdentity(Identity, Identity.GetTags(), e.To, e.From, Account as Account);
3823 }
3824 }
3825 catch (Exception ex)
3826 {
3827 await e.IqError(ex, e.To);
3828 }
3829 }
3830
3831 internal async void CheckAuthenticityOfIdentity(LegalIdentity Identity, KeyValuePair<string, object>[] Tags,
3832 XmppAddress ComponentAddress, XmppAddress ClientJid, Account Account)
3833 {
3834 try
3835 {
3836 int NrPhotos = 0;
3837
3838 if (!(Identity.Attachments is null))
3839 {
3840 foreach (AttachmentReference Ref in Identity.Attachments)
3841 {
3842 if (Ref.ContentType.StartsWith("image/"))
3843 NrPhotos++;
3844 }
3845 }
3846
3847 IIdentityAuthenticatorService Authenticator = Types.FindBest<IIdentityAuthenticatorService, IIdentityApplication>(new IdentityApplication()
3848 {
3849 Claims = Tags,
3850 NrPhotos = NrPhotos
3851 });
3852
3853 if (Authenticator is null)
3854 return;
3855
3856 List<IPhoto> Photos = new List<IPhoto>();
3857
3858 if (!(Identity.Attachments is null))
3859 {
3860 foreach (AttachmentReference Ref in Identity.Attachments)
3861 {
3862 if (Ref.ContentType.StartsWith("image/"))
3863 {
3864 Attachment Attachment = await Database.FindFirstIgnoreRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
3865 if (Attachment is null || Attachment.Size > int.MaxValue)
3866 continue;
3867
3868 using (FileStream AttachmentFile = File.OpenRead(Attachment.LocalFileName))
3869 {
3870 Aes Aes = Aes.Create();
3871
3872 Aes.BlockSize = 128;
3873 Aes.KeySize = 256;
3874 Aes.Mode = CipherMode.CBC;
3875 Aes.Padding = PaddingMode.Zeros;
3876
3877 byte[] Key = new byte[32];
3878 byte[] IV = new byte[16];
3879
3880 Array.Copy(Attachment.Salt, 0, Key, 0, 32);
3881 Array.Copy(Attachment.Salt, 32, IV, 0, 16);
3882
3883 using (ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV))
3884 {
3885 using (CryptoStream DecryptedAttachmentFile = new CryptoStream(AttachmentFile, Decryptor, CryptoStreamMode.Read))
3886 {
3887 int c = (int)Attachment.Size;
3888 byte[] Bin = new byte[c];
3889
3890 await DecryptedAttachmentFile.ReadAllAsync(Bin, 0, c);
3891
3892 Photos.Add(new Photo()
3893 {
3895 Binary = Bin
3896 });
3897 }
3898 }
3899 }
3900 }
3901 }
3902 }
3903
3904 IAuthenticationResult Result = await Authenticator.IsValid(Tags, Photos);
3905
3906 if (!string.IsNullOrEmpty(Result.ErrorMessage))
3907 {
3908 if (Result.ErrorType == global::Paiwise.ErrorType.Server || Result.ErrorType == global::Paiwise.ErrorType.Service)
3909 Log.Error(Result.ErrorMessage, string.Empty, Authenticator.GetType().Namespace, Result.ErrorCode, Result.Tags);
3910 else
3911 Log.Warning(Result.ErrorMessage, string.Empty, Authenticator.GetType().Namespace, Result.ErrorCode, Result.Tags);
3912
3913 StringBuilder Xml = new StringBuilder();
3914
3915 Xml.Append("<body>");
3916 Xml.Append(XML.Encode(Result.ErrorMessage));
3917 Xml.Append("</body>");
3918 Xml.Append("<clientMessage xmlns='");
3919 Xml.Append(NamespaceLegalIdentity(Identity.Version));
3920 Xml.Append("' code='");
3921 Xml.Append(XML.Encode(Result.ErrorCode));
3922 Xml.Append("' type='");
3923 Xml.Append(Result.ErrorType.ToString());
3924 Xml.Append("'/>");
3925
3926 await this.Server.SendMessage(string.Empty, string.Empty, ComponentAddress, ClientJid.ToBareJID(), Result.ErrorLanguage, Xml.ToString());
3927 }
3928
3929 if (!Result.Result.HasValue)
3930 return;
3931
3932 if (Result.Result.Value)
3933 {
3934 await XmppServerModule.Legal.UpdateState(Identity, IdentityState.Approved, Authenticator.GetType().FullName, Account);
3935
3936 Log.Notice("Legal identity application has been automatically approved.",
3937 Identity.Id.Value, Authenticator.GetType().FullName, Tags);
3938
3939 StringBuilder Markdown = new StringBuilder();
3940
3941 Markdown.Append("Legal identity application has been automatically approved: `");
3942 Markdown.Append(Identity.Id);
3943 Markdown.Append("` (by `"); // TODO: Individual page for reviewing identities.
3944 Markdown.Append(Authenticator.GetType().FullName);
3945 Markdown.AppendLine("`)");
3946 Markdown.AppendLine();
3947 Output(Markdown, Tags);
3948
3949 await Gateway.SendNotification(Markdown.ToString());
3950 }
3951 else
3952 {
3953 await XmppServerModule.Legal.UpdateState(Identity, IdentityState.Rejected, Authenticator.GetType().FullName, Account);
3954
3955 Log.Warning("Legal identity application has been automatically rejected.",
3956 Identity.Id.Value, Authenticator.GetType().FullName, Tags);
3957
3958 StringBuilder Markdown = new StringBuilder();
3959
3960 Markdown.Append("Legal identity application has been automatically rejected: `");
3961 Markdown.Append(Identity.Id);
3962 Markdown.AppendLine("`"); // TODO: Individual page for reviewing identities.
3963 Markdown.AppendLine();
3964 Output(Markdown, Tags);
3965
3966 await Gateway.SendNotification(Markdown.ToString());
3967 }
3968 }
3969 catch (Exception ex)
3970 {
3971 Log.Exception(ex);
3972 }
3973 }
3974
3981 public static Task<byte[]> LoadEncryptedFile(string FileName, byte[] Salt)
3982 {
3983 return LoadEncryptedFile(FileName, Salt, null);
3984 }
3985
3993 public static async Task<byte[]> LoadEncryptedFile(string FileName, byte[] Salt, long? Size)
3994 {
3995 using (FileStream File = System.IO.File.OpenRead(FileName))
3996 {
3997 Aes Aes = Aes.Create();
3998
3999 Aes.BlockSize = 128;
4000 Aes.KeySize = 256;
4001 Aes.Mode = CipherMode.CBC;
4002 Aes.Padding = PaddingMode.Zeros;
4003
4004 byte[] Key = new byte[32];
4005 byte[] IV = new byte[16];
4006
4007 Array.Copy(Salt, 0, Key, 0, 32);
4008 Array.Copy(Salt, 32, IV, 0, 16);
4009
4010 using (ICryptoTransform Decryptor = Aes.CreateDecryptor(Key, IV))
4011 {
4012 using (CryptoStream DecryptedFile = new CryptoStream(File, Decryptor, CryptoStreamMode.Read))
4013 {
4014 if (Size.HasValue)
4015 {
4016 long c = Size.Value;
4017 if (c > int.MaxValue)
4018 throw new IOException("File too large.");
4019
4020 int c0 = (int)c;
4021 byte[] Bin = new byte[c0];
4022
4023 await DecryptedFile.ReadAllAsync(Bin, 0, c0);
4024
4025 return Bin;
4026 }
4027 else
4028 {
4029 using (MemoryStream ms = new MemoryStream())
4030 {
4031 await DecryptedFile.CopyToAsync(ms);
4032 return ms.ToArray();
4033 }
4034 }
4035 }
4036 }
4037 }
4038 }
4039
4040 private class IdentityApplication : IIdentityApplication
4041 {
4042 public KeyValuePair<string, object>[] Claims { get; set; }
4043 public int NrPhotos { get; set; }
4044 }
4045
4046 private class Photo : IPhoto
4047 {
4048 public string ContentType { get; set; }
4049 public byte[] Binary { get; set; }
4050 }
4051
4052 #endregion
4053
4054 #region Legal Identity references
4055
4056 internal static async Task CheckLegalIdentityReferences()
4057 {
4058 string[] CollectionNames = await Database.GetCollections();
4059
4060 if (Array.IndexOf(CollectionNames, "LegalIdentityReferences") < 0)
4061 {
4062 foreach (LegalIdentity Identity in await Database.Find<LegalIdentity>(
4063 new FilterFieldEqualTo("State", IdentityState.Approved)))
4064 {
4065 await UpdateLegalIdentityReference(Identity);
4066 }
4067 }
4068 }
4069
4070 internal static async Task AddLegalIdentityReference(LegalIdentity Identity)
4071 {
4073 {
4074 Country = Identity["COUNTRY"],
4075 PNr = Identity["PNR"],
4076 LegalId = Identity.Id,
4077 Provider = Identity.Provider,
4078 ArchiveDays = CalcRefArchiveDays(Identity)
4079 });
4080 }
4081
4088 public static async Task<LegalIdentityReference> FindLegalIdentityReference(string LegalId)
4089 {
4090 return await Database.FindFirstDeleteRest<LegalIdentityReference>(
4091 new FilterFieldEqualTo("LegalId", LegalId));
4092 }
4093
4103 public static async Task<IEnumerable<LegalIdentityReference>> FindLegalIdentityReferences(string Country, string PNr)
4104 {
4106 new FilterFieldEqualTo("Country", Country),
4107 new FilterFieldEqualTo("PNr", PNr)));
4108 }
4109
4110 private static async Task UpdateLegalIdentityReference(LegalIdentity Identity)
4111 {
4112 LegalIdentityReference Ref = await FindLegalIdentityReference(Identity.Id);
4113
4114 if (Ref is null)
4115 await AddLegalIdentityReference(Identity);
4116 else
4117 {
4118 string s = Identity["COUNTRY"];
4119 bool Updated = false;
4120
4121 if (Ref.Country != s)
4122 {
4123 Ref.Country = s;
4124 Updated = true;
4125 }
4126
4127 s = Identity["PNR"];
4128 if (Ref.PNr != s)
4129 {
4130 Ref.PNr = s;
4131 Updated = true;
4132 }
4133
4134 if (Ref.Provider != Identity.Provider)
4135 {
4136 Ref.Provider = Identity.Provider;
4137 Updated = true;
4138 }
4139
4140 if (Updated)
4141 await Database.Update(Ref);
4142 }
4143 }
4144
4145 private static Task DeleteLegalIdentityReference(string LegalId)
4146 {
4147 return Database.FindDelete<LegalIdentityReference>(new FilterFieldEqualTo("LegalId", LegalId));
4148 }
4149
4150 private static int CalcRefArchiveDays(LegalIdentity Identity)
4151 {
4152 DateTime Expires = Identity.To;
4153 if (Expires == DateTime.MaxValue)
4154 return int.MaxValue;
4155
4156 TimeSpan Span = Expires - DateTime.Now;
4157 double Days = Math.Ceiling(Span.TotalDays);
4158
4159 if (Days < 0)
4160 return 0;
4161 else if (Days > int.MaxValue)
4162 return int.MaxValue;
4163 else
4164 return (int)Days;
4165 }
4166
4167 #endregion
4168
4169 #region Smart Contracts
4170
4171 internal async Task<Contract> GetContract(CaseInsensitiveString ContractId)
4172 {
4173 TaskCompletionSource<IqResultEventArgs> T = new TaskCompletionSource<IqResultEventArgs>();
4174 XmppAddress ContractAddress = new XmppAddress(ContractId);
4175
4176 if (this.Server.IsServerDomain(ContractAddress.Domain, true) || this.IsComponentDomain(ContractAddress.Domain, true))
4177 return await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
4178 else
4179 {
4180 if (!await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(ContractAddress.Domain), string.Empty,
4181 "<getContract xmlns='" + NamespaceSmartContracts(NamespaceSet.Current) + "' id='" + XML.Encode(ContractId) + "'/>",
4182 (sender2, e2) =>
4183 {
4184 T.TrySetResult(e2);
4185 return Task.CompletedTask;
4186 }, null))
4187 {
4188 return null;
4189 }
4190
4191 await T.Task;
4192
4193 IqResultEventArgs e3 = T.Task.Result;
4194 if (!e3.Ok || e3.FirstElement is null)
4195 return null;
4196
4197 ParsedContract Parsed = await Contract.Parse(e3.FirstElement, this);
4198 return Parsed?.Contract;
4199 }
4200 }
4201
4202 private async Task PetitionContractHandler(object Sender, IqEventArgs e)
4203 {
4204 try
4205 {
4206 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
4207 string PetitionId = XML.Attribute(e.Query, "pid");
4208 string Purpose = XML.Attribute(e.Query, "purpose");
4209 string Nonce = XML.Attribute(e.Query, "nonce");
4210 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
4211 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + ContractId + ":" + Purpose + ":" + Nonce + ":" + e.From.BareJid.LowerCase);
4212 XmlElement ContextXml = null;
4213
4214 foreach (XmlNode N in e.Query)
4215 {
4216 if (!(N is XmlElement E))
4217 continue;
4218
4219 if (ContextXml is null)
4220 ContextXml = E;
4221 else
4222 {
4223 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
4224 return;
4225 }
4226 }
4227
4228 (LegalIdentity ReqIdentity, Dictionary<string, string> ReqAttachmentUrls) = await this.ValidateSenderSignature(
4229 e.From, new ExternalRequest(e), DateTime.Now, Data, Signature, null); // TODO: Authorize access to requestor attachments
4230
4231 if (ReqIdentity is null)
4232 return;
4233
4234 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
4235 if (Contract is null)
4236 {
4237 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
4238 return;
4239 }
4240
4241 StringBuilder Msg = new StringBuilder();
4242
4243 Msg.Append("<petitionContractMsg id=\"");
4244 Msg.Append(XML.Encode(ContractId));
4245 Msg.Append("\" pid=\"");
4246 Msg.Append(XML.Encode(PetitionId));
4247 Msg.Append("\" from=\"");
4248 Msg.Append(XML.Encode(e.From.Address.Value));
4249 Msg.Append("\" purpose=\"");
4250 Msg.Append(XML.Encode(Purpose));
4251
4252 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
4253 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
4254 {
4255 Msg.Append("\" clientEp=\"");
4256 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
4257 }
4258 else
4259 {
4260 ClientInformation ClientInfo = await this.GetNetworkIdentity(ReqIdentity.Id, true, false, Contract.Version);
4261 if (!(ClientInfo is null))
4262 {
4263 string ClientEndpoint = ClientInfo.MostRecentEndpoint;
4264
4265 if (!string.IsNullOrEmpty(ClientEndpoint))
4266 {
4267 Msg.Append("\" clientEp=\"");
4268 Msg.Append(XML.Encode(ClientEndpoint));
4269 }
4270 }
4271 }
4272
4273 Msg.Append("\" xmlns=\"");
4274 Msg.Append(NamespaceSmartContracts(Contract.Version));
4275 Msg.Append("\">");
4276 ReqIdentity.Serialize(Msg, true, true, true, true, true, true, true, ReqAttachmentUrls, this);
4277
4278 if (!(ContextXml is null))
4279 Msg.Append(ContextXml.OuterXml);
4280
4281 Msg.Append("</petitionContractMsg>");
4282
4283 string Xml = Msg.ToString();
4284
4285 if (!(Contract.ClientSignatures is null))
4286 {
4287 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
4288 {
4289 await this.Server.SendMessage(string.Empty, string.Empty, e.To, new XmppAddress(ClientSignature.BareJid),
4290 string.Empty, Xml);
4291 }
4292 }
4293
4294 await e.IqResult(string.Empty, e.To);
4295 }
4296 catch (Exception ex)
4297 {
4298 await e.IqError(ex, e.To);
4299 }
4300 }
4301
4302 private async Task PetitionContractResponseHandler(object Sender, IqEventArgs e)
4303 {
4304 try
4305 {
4306 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
4307 string PetitionId = XML.Attribute(e.Query, "pid");
4308 XmppAddress RequestorFullJid = new XmppAddress(XML.Attribute(e.Query, "jid"));
4309 bool Response = XML.Attribute(e.Query, "response", false);
4311 XmlElement ContextXml = null;
4312
4313 foreach (XmlNode N in e.Query)
4314 {
4315 if (!(N is XmlElement E))
4316 continue;
4317
4318 if (ContextXml is null)
4319 ContextXml = E;
4320 else
4321 {
4322 await e.IqErrorBadRequest(e.To, "Invalid context.", "en");
4323 return;
4324 }
4325 }
4326
4327 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
4328 if (Contract is null)
4329 {
4330 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
4331 return;
4332 }
4333
4334 this.ContractAuthorization(RequestorFullJid.BareJid, e.From.BareJid, ContractId, Response);
4335
4336 StringBuilder Msg = new StringBuilder();
4337
4338 Msg.Append("<petitionContractResponseMsg pid=\"");
4339 Msg.Append(XML.Encode(PetitionId));
4340 Msg.Append("\" response=\"");
4341 Msg.Append(CommonTypes.Encode(Response));
4342
4343 if (this.Server.TryGetClientConnection(e.From.Address, out IClientConnection Connection) &&
4344 !string.IsNullOrEmpty(Connection.RemoteEndpoint))
4345 {
4346 Msg.Append("\" clientEp=\"");
4347 Msg.Append(XML.Encode(Connection.RemoteEndpoint));
4348 }
4349
4350 Msg.Append("\" xmlns=\"");
4351 Msg.Append(NamespaceSmartContracts(Contract.Version));
4352 Msg.Append("\">");
4353
4354 Contract?.Serialize(Msg, false, true, true, true, true, true, true, null, this);
4355
4356 if (!(ContextXml is null))
4357 Msg.Append(ContextXml.OuterXml);
4358
4359 Msg.Append("</petitionContractResponseMsg>");
4360
4361 await this.Server.SendMessage(string.Empty, string.Empty, e.To, RequestorFullJid, string.Empty, Msg.ToString());
4362
4363 await e.IqResult(string.Empty, e.To);
4364 }
4365 catch (Exception ex)
4366 {
4367 await e.IqError(ex, e.To);
4368 }
4369 }
4370
4371 internal void ContractAuthorization(CaseInsensitiveString ToBareJid, CaseInsensitiveString FromBareJid,
4372 CaseInsensitiveString ContractId, bool Authorized)
4373 {
4374 this.Authorization("C:", ToBareJid, FromBareJid, ContractId, Authorized ? 1 : 0);
4375 }
4376
4377 internal bool IsAccessToContractAuthorized(CaseInsensitiveString BareJid, CaseInsensitiveString ContractId)
4378 {
4379 CaseInsensitiveString Key = "C:" + ContractId + ":" + BareJid;
4380 return this.petitions.TryGetValue(Key, out int i) && (i > 0);
4381 }
4382
4383 private async Task CreateContractHandler(object Sender, IqEventArgs e)
4384 {
4385 try
4386 {
4387 if (!this.Server.IsServerDomain(e.From.Domain, true))
4388 {
4389 await e.IqErrorForbidden(e.To, "Only accounts on the broker can create new contracts.", "en");
4390 return;
4391 }
4392
4393 LinkedList<ClientSignature> SignaturesToTransfer = null;
4394 XmlElement E = null;
4395 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
4396 Contract Contract = null;
4397 Dictionary<CaseInsensitiveString, Parameter> Parameters = new Dictionary<CaseInsensitiveString, Parameter>();
4398 LinkedList<CaseInsensitiveString> ParameterOrder = new LinkedList<CaseInsensitiveString>();
4399 Dictionary<CaseInsensitiveString, Parameter> TransientParameters = null;
4400 bool ParametersChecked = false;
4401 bool HasTransientParameters = false;
4402
4403 foreach (XmlNode N in e.Query.ChildNodes)
4404 {
4405 E = N as XmlElement;
4406 if (E is null)
4407 continue;
4408
4409 switch (E.LocalName ?? string.Empty)
4410 {
4411 case "contract":
4412 ParsedContract Parsed = await Contract.Parse(E, this);
4413 Contract = Parsed?.Contract;
4414
4415 if (Contract is null)
4416 {
4417 await e.IqErrorBadRequest(e.To, "Invalid contract.", "en");
4418 return;
4419 }
4420
4421 bool HasStatus = Parsed.HasStatus;
4422 bool ParametersValid = Parsed.ParametersValid;
4423
4424 if (HasStatus)
4425 {
4426 await e.IqErrorBadRequest(e.To, "Status element not permitted when creating new contract.", "en");
4427 return;
4428 }
4429
4431 {
4432 await e.IqErrorBadRequest(e.To, "id attribute must not be set by client.", "en");
4433 return;
4434 }
4435
4436 if (!(Contract.ClientSignatures is null) && Contract.ClientSignatures.Length > 0)
4437 {
4438 await e.IqErrorBadRequest(e.To, "Predefined signatures not permitted.", "en");
4439 return;
4440 }
4441
4442 if (!(Contract.ServerSignature is null))
4443 {
4444 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
4445 return;
4446 }
4447
4448 if (Contract.PartsMode == ContractParts.ExplicitlyDefined && (Contract.Parts is null || Contract.Parts.Length == 0))
4449 {
4450 await e.IqErrorBadRequest(e.To, "No explicit parts defined.", "en");
4451 return;
4452 }
4453
4454 if (!ParametersValid && Contract.PartsMode != ContractParts.TemplateOnly)
4455 {
4456 await e.IqErrorBadRequest(e.To, "Parameter " + Parsed.FirstParameterErrorName +
4457 " has invalid value: " + Parsed.FirstParameterError, "en");
4458 return;
4459 }
4460
4461 ParametersChecked = true;
4462 Contract.State = ContractState.Proposed;
4463 break;
4464
4465 case "template":
4466 CaseInsensitiveString TemplateId = XML.Attribute(E, "id");
4467
4468 if (CaseInsensitiveString.IsNullOrEmpty(TemplateId) || TemplateId.IndexOf('@') < 0)
4469 {
4470 await e.IqErrorBadRequest(e.To, "Invalid contract template identity.", "en");
4471 return;
4472 }
4473
4474 Contract = await this.GetContract(TemplateId);
4475 if (Contract is null)
4476 {
4477 await e.IqErrorItemNotFound(e.To, "Contract template not found: " + TemplateId, "en");
4478 return;
4479 }
4480
4481 if (!Contract.CanActAsTemplate)
4482 {
4483 await e.IqErrorNotAcceptable(e.To, "Referenced contract cannot be used as a template.", "en");
4484 return;
4485 }
4486
4487 if (Contract.State == ContractState.Obsoleted ||
4488 Contract.State == ContractState.Deleted ||
4489 Contract.State == ContractState.Proposed ||
4490 Contract.State == ContractState.Rejected ||
4491 Contract.State == ContractState.Failed)
4492 {
4493 await e.IqErrorNotAcceptable(e.To, "Referenced contract not in an acceptable state.", "en");
4494 return;
4495 }
4496
4497 if (!await Contract.CanRead(e.From, this.Server, this))
4498 {
4499 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
4500 return;
4501 }
4502
4503 if (!(Contract.ClientSignatures is null))
4504 {
4505 foreach (ClientSignature Signature in Contract.ClientSignatures)
4506 {
4507 if (Signature.Transferable)
4508 {
4509 if (SignaturesToTransfer is null)
4510 SignaturesToTransfer = new LinkedList<ClientSignature>();
4511
4512 SignaturesToTransfer.AddLast(Signature);
4513 }
4514 }
4515 }
4516
4517 Contract = new Contract()
4518 {
4519 Version = QueryVersion,
4520 ForMachines = Contract.ForMachines,
4521 ForMachinesLocalName = Contract.ForMachinesLocalName,
4522 ForMachinesNamespace = Contract.ForMachinesNamespace,
4523 ForHumans = (HumanReadableText[])Contract.ForHumans?.Clone(),
4524 Roles = (Role[])Contract.Roles?.Clone(),
4525 Parameters = (Parameter[])Contract.Parameters?.Clone(),
4526 State = ContractState.Approved,
4527 TemplateId = CaseInsensitiveString.IsNullOrEmpty(Contract.TemplateId) ? TemplateId : Contract.TemplateId,
4528 SignAfter = Contract.SignAfter,
4529 SignBefore = Contract.SignBefore,
4530 Nonce = null
4531 };
4532
4533 bool HasVisibility = false;
4534 bool HasDuration = false;
4535 bool HasArchiveReq = false;
4536 bool HasArchiveOpt = false;
4537 bool WellDefined = true;
4538
4539 foreach (XmlAttribute Attr in E.Attributes)
4540 {
4541 switch (Attr.Name)
4542 {
4543 case "visibility":
4544 if (Enum.TryParse<ContractVisibility>(Attr.Value, out ContractVisibility Visibility))
4545 {
4546 Contract.Visibility = Visibility;
4547 HasVisibility = true;
4548 }
4549 else
4550 WellDefined = false;
4551 break;
4552
4553 case "duration":
4554 if (Duration.TryParse(Attr.Value, out Duration D))
4555 {
4556 Contract.Duration = D;
4557 HasDuration = true;
4558 }
4559 else
4560 WellDefined = false;
4561 break;
4562
4563 case "archiveReq":
4564 if (Duration.TryParse(Attr.Value, out D))
4565 {
4566 Contract.ArchiveRequired = D;
4567 HasArchiveReq = true;
4568 }
4569 else
4570 WellDefined = false;
4571 break;
4572
4573 case "archiveOpt":
4574 if (Duration.TryParse(Attr.Value, out D))
4575 {
4576 Contract.ArchiveOptional = D;
4577 HasArchiveOpt = true;
4578 }
4579 else
4580 WellDefined = false;
4581 break;
4582
4583 case "signAfter":
4584 if (XML.TryParse(Attr.Value, out DateTime TP))
4585 Contract.SignAfter = TP;
4586 else
4587 WellDefined = false;
4588 break;
4589
4590 case "signBefore":
4591 if (XML.TryParse(Attr.Value, out TP))
4592 Contract.SignBefore = TP;
4593 else
4594 WellDefined = false;
4595 break;
4596
4597 case "canActAsTemplate":
4598 if (CommonTypes.TryParse(Attr.Value, out bool b))
4599 {
4600 Contract.CanActAsTemplate = b;
4601 HasArchiveOpt = true;
4602 }
4603 else
4604 WellDefined = false;
4605 break;
4606
4607 case "nonce":
4608 try
4609 {
4610 Contract.Nonce = Convert.FromBase64String(Attr.Value);
4611 }
4612 catch (Exception)
4613 {
4614 await e.IqErrorBadRequest(e.To, "Invalid nonce value.", "en");
4615 return;
4616 }
4617 break;
4618
4619 case "xmlns":
4620 case "id":
4621 break;
4622
4623 default:
4624 if (Attr.Prefix != "xmlns")
4625 WellDefined = false;
4626 break;
4627 }
4628 }
4629
4630 if (!(WellDefined && HasVisibility && HasDuration && HasArchiveReq && HasArchiveOpt))
4631 {
4632 await e.IqErrorBadRequest(e.To, "Invalid request, missing attributes.", "en");
4633 return;
4634 }
4635
4636 if (Contract.SignBefore <= Contract.SignAfter)
4637 {
4638 await e.IqErrorBadRequest(e.To, "Signature timepoints invalid.", "en");
4639 return;
4640 }
4641
4642 foreach (XmlNode N2 in E.ChildNodes)
4643 {
4644 if (N2 is XmlElement E2)
4645 {
4646 switch (E2.LocalName)
4647 {
4648 case "parts":
4649 List<Part> Parts = null;
4650 ContractParts? Mode = null;
4651
4652 foreach (XmlNode N3 in E2.ChildNodes)
4653 {
4654 if (N3 is XmlElement E3)
4655 {
4656 switch (E3.LocalName)
4657 {
4658 case "open":
4659 if (Mode.HasValue)
4660 {
4661 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
4662 return;
4663 }
4664
4665 Mode = ContractParts.Open;
4666 break;
4667
4668 case "templateOnly":
4669 if (Mode.HasValue)
4670 {
4671 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
4672 return;
4673 }
4674
4675 Mode = ContractParts.TemplateOnly;
4676 break;
4677
4678 case "part":
4679 if (Mode.HasValue)
4680 {
4681 if (Mode.Value != ContractParts.ExplicitlyDefined)
4682 {
4683 await e.IqErrorBadRequest(e.To, "Part mode inconsistency.", "en");
4684 return;
4685 }
4686 }
4687 else
4688 Mode = ContractParts.ExplicitlyDefined;
4689
4690 CaseInsensitiveString LegalId = null;
4691 CaseInsensitiveString RoleRef = null;
4692
4693 foreach (XmlAttribute Attr in E3.Attributes)
4694 {
4695 switch (Attr.Name)
4696 {
4697 case "legalId":
4698 LegalId = Attr.Value;
4699 break;
4700
4701 case "role":
4702 RoleRef = Attr.Value;
4703 break;
4704
4705 case "xmlns":
4706 break;
4707
4708 default:
4709 if (Attr.Prefix != "xmlns")
4710 WellDefined = false;
4711 break;
4712 }
4713 }
4714
4716 {
4717 await e.IqErrorBadRequest(e.To, "Invalid part definition.", "en");
4718 return;
4719 }
4720
4721 bool RoleFound = false;
4722
4723 if (!(Contract.Roles is null))
4724 {
4725 foreach (Role Role2 in Contract.Roles)
4726 {
4727 if (Role2.Name == RoleRef)
4728 {
4729 RoleFound = true;
4730 break;
4731 }
4732 }
4733 }
4734
4735 if (!RoleFound)
4736 {
4737 await e.IqErrorBadRequest(e.To, "Undefined role.", "en");
4738 return;
4739 }
4740
4741 if (Parts is null)
4742 Parts = new List<Part>();
4743
4744 Parts.Add(new Part()
4745 {
4746 LegalId = LegalId,
4747 Role = RoleRef
4748 });
4749
4750 break;
4751
4752 default:
4753 WellDefined = false;
4754 break;
4755 }
4756 }
4757 }
4758
4759 if (!WellDefined || !Mode.HasValue)
4760 {
4761 await e.IqErrorBadRequest(e.To, "Parts not well-defined.", "en");
4762 return;
4763 }
4764
4765 Contract.PartsMode = Mode.Value;
4766 Contract.Parts = Parts?.ToArray();
4767 break;
4768
4769 case "parameters":
4770 if (Contract.Parameters is null)
4771 {
4772 await e.IqErrorBadRequest(e.To, "Referenced contract does not have parameters.", "en");
4773 return;
4774 }
4775
4776 foreach (Parameter P3 in Contract.Parameters)
4777 {
4778 if (!Parameters.ContainsKey(P3.Name))
4779 {
4780 Parameters[P3.Name] = P3;
4781 ParameterOrder.AddLast(P3.Name);
4782 }
4783 }
4784
4785 foreach (XmlNode N3 in E2.ChildNodes)
4786 {
4787 if (N3 is XmlElement E3)
4788 {
4789 CaseInsensitiveString Name = XML.Attribute(E3, "name");
4791 {
4792 await e.IqErrorBadRequest(e.To, "Missing parameter name.", "en");
4793 return;
4794 }
4795
4796 if (!Parameters.TryGetValue(Name, out Parameter OrgParameter))
4797 {
4798 await e.IqErrorBadRequest(e.To, "Parameter does not exist in original contract: " + Name, "en");
4799 return;
4800 }
4801
4802 Parameter ParameterDefinition = await this.TryParseParameter(E3, e, Name, OrgParameter);
4803 if (ParameterDefinition is null)
4804 return;
4805
4806 Parameters[Name] = ParameterDefinition;
4807 }
4808 }
4809
4811 {
4812 ParametersChecked = true;
4813
4814 KeyValuePair<Parameter, string> P2 = await Contract.CheckParameters(Parameters.Values, Contract.Duration, this, true);
4815 Parameter FailingParameter = P2.Key;
4816 if (!(FailingParameter is null))
4817 {
4818 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter.Name + " contains invalid value: " + P2.Value, "en");
4819 return;
4820 }
4821 }
4822
4823 int c = Parameters.Count;
4824 int i = 0;
4825 Parameter[] NewParameters = new Parameter[c];
4826
4827 foreach (string Name in ParameterOrder)
4828 NewParameters[i++] = Parameters[Name];
4829
4830 Contract.Parameters = NewParameters;
4831 break;
4832
4833 default:
4834 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
4835 return;
4836 }
4837 }
4838 }
4839 break;
4840
4841 case "transient":
4842 if (Contract.Parameters is null)
4843 {
4844 await e.IqErrorBadRequest(e.To, "Referenced contract does not have parameters.", "en");
4845 return;
4846 }
4847
4848 if (ParameterOrder.First is null)
4849 {
4850 foreach (Parameter P3 in Contract.Parameters)
4851 {
4852 if (!Parameters.ContainsKey(P3.Name))
4853 {
4854 Parameters[P3.Name] = P3;
4855 ParameterOrder.AddLast(P3.Name);
4856 }
4857 }
4858 }
4859
4860 foreach (XmlNode N3 in E.ChildNodes)
4861 {
4862 if (N3 is XmlElement E3)
4863 {
4864 CaseInsensitiveString Name = XML.Attribute(E3, "name");
4866 {
4867 await e.IqErrorBadRequest(e.To, "Missing parameter name.", "en");
4868 return;
4869 }
4870
4871 if (!Parameters.TryGetValue(Name, out Parameter OrgParameter))
4872 {
4873 await e.IqErrorBadRequest(e.To, "Parameter does not exist in original contract: " + Name, "en");
4874 return;
4875 }
4876
4877 Parameter ParameterDefinition = await this.TryParseParameter(E3, e, Name, OrgParameter);
4878 if (ParameterDefinition is null)
4879 return;
4880
4881 if (TransientParameters is null)
4882 TransientParameters = new Dictionary<CaseInsensitiveString, Parameter>();
4883
4884 TransientParameters[Name] = ParameterDefinition;
4885 }
4886 }
4887
4888 if (!(TransientParameters is null))
4889 {
4890 foreach (Parameter TransientParameter in TransientParameters.Values)
4891 {
4892 if (Contract.TryGetParameter(TransientParameter.Name, out Parameter ContractParamteter))
4893 {
4894 ContractParamteter.StringValue = TransientParameter.StringValue;
4895 ContractParamteter.ProtectedValue = TransientParameter.ProtectedValue;
4896 }
4897 }
4898 }
4899
4900 if (Contract.PartsMode != ContractParts.TemplateOnly)
4901 {
4902 KeyValuePair<Parameter, string> P4 = await Contract.CheckParameters(Parameters.Values, Contract.Duration, this, true);
4903 Parameter FailingParameter2 = P4.Key;
4904 if (!(FailingParameter2 is null))
4905 {
4906 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter2.Name + " contains invalid value: " + P4.Value, "en");
4907 return;
4908 }
4909 }
4910
4911 ParametersChecked = true;
4912 HasTransientParameters = true;
4913 break;
4914
4915 default:
4916 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
4917 return;
4918 }
4919 }
4920
4921 if (Contract is null)
4922 {
4923 await e.IqErrorBadRequest(e.To, "No contract provided.", "en");
4924 return;
4925 }
4926
4927 if (Contract.Duration <= Duration.Zero)
4928 {
4929 await e.IqErrorBadRequest(e.To, "Contract duration must be positive.", "en");
4930 return;
4931 }
4932
4933 if (!ParametersChecked)
4934 {
4935 KeyValuePair<Parameter, string> P5 = await Contract.CheckParameters(Parameters.Values, Contract.Duration, this, true);
4936 Parameter FailingParameter3 = P5.Key;
4937 if (!(FailingParameter3 is null))
4938 {
4939 await e.IqErrorBadRequest(e.To, "Contract parameter " + FailingParameter3.Name + " contains invalid value: " + P5.Value, "en");
4940 return;
4941 }
4942 }
4943
4944 Contract.Provider = e.To.Address;
4945 Contract.Account = e.From.Account;
4946 Contract.Created = NowSecond;
4947 Contract.Updated = DateTime.MinValue;
4948
4949 KeyValuePair<ContentIntegrity, string> P = await Contract.CheckContentIntegrity(this);
4950 string IntegrityParameter = P.Value;
4951
4952 switch (P.Key)
4953 {
4954 case ContentIntegrity.Ok:
4955 break;
4956
4957 case ContentIntegrity.RolesNotDefined:
4958 await e.IqErrorBadRequest(e.To, "No roles have been defined.", "en");
4959 return;
4960
4961 case ContentIntegrity.PartsNotDefined:
4962 await e.IqErrorBadRequest(e.To, "Part definition expected.", "en");
4963 return;
4964
4965 case ContentIntegrity.RoleCountsMismatch:
4966 await e.IqErrorBadRequest(e.To, "Role counts are incorrect for role " + IntegrityParameter, "en");
4967 return;
4968
4969 case ContentIntegrity.DuplicateRoleDefinition:
4970 await e.IqErrorBadRequest(e.To, "Duplicate role definition for role " + IntegrityParameter, "en");
4971 return;
4972
4973 case ContentIntegrity.DuplicateParameterDefinition:
4974 await e.IqErrorBadRequest(e.To, "Duplicate parameter definition for parameter " + IntegrityParameter, "en");
4975 return;
4976
4977 case ContentIntegrity.InvalidLegalIdReference:
4978 await e.IqErrorBadRequest(e.To, "Invalid Legal ID reference: " + IntegrityParameter, "en");
4979 return;
4980
4981 case ContentIntegrity.InvalidRoleReference:
4982 await e.IqErrorBadRequest(e.To, "Invalid role reference: " + IntegrityParameter, "en");
4983 return;
4984
4985 case ContentIntegrity.TooFewPartsOfRole:
4986 await e.IqErrorBadRequest(e.To, "Too few parts for role " + IntegrityParameter, "en");
4987 return;
4988
4989 case ContentIntegrity.TooManyPartsOfRole:
4990 await e.IqErrorBadRequest(e.To, "Too many parts for role " + IntegrityParameter, "en");
4991 return;
4992
4993 case ContentIntegrity.InvalidRoleIndex:
4994 await e.IqErrorBadRequest(e.To, "Role index is invalid for role reference parameter " + IntegrityParameter, "en");
4995 return;
4996
4997 case ContentIntegrity.MissingRoleProperty:
4998 await e.IqErrorBadRequest(e.To, "Missing property reference for role reference parameter " + IntegrityParameter, "en");
4999 return;
5000
5001 case ContentIntegrity.InvalidMachineReadable:
5002 await e.IqErrorBadRequest(e.To, "Machine-readable information is missing or is invalid.", "en");
5003 return;
5004
5005 case ContentIntegrity.MissingRoleHumanReadable:
5006 await e.IqErrorBadRequest(e.To, "Missing human-readable information for role " + IntegrityParameter, "en");
5007 return;
5008
5009 case ContentIntegrity.MissingParameterHumanReadable:
5010 await e.IqErrorBadRequest(e.To, "Missing human-readable information for parameter " + IntegrityParameter, "en");
5011 return;
5012
5013 case ContentIntegrity.ParameterValidationExpressionError:
5014 await e.IqErrorBadRequest(e.To, "Validation expression error for parameter " + IntegrityParameter, "en");
5015 return;
5016
5017 case ContentIntegrity.InvalidParameterReference:
5018 await e.IqErrorBadRequest(e.To, "An undefined parameter is referenced: " + IntegrityParameter, "en");
5019 return;
5020
5021 case ContentIntegrity.MissingHumanReadable:
5022 await e.IqErrorBadRequest(e.To, "Missing human-readable information.", "en");
5023 return;
5024
5025 case ContentIntegrity.DuplicateLocalization:
5026 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for contract: " + IntegrityParameter, "en");
5027 return;
5028
5029 case ContentIntegrity.DuplicateRoleLocalization:
5030 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for role: " + IntegrityParameter, "en");
5031 return;
5032
5033 case ContentIntegrity.DuplicateParameterLocalization:
5034 await e.IqErrorBadRequest(e.To, "Duplication language reference for human-readable text for parameter: " + IntegrityParameter, "en");
5035 return;
5036
5037 default:
5038 await e.IqErrorServiceUnavailable(e.To, "Contract integrity check failed for unknown reasons.", "en");
5039 return;
5040 }
5041
5042 LegalIdentity Identity = await this.GetCurrentApprovedLegalIdentityAsync(e.From.Account);
5043 if (Identity is null)
5044 {
5045 await e.IqErrorForbidden(e.To, "No current approved legal identity found for account.", "en");
5046 return;
5047 }
5048
5049 string Errors = await this.ValidateContent(Contract);
5050 if (!string.IsNullOrEmpty(Errors))
5051 {
5052 await e.IqErrorBadRequest(e.To, Errors, "en");
5053 return;
5054 }
5055
5056 StringBuilder Xml = new StringBuilder();
5057
5058 if (!(SignaturesToTransfer is null))
5059 {
5060 Dictionary<string, int> RoleCounters = new Dictionary<string, int>();
5061 List<ClientSignature> Transfered = null;
5062
5063 Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
5064 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
5065
5066 foreach (ClientSignature Signature in SignaturesToTransfer)
5067 {
5068 (Identity, _) = await this.ValidateSenderSignature(new XmppAddress(Signature.BareJid), null,
5069 Signature.Timestamp, Data, Signature.DigitalSignature, null); // TODO: Authorize access to requestor attachments
5070
5071 if (!(Identity is null))
5072 {
5073 if (!RoleCounters.TryGetValue(Signature.Role, out int RoleIndex))
5074 RoleIndex = 0;
5075
5076 RoleIndex++;
5077 if (Contract.TrySetRoleParameters(Signature.Role, RoleIndex, Identity, Signature.Timestamp, Signature.DigitalSignature) is null)
5078 {
5079 if (Transfered is null)
5080 Transfered = new List<ClientSignature>();
5081
5082 Transfered.Add(Signature);
5083 RoleCounters[Signature.Role] = RoleIndex;
5084 }
5085 }
5086 }
5087
5088 Contract.ClientSignatures = Transfered?.ToArray();
5089
5090 Xml.Clear();
5091 }
5092
5093 if (HasTransientParameters)
5095
5096 await Database.Insert(Contract);
5097
5098 Contract.ContractId = Contract.ObjectId + "@" + e.To.Address;
5099 Contract.Sign(this);
5100
5101 await Database.Update(Contract);
5102
5103 if (HasTransientParameters)
5104 this.AddTransientParameters(Contract.ContractId, TransientParameters);
5105
5106 KeyValuePair<string, object>[] Tags = Contract.GetTags();
5107
5108 switch (Contract.State)
5109 {
5110 case ContractState.Proposed:
5111 Log.Informational("Contract proposal registered.",
5112 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
5113 break;
5114
5115 case ContractState.Approved:
5116 Log.Informational("Contract registered and automatically approved.",
5117 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
5118 break;
5119
5120 default:
5121 Log.Informational("Contract registered.",
5122 Contract.ContractId.Value, e.From.BareJid.Value, "ContractRegistered", Tags);
5123 break;
5124 }
5125
5126 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Contract.ContractId, true);
5127
5128 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
5129 string ContractXml = Xml.ToString();
5130
5131 await e.IqResult(ContractXml, e.To);
5132
5133 if (Contract.State == ContractState.Proposed)
5134 {
5135 StringBuilder Markdown = new StringBuilder();
5136
5137 Markdown.Append("Contract proposal received: [");
5138 Markdown.Append(MarkdownDocument.Encode(Contract.ContractId));
5139 Markdown.Append("](");
5140 Markdown.Append(Gateway.GetUrl("/Contract.md?ID=" + Contract.ContractId));
5141 Markdown.AppendLine(")");
5142 Markdown.AppendLine();
5143 Output(Markdown, Tags);
5144
5145 await Gateway.SendNotification(Markdown.ToString());
5146 }
5147
5148 await this.SendContractUpdatedEvent(Contract, true);
5149 }
5150 catch (Exception ex)
5151 {
5152 await e.IqError(ex, e.To);
5153 }
5154 }
5155
5156 internal void AddTransientParameters(CaseInsensitiveString ContractId,
5157 Dictionary<CaseInsensitiveString, Parameter> TransientParameters)
5158 {
5159 this.transientParameters.Add(ContractId, TransientParameters);
5160 }
5161
5162 private async Task<Parameter> TryParseParameter(XmlElement E3, IqEventArgs e, CaseInsensitiveString Name, Parameter OrgParameter)
5163 {
5164 byte[] ProtectedValue = null;
5165
5166 if (E3.HasAttribute("guide") && XML.Attribute(E3, "guide") != OrgParameter.Guide)
5167 {
5168 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter guide strings.", "en");
5169 return null;
5170 }
5171
5172 if (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression)
5173 {
5174 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5175 return null;
5176 }
5177
5178 if (E3.HasAttribute("protection"))
5179 {
5180 if (XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OrgParameter.Protection)
5181 {
5182 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter protection rules.", "en");
5183 return null;
5184 }
5185 }
5186
5187 if (E3.HasAttribute("protected"))
5188 {
5189 try
5190 {
5191 ProtectedValue = Convert.FromBase64String(XML.Attribute(E3, "protected"));
5192 }
5193 catch (Exception)
5194 {
5195 await e.IqErrorBadRequest(e.To, "Invalid base64-encoded protected value.", "en");
5196 return null;
5197 }
5198 }
5199 else if (OrgParameter.Protection == ProtectionLevel.Transient)
5200 ProtectedValue = OrgParameter.ProtectedValue;
5201
5202 switch (E3.LocalName)
5203 {
5204 case "stringParameter":
5205 if (!(OrgParameter is StringParameter OldStringParameter))
5206 {
5207 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5208 return null;
5209 }
5210
5211 if ((E3.HasAttribute("regEx") && XML.Attribute(E3, "regEx") != OldStringParameter.RegEx) ||
5212 (E3.HasAttribute("min") && XML.Attribute(E3, "min") != OldStringParameter.Min) ||
5213 (E3.HasAttribute("max") && XML.Attribute(E3, "max") != OldStringParameter.Max) ||
5214 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldStringParameter.MinIncluded) ||
5215 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldStringParameter.MaxIncluded) ||
5216 (E3.HasAttribute("minLength") && XML.Attribute(E3, "minLength", 0) != OldStringParameter.MinLength) ||
5217 (E3.HasAttribute("maxLength") && XML.Attribute(E3, "maxLength", 0) != OldStringParameter.MaxLength) ||
5218 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldStringParameter.Protection) ||
5219 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5220 {
5221 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5222 return null;
5223 }
5224
5225 return new StringParameter()
5226 {
5227 Name = Name,
5228 Value = XML.Attribute(E3, "value"),
5229 Guide = OrgParameter.Guide,
5230 Expression = OrgParameter.Expression,
5231 RegEx = OldStringParameter.RegEx,
5232 Min = OldStringParameter.Min,
5233 Max = OldStringParameter.Max,
5234 MinIncluded = OldStringParameter.MinIncluded,
5235 MaxIncluded = OldStringParameter.MaxIncluded,
5236 MinLength = OldStringParameter.MinLength,
5237 MaxLength = OldStringParameter.MaxLength,
5238 Descriptions = OrgParameter.Descriptions,
5239 Protection = OrgParameter.Protection,
5240 ProtectedValue = ProtectedValue
5241 };
5242
5243 case "numericalParameter":
5244 if (!(OrgParameter is NumericalParameter OldNumericalParameter))
5245 {
5246 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5247 return null;
5248 }
5249
5250 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", 0.0m) != OldNumericalParameter.Min) ||
5251 (E3.HasAttribute("max") && XML.Attribute(E3, "max", 0.0m) != OldNumericalParameter.Max) ||
5252 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldNumericalParameter.MinIncluded) ||
5253 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldNumericalParameter.MaxIncluded) ||
5254 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldNumericalParameter.Protection) ||
5255 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5256 {
5257 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5258 return null;
5259 }
5260
5261 return new NumericalParameter()
5262 {
5263 Name = Name,
5264 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", 0.0m) : (decimal?)null,
5265 Guide = OrgParameter.Guide,
5266 Expression = OrgParameter.Expression,
5267 Min = OldNumericalParameter.Min,
5268 Max = OldNumericalParameter.Max,
5269 MinIncluded = OldNumericalParameter.MinIncluded,
5270 MaxIncluded = OldNumericalParameter.MaxIncluded,
5271 Descriptions = OrgParameter.Descriptions,
5272 Protection = OrgParameter.Protection,
5273 ProtectedValue = ProtectedValue
5274 };
5275
5276 case "booleanParameter":
5277 if (!(OrgParameter is BooleanParameter OldBooleanParameter))
5278 {
5279 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5280 return null;
5281 }
5282
5283 if ((E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldBooleanParameter.Protection) ||
5284 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5285 {
5286 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5287 return null;
5288 }
5289
5290 return new BooleanParameter()
5291 {
5292 Name = Name,
5293 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", false) : (bool?)null,
5294 Guide = OrgParameter.Guide,
5295 Expression = OrgParameter.Expression,
5296 Descriptions = OrgParameter.Descriptions,
5297 Protection = OrgParameter.Protection,
5298 ProtectedValue = ProtectedValue
5299 };
5300
5301 case "dateParameter":
5302 if (!(OrgParameter is DateParameter OldDateParameter))
5303 {
5304 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5305 return null;
5306 }
5307
5308 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", DateTime.MinValue) != OldDateParameter.Min) ||
5309 (E3.HasAttribute("max") && XML.Attribute(E3, "max", DateTime.MinValue) != OldDateParameter.Max) ||
5310 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDateParameter.MinIncluded) ||
5311 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDateParameter.MaxIncluded) ||
5312 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDateParameter.Protection) ||
5313 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5314 {
5315 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5316 return null;
5317 }
5318
5319 return new DateParameter()
5320 {
5321 Name = Name,
5322 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", DateTime.MinValue).Date : (DateTime?)null,
5323 Guide = OrgParameter.Guide,
5324 Expression = OrgParameter.Expression,
5325 Min = OldDateParameter.Min,
5326 Max = OldDateParameter.Max,
5327 MinIncluded = OldDateParameter.MinIncluded,
5328 MaxIncluded = OldDateParameter.MaxIncluded,
5329 Descriptions = OrgParameter.Descriptions,
5330 Protection = OrgParameter.Protection,
5331 ProtectedValue = ProtectedValue
5332 };
5333
5334 case "dateTimeParameter":
5335 if (!(OrgParameter is DateTimeParameter OldDateTimeParameter))
5336 {
5337 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5338 return null;
5339 }
5340
5341 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", DateTime.MinValue) != OldDateTimeParameter.Min) ||
5342 (E3.HasAttribute("max") && XML.Attribute(E3, "max", DateTime.MinValue) != OldDateTimeParameter.Max) ||
5343 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDateTimeParameter.MinIncluded) ||
5344 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDateTimeParameter.MaxIncluded) ||
5345 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDateTimeParameter.Protection) ||
5346 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5347 {
5348 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5349 return null;
5350 }
5351
5352 return new DateTimeParameter()
5353 {
5354 Name = Name,
5355 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", DateTime.MinValue) : (DateTime?)null,
5356 Guide = OrgParameter.Guide,
5357 Expression = OrgParameter.Expression,
5358 Min = OldDateTimeParameter.Min,
5359 Max = OldDateTimeParameter.Max,
5360 MinIncluded = OldDateTimeParameter.MinIncluded,
5361 MaxIncluded = OldDateTimeParameter.MaxIncluded,
5362 Descriptions = OrgParameter.Descriptions,
5363 Protection = OrgParameter.Protection,
5364 ProtectedValue = ProtectedValue
5365 };
5366
5367 case "timeParameter":
5368 if (!(OrgParameter is TimeParameter OldTimeParameter))
5369 {
5370 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5371 return null;
5372 }
5373
5374 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", TimeSpan.Zero) != OldTimeParameter.Min) ||
5375 (E3.HasAttribute("max") && XML.Attribute(E3, "max", TimeSpan.Zero) != OldTimeParameter.Max) ||
5376 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldTimeParameter.MinIncluded) ||
5377 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldTimeParameter.MaxIncluded) ||
5378 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldTimeParameter.Protection) ||
5379 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5380 {
5381 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5382 return null;
5383 }
5384
5385 return new TimeParameter()
5386 {
5387 Name = Name,
5388 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", TimeSpan.Zero) : (TimeSpan?)null,
5389 Guide = OrgParameter.Guide,
5390 Expression = OrgParameter.Expression,
5391 Min = OldTimeParameter.Min,
5392 Max = OldTimeParameter.Max,
5393 MinIncluded = OldTimeParameter.MinIncluded,
5394 MaxIncluded = OldTimeParameter.MaxIncluded,
5395 Descriptions = OrgParameter.Descriptions,
5396 Protection = OrgParameter.Protection,
5397 ProtectedValue = ProtectedValue
5398 };
5399
5400 case "durationParameter":
5401 if (!(OrgParameter is DurationParameter OldDurationParameter))
5402 {
5403 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5404 return null;
5405 }
5406
5407 if ((E3.HasAttribute("min") && XML.Attribute(E3, "min", Duration.Zero) != OldDurationParameter.Min) ||
5408 (E3.HasAttribute("max") && XML.Attribute(E3, "max", Duration.Zero) != OldDurationParameter.Max) ||
5409 (E3.HasAttribute("minIncluded") && XML.Attribute(E3, "minIncluded", true) != OldDurationParameter.MinIncluded) ||
5410 (E3.HasAttribute("maxIncluded") && XML.Attribute(E3, "maxIncluded", true) != OldDurationParameter.MaxIncluded) ||
5411 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldDurationParameter.Protection) ||
5412 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5413 {
5414 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5415 return null;
5416 }
5417
5418 return new DurationParameter()
5419 {
5420 Name = Name,
5421 Value = E3.HasAttribute("value") ? XML.Attribute(E3, "value", Duration.Zero) : (Duration?)null,
5422 Guide = OrgParameter.Guide,
5423 Expression = OrgParameter.Expression,
5424 Min = OldDurationParameter.Min,
5425 Max = OldDurationParameter.Max,
5426 MinIncluded = OldDurationParameter.MinIncluded,
5427 MaxIncluded = OldDurationParameter.MaxIncluded,
5428 Descriptions = OrgParameter.Descriptions,
5429 Protection = OrgParameter.Protection,
5430 ProtectedValue = ProtectedValue
5431 };
5432
5433 case "calcParameter":
5434 if (!(OrgParameter is CalcParameter))
5435 {
5436 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5437 return null;
5438 }
5439
5440 if (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression)
5441 {
5442 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5443 return null;
5444 }
5445
5446 return new CalcParameter()
5447 {
5448 Name = Name,
5449 Guide = OrgParameter.Guide,
5450 Expression = OrgParameter.Expression,
5451 Descriptions = OrgParameter.Descriptions,
5452 Protection = OrgParameter.Protection,
5453 ProtectedValue = ProtectedValue
5454 };
5455
5456 case "roleParameter":
5457 if (!(OrgParameter is RoleParameter OldRoleParameter))
5458 {
5459 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5460 return null;
5461 }
5462
5463 if ((E3.HasAttribute("role") && XML.Attribute(E3, "role") != OldRoleParameter.Role) ||
5464 (E3.HasAttribute("index") && XML.Attribute(E3, "index", 0) != OldRoleParameter.Index) ||
5465 (E3.HasAttribute("property") && XML.Attribute(E3, "property") != OldRoleParameter.Property) ||
5466 (E3.HasAttribute("required") && XML.Attribute(E3, "required", false) != OldRoleParameter.Required) ||
5467 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldRoleParameter.Protection) ||
5468 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5469 {
5470 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5471 return null;
5472 }
5473
5474 return new RoleParameter()
5475 {
5476 Name = Name,
5477 Role = OldRoleParameter.Role,
5478 Index = OldRoleParameter.Index,
5479 Property = OldRoleParameter.Property,
5480 Required = OldRoleParameter.Required,
5481 Guide = OrgParameter.Guide,
5482 Expression = OrgParameter.Expression,
5483 Descriptions = OrgParameter.Descriptions,
5484 Protection = OrgParameter.Protection,
5485 ProtectedValue = ProtectedValue
5486 };
5487
5488 case "contractReferenceParameter":
5489 if (!(OrgParameter is ContractReferenceParameter OldContractReferenceParameter))
5490 {
5491 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter types.", "en");
5492 return null;
5493 }
5494
5495 if ((E3.HasAttribute("localName") && XML.Attribute(E3, "localName") != OldContractReferenceParameter.LocalName) ||
5496 (E3.HasAttribute("namespace") && XML.Attribute(E3, "namespace") != OldContractReferenceParameter.Namespace) ||
5497 (E3.HasAttribute("templateId") && XML.Attribute(E3, "templateId") != OldContractReferenceParameter.TemplateId) ||
5498 (E3.HasAttribute("provider") && XML.Attribute(E3, "provider") != OldContractReferenceParameter.Provider) ||
5499 (E3.HasAttribute("creatorRole") && XML.Attribute(E3, "creatorRole") != OldContractReferenceParameter.CreatorRole) ||
5500 (E3.HasAttribute("required") && XML.Attribute(E3, "required", false) != OldContractReferenceParameter.Required) ||
5501 (E3.HasAttribute("protection") && XML.Attribute(E3, "protection", ProtectionLevel.Normal) != OldContractReferenceParameter.Protection) ||
5502 (E3.HasAttribute("exp") && XML.Attribute(E3, "exp") != OrgParameter.Expression))
5503 {
5504 await e.IqErrorBadRequest(e.To, "Not allowed to change parameter validation rules.", "en");
5505 return null;
5506 }
5507
5508 return new ContractReferenceParameter()
5509 {
5510 Name = Name,
5511 Value = XML.Attribute(E3, "value"),
5512 Guide = OrgParameter.Guide,
5513 Expression = OrgParameter.Expression,
5514 Descriptions = OrgParameter.Descriptions,
5515 Labels = OldContractReferenceParameter.Labels,
5516 LocalName = OldContractReferenceParameter.LocalName,
5517 Namespace = OldContractReferenceParameter.Namespace,
5518 TemplateId = OldContractReferenceParameter.TemplateId,
5519 Provider = OldContractReferenceParameter.Provider,
5520 CreatorRole = OldContractReferenceParameter.CreatorRole,
5521 Required = OldContractReferenceParameter.Required,
5522 Protection = OrgParameter.Protection,
5523 ProtectedValue = ProtectedValue
5524 };
5525
5526 default:
5527 await e.IqErrorBadRequest(e.To, "Invalid request.", "en");
5528 return null;
5529 }
5530 }
5531
5532 internal async Task<string> CheckContentIntegrity(Contract Contract)
5533 {
5534 KeyValuePair<ContentIntegrity, string> P = await Contract.CheckContentIntegrity(this);
5535 string IntegrityParameter = P.Value;
5536
5537 switch (P.Key)
5538 {
5539 case ContentIntegrity.Ok:
5540 return null;
5541
5542 case ContentIntegrity.RolesNotDefined:
5543 return "No roles have been defined.";
5544
5545 case ContentIntegrity.PartsNotDefined:
5546 return "Part definition expected.";
5547
5548 case ContentIntegrity.RoleCountsMismatch:
5549 return "Role counts are incorrect for role " + IntegrityParameter;
5550
5551 case ContentIntegrity.DuplicateRoleDefinition:
5552 return "Duplicate role definition for role " + IntegrityParameter;
5553
5554 case ContentIntegrity.DuplicateParameterDefinition:
5555 return "Duplicate parameter definition for parameter " + IntegrityParameter;
5556
5557 case ContentIntegrity.InvalidLegalIdReference:
5558 return "Invalid Legal ID reference: " + IntegrityParameter;
5559
5560 case ContentIntegrity.InvalidRoleReference:
5561 return "Invalid role reference: " + IntegrityParameter;
5562
5563 case ContentIntegrity.TooFewPartsOfRole:
5564 return "Too few parts for role " + IntegrityParameter;
5565
5566 case ContentIntegrity.TooManyPartsOfRole:
5567 return "Too many parts for role " + IntegrityParameter;
5568
5569 case ContentIntegrity.InvalidRoleIndex:
5570 return "Role index is invalid for role reference parameter " + IntegrityParameter;
5571
5572 case ContentIntegrity.MissingRoleProperty:
5573 return "Missing property reference for role reference parameter " + IntegrityParameter;
5574
5575 case ContentIntegrity.InvalidMachineReadable:
5576 return "Machine-readable information is missing or is invalid.";
5577
5578 case ContentIntegrity.MissingRoleHumanReadable:
5579 return "Missing human-readable information for role " + IntegrityParameter;
5580
5581 case ContentIntegrity.MissingParameterHumanReadable:
5582 return "Missing human-readable information for parameter " + IntegrityParameter;
5583
5584 case ContentIntegrity.ParameterValidationExpressionError:
5585 return "Validation expression error for parameter " + IntegrityParameter;
5586
5587 case ContentIntegrity.InvalidParameterReference:
5588 return "An undefined parameter is referenced: " + IntegrityParameter;
5589
5590 case ContentIntegrity.MissingHumanReadable:
5591 return "Missing human-readable information.";
5592
5593 case ContentIntegrity.DuplicateLocalization:
5594 return "Duplication language reference for human-readable text for contract: " + IntegrityParameter;
5595
5596 case ContentIntegrity.DuplicateRoleLocalization:
5597 return "Duplication language reference for human-readable text for role: " + IntegrityParameter;
5598
5599 case ContentIntegrity.DuplicateParameterLocalization:
5600 return "Duplication language reference for human-readable text for parameter: " + IntegrityParameter;
5601
5602 default:
5603 return "Contract integrity check failed for unknown reasons.";
5604 }
5605 }
5606
5607 internal async Task<string> ValidateContent(Contract Contract)
5608 {
5609 Contract.ContentSchemaDigest = null;
5610 Contract.ContentSchemaHashFunction = HashFunction.SHA256;
5611
5612 if (string.IsNullOrEmpty(Contract.ForMachines))
5613 return "No machine-readable content.";
5614
5615 XmlDocument Doc = new XmlDocument()
5616 {
5617 PreserveWhitespace = true
5618 };
5619
5620 try
5621 {
5622 Doc.LoadXml(Contract.ForMachines);
5623
5624 if (Doc.DocumentElement is null)
5625 return "Invalid machine-readable XML: No root element.";
5626 }
5627 catch (XmlException ex)
5628 {
5629 ex = XML.AnnotateException(ex, Contract.ForMachines);
5630 return "Invalid machine-readable XML: " + ex.Message;
5631 }
5632 catch (Exception ex)
5633 {
5634 return "Invalid machine-readable XML: " + ex.Message;
5635 }
5636
5637 (string ErrorMessage, Dictionary<string, ValidationSchema> Schemas) = await this.ValidateContent(Doc);
5638 if (!string.IsNullOrEmpty(ErrorMessage))
5639 return ErrorMessage;
5640
5641 int NrSchemas = Schemas.Count;
5642 if (NrSchemas == 0 || !Schemas.ContainsKey(Contract.ForMachinesNamespace))
5643 return "Machine-readable content not defined by schemas.";
5644
5645 int i = 0;
5646 int c = Schemas.Count;
5647 SchemaReference[] References = new SchemaReference[c];
5648
5649 foreach (ValidationSchema Schema2 in Schemas.Values)
5650 {
5651 References[i++] = new SchemaReference()
5652 {
5653 Namespace = Schema2.Namespace,
5654 Digest = Convert.FromBase64String(Schema2.HashBase64),
5655 Algorithm = Schema2.Function
5656 };
5657 }
5658
5659 Contract.SchemaReferences = References;
5660
5661 if (Contract.TryGetSchemaReference(Contract.ForMachinesNamespace, out SchemaReference Ref))
5662 {
5663 Contract.ContentSchemaDigest = Ref.Digest;
5664 Contract.ContentSchemaHashFunction = Ref.Algorithm;
5665 }
5666 else
5667 return "M2M content schema not found.";
5668
5669 return null;
5670 }
5671
5672 internal async Task<(string, Dictionary<string, ValidationSchema>)> ValidateContent(XmlDocument Doc)
5673 {
5674 Dictionary<string, ValidationSchema> Schemas = new Dictionary<string, ValidationSchema>();
5675 LinkedList<XmlNode> ToCheck = new LinkedList<XmlNode>();
5676
5677 ToCheck.AddLast(Doc.DocumentElement);
5678
5679 while (!(ToCheck.First is null))
5680 {
5681 XmlNode N = ToCheck.First.Value;
5682 ToCheck.RemoveFirst();
5683
5684 while (!(N is null))
5685 {
5686 if (N is XmlElement E)
5687 {
5688 if (!string.IsNullOrEmpty(E.NamespaceURI))
5689 Schemas[E.NamespaceURI] = null;
5690
5691 if (E.HasAttributes)
5692 {
5693 foreach (XmlAttribute Attr in E.Attributes)
5694 {
5695 if (!string.IsNullOrEmpty(Attr.NamespaceURI) &&
5696 Attr.NamespaceURI != "http://www.w3.org/XML/1998/namespace" &&
5697 Attr.NamespaceURI != "http://www.w3.org/2000/xmlns/")
5698 {
5699 Schemas[Attr.NamespaceURI] = null;
5700 }
5701 }
5702 }
5703
5704 foreach (XmlNode N2 in E.ChildNodes)
5705 ToCheck.AddLast(N2);
5706 }
5707
5708 N = N.NextSibling;
5709 }
5710 }
5711
5712 int NrSchemas = Schemas.Count;
5713 ValidationSchema Schema;
5714 string ErrorMsg;
5715 string[] Namespaces = new string[NrSchemas];
5716 Dictionary<string, bool> Loaded = new Dictionary<string, bool>();
5717 Schemas.Keys.CopyTo(Namespaces, 0);
5718
5719 foreach (string Namespace in Namespaces)
5720 {
5721 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(new FilterFieldEqualTo("Namespace", Namespace), "-Created");
5722
5723 if (Schema is null)
5724 {
5725 (Schema, ErrorMsg) = await this.LoadSchema(Namespace, null);
5726 if (!string.IsNullOrEmpty(ErrorMsg))
5727 return (ErrorMsg, null);
5728
5729 Loaded[Namespace] = true;
5730 }
5731
5732 Schemas[Namespace] = Schema;
5733 }
5734
5735 ErrorMsg = this.ValidateXml(Schemas.Values, Doc);
5736 if (!string.IsNullOrEmpty(ErrorMsg))
5737 {
5738 bool Changed = false;
5739
5740 foreach (string Namespace in Namespaces)
5741 {
5742 if (Loaded.ContainsKey(Namespace))
5743 continue;
5744
5745 Schema = Schemas[Namespace];
5746 (ValidationSchema Schema2, string ErrorMsg2) = await this.LoadSchema(Namespace, Schema);
5747 if (!string.IsNullOrEmpty(ErrorMsg2))
5748 return (ErrorMsg2, null);
5749
5750 if (Schema2 != Schema)
5751 {
5752 Schemas[Namespace] = Schema2;
5753 Changed = true;
5754 }
5755 }
5756
5757 if (!Changed)
5758 return (ErrorMsg, null);
5759
5760 ErrorMsg = this.ValidateXml(Schemas.Values, Doc);
5761 if (!string.IsNullOrEmpty(ErrorMsg))
5762 return (ErrorMsg, null);
5763 }
5764
5765 return (null, Schemas);
5766 }
5767
5768 private async Task<(ValidationSchema, string)> LoadSchema(string Namespace, ValidationSchema PrevSchema)
5769 {
5770 Stream File;
5771 string ContentType;
5772
5773 switch (Namespace)
5774 {
5775 case "http://www.w3.org/XML/1998/namespace":
5776 Type T = typeof(Networking.XMPP.Contracts.ContractsClient);
5777 Assembly A = T.Assembly;
5778 File = A.GetManifestResourceStream(T.Namespace + ".Schema.Xml.xsd");
5780 break;
5781
5782 case "http://www.w3.org/2000/xmlns/":
5783 T = typeof(Networking.XMPP.Contracts.ContractsClient);
5784 A = T.Assembly;
5785 File = A.GetManifestResourceStream(T.Namespace + ".Schema.Xmlns.xsd");
5787 break;
5788
5789 default:
5790 Uri Uri = new Uri(Namespace);
5791
5792 if (!InternetContent.CanGet(Uri, out Grade _, out IContentGetter Getter))
5793 return (null, "Schema not downloadable: " + Namespace);
5794
5795 KeyValuePair<string, TemporaryStream> P;
5796
5797 try
5798 {
5799 P = await Getter.GetTempStreamAsync(Uri, null, null, 10000,
5800 new KeyValuePair<string, string>("Accept", "application/xml, text/xml"),
5801 new KeyValuePair<string, string>("Accept-Charset", "utf-8"));
5802
5803 File = P.Value;
5804 ContentType = P.Key;
5805 }
5806 catch (Exception ex)
5807 {
5808 return (null, "Unable to get schema file " + Namespace + ". The following error was reported: " + ex.Message);
5809 }
5810 break;
5811 }
5812
5813 try
5814 {
5815 if (File.Length > int.MaxValue)
5816 throw new OutOfMemoryException("Schema file too large.");
5817
5818 int Len = (int)File.Length;
5819 byte[] Bin = new byte[Len];
5820
5821 File.Position = 0;
5822 await File.ReadAllAsync(Bin, 0, Len);
5823
5824 string Digest = Convert.ToBase64String(Hashes.ComputeHash(HashFunction.SHA384, Bin));
5825
5826 if (!(PrevSchema is null) && Digest == PrevSchema.HashBase64)
5827 return (PrevSchema, null);
5828
5829 ValidationSchema Schema = new ValidationSchema()
5830 {
5831 Namespace = Namespace,
5832 Function = HashFunction.SHA384,
5833 HashBase64 = Digest,
5834 Created = NowSecond,
5835 XmlSchema = Bin,
5837 };
5838
5839 await Database.Insert(Schema);
5840
5841 return (Schema, null);
5842 }
5843 finally
5844 {
5845 File.Dispose();
5846 }
5847 }
5848
5849 private string ValidateXml(IEnumerable<ValidationSchema> Schemas, XmlDocument Xml)
5850 {
5851 StringBuilder Errors = null;
5852 StringBuilder Warnings = null;
5853
5854 try
5855 {
5856 if (Xml.Schemas.Count > 0)
5857 {
5858 LinkedList<XmlSchema> ToRemove = new LinkedList<XmlSchema>();
5859
5860 foreach (XmlSchema Schema in Xml.Schemas.Schemas())
5861 ToRemove.AddLast(Schema);
5862
5863 foreach (XmlSchema Schema in ToRemove)
5864 Xml.Schemas.Remove(Schema);
5865 }
5866
5867 foreach (ValidationSchema Schema in Schemas)
5868 {
5869 using (XmlReader r = XmlReader.Create(new MemoryStream(Schema.XmlSchema)))
5870 {
5871 XmlSchema XmlSchema = XmlSchema.Read(r, null);
5872 Xml.Schemas.Add(XmlSchema);
5873 }
5874 }
5875
5876 Xml.Validate((sender2, e2) =>
5877 {
5878 switch (e2.Severity)
5879 {
5880 case XmlSeverityType.Error:
5881 if (Errors is null)
5882 Errors = new StringBuilder();
5883
5884 Errors.AppendLine(e2.Message);
5885 break;
5886
5887 case XmlSeverityType.Warning:
5888 if (Warnings is null)
5889 Warnings = new StringBuilder();
5890
5891 Warnings.AppendLine(e2.Message);
5892 break;
5893 }
5894 });
5895 }
5896 catch (Exception ex)
5897 {
5898 if (Errors is null)
5899 Errors = new StringBuilder();
5900
5901 Errors.AppendLine(ex.Message);
5902 }
5903
5904 if (!(Errors is null) || !(Warnings is null))
5905 {
5906 StringBuilder Report = new StringBuilder();
5907
5908 if (!(Errors is null))
5909 {
5910 Report.AppendLine("Errors found during validation:");
5911 Report.AppendLine(new string('-', 40));
5912 Report.Append(Errors.ToString());
5913
5914 if (!(Warnings is null))
5915 Report.AppendLine();
5916 }
5917
5918 if (!(Warnings is null))
5919 {
5920 Report.AppendLine("Warnings found during validation:");
5921 Report.AppendLine(new string('-', 40));
5922 Report.Append(Warnings.ToString());
5923 }
5924
5925 return Report.ToString();
5926 }
5927
5928 return null;
5929 }
5930
5936 public static async Task<Tuple<byte[], XmlSchema>> LoadSchema(string Url)
5937 {
5938 try
5939 {
5940 HttpRequestMessage Request = null;
5941 HttpResponseMessage Response = null;
5942 HttpClient Client = new HttpClient();
5943
5944 try
5945 {
5946 Client.Timeout = TimeSpan.FromMilliseconds(30000);
5947 Client.DefaultRequestHeaders.ExpectContinue = false;
5948
5949 Uri Uri = new Uri(Url);
5950 Request = new HttpRequestMessage(HttpMethod.Head, Uri);
5951 Request.Headers.Add("Accept", "application/xml, text/xml");
5952 Request.Headers.Add("Accept-Charset", "utf-8");
5953
5954 Response = await Client.SendAsync(Request);
5955 if (!Response.IsSuccessStatusCode)
5956 return null;
5957
5958 Request.Dispose();
5959 Response.Dispose();
5960
5961 Request = new HttpRequestMessage(HttpMethod.Get, Uri);
5962 Request.Headers.Add("Accept", "application/xml, text/xml");
5963 Request.Headers.Add("Accept-Charset", "utf-8");
5964
5965 Response = await Client.SendAsync(Request);
5966 if (!Response.IsSuccessStatusCode)
5967 return null;
5968
5969 string ContentType = null;
5970
5971 if (Response.Headers.TryGetValues("Content-Type", out IEnumerable<string> Values))
5972 {
5973 foreach (string s in Values)
5974 {
5975 ContentType = s;
5976 break;
5977 }
5978 }
5979
5980 if (ContentType is null)
5981 ContentType = XmlCodec.DefaultContentType;
5982
5983 byte[] Data = await Response.Content.ReadAsByteArrayAsync();
5984 object Decoded = await InternetContent.DecodeAsync(ContentType, Data, Uri);
5985
5986 if (!(Decoded is XmlDocument Doc))
5987 return null;
5988
5989 using (XmlReader r = XmlReader.Create(new MemoryStream(Data)))
5990 {
5991 XmlSchema Schema = XmlSchema.Read(r, null);
5992 return new Tuple<byte[], XmlSchema>(Data, Schema);
5993 }
5994 }
5995 finally
5996 {
5997 Request?.Dispose();
5998 Response?.Dispose();
5999 Client.Dispose();
6000 }
6001 }
6002 catch (Exception ex)
6003 {
6004 Log.Exception(ex);
6005 }
6006
6007 return null;
6008 }
6009
6010 private async Task GetCreatedContractsHandler(object Sender, IqEventArgs e)
6011 {
6012 try
6013 {
6014 if (!this.Server.IsServerDomain(e.From.Domain, true))
6015 {
6016 await e.IqErrorForbidden(e.To, "Not an account on the broker.", "en");
6017 return;
6018 }
6019
6020 int Offset = XML.Attribute(e.Query, "offset", 0);
6021 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
6022 bool References = XML.Attribute(e.Query, "references", true);
6023 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
6024
6025 IEnumerable<Contract> Contracts = await Database.Find<Contract>(Offset, MaxCount,
6026 new FilterFieldEqualTo("Account", e.From.Account), "Created");
6027
6028 string Xml = await this.SerializeContractReferences(Contracts, References, QueryVersion);
6029
6030 await e.IqResult(Xml, e.To);
6031 }
6032 catch (Exception ex)
6033 {
6034 await e.IqError(ex, e.To);
6035 }
6036 }
6037
6038 internal async Task<string> SerializeContractReferences(IEnumerable<IContractReference> Contracts, bool References, NamespaceSet Version)
6039 {
6040 StringBuilder Xml = new StringBuilder();
6041
6042 if (References)
6043 Xml.Append("<contractReferences xmlns='");
6044 else
6045 Xml.Append("<contracts xmlns='");
6046
6047 Xml.Append(NamespaceSmartContracts(Version));
6048 Xml.Append("'>");
6049
6050 foreach (IContractReference Contract in Contracts)
6051 {
6052 if (References)
6053 {
6054 Xml.Append("<ref id='");
6055 Xml.Append(XML.Encode(Contract.ContractId));
6056 Xml.Append("'/>");
6057 }
6058 else if (Contract is Contract Contract2)
6059 Contract2.Serialize(Xml, Contract2.Version != Version, true, true, true, true, true, true, null, this);
6060 else
6061 {
6062 Contract2 = await this.GetContract(Contract.ContractId);
6063 if (Contract2 is null)
6064 {
6065 Xml.Append("<ref id='");
6066 Xml.Append(XML.Encode(Contract.ContractId));
6067 Xml.Append("'/>");
6068 }
6069 else
6070 Contract2.Serialize(Xml, Contract2.Version != Version, true, true, true, true, true, true, null, this);
6071 }
6072 }
6073
6074 if (References)
6075 Xml.Append("</contractReferences>");
6076 else
6077 Xml.Append("</contracts>");
6078
6079 return Xml.ToString();
6080 }
6081
6082 public async Task<CaseInsensitiveString> GetComponent(CaseInsensitiveString ServerDomain, string Feature)
6083 {
6084 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(ServerDomain, new string[] { Feature });
6085 return P.Key;
6086 }
6087
6088 public async Task<KeyValuePair<CaseInsensitiveString, string>> GetComponent(CaseInsensitiveString ServerDomain, params string[] Features)
6089 {
6090 if (string.IsNullOrEmpty(Gateway.Domain) && !(Gateway.XmppClient is null)) // S2S not possible in developer environment, use C2S.
6091 {
6092 KeyValuePair<string, string> P = await Gateway.XmppClient.FindComponentAsync(ServerDomain, Features);
6093 return new KeyValuePair<CaseInsensitiveString, string>(P.Key, P.Value);
6094 }
6095
6096 foreach (string Feature in Features)
6097 {
6098 string Key = ServerDomain + "#" + Feature;
6099
6100 if (this.remoteComponents.TryGetValue(Key, out object Obj) && Obj is CaseInsensitiveString JID)
6101 return new KeyValuePair<CaseInsensitiveString, string>(JID, Feature);
6102 }
6103
6104 TaskCompletionSource<KeyValuePair<CaseInsensitiveString, string>> Result =
6105 new TaskCompletionSource<KeyValuePair<CaseInsensitiveString, string>>();
6106
6107 await this.Server.SendIqRequest("get", new XmppAddress(this.Server.Domain), new XmppAddress(ServerDomain), string.Empty,
6108 "<query xmlns='http://jabber.org/protocol/disco#items'/>", async (Sender, e) =>
6109 {
6110 if (e.Ok && !(e.FirstElement is null))
6111 {
6112 List<CaseInsensitiveString> JIDs = new List<CaseInsensitiveString>() { ServerDomain };
6113 object SynchObject = new object();
6114
6115 foreach (XmlNode N in e.FirstElement)
6116 {
6117 if (N is XmlElement E && E.LocalName == "item")
6118 {
6119 CaseInsensitiveString JID = XML.Attribute(E, "jid");
6121 JIDs.Add(JID);
6122 }
6123 }
6124
6125 int Count = JIDs.Count;
6126
6127 foreach (CaseInsensitiveString JID2 in JIDs)
6128 {
6129 await this.Server.SendIqRequest("get", new XmppAddress(this.Server.Domain), new XmppAddress(JID2), string.Empty,
6130 "<query xmlns='http://jabber.org/protocol/disco#info'/>", (sender2, e2) =>
6131 {
6132 if (e2.Ok)
6133 {
6134 foreach (XmlNode N2 in e2.FirstElement)
6135 {
6136 if (!(N2 is XmlElement E2) || E2.LocalName != "feature")
6137 continue;
6138
6139 string Var = XML.Attribute(E2, "var");
6140
6141 foreach (string Feature in Features)
6142 {
6143 if (Var == Feature)
6144 {
6145 lock (SynchObject)
6146 {
6147 if (Count > 0)
6148 Count = -1;
6149 else
6150 break;
6151 }
6152
6153 CaseInsensitiveString s = (CaseInsensitiveString)e2.State;
6154 string Key = ServerDomain + "#" + Feature;
6155
6156 this.remoteComponents[Key] = s;
6157 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(s, Feature));
6158
6159 return Task.CompletedTask;
6160 }
6161 }
6162 }
6163 }
6164
6165 lock (SynchObject)
6166 {
6167 Count--;
6168 if (Count != 0)
6169 return Task.CompletedTask;
6170 }
6171
6172 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(null, null));
6173
6174 return Task.CompletedTask;
6175 }, JID2);
6176 }
6177 }
6178 else
6179 Result.TrySetResult(new KeyValuePair<CaseInsensitiveString, string>(null, null));
6180 }, null);
6181
6182 return await Result.Task;
6183 }
6184
6185 private async Task SignContractHandler(object Sender, IqEventArgs e)
6186 {
6187 try
6188 {
6189 CaseInsensitiveString ContractId = null;
6191 byte[] Signature = null;
6192 bool Transferable = false;
6193
6194 foreach (XmlAttribute Attr in e.Query.Attributes)
6195 {
6196 switch (Attr.Name)
6197 {
6198 case "id":
6199 ContractId = Attr.Value;
6200 break;
6201
6202 case "role":
6203 Role = Attr.Value;
6204 break;
6205
6206 case "s":
6207 Signature = Convert.FromBase64String(Attr.Value);
6208 break;
6209
6210 case "transferable":
6211 if (CommonTypes.TryParse(Attr.Value, out bool b))
6212 Transferable = b;
6213 else
6214 {
6215 await e.IqErrorBadRequest(e.To, "Invalid transferable value.", "en");
6216 return;
6217 }
6218 break;
6219 }
6220 }
6221
6222 if (ContractId is null || Role is null || Signature is null)
6223 {
6224 await e.IqErrorBadRequest(e.To, "Attributes missing.", "en");
6225 return;
6226 }
6227
6228 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase))
6229 {
6230 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
6231 if (Contract is null)
6232 {
6233 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
6234 return;
6235 }
6236
6237 if (Contract.PartsMode == ContractParts.TemplateOnly)
6238 {
6239 await e.IqErrorForbidden(e.To, "Contract is a template only, and cannot be signed.", "en");
6240 return;
6241 }
6242
6243 switch (Contract.State)
6244 {
6245 case ContractState.Obsoleted:
6246 await e.IqErrorForbidden(e.To, "Contract is obsoleted.", "en");
6247 return;
6248
6249 case ContractState.Deleted:
6250 await e.IqErrorForbidden(e.To, "Contract is deleted.", "en");
6251 return;
6252
6253 case ContractState.Proposed:
6254 await e.IqErrorForbidden(e.To, "Contract is not yet approved to be signed.", "en");
6255 return;
6256
6257 case ContractState.Rejected:
6258 await e.IqErrorForbidden(e.To, "Contract proposal has been rejected.", "en");
6259 return;
6260
6261 case ContractState.Failed:
6262 await e.IqErrorForbidden(e.To, "Contract proposal has failed.", "en");
6263 return;
6264 }
6265
6266 DateTime Now = DateTime.Now;
6267 if (Contract.SignAfter.HasValue && Now < Contract.SignAfter)
6268 {
6269 await e.IqErrorForbidden(e.To, "Contract will be open for signatures after " + Contract.SignAfter.ToString(), "en");
6270 return;
6271 }
6272
6273 if (Contract.SignBefore.HasValue && Now > Contract.SignBefore)
6274 {
6275 await e.IqErrorForbidden(e.To, "Contract closed for signatures as if " + Contract.SignBefore.ToString(), "en");
6276 return;
6277 }
6278
6280 {
6281 KeyValuePair<Parameter, string> P = await Contract.CheckParameters(this, false);
6282 Parameter FailingParameter = P.Key;
6283
6284 if (!(FailingParameter is null))
6285 {
6286 await e.IqErrorResourceConstraint(e.To, "Contract parameter " + FailingParameter.Name + " contains invalid value: " + P.Value, "en");
6287 return;
6288 }
6289 }
6290
6291 int NrSignaturesForRole = 0;
6292 bool RoleFound = false;
6293
6294 if (!(Contract.ClientSignatures is null))
6295 {
6296 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6297 {
6298 if (ClientSignature.Role == Role)
6299 NrSignaturesForRole++;
6300 }
6301 }
6302
6303 if (!(Contract.Roles is null))
6304 {
6305 foreach (Role Role2 in Contract.Roles)
6306 {
6307 if (Role2.Name == Role)
6308 {
6309 if (NrSignaturesForRole >= Role2.MaxCount)
6310 {
6311 await e.IqErrorForbidden(e.To, "No more signatures of specified role allowed.", "en");
6312 return;
6313 }
6314
6315 RoleFound = true;
6316 break;
6317 }
6318 }
6319 }
6320
6321 if (!RoleFound)
6322 {
6323 await e.IqErrorForbidden(e.To, "No such role defined in contract.", "en");
6324 return;
6325 }
6326
6327 StringBuilder Xml = new StringBuilder();
6328 Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
6329 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
6330
6331 (LegalIdentity Identity, _) = await this.ValidateSenderSignature(e.From, new ExternalRequest(e), Now, Data, Signature, null); // TODO: Authorize access to requestor attachments
6332 if (Identity is null)
6333 return;
6334
6335 if (!await CheckPeerReviewFields(e, Identity))
6336 return;
6337
6338 if (Contract.PartsMode == ContractParts.ExplicitlyDefined)
6339 {
6340 bool InList = false;
6341
6342 if (!(Contract.Parts is null))
6343 {
6344 foreach (Part Part in Contract.Parts)
6345 {
6346 if (Part.LegalId == Identity.Id && Part.Role == Role)
6347 {
6348 InList = true;
6349 break;
6350 }
6351 }
6352
6353 if (!InList)
6354 {
6355 XmppAddress SignatoryId = new XmppAddress(Identity.Id);
6356
6357 foreach (Part Part in Contract.Parts)
6358 {
6359 if (Part.Role != Role)
6360 continue;
6361
6362 XmppAddress PartId = new XmppAddress(Part.LegalId);
6363 if (SignatoryId.Domain != PartId.Domain)
6364 continue;
6365
6366 bool HasSigned = false;
6367
6368 if (!(Contract.ClientSignatures is null))
6369 {
6370 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6371 {
6373 {
6374 HasSigned = true;
6375 break;
6376 }
6377 }
6378 }
6379
6380 if (HasSigned)
6381 continue;
6382
6383 if (!await this.CanSignAs(Part.LegalId, Identity.Id))
6384 continue;
6385
6386 InList = true;
6387 break;
6388 }
6389 }
6390 }
6391
6392 if (!InList)
6393 {
6394 await e.IqErrorForbidden(e.To, "Legal identity not in explicitly defined list of parts in contract.", "en");
6395 return;
6396 }
6397 }
6398
6399 if (!(Contract.ClientSignatures is null))
6400 {
6401 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6402 {
6403 if (ClientSignature.LegalId == Identity.Id && ClientSignature.Role == Role)
6404 {
6405 // Contract already signed. Return successful response without altering the contract.
6406
6407 Xml.Clear();
6408 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
6409 await e.IqResult(Xml.ToString(), e.To);
6410
6411 return;
6412 }
6413 }
6414 }
6415
6416 DateTime SignatureTimestamp = DateTime.Now;
6417 string s = Contract.TrySetRoleParameters(Role, NrSignaturesForRole + 1, Identity, SignatureTimestamp, Signature);
6418 if (!string.IsNullOrEmpty(s))
6419 {
6420 await e.IqErrorForbidden(e.To, "Legal identity does not have required property " + s + " defined.", "en");
6421 return;
6422 }
6423
6424 int c = (Contract.ClientSignatures?.Length ?? 0) + 1;
6425 ClientSignature[] Signatures = new ClientSignature[c];
6426
6427 Contract.ClientSignatures?.CopyTo(Signatures, 0);
6428 Signatures[c - 1] = new ClientSignature()
6429 {
6430 BareJid = e.From.BareJid,
6431 LegalId = Identity.Id,
6432 Role = Role,
6433 DigitalSignature = Signature,
6434 Timestamp = SignatureTimestamp,
6435 Transferable = Transferable
6436 };
6437
6438 Contract.ClientSignatures = Signatures;
6439
6440 if (Contract.From == DateTime.MinValue)
6441 {
6442 if (await Contract.IsLegallyBinding(false, true, this))
6443 {
6444 Contract.State = ContractState.Signed;
6445 Contract.From = Contract.Created;
6446
6447 if (Contract.Duration.HasValue)
6448 Contract.To = Contract.From + Contract.Duration.Value;
6449 else
6450 Contract.To = Contract.From;
6451 }
6452 else
6453 Contract.State = ContractState.BeingSigned;
6454 }
6455
6456 Contract.Sign(this);
6457
6458 await Database.Update(Contract);
6459
6461 {
6462 ContractId = Contract.ContractId,
6463 LegalId = Identity.Id,
6464 BareJid = e.From.BareJid
6465 };
6466
6468
6469 Log.Informational("Contract signed as " + Role.Value + ".",
6470 Contract.ContractId.Value, Identity.Id.Value, "ContractSigned",
6471 new KeyValuePair<string, object>("BareJid", e.From.BareJid.Value),
6472 new KeyValuePair<string, object>("Role", Role.Value),
6473 new KeyValuePair<string, object>("Transferable", Transferable));
6474
6475 Xml.Clear();
6476 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
6477 await e.IqResult(Xml.ToString(), e.To);
6478
6479 try
6480 {
6481 string ContractNamespace = NamespaceSmartContracts(Contract.Version);
6482 Dictionary<CaseInsensitiveString, string> StatusRecipients = new Dictionary<CaseInsensitiveString, string>()
6483 {
6484 { Contract.Account + "@" + this.Server.Domain, ContractNamespace }
6485 };
6486
6487 if (!(Contract.ClientSignatures is null))
6488 {
6489 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6490 {
6491 StatusRecipients[ClientSignature.BareJid] = ContractNamespace;
6492
6494
6495 if (!this.Server.IsServerDomain(Addr.Domain, true))
6496 {
6497 KeyValuePair<CaseInsensitiveString, string> P2 = await this.GetComponent(Addr.Domain, NamespacesSmartContracts);
6500 StatusRecipients[Component] = P2.Value;
6501 }
6502 }
6503 }
6504
6505 foreach (KeyValuePair<CaseInsensitiveString, string> P3 in StatusRecipients)
6506 {
6507 CaseInsensitiveString StatusRecipient = P3.Key;
6508 string Namespace = P3.Value;
6509
6510 Xml.Clear();
6511
6512 Xml.Append("<contractSigned xmlns='");
6513 Xml.Append(Namespace);
6514 Xml.Append("' contractId='");
6515 Xml.Append(XML.Encode(Contract.ContractId));
6516 Xml.Append("' legalId='");
6517 Xml.Append(XML.Encode(Identity.Id));
6518 Xml.Append("' role='");
6519 Xml.Append(XML.Encode(Role));
6520 Xml.Append("'/>");
6521
6522 string Event = Xml.ToString();
6523
6524 try
6525 {
6526 await this.Server.SendMessage(string.Empty, string.Empty, e.To, new XmppAddress(StatusRecipient),
6527 string.Empty, Event);
6528 }
6529 catch (Exception ex)
6530 {
6532 }
6533 }
6534 }
6535 catch (Exception ex)
6536 {
6538 }
6539
6540 try
6541 {
6542 await this.ContractSigned(Contract, true, this.eDaler, this.pubsub);
6543 }
6544 catch (Exception ex)
6545 {
6547 }
6548 }
6549 }
6550 catch (Exception ex)
6551 {
6552 await e.IqError(ex, e.To);
6553 }
6554 }
6555
6556 internal async Task<Contract> SignContract(Contract Contract, bool ContractIsLocked, CaseInsensitiveString Role, bool Transferable,
6557 byte[] Signature, XmppAddress SignerJid)
6558 {
6559 if (Contract.PartsMode == ContractParts.TemplateOnly)
6560 throw new Exception("Contract is a template only, and cannot be signed.");
6561
6562 switch (Contract.State)
6563 {
6564 case ContractState.Obsoleted:
6565 throw new Exception("Contract is obsoleted.");
6566
6567 case ContractState.Deleted:
6568 throw new Exception("Contract is deleted.");
6569
6570 case ContractState.Proposed:
6571 throw new Exception("Contract is not yet approved to be signed.");
6572
6573 case ContractState.Rejected:
6574 throw new Exception("Contract proposal has been rejected.");
6575
6576 case ContractState.Failed:
6577 throw new Exception("Contract proposal has failed.");
6578 }
6579
6580 DateTime Now = DateTime.Now;
6581 if (Contract.SignAfter.HasValue && Now < Contract.SignAfter)
6582 throw new Exception("Contract will be open for signatures after " + Contract.SignAfter.ToString());
6583
6584 if (Contract.SignBefore.HasValue && Now > Contract.SignBefore)
6585 throw new Exception("Contract closed for signatures as if " + Contract.SignBefore.ToString());
6586
6587 KeyValuePair<Parameter, string> P = await Contract.CheckParameters(this, false);
6588 Parameter FailingParameter = P.Key;
6589
6590 if (!(FailingParameter is null))
6591 throw new Exception("Contract parameter " + FailingParameter.Name + " contains invalid value: " + P.Value);
6592
6593 int NrSignaturesForRole = 0;
6594 bool RoleFound = false;
6595
6596 if (!(Contract.ClientSignatures is null))
6597 {
6598 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6599 {
6600 if (ClientSignature.Role == Role)
6601 NrSignaturesForRole++;
6602 }
6603 }
6604
6605 if (!(Contract.Roles is null))
6606 {
6607 foreach (Role Role2 in Contract.Roles)
6608 {
6609 if (Role2.Name == Role)
6610 {
6611 if (NrSignaturesForRole >= Role2.MaxCount)
6612 throw new Exception("No more signatures of specified role allowed.");
6613
6614 RoleFound = true;
6615 break;
6616 }
6617 }
6618 }
6619
6620 if (!RoleFound)
6621 throw new Exception("No such role defined in contract.");
6622
6623 StringBuilder Xml = new StringBuilder();
6624 Contract.Serialize(Xml, false, false, false, false, false, false, false, null, this);
6625 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
6626
6627 InternalProcessing Request = new InternalProcessing(string.Empty);
6628 (LegalIdentity Identity, _) = await this.ValidateSenderSignature(SignerJid, Request, Now, Data, Signature, null); // TODO: Authorize access to requestor attachments
6629 if (Identity is null)
6630 {
6631 string Msg = Request.ErrorMessage;
6632 if (string.IsNullOrEmpty(Msg))
6633 Msg = "Unable to validate sender signature.";
6634
6635 throw new Exception(Msg);
6636 }
6637
6638 if (Contract.PartsMode == ContractParts.ExplicitlyDefined)
6639 {
6640 bool InList = false;
6641
6642 if (!(Contract.Parts is null))
6643 {
6644 foreach (Part Part in Contract.Parts)
6645 {
6646 if (Part.LegalId == Identity.Id && Part.Role == Role)
6647 {
6648 InList = true;
6649 break;
6650 }
6651 }
6652 }
6653
6654 if (!InList)
6655 throw new Exception("Legal identity not in explicitly defined list of parts in contract.");
6656 }
6657
6658 if (!(Contract.ClientSignatures is null))
6659 {
6660 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6661 {
6662 if (ClientSignature.LegalId == Identity.Id && ClientSignature.Role == Role)
6663 return Contract; // Contract already signed. Return successful response without altering the contract.
6664 }
6665 }
6666
6667 DateTime SignatureTimestamp = DateTime.Now;
6668 string s = Contract.TrySetRoleParameters(Role, NrSignaturesForRole + 1, Identity, SignatureTimestamp, Signature);
6669 if (!string.IsNullOrEmpty(s))
6670 throw new Exception("Legal identity does not have required property " + s + " defined.");
6671
6672 int c = (Contract.ClientSignatures?.Length ?? 0) + 1;
6673 ClientSignature[] Signatures = new ClientSignature[c];
6674
6675 Contract.ClientSignatures?.CopyTo(Signatures, 0);
6676 Signatures[c - 1] = new ClientSignature()
6677 {
6678 BareJid = SignerJid.BareJid,
6679 LegalId = Identity.Id,
6680 Role = Role,
6681 DigitalSignature = Signature,
6682 Timestamp = SignatureTimestamp,
6683 Transferable = Transferable
6684 };
6685
6686 Contract.ClientSignatures = Signatures;
6687
6688 if (Contract.From == DateTime.MinValue)
6689 {
6690 if (await Contract.IsLegallyBinding(false, true, this))
6691 {
6692 Contract.State = ContractState.Signed;
6693 Contract.From = Contract.Created;
6694
6695 if (Contract.Duration.HasValue)
6696 Contract.To = Contract.From + Contract.Duration.Value;
6697 else
6698 Contract.To = Contract.From;
6699 }
6700 else
6701 Contract.State = ContractState.BeingSigned;
6702 }
6703
6704 Contract.Sign(this);
6705
6706 await Database.Update(Contract);
6707
6709 {
6710 ContractId = Contract.ContractId,
6711 LegalId = Identity.Id,
6712 BareJid = SignerJid.BareJid
6713 };
6714
6716
6717 Log.Informational("Contract signed as " + Role.Value + ".",
6718 Contract.ContractId.Value, Identity.Id.Value, "ContractSigned",
6719 new KeyValuePair<string, object>("BareJid", SignerJid.BareJid.Value),
6720 new KeyValuePair<string, object>("Role", Role.Value),
6721 new KeyValuePair<string, object>("Transferable", Transferable));
6722
6723 try
6724 {
6725 string ContractNamespace = NamespaceSmartContracts(Contract.Version);
6726 Dictionary<CaseInsensitiveString, string> StatusRecipients = new Dictionary<CaseInsensitiveString, string>()
6727 {
6728 { Contract.Account + "@" + this.Server.Domain, ContractNamespace }
6729 };
6730
6731 if (!(Contract.ClientSignatures is null))
6732 {
6733 foreach (ClientSignature ClientSignature in Contract.ClientSignatures)
6734 {
6735 StatusRecipients[ClientSignature.BareJid] = ContractNamespace;
6736
6738
6739 if (!this.Server.IsServerDomain(Addr.Domain, true))
6740 {
6741 KeyValuePair<CaseInsensitiveString, string> P2 = await this.GetComponent(Addr.Domain, NamespacesSmartContracts);
6744 StatusRecipients[Component] = P2.Value;
6745 }
6746 }
6747 }
6748
6749 foreach (KeyValuePair<CaseInsensitiveString, string> P3 in StatusRecipients)
6750 {
6751 CaseInsensitiveString StatusRecipient = P3.Key;
6752 string Namespace = P3.Value;
6753 Xml.Clear();
6754
6755 Xml.Append("<contractSigned xmlns='");
6756 Xml.Append(Namespace);
6757 Xml.Append("' contractId='");
6758 Xml.Append(XML.Encode(Contract.ContractId));
6759 Xml.Append("' legalId='");
6760 Xml.Append(XML.Encode(Identity.Id));
6761 Xml.Append("' role='");
6762 Xml.Append(XML.Encode(Role));
6763 Xml.Append("'/>");
6764
6765 string Event = Xml.ToString();
6766
6767 try
6768 {
6769 await this.Server.SendMessage(string.Empty, string.Empty, this.MainDomain, new XmppAddress(StatusRecipient),
6770 string.Empty, Event);
6771 }
6772 catch (Exception ex)
6773 {
6775 }
6776 }
6777 }
6778 catch (Exception ex)
6779 {
6781 }
6782
6783 try
6784 {
6785 await this.ContractSigned(Contract, ContractIsLocked, this.eDaler, this.pubsub);
6786 }
6787 catch (Exception ex)
6788 {
6790 }
6791
6792 return Contract;
6793 }
6794
6802 private async Task ContractSigned(Contract Contract, bool ContractIsLocked,
6804 {
6805 if (!this.transientParameters.TryGetValue(Contract.ContractId,
6806 out Dictionary<CaseInsensitiveString, Parameter> TransientParameters))
6807 {
6808 TransientParameters = null;
6809 }
6810
6811 switch (Contract.ForMachinesNamespace)
6812 {
6814 await MarketplaceProcessor.ContractSigned(Contract, ContractIsLocked,
6815 TransientParameters, this, EDaler, PubSub);
6816 break;
6817
6819 await NeuroFeaturesProcessor.ContractSigned(Contract, ContractIsLocked,
6820 TransientParameters, this, EDaler);
6821 break;
6822
6824 await PaiwiseProcessor.ContractSigned(Contract, ContractIsLocked,
6825 TransientParameters, this, EDaler);
6826 break;
6827 }
6828
6829 if (Contract.State == ContractState.Signed)
6830 {
6831 foreach (Payment Payment in await Database.Find<Payment>(
6832 new FilterFieldEqualTo("ConditionContractId", Contract.ContractId)))
6833 {
6834 if (!Payment.Processed.HasValue)
6835 PaiwiseProcessor.QueueForProcessing(Payment, EDaler);
6836 }
6837 }
6838 }
6839
6840 internal async Task<LegalIdentity> ValidateLocalSenderSignature(XmppAddress Sender, IqEventArgs e, DateTime Timestamp, Stream Data, byte[] Signature)
6841 {
6842 if (this.Server.IsServerDomain(Sender.Domain, true))
6843 {
6844 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
6845 new FilterFieldEqualTo("Account", Sender.Account),
6846 new FilterFieldEqualTo("State", IdentityState.Approved),
6847 new FilterFieldLesserOrEqualTo("From", Timestamp),
6848 new FilterFieldGreaterOrEqualTo("To", Timestamp)), "-Created"))
6849 {
6850 if (ID.ValidateSignature(Data, Signature))
6851 return ID;
6852 }
6853
6854 e?.IqErrorForbidden(e.To, "Signature proving access to private keys not correct.", "en");
6855 }
6856 else
6857 e?.IqErrorForbidden(e.To, "Sender must correspond to an account on the local machine.", "en");
6858
6859 return null;
6860 }
6861
6862 internal async Task<(LegalIdentity, Dictionary<string, string>)> ValidateSenderSignature(XmppAddress Sender,
6863 EDalerUriState Request, DateTime Timestamp, byte[] Data, byte[] Signature, string ForBareJid)
6864 {
6865 LegalIdentity Identity = null;
6866 Dictionary<string, string> AttachmentUrls = null;
6867
6868 if (this.Server.IsServerDomain(Sender.Domain, true))
6869 {
6870 bool IdFound = false;
6871
6872 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
6873 new FilterFieldEqualTo("Account", Sender.Account),
6874 new FilterFieldEqualTo("State", IdentityState.Approved),
6875 new FilterFieldLesserOrEqualTo("From", Timestamp),
6876 new FilterFieldGreaterOrEqualTo("To", Timestamp)), "-Created"))
6877 {
6878 IdFound = true;
6879
6880 if (ID.ValidateSignature(Data, Signature))
6881 {
6882 if (!string.IsNullOrEmpty(ForBareJid))
6883 this.IdentityAuthorization(ForBareJid, Sender.BareJid, ID.Id, true);
6884
6885 Identity = ID;
6886 AttachmentUrls = this.GetAttachmentUrls(ID);
6887 break;
6888 }
6889 }
6890
6891 if (!IdFound)
6892 {
6893 Request?.Error(EDalerUriErrorType.ServiceUnavailable, "No approved legal identities found for " +
6894 Sender.BareJid + ".", false);
6895
6896 return (null, null);
6897 }
6898 }
6899 else
6900 {
6901 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(Sender.Domain, NamespacesLegalIdentity);
6902 CaseInsensitiveString RemoteLegalComponent = P.Key;
6903 string RemoteNamespace = P.Value;
6904 NamespaceSet RemoteVersion = XmppServerModule.GetVersion(RemoteNamespace);
6905
6906 if (CaseInsensitiveString.IsNullOrEmpty(RemoteLegalComponent))
6907 {
6908 Request?.Error(EDalerUriErrorType.ServiceUnavailable, this.MainDomain.Address + " could not find legal component of " + Sender.Domain + ", to validate signature.", false);
6909 return (null, null);
6910 }
6911
6912 if (string.IsNullOrEmpty(Gateway.Domain) && !(Gateway.ContractsClient is null)) // Development mode. S2S connections not available. Need to perform validation using C2S.
6913 {
6914 Networking.XMPP.Contracts.LegalIdentity Identity2 = await Gateway.ContractsClient.ValidateSignatureAsync(string.Empty, Data, Signature);
6915 if (Identity2 is null)
6916 {
6917 Request?.Error(EDalerUriErrorType.Forbidden, "Signature does not match any of the approved legal identities of " +
6918 Sender.BareJid + ".", false);
6919 }
6920
6921 StringBuilder sb = new StringBuilder();
6922 Identity2.Serialize(sb, true, true, true, true, true, true, true);
6923
6924 XmlDocument Doc = new XmlDocument()
6925 {
6926 PreserveWhitespace = true
6927 };
6928 Doc.LoadXml(sb.ToString());
6929
6930 Identity = LegalIdentity.Parse(Doc.DocumentElement, out _, out AttachmentUrls);
6931 }
6932 else
6933 {
6934 IqResultEventArgs e2 = await this.ValidateRemoteSignature(RemoteLegalComponent, Sender.BareJid, Data, Signature, ForBareJid, RemoteVersion);
6935 if (e2 is null)
6936 {
6937 Request?.Error(EDalerUriErrorType.ServiceUnavailable, this.MainDomain.Address + " was unable to validate remote signature.", false);
6938 return (null, null);
6939 }
6940
6941 if (!e2.Ok || e2.FirstElement is null)
6942 {
6943 Request?.Error(e2.ErrorTypeString, e2.ErrorElement?.OuterXml ?? string.Empty, e2.ErrorText);
6944 return (null, null);
6945 }
6946
6947 Identity = LegalIdentity.Parse(e2.FirstElement, out bool _, out AttachmentUrls);
6948 }
6949 }
6950
6951 if (Identity is null)
6952 {
6953 Request?.Error(EDalerUriErrorType.Forbidden, "Signature does not match any of the approved legal identities of " +
6954 Sender.BareJid + ".", false);
6955 }
6956
6957 return (Identity, AttachmentUrls);
6958 }
6959
6960 internal Dictionary<string, string> GetAttachmentUrls(LegalIdentity Identity)
6961 {
6962 if (Identity.Attachments is null)
6963 return null;
6964
6965 Dictionary<string, string> Urls = new Dictionary<string, string>();
6966
6967 foreach (AttachmentReference Ref in Identity.Attachments)
6968 Urls[Ref.Id] = Gateway.GetUrl("/Attachments/" + Ref.Id.Value, this.HttpServer);
6969
6970 return Urls;
6971 }
6972
6973 internal async Task<(LegalIdentity, Dictionary<string, string>)> ValidateSignature(XmppAddress Signatory, DateTime Timestamp, byte[] Data,
6974 byte[] Signature)
6975 {
6976 LegalIdentity Identity = null;
6977 Dictionary<string, string> AttachmentUrls;
6978
6979 if (this.Server.IsServerDomain(Signatory.Domain, true))
6980 {
6981 foreach (LegalIdentity ID in await Database.Find<LegalIdentity>(new FilterAnd(
6982 new FilterFieldEqualTo("Account", Signatory.Account),
6983 new FilterFieldEqualTo("State", IdentityState.Approved),
6984 new FilterFieldLesserOrEqualTo("From", Timestamp),
6985 new FilterFieldGreaterOrEqualTo("To", Timestamp)), "-Created"))
6986 {
6987 if (ID.ValidateSignature(Data, Signature))
6988 {
6989 Identity = ID;
6990 AttachmentUrls = this.GetAttachmentUrls(ID);
6991 break;
6992 }
6993 }
6994
6995 AttachmentUrls = null;
6996 }
6997 else
6998 {
6999 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(Signatory.Domain, NamespacesLegalIdentity);
7000 CaseInsensitiveString RemoteLegalComponent = P.Key;
7001
7002 if (CaseInsensitiveString.IsNullOrEmpty(RemoteLegalComponent))
7003 return (null, null);
7004
7005 NamespaceSet RemoteVersion = XmppServerModule.GetVersion(RemoteLegalComponent);
7006
7007 IqResultEventArgs e2 = await this.ValidateRemoteSignature(RemoteLegalComponent, Signatory.BareJid, Data, Signature, null, RemoteVersion);
7008 if (e2 is null || !e2.Ok || e2.FirstElement is null)
7009 return (null, null);
7010
7011 Identity = LegalIdentity.Parse(e2.FirstElement, out _, out AttachmentUrls);
7012 }
7013
7014 return (Identity, AttachmentUrls);
7015 }
7016
7017 private async Task<IqResultEventArgs> ValidateRemoteSignature(string RemoteLegalComponent, string BareJid, byte[] Data, byte[] Signature,
7018 string ForBareJid, NamespaceSet Version)
7019 {
7020 TaskCompletionSource<IqResultEventArgs> T = new TaskCompletionSource<IqResultEventArgs>();
7021 StringBuilder Xml = new StringBuilder();
7022
7023 Xml.Clear();
7024 Xml.Append("<validateSignature data=\"");
7025 Xml.Append(Convert.ToBase64String(Data));
7026 Xml.Append("\" s=\"");
7027 Xml.Append(Convert.ToBase64String(Signature));
7028 Xml.Append("\" bareJid=\"");
7029 Xml.Append(XML.Encode(BareJid));
7030
7031 if (!string.IsNullOrEmpty(ForBareJid))
7032 {
7033 Xml.Append("\" for=\"");
7034 Xml.Append(XML.Encode(ForBareJid));
7035 }
7036
7037 Xml.Append("\" xmlns=\"");
7038 Xml.Append(NamespaceLegalIdentity(Version));
7039 Xml.Append("\"/>");
7040
7041 if (!await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(RemoteLegalComponent), string.Empty,
7042 Xml.ToString(), (sender2, e2) =>
7043 {
7044 T.TrySetResult(e2);
7045 return Task.CompletedTask;
7046 }, null))
7047 {
7048 return null;
7049 }
7050
7051 return await T.Task;
7052 }
7053
7054 private async Task ContractSignedHandler(object Sender, MessageEventArgs e)
7055 {
7056 CaseInsensitiveString ContractId = XML.Attribute(e.Content, "contractId");
7057 CaseInsensitiveString LegalId = XML.Attribute(e.Content, "legalId");
7058
7059 // Note: Which role has been signed is not important in S2S communication.
7060
7061 XmppAddress ContractAddress = new XmppAddress(ContractId);
7062
7063 if (ContractAddress.Domain != e.From.Domain)
7064 {
7065 Log.Warning("Client signature message ignored. Source domain not equal to contract domain.");
7066 return;
7067 }
7068
7069 XmppAddress LegalAddress = new XmppAddress(LegalId);
7070 if (!this.IsComponentDomain(LegalAddress.Domain, true))
7071 {
7072 Log.Warning("Client signature message ignored. Destination domain not equal to domain of legal identity signing the contract.");
7073 return;
7074 }
7075
7076 Contract Contract = await this.GetContract(ContractId);
7077 if (Contract is null)
7078 {
7079 Log.Warning("Client signature message ignored. Unable to get contract.");
7080 return;
7081 }
7082
7083 if (Contract.ClientSignatures is null)
7084 {
7085 Log.Warning("Client signature message ignored. No recorded client signatures in contract.");
7086 return;
7087 }
7088
7089 CaseInsensitiveString BareJid = null;
7090
7091 foreach (ClientSignature Signature in Contract.ClientSignatures)
7092 {
7093 if (Signature.LegalId == LegalId)
7094 {
7095 BareJid = Signature.BareJid;
7096 break;
7097 }
7098 }
7099
7100 if (BareJid is null)
7101 {
7102 Log.Warning("Client signature message ignored. Legal Identity not in list of client signatures.");
7103 return;
7104 }
7105
7106 if (!await this.ValidateServerSignature(Contract))
7107 {
7108 Log.Warning("Client signature message ignored. Server signature not valid.");
7109 return;
7110 }
7111
7113 {
7114 ContractId = Contract.ContractId,
7115 LegalId = LegalId,
7116 BareJid = BareJid
7117 };
7118
7120 }
7121
7122 private Task<bool> ValidateServerSignature(Contract Contract)
7123 {
7124 if (Contract.ServerSignature is null || Contract.ServerSignature.DigitalSignature is null)
7125 return Task.FromResult(false);
7126
7127 StringBuilder Xml = new StringBuilder();
7128 Contract.Serialize(Xml, false, true, true, true, true, false, false, null, this);
7129 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
7130 XmppAddress ContractAddress = new XmppAddress(Contract.ContractId);
7131
7132 return this.ValidateServerSignature(ContractAddress.Domain, Data, Contract.ServerSignature.DigitalSignature);
7133 }
7134
7135 public async Task<bool> ValidateServerSignature(CaseInsensitiveString ServerDomain, byte[] Data, byte[] Signature)
7136 {
7137 if (this.IsComponentDomain(ServerDomain, true))
7138 return LedgerConfiguration.Verify(Data, Signature);
7139 else
7140 {
7141 Networking.XMPP.IE2eEndpoint PubKey = await this.GetPublicKey(ServerDomain);
7142 return PubKey.Verify(Data, Signature);
7143 }
7144 }
7145
7146 public async Task<Networking.XMPP.IE2eEndpoint> GetPublicKey(string ServerDomain)
7147 {
7148 string Key = ServerDomain + "|pub";
7149
7150 if (this.remoteComponents.TryGetValue(Key, out object Obj) && Obj is Networking.XMPP.IE2eEndpoint PubKey)
7151 return PubKey;
7152
7153 TaskCompletionSource<Networking.XMPP.IE2eEndpoint> Result = new TaskCompletionSource<Networking.XMPP.IE2eEndpoint>();
7154 string Namespace = NamespaceLegalIdentity(NamespaceSet.Current);
7155
7156 await this.Server.SendIqRequest("get", this.MainDomain, new XmppAddress(ServerDomain), string.Empty,
7157 "<getPublicKey xmlns=\"" + Namespace + "\"/>", (Sender, e) =>
7158 {
7159 Networking.XMPP.IE2eEndpoint ServerKey = null;
7160 XmlElement E;
7161
7162 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "publicKey" && E.NamespaceURI == Namespace)
7163 {
7164 foreach (XmlNode N in E.ChildNodes)
7165 {
7166 if (N is XmlElement E2)
7167 {
7168 ServerKey = EndpointSecurity.ParseE2eKey(E2);
7169 if (!(ServerKey is null))
7170 {
7171 Result.TrySetResult(ServerKey);
7172 return Task.CompletedTask;
7173 }
7174 }
7175 }
7176 }
7177
7178 Result.TrySetResult(null);
7179 return Task.CompletedTask;
7180 }, null);
7181
7182 await Result.Task;
7183
7184 PubKey = Result.Task.Result;
7185 if (!(PubKey is null))
7186 this.remoteComponents[Key] = PubKey;
7187
7188 return PubKey;
7189 }
7190
7191 private async Task GetSignedContractsHandler(object Sender, IqEventArgs e)
7192 {
7193 try
7194 {
7195 int Offset = XML.Attribute(e.Query, "offset", 0);
7196 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
7197 bool References = XML.Attribute(e.Query, "references", true);
7198 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
7199
7200 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(Offset, MaxCount,
7201 new FilterFieldEqualTo("BareJid", e.From.BareJid), "LegalId", "ContractId");
7202
7203 string Xml = await this.SerializeContractReferences(Signatures, References, QueryVersion);
7204
7205 await e.IqResult(Xml, e.To);
7206 }
7207 catch (Exception ex)
7208 {
7209 await e.IqError(ex, e.To);
7210 }
7211 }
7212
7213 private async Task GetContractHandler(object Sender, IqEventArgs e)
7214 {
7215 try
7216 {
7217 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
7218
7219 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
7220 {
7221 await e.IqErrorBadRequest(e.To, "No Contract ID specified.", "en");
7222 return;
7223 }
7224
7225 // Note: Do not log the contract for reading for simply trying to get
7226 // the current state. While the contract is locked for writing
7227 // while being signed, external parties may need to get the contract
7228 // to validate signatures, for example, for validating signatures
7229 // perfor approving payments.
7230
7231 //using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
7232 {
7233 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7234 if (Contract is null)
7235 {
7236 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7237 return;
7238 }
7239
7240 if (!await Contract.CanRead(e.From, this.Server, this))
7241 {
7242 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, ContractId, false);
7243 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
7244 return;
7245 }
7246
7247 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, ContractId, true);
7248
7249 StringBuilder Xml = new StringBuilder();
7250 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
7251 await e.IqResult(Xml.ToString(), e.To);
7252 }
7253 }
7254 catch (Exception ex)
7255 {
7256 await e.IqError(ex, e.To);
7257 }
7258 }
7259
7260 private async Task GetContractsHandler(object Sender, IqEventArgs e)
7261 {
7262 try
7263 {
7264 StringBuilder Xml = new StringBuilder();
7265
7266 Xml.Append("<contracts xmlns='");
7267 Xml.Append(e.Query.NamespaceURI);
7268 Xml.Append("'>");
7269
7270 foreach (XmlNode N in e.Query.ChildNodes)
7271 {
7272 if (N is XmlElement E && E.LocalName == "ref" && E.NamespaceURI == e.Query.NamespaceURI)
7273 {
7274 CaseInsensitiveString Id = XML.Attribute(E, "id");
7275 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", Id), "Created");
7276
7277 if (!(Contract is null) &&
7278 await Contract.CanRead(e.From, this.Server, this))
7279 {
7280 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Id, true);
7281 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
7282 }
7283 else
7284 {
7285 if (!(Contract is null))
7286 this.ContractAuthorization(e.From.BareJid, e.From.BareJid, Id, false);
7287
7288 Xml.Append("<ref id='");
7289 Xml.Append(XML.Encode(Id));
7290 Xml.Append("'/>");
7291 }
7292 }
7293 }
7294
7295 Xml.Append("</contracts>");
7296
7297 await e.IqResult(Xml.ToString(), e.To);
7298 }
7299 catch (Exception ex)
7300 {
7301 await e.IqError(ex, e.To);
7302 }
7303 }
7304
7305 private async Task IsPartHandler(object Sender, IqEventArgs e)
7306 {
7307 try
7308 {
7309 CaseInsensitiveString BareJid = XML.Attribute(e.Query, "bareJid");
7310
7312 {
7313 await e.IqErrorBadRequest(e.To, "No Bare JID specified.", "en");
7314 return;
7315 }
7316
7317 XmppAddress BareAddress = new XmppAddress(BareJid);
7318 if (!BareAddress.IsBareJID)
7319 {
7320 await e.IqErrorBadRequest(e.To, "Invalid Bare JID.", "en");
7321 return;
7322 }
7323
7324 if (!this.Server.IsServerDomain(BareAddress.Domain, true))
7325 {
7326 await e.IqErrorBadRequest(e.To, "Bare JID not registered on server.", "en");
7327 return;
7328 }
7329
7330 if (!e.From.IsDomain)
7331 {
7332 await e.IqErrorForbidden(e.To, "Not authorized to execute request.", "en");
7333 return;
7334 }
7335
7336 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
7337 KeyValuePair<CaseInsensitiveString, string> P = await this.GetComponent(e.From.Address, NamespacesLegalIdentity);
7339 string Namespace = P.Value;
7340
7341 if (Component != e.From.Address)
7342 {
7343 await e.IqErrorForbidden(e.To, "Not authorized to execute request.", "en");
7344 return;
7345 }
7346
7347 foreach (XmlNode N in e.Query.ChildNodes)
7348 {
7349 if (N is XmlElement E && E.LocalName == "idRef")
7350 {
7351 CaseInsensitiveString LegalId = XML.Attribute(E, "id");
7353 continue;
7354
7355 XmppAddress LegalIdAddress = new XmppAddress(LegalId);
7356 if (!this.IsComponentDomain(LegalIdAddress.Domain, true))
7357 continue;
7358
7359 using (Semaphore Semaphore = await Semaphores.BeginRead("iotid:" + LegalId.LowerCase))
7360 {
7361 LegalIdentity Identity = await GetLocalLegalIdentity(LegalId);
7362 if (Identity is null)
7363 continue;
7364
7365 if (Identity.Account == BareAddress.Account)
7366 {
7367 await e.IqResult("<part xmlns='" + e.Query.NamespaceURI + "'>true</part>", e.To);
7368 return;
7369 }
7370 }
7371 }
7372 }
7373
7374 await e.IqResult("<part xmlns='" + e.Query.NamespaceURI + "'>false</part>", e.To);
7375 }
7376 catch (Exception ex)
7377 {
7378 await e.IqError(ex, e.To);
7379 }
7380 }
7381
7382 private async Task ObsoleteContractHandler(object Sender, IqEventArgs e)
7383 {
7384 try
7385 {
7386 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
7387
7388 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase))
7389 {
7390 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7391 if (Contract is null)
7392 {
7393 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7394 return;
7395 }
7396
7397 switch (await Contract.CanRevoke(e.From, this.Server, this))
7398 {
7399 case 0: // OK, creator, contract not legally biding.
7400 case 1: // OK, role dictates part can revoke contract/consent.
7401 break;
7402
7403 case 2:
7404 await e.IqErrorForbidden(e.To, "Contract is legally binding and cannot be obsoleted.", "en");
7405 return;
7406
7407 case 3:
7408 await e.IqErrorForbidden(e.To, "Only the creator can obsolete a contract.", "en");
7409 return;
7410
7411 case 4:
7412 await e.IqErrorForbidden(e.To, "Not a part in the contract.", "en");
7413 return;
7414
7415 case 5:
7416 await e.IqErrorForbidden(e.To, "Your role cannot revoke signature/consent.", "en");
7417 return;
7418 }
7419
7420 Contract.State = ContractState.Obsoleted;
7421 Contract.Sign(this);
7422
7423 await Database.Update(Contract);
7424
7425 Log.Informational("Contract obsoleted.", Contract.ContractId.Value, e.From.Address.Value, "ContractObsoleted");
7426
7427 StringBuilder Xml = new StringBuilder();
7428 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
7429 await e.IqResult(Xml.ToString(), e.To);
7430
7431 await this.SendContractUpdatedEvent(Contract, false);
7432 }
7433 }
7434 catch (Exception ex)
7435 {
7436 await e.IqError(ex, e.To);
7437 }
7438 }
7439
7440 internal async Task SendContractUpdatedEvent(Contract Contract, bool Created)
7441 {
7442 StringBuilder Xml = new StringBuilder();
7443
7444 Xml.Append("<contract");
7445 Xml.Append(Created ? "Created" : "Updated");
7446 Xml.Append(" xmlns='");
7447 Xml.Append(NamespaceSmartContracts(Contract.Version));
7448 Xml.Append("' contractId='");
7449 Xml.Append(XML.Encode(Contract.ContractId));
7450 Xml.Append("'/>");
7451
7452 string Event = Xml.ToString();
7453
7454 foreach (string StatusRecipient in Contract.GetStakeholders(this.Server))
7455 await this.Server.SendMessage(string.Empty, string.Empty, Contract.Provider, StatusRecipient, string.Empty, Event);
7456 }
7457
7458 private async Task DeleteContractHandler(object Sender, IqEventArgs e)
7459 {
7460 try
7461 {
7462 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
7463
7464 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase))
7465 {
7466 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7467 if (Contract is null)
7468 {
7469 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7470 return;
7471 }
7472
7473 if (!Contract.IsCreator(e.From, this.Server))
7474 {
7475 await e.IqErrorForbidden(e.To, "Only the creator can delete a contract.", "en");
7476 return;
7477 }
7478
7479 if (await Contract.IsLegallyBinding(true, false, this))
7480 {
7481 await e.IqErrorForbidden(e.To, "Contract is legally binding and cannot be deleted.", "en");
7482 return;
7483 }
7484
7485 if (await Contract.IsLegallyBinding(false, false, this) && DateTime.Now < Contract.To + Contract.ArchiveRequired)
7486 {
7487 await e.IqErrorForbidden(e.To, "Contract cannot be deleted before its required archivation period expires.", "en");
7488 return;
7489 }
7490
7491 Contract.State = ContractState.Deleted;
7492 Contract.Sign(this);
7493
7494 await Database.Delete(Contract);
7495 await this.DeleteAttachments(Contract.Attachments);
7496 Contract.Attachments = null;
7497
7498 Log.Informational("Contract deleted.", Contract.ContractId.Value, e.From.Address.Value, "ContractDeleted");
7499
7500 StringBuilder Xml = new StringBuilder();
7501 Contract.Serialize(Xml, true, true, true, false, true, true, false, null, this);
7502 await e.IqResult(Xml.ToString(), e.To);
7503
7504 await this.SendContractDeletedEvent(Contract);
7505 }
7506 }
7507 catch (Exception ex)
7508 {
7509 await e.IqError(ex, e.To);
7510 }
7511 }
7512
7513 private async Task DeleteAttachments(params AttachmentReference[] AttachmentReferences)
7514 {
7515 if (!(AttachmentReferences is null))
7516 {
7517 foreach (AttachmentReference Ref in AttachmentReferences)
7518 {
7519 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", Ref.Id));
7520 if (!(Attachment is null))
7521 {
7522 await Database.Delete(Attachment);
7523
7524 if (File.Exists(Attachment.LocalFileName))
7525 {
7526 try
7527 {
7528 File.Delete(Attachment.LocalFileName);
7529 }
7530 catch (Exception ex)
7531 {
7532 Log.Exception(ex);
7533 }
7534 }
7535 }
7536 }
7537 }
7538 }
7539
7540 private async Task SendContractDeletedEvent(Contract Contract)
7541 {
7542 StringBuilder Xml = new StringBuilder();
7543
7544 Xml.Append("<contractDeleted xmlns='");
7545 Xml.Append(NamespaceSmartContracts(Contract.Version));
7546 Xml.Append("' contractId='");
7547 Xml.Append(XML.Encode(Contract.ContractId));
7548 Xml.Append("'/>");
7549
7550 string Event = Xml.ToString();
7551
7552 foreach (string StatusRecipient in Contract.GetStakeholders(this.Server))
7553 await this.Server.SendMessage(string.Empty, string.Empty, Contract.Provider, StatusRecipient, string.Empty, Event);
7554 }
7555
7556 private async Task UpdateContractHandler(object Sender, IqEventArgs e)
7557 {
7558 try
7559 {
7560 if (!this.Server.IsServerDomain(e.From.Domain, true))
7561 {
7562 await e.IqErrorForbidden(e.To, "Only accounts on the broker can manage contracts on the broker.", "en");
7563 return;
7564 }
7565
7566 Contract UpdatedContract = null;
7567 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
7568 string Namespace = NamespaceSmartContracts(QueryVersion);
7569
7570 foreach (XmlNode N in e.Query.ChildNodes)
7571 {
7572 if (N is XmlElement E && E.LocalName == "contract" && E.NamespaceURI == Namespace)
7573 {
7574 ParsedContract Parsed = await Contract.Parse(E, this);
7575 UpdatedContract = Parsed?.Contract;
7576
7577 if (UpdatedContract is null)
7578 {
7579 await e.IqErrorBadRequest(e.To, "Invalid contract.", "en");
7580 return;
7581 }
7582
7583 bool HasStatus = Parsed.HasStatus;
7584 bool ParametersValid = Parsed.ParametersValid;
7585
7586 if (HasStatus)
7587 {
7588 await e.IqErrorBadRequest(e.To, "Status element not permitted when updating contract.", "en");
7589 return;
7590 }
7591
7592 if (!ParametersValid)
7593 {
7594 await e.IqErrorBadRequest(e.To, "Contract parameter " + Parsed.FirstParameterErrorName +
7595 " has invalid value: " + Parsed.FirstParameterError, "en");
7596 return;
7597 }
7598
7599 break;
7600 }
7601 }
7602
7603 if (UpdatedContract is null)
7604 {
7605 await e.IqErrorBadRequest(e.To, "No contract.", "en");
7606 return;
7607 }
7608
7609 CaseInsensitiveString ContractId = UpdatedContract.ContractId;
7610 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
7611 {
7612 await e.IqErrorBadRequest(e.To, "id attribute must be specified by client.", "en");
7613 return;
7614 }
7615
7616 if (!(UpdatedContract.ClientSignatures is null) && UpdatedContract.ClientSignatures.Length > 0)
7617 {
7618 await e.IqErrorBadRequest(e.To, "Cannot update a contract with signatures.", "en");
7619 return;
7620 }
7621
7622 if (!(UpdatedContract.ServerSignature is null))
7623 {
7624 await e.IqErrorBadRequest(e.To, "Server signature cannot be provided by client.", "en");
7625 return;
7626 }
7627
7628 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase))
7629 {
7630 Contract PrevContract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7631 if (PrevContract is null)
7632 {
7633 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7634 return;
7635 }
7636
7637 if (!PrevContract.IsCreator(e.From, this.Server))
7638 {
7639 await e.IqErrorForbidden(e.To, "Only the creator can update the contract.", "en");
7640 return;
7641 }
7642
7643 if (!(PrevContract.ClientSignatures is null) && PrevContract.ClientSignatures.Length > 0)
7644 {
7645 await e.IqErrorBadRequest(e.To, "Cannot update a contract with signatures.", "en");
7646 return;
7647 }
7648
7649 switch (PrevContract.State)
7650 {
7651 case ContractState.Proposed:
7652 case ContractState.Rejected:
7653 case ContractState.Approved:
7654 case ContractState.Obsoleted:
7655 break;
7656
7657 case ContractState.BeingSigned:
7658 case ContractState.Deleted:
7659 case ContractState.Signed:
7660 case ContractState.Failed:
7661 default:
7662 await e.IqErrorForbidden(e.To, "Current state of the contract does not allow updates.", "en");
7663 return;
7664 }
7665
7666 LegalIdentity Identity = await this.GetCurrentApprovedLegalIdentityAsync(e.From.Account);
7667 if (Identity is null)
7668 {
7669 await e.IqErrorForbidden(e.To, "No current approved legal identity found for account.", "en");
7670 return;
7671 }
7672
7673 string Errors = await this.ValidateContent(UpdatedContract);
7674 if (!string.IsNullOrEmpty(Errors))
7675 {
7676 await e.IqErrorBadRequest(e.To, Errors, "en");
7677 return;
7678 }
7679
7680 UpdatedContract.ObjectId = PrevContract.ObjectId;
7681 UpdatedContract.TemplateId = PrevContract.TemplateId;
7682 UpdatedContract.Provider = PrevContract.Provider;
7683 UpdatedContract.Account = PrevContract.Account;
7684 UpdatedContract.Created = PrevContract.Created;
7685 UpdatedContract.Nonce = PrevContract.Nonce;
7686 UpdatedContract.Updated = NowSecond;
7687 UpdatedContract.Version = QueryVersion;
7688
7689 if (PrevContract.State == ContractState.Approved && !PrevContract.UpdateRequiresReview(UpdatedContract))
7690 UpdatedContract.State = ContractState.Approved;
7691 else
7692 UpdatedContract.State = ContractState.Proposed;
7693
7694 UpdatedContract.Sign(this);
7695
7696 await Database.Update(UpdatedContract);
7697
7698 Log.Informational("Contract updated.", UpdatedContract.ContractId.Value, e.From.Address.Value, "ContractUpdated");
7699
7700 StringBuilder Xml = new StringBuilder();
7701 UpdatedContract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
7702 await e.IqResult(Xml.ToString(), e.To);
7703
7704 await this.SendContractUpdatedEvent(UpdatedContract, false);
7705 }
7706 }
7707 catch (Exception ex)
7708 {
7709 await e.IqError(ex, e.To);
7710 }
7711 }
7712
7713 private async Task GetSchemasHandler(object Sender, IqEventArgs e)
7714 {
7715 try
7716 {
7717 StringBuilder Xml = new StringBuilder();
7718 CaseInsensitiveString LastNamespace = null;
7719
7720 Xml.Append("<schemas xmlns='");
7721 Xml.Append(e.Query.NamespaceURI);
7722 Xml.Append("'>");
7723
7724 foreach (ValidationSchema Schema in await Database.Find<ValidationSchema>("Namespace", "HashBase64"))
7725 {
7726 if (Schema.Namespace != LastNamespace)
7727 {
7728 if (!(LastNamespace is null))
7729 Xml.Append("</schemaRef>");
7730
7731 LastNamespace = Schema.Namespace;
7732
7733 Xml.Append("<schemaRef namespace='");
7734 Xml.Append(XML.Encode(LastNamespace));
7735 Xml.Append("'>");
7736 }
7737
7738 Xml.Append("<digest function='");
7739 Xml.Append(Schema.Function.ToString());
7740 Xml.Append("'>");
7741 Xml.Append(Schema.HashBase64);
7742 Xml.Append("</digest>");
7743 }
7744
7745 if (!(LastNamespace is null))
7746 Xml.Append("</schemaRef>");
7747
7748 Xml.Append("</schemas>");
7749
7750 await e.IqResult(Xml.ToString(), e.To);
7751 }
7752 catch (Exception ex)
7753 {
7754 await e.IqError(ex, e.To);
7755 }
7756 }
7757
7758 private async Task GetSchemaHandler(object Sender, IqEventArgs e)
7759 {
7760 try
7761 {
7762 HashFunction Function = HashFunction.SHA256;
7763 string DigestBase64 = null;
7764 CaseInsensitiveString Namespace = XML.Attribute(e.Query, "namespace");
7765
7766 if (CaseInsensitiveString.IsNullOrEmpty(Namespace))
7767 {
7768 await e.IqErrorBadRequest(e.To, "No namespace provided.", "en");
7769 return;
7770 }
7771
7772 foreach (XmlNode N in e.Query.ChildNodes)
7773 {
7774 if (N is XmlElement E && E.LocalName == "digest" && E.NamespaceURI == e.Query.NamespaceURI)
7775 {
7776 DigestBase64 = E.InnerText;
7777 if (!Enum.TryParse(XML.Attribute(E, "function"), out Function))
7778 {
7779 await e.IqErrorBadRequest(e.To, "Invalid hash function.", "en");
7780 return;
7781 }
7782 }
7783 }
7784
7785 ValidationSchema Schema;
7786
7787 if (DigestBase64 is null)
7788 {
7789 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(
7790 new FilterFieldEqualTo("Namespace", Namespace), "-Created");
7791 }
7792 else
7793 {
7794 Schema = await Database.FindFirstIgnoreRest<ValidationSchema>(new FilterAnd(
7795 new FilterFieldEqualTo("Namespace", Namespace),
7796 new FilterFieldEqualTo("HashBase64", DigestBase64),
7797 new FilterFieldEqualTo("Function", Function)), "-Created");
7798 }
7799
7800 if (Schema is null)
7801 {
7802 string ErrorMsg;
7803
7804 (Schema, ErrorMsg) = await this.LoadSchema(Namespace, null);
7805 if (!string.IsNullOrEmpty(ErrorMsg))
7806 await e.IqErrorBadRequest(e.To, "Unable to download schema: " + ErrorMsg, "en");
7807
7808 if (Schema is null)
7809 {
7810 await e.IqErrorItemNotFound(e.To, "Schema not found.", "en");
7811 return;
7812 }
7813 }
7814
7815 StringBuilder Xml = new StringBuilder();
7816
7817 Xml.Append("<schema xmlns='");
7818 Xml.Append(e.Query.NamespaceURI);
7819 Xml.Append("'>");
7820 Xml.Append(Convert.ToBase64String(Schema.XmlSchema));
7821 Xml.Append("</schema>");
7822
7823 await e.IqResult(Xml.ToString(), e.To);
7824 }
7825 catch (Exception ex)
7826 {
7827 await e.IqError(ex, e.To);
7828 }
7829 }
7830
7831 private async Task GetLegalIdentitiesOfContractHandler(object Sender, IqEventArgs e)
7832 {
7833 try
7834 {
7835 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
7836 bool Current = XML.Attribute(e.Query, "current", false);
7837 bool Historic = XML.Attribute(e.Query, "historic", true);
7838
7839 if (!(Current || Historic))
7840 {
7841 await e.IqErrorBadRequest(e.To, "Both current and historic attributes cannot both be false.", "en");
7842 return;
7843 }
7844
7845 XmppAddress ContractAddress = new XmppAddress(ContractId);
7846 List<LegalIdentity> Identities = new List<LegalIdentity>();
7847 List<Dictionary<string, string>> AttachmentUrlss = new List<Dictionary<string, string>>();
7848 LegalIdentity Identity, CurrentIdentity;
7849 DateTime Now = DateTime.Now;
7850
7851 if (this.IsComponentDomain(ContractAddress.Domain, true))
7852 {
7854
7855 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
7856 {
7857 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
7858 if (Contract is null)
7859 {
7860 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
7861 return;
7862 }
7863
7864 if (!await Contract.CanRead(e.From, this.Server, this))
7865 {
7866 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
7867 return;
7868 }
7869 }
7870
7871 if (!(Contract.ClientSignatures is null))
7872 {
7873 Dictionary<string, bool> Domains = null;
7874
7875 foreach (ClientSignature Signature in Contract.ClientSignatures)
7876 {
7877 XmppAddress LegalId = new XmppAddress(Signature.LegalId);
7878 if (this.IsComponentDomain(LegalId.Domain, true))
7879 {
7880 Identity = await GetLocalLegalIdentity(Signature.LegalId);
7881
7882 if (Historic)
7883 {
7884 Identities.Add(Identity);
7885 AttachmentUrlss.Add(null);
7886 }
7887
7888 if (Current)
7889 {
7890 CurrentIdentity = await this.GetApprovedLegalIdentityAsync(Identity.Account, Now);
7891 if (CurrentIdentity.Id != Identity.Id)
7892 {
7893 Identities.Add(CurrentIdentity);
7894 AttachmentUrlss.Add(null);
7895 }
7896 }
7897 }
7898 else
7899 {
7900 if (Domains is null)
7901 Domains = new Dictionary<string, bool>();
7902
7903 Domains[LegalId.Domain] = true;
7904 }
7905 }
7906
7907 if (!(Domains is null))
7908 {
7909 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
7910 object SynchObj = new object();
7911 int Count = Domains.Count;
7912 StringBuilder Xml = new StringBuilder();
7913 string ContractsNamespace = NamespaceSmartContracts(Contract.Version);
7914
7915 Xml.Append("<getLegalIdentities xmlns='");
7916 Xml.Append(ContractsNamespace);
7917 Xml.Append("' contractId='");
7918 Xml.Append(XML.Encode(ContractId));
7919 Xml.Append("' current='");
7920 Xml.Append(CommonTypes.Encode(Current));
7921 Xml.Append("' historic='");
7922 Xml.Append(CommonTypes.Encode(Historic));
7923 Xml.Append("'/>");
7924
7925 string Request = Xml.ToString();
7926 bool Responded = false;
7927
7928 foreach (KeyValuePair<string, bool> ByDomain in Domains)
7929 {
7930 await this.Server.SendIqRequest("get", this.MainDomain,
7931 new XmppAddress(ByDomain.Key), string.Empty, Request, async (sender2, e2) =>
7932 {
7933 bool Last;
7934
7935 lock (SynchObj)
7936 {
7937 Last = --Count == 0;
7938 }
7939
7940 if (e2.Ok)
7941 {
7942 XmlElement E = e2.FirstElement;
7943
7944 if (!(E is null) && E.LocalName == "identities")
7945 {
7946 foreach (XmlNode N in E.ChildNodes)
7947 {
7948 if (N is XmlElement E2 && E2.LocalName == "identity")
7949 {
7950 Identity = LegalIdentity.Parse(E2, out bool HasStatus, out Dictionary<string, string> AttachmentUrls);
7951
7952 lock (Identities)
7953 {
7954 Identities.Add(Identity);
7955 AttachmentUrlss.Add(AttachmentUrls);
7956 }
7957 }
7958 }
7959 }
7960
7961 Result.TrySetResult(true);
7962 }
7963 else
7964 {
7965 if (Result.TrySetResult(false) && !Responded)
7966 {
7967 Responded = true;
7968 await e.IqError(e2.ErrorTypeString, e2.ErrorElement?.OuterXml ?? string.Empty, e2.To, e2.ErrorText, string.Empty);
7969 }
7970 }
7971 }, null);
7972 }
7973
7974 if (!await Result.Task || Responded)
7975 return;
7976 }
7977 }
7978 }
7979 else
7980 {
7981 if (e.From.Address != ContractAddress.Domain)
7982 {
7983 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
7984 return;
7985 }
7986
7987 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(
7988 new FilterFieldEqualTo("ContractId", ContractId), "LegalId");
7989
7990 foreach (ContractSignature Signature in Signatures)
7991 {
7992 Identity = await GetLocalLegalIdentity(Signature.LegalId);
7993
7994 if (Historic)
7995 {
7996 Identities.Add(Identity);
7997 AttachmentUrlss.Add(null);
7998 }
7999
8000 if (Current)
8001 {
8002 CurrentIdentity = await this.GetApprovedLegalIdentityAsync(Identity.Account, Now);
8003 if (CurrentIdentity.Id != Identity.Id)
8004 {
8005 Identities.Add(CurrentIdentity);
8006 AttachmentUrlss.Add(null);
8007 }
8008 }
8009 }
8010 }
8011
8012 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
8013 string Response;
8014
8015 lock (Identities)
8016 {
8017 Response = this.SerializeIdentities(Identities, AttachmentUrlss, QueryVersion);
8018 }
8019
8020 await e.IqResult(Response, e.To);
8021 }
8022 catch (Exception ex)
8023 {
8024 await e.IqError(ex, e.To);
8025 }
8026 }
8027
8028 private async Task GetNetworkIdentitiesHandler(object Sender, IqEventArgs e)
8029 {
8030 try
8031 {
8032 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
8033
8034 XmppAddress ContractAddress = new XmppAddress(ContractId);
8035 List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> Identities = new List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>>();
8036
8037 if (this.IsComponentDomain(ContractAddress.Domain, true))
8038 {
8040
8041 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
8042 {
8043 Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
8044 if (Contract is null)
8045 {
8046 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
8047 return;
8048 }
8049
8050 if (!await Contract.CanRead(e.From, this.Server, this))
8051 {
8052 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
8053 return;
8054 }
8055 }
8056
8057 IqResultEventArgs ErrorResponse = await this.GetNetworkIdentities(Contract, Identities);
8058 if (!(ErrorResponse is null))
8059 {
8060 await e.IqError(ErrorResponse.ErrorTypeString, ErrorResponse.ErrorElement?.OuterXml ?? string.Empty,
8061 ErrorResponse.To, ErrorResponse.ErrorText, string.Empty);
8062 return;
8063 }
8064 }
8065 else
8066 {
8067 if (e.From.Address != ContractAddress.Domain)
8068 {
8069 await e.IqErrorForbidden(e.To, "Not authorized to access contract.", "en");
8070 return;
8071 }
8072
8073 IEnumerable<ContractSignature> Signatures = await Database.Find<ContractSignature>(
8074 new FilterFieldEqualTo("ContractId", ContractId), "LegalId");
8075
8076 foreach (ContractSignature Signature in Signatures)
8077 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(Signature.LegalId, Signature.BareJid));
8078 }
8079
8080 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
8081 StringBuilder Response = new StringBuilder();
8082
8083 Response.Append("<networkIdentities xmlns='");
8084 Response.Append(NamespaceSmartContracts(QueryVersion));
8085 Response.Append("'>");
8086
8087 lock (Identities)
8088 {
8089 foreach (KeyValuePair<CaseInsensitiveString, CaseInsensitiveString> P in Identities)
8090 {
8091 Response.Append("<networkIdentity bareJid='");
8092 Response.Append(XML.Encode(P.Value.Value));
8093 Response.Append("' legalId='");
8094 Response.Append(XML.Encode(P.Key.Value));
8095 Response.Append("'/>");
8096 }
8097 }
8098
8099 Response.Append("</networkIdentities>");
8100
8101 await e.IqResult(Response.ToString(), e.To);
8102 }
8103 catch (Exception ex)
8104 {
8105 await e.IqError(ex, e.To);
8106 }
8107 }
8108
8109 internal async Task<IqResultEventArgs> GetNetworkIdentities(Contract Contract,
8110 List<KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>> Identities)
8111 {
8112 if (!(Contract.ClientSignatures is null))
8113 {
8114 Dictionary<string, bool> Domains = null;
8115
8116 foreach (ClientSignature Signature in Contract.ClientSignatures)
8117 {
8118 XmppAddress LegalId = new XmppAddress(Signature.LegalId);
8119 if (this.IsComponentDomain(LegalId.Domain, true))
8120 {
8121 int i = LegalId.Domain.IndexOf('.');
8122 string JidDomain = i < 0 ? this.Server.Domain : LegalId.Domain.Substring(i + 1);
8123 LegalIdentity Identity = await GetLocalLegalIdentity(Signature.LegalId);
8124 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(Identity.Id, Identity.Account + "@" + JidDomain));
8125 }
8126 else
8127 {
8128 if (Domains is null)
8129 Domains = new Dictionary<string, bool>();
8130
8131 Domains[LegalId.Domain] = true;
8132 }
8133 }
8134
8135 if (!(Domains is null))
8136 {
8137 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
8138 object SynchObj = new object();
8139 int Count = Domains.Count;
8140 StringBuilder Xml = new StringBuilder();
8141
8142 Xml.Append("<getNetworkIdentities xmlns='");
8143 Xml.Append(LegalComponent.NamespaceSmartContracts(Contract.Version));
8144 Xml.Append("' contractId='");
8145 Xml.Append(XML.Encode(Contract.ContractId));
8146 Xml.Append("'/>");
8147
8148 string Request = Xml.ToString();
8149 IqResultEventArgs ErrorResponse = null;
8150
8151 foreach (KeyValuePair<string, bool> ByDomain in Domains)
8152 {
8153 await this.Server.SendIqRequest("get", this.MainDomain,
8154 new XmppAddress(ByDomain.Key), string.Empty, Request, (sender2, e2) =>
8155 {
8156 bool Last;
8157
8158 lock (SynchObj)
8159 {
8160 Last = --Count == 0;
8161 }
8162
8163 if (e2.Ok)
8164 {
8165 XmlElement E = e2.FirstElement;
8166
8167 if (!(E is null) && E.LocalName == "networkIdentities")
8168 {
8169 foreach (XmlNode N in E.ChildNodes)
8170 {
8171 if (N is XmlElement E2 && E2.LocalName == "networkIdentity")
8172 {
8173 lock (Identities)
8174 {
8175 Identities.Add(new KeyValuePair<CaseInsensitiveString, CaseInsensitiveString>(
8176 XML.Attribute(E2, "legalId"), XML.Attribute(E2, "bareJid")));
8177 }
8178 }
8179 }
8180 }
8181
8182 Result.TrySetResult(true);
8183 }
8184 else
8185 {
8186 if (ErrorResponse is null)
8187 {
8188 ErrorResponse = e2;
8189 Result.TrySetResult(false);
8190 }
8191 }
8192
8193 return Task.CompletedTask;
8194 }, null);
8195 }
8196
8197 if (!await Result.Task)
8198 return ErrorResponse;
8199 }
8200 }
8201
8202 return null;
8203 }
8204
8205 private async Task SearchPublicContractsHandler(object Sender, IqEventArgs e)
8206 {
8207 try
8208 {
8209 int Offset = XML.Attribute(e.Query, "offset", 0);
8210 int MaxCount = XML.Attribute(e.Query, "maxCount", int.MaxValue);
8211 List<CustomFilter> CustomFilters = new List<CustomFilter>();
8212 List<Filter> Filters = new List<Filter>()
8213 {
8214 new FilterFieldEqualTo("Visibility", ContractVisibility.PublicSearchable)
8215 };
8216
8217 foreach (XmlNode N in e.Query.ChildNodes)
8218 {
8219 if (N is XmlElement E)
8220 {
8221 switch (E.LocalName)
8222 {
8223 case "localName":
8224 if (!await this.AddStrFilter(E, e, Filters, "ForMachinesLocalName"))
8225 return;
8226 break;
8227
8228 case "namespace":
8229 if (!await this.AddStrFilter(E, e, Filters, "ForMachinesNamespace"))
8230 return;
8231 break;
8232
8233 case "template":
8234 if (!await this.AddStrFilter(E, e, Filters, "TemplateId"))
8235 return;
8236 break;
8237
8238 case "role":
8239 if (!await this.AddRoleFilter(E, e, CustomFilters))
8240 return;
8241 break;
8242
8243 case "parameter":
8244 if (!await this.AddParameterFilter(E, e, CustomFilters, XML.Attribute(E, "name")))
8245 return;
8246 break;
8247
8248 case "created":
8249 if (!await this.AddDateTimeFilter(E, e, Filters, "Created"))
8250 return;
8251 break;
8252
8253 case "updated":
8254 if (!await this.AddDateTimeFilter(E, e, Filters, "Updated"))
8255 return;
8256 break;
8257
8258 case "from":
8259 if (!await this.AddDateTimeFilter(E, e, Filters, "From"))
8260 return;
8261 break;
8262
8263 case "to":
8264 if (!await this.AddDateTimeFilter(E, e, Filters, "To"))
8265 return;
8266 break;
8267
8268 case "duration":
8269 if (!await this.AddDurationFilter(E, e, CustomFilters))
8270 return;
8271 break;
8272
8273 default:
8274 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8275 return;
8276 }
8277 }
8278 }
8279
8280 List<Contract> Result = new List<Contract>();
8281 Filter SearchFilter = Filters.Count == 1 ? Filters[0] : new FilterAnd(Filters.ToArray());
8282 bool HasCustomFilters = CustomFilters.Count > 0;
8283 IEnumerable<Contract> Contracts;
8284 bool More = false;
8285
8286 if (HasCustomFilters)
8287 Contracts = await Database.Find<Contract>(SearchFilter, "ContractId");
8288 else
8289 {
8290 Contracts = await Database.Find<Contract>(Offset, MaxCount == int.MaxValue ? MaxCount : MaxCount + 1, SearchFilter, "ContractId");
8291 Offset = 0;
8292 }
8293
8294 foreach (Contract Contract in Contracts)
8295 {
8296 if (HasCustomFilters)
8297 {
8298 bool Included = true;
8299
8300 foreach (CustomFilter Filter in CustomFilters)
8301 {
8302 if (!Filter.IsIncluded(Contract))
8303 {
8304 Included = false;
8305 break;
8306 }
8307 }
8308
8309 if (!Included)
8310 continue;
8311 }
8312
8313 if (Offset > 0)
8314 {
8315 Offset--;
8316 continue;
8317 }
8318
8319 if (MaxCount > 0)
8320 MaxCount--;
8321 else
8322 {
8323 More = true;
8324 break;
8325 }
8326
8327 Result.Add(Contract);
8328 }
8329
8330 NamespaceSet QueryVersion = XmppServerModule.GetVersion(e.Query.NamespaceURI);
8331 StringBuilder Xml = new StringBuilder();
8332
8333 Xml.Append("<searchResult xmlns='");
8334 Xml.Append(NamespaceSmartContracts(QueryVersion));
8335 Xml.Append("' more='");
8336 Xml.Append(CommonTypes.Encode(More));
8337 Xml.Append("'>");
8338
8339 foreach (IContractReference Contract in Result)
8340 {
8341 Xml.Append("<ref id='");
8342 Xml.Append(XML.Encode(Contract.ContractId));
8343 Xml.Append("'/>");
8344 }
8345
8346 Xml.Append("</searchResult>");
8347
8348 await e.IqResult(Xml.ToString(), e.To);
8349 }
8350 catch (Exception ex)
8351 {
8352 await e.IqError(ex, e.To);
8353 }
8354 }
8355
8356 private async Task<bool> AddStrFilter(XmlElement E, IqEventArgs e, List<Filter> Filters, string Field)
8357 {
8358 foreach (XmlNode N2 in E.ChildNodes)
8359 {
8360 if (N2 is XmlElement E2)
8361 {
8362 switch (E2.LocalName)
8363 {
8364 case "eq":
8365 Filters.Add(new FilterFieldEqualTo(Field, E2.InnerText));
8366 break;
8367
8368 case "like":
8369 Filters.Add(new FilterFieldLikeRegEx(Field, E2.InnerText));
8370 break;
8371
8372 default:
8373 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8374 return false;
8375 }
8376 }
8377 }
8378
8379 return true;
8380 }
8381
8382 private async Task<bool> AddRoleFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters)
8383 {
8384 foreach (XmlNode N2 in E.ChildNodes)
8385 {
8386 if (N2 is XmlElement E2)
8387 {
8388 switch (E2.LocalName)
8389 {
8390 case "eq":
8391 Filters.Add(new RoleEqFilter()
8392 {
8393 Value = E2.InnerText
8394 });
8395 break;
8396
8397 case "like":
8398 Filters.Add(new RoleLikeFilter()
8399 {
8400 Value = E2.InnerText
8401 });
8402 break;
8403
8404 default:
8405 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8406 return false;
8407 }
8408 }
8409 }
8410
8411 return true;
8412 }
8413
8414 private async Task<bool> AddParameterFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters, string Name)
8415 {
8416 foreach (XmlNode N2 in E.ChildNodes)
8417 {
8418 if (N2 is XmlElement E2)
8419 {
8420 switch (E2.LocalName)
8421 {
8422 case "eqStr":
8423 Filters.Add(new StringParameterEqFilter()
8424 {
8425 Name = Name,
8426 Value = E2.InnerText
8427 });
8428 break;
8429
8430 case "neqStr":
8431 Filters.Add(new StringParameterNEqFilter()
8432 {
8433 Name = Name,
8434 Value = E2.InnerText
8435 });
8436 break;
8437
8438 case "gtStr":
8439 Filters.Add(new StringParameterGtFilter()
8440 {
8441 Name = Name,
8442 Value = E2.InnerText
8443 });
8444 break;
8445
8446 case "gteStr":
8447 Filters.Add(new StringParameterGteFilter()
8448 {
8449 Name = Name,
8450 Value = E2.InnerText
8451 });
8452 break;
8453
8454 case "ltStr":
8455 Filters.Add(new StringParameterLtFilter()
8456 {
8457 Name = Name,
8458 Value = E2.InnerText
8459 });
8460 break;
8461
8462 case "lteStr":
8463 Filters.Add(new StringParameterLteFilter()
8464 {
8465 Name = Name,
8466 Value = E2.InnerText
8467 });
8468 break;
8469
8470 case "like":
8471 Filters.Add(new StringParameterLikeFilter()
8472 {
8473 Name = Name,
8474 Value = E2.InnerText
8475 });
8476 break;
8477
8478 case "eqNum":
8479 if (!CommonTypes.TryParse(E2.InnerText, out decimal NumValue))
8480 {
8481 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8482 return false;
8483 }
8484
8485 Filters.Add(new NumericalParameterEqFilter()
8486 {
8487 Name = Name,
8488 Value = NumValue
8489 });
8490 break;
8491
8492 case "neqNum":
8493 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
8494 {
8495 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8496 return false;
8497 }
8498
8499 Filters.Add(new NumericalParameterNEqFilter()
8500 {
8501 Name = Name,
8502 Value = NumValue
8503 });
8504 break;
8505
8506 case "gtNum":
8507 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
8508 {
8509 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8510 return false;
8511 }
8512
8513 Filters.Add(new NumericalParameterGtFilter()
8514 {
8515 Name = Name,
8516 Value = NumValue
8517 });
8518 break;
8519
8520 case "gteNum":
8521 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
8522 {
8523 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8524 return false;
8525 }
8526
8527 Filters.Add(new NumericalParameterGteFilter()
8528 {
8529 Name = Name,
8530 Value = NumValue
8531 });
8532 break;
8533
8534 case "ltNum":
8535 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
8536 {
8537 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8538 return false;
8539 }
8540
8541 Filters.Add(new NumericalParameterLtFilter()
8542 {
8543 Name = Name,
8544 Value = NumValue
8545 });
8546 break;
8547
8548 case "lteNum":
8549 if (!CommonTypes.TryParse(E2.InnerText, out NumValue))
8550 {
8551 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8552 return false;
8553 }
8554
8555 Filters.Add(new NumericalParameterLteFilter()
8556 {
8557 Name = Name,
8558 Value = NumValue
8559 });
8560 break;
8561
8562 case "eqB":
8563 if (!CommonTypes.TryParse(E2.InnerText, out bool BoolValue))
8564 {
8565 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8566 return false;
8567 }
8568
8569 Filters.Add(new BooleanParameterEqFilter()
8570 {
8571 Name = Name,
8572 Value = BoolValue
8573 });
8574 break;
8575
8576 case "neqB":
8577 if (!CommonTypes.TryParse(E2.InnerText, out BoolValue))
8578 {
8579 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8580 return false;
8581 }
8582
8583 Filters.Add(new BooleanParameterNEqFilter()
8584 {
8585 Name = Name,
8586 Value = BoolValue
8587 });
8588 break;
8589
8590
8591 case "eqD":
8592 if (!XML.TryParse(E2.InnerText, out DateTime TP) || TP.TimeOfDay != TimeSpan.Zero)
8593 {
8594 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8595 return false;
8596 }
8597
8598 Filters.Add(new DateParameterEqFilter()
8599 {
8600 Name = Name,
8601 Value = TP
8602 });
8603 break;
8604
8605 case "neqD":
8606 if (!XML.TryParse(E2.InnerText, out TP))
8607 {
8608 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8609 return false;
8610 }
8611
8612 Filters.Add(new DateParameterNEqFilter()
8613 {
8614 Name = Name,
8615 Value = TP
8616 });
8617 break;
8618
8619 case "gtD":
8620 if (!XML.TryParse(E2.InnerText, out TP))
8621 {
8622 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8623 return false;
8624 }
8625
8626 Filters.Add(new DateParameterGtFilter()
8627 {
8628 Name = Name,
8629 Value = TP
8630 });
8631 break;
8632
8633 case "gteD":
8634 if (!XML.TryParse(E2.InnerText, out TP))
8635 {
8636 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8637 return false;
8638 }
8639
8640 Filters.Add(new DateParameterGteFilter()
8641 {
8642 Name = Name,
8643 Value = TP
8644 });
8645 break;
8646
8647 case "ltD":
8648 if (!XML.TryParse(E2.InnerText, out TP))
8649 {
8650 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8651 return false;
8652 }
8653
8654 Filters.Add(new DateParameterLtFilter()
8655 {
8656 Name = Name,
8657 Value = TP
8658 });
8659 break;
8660
8661 case "lteD":
8662 if (!XML.TryParse(E2.InnerText, out TP))
8663 {
8664 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8665 return false;
8666 }
8667
8668 Filters.Add(new DateParameterLteFilter()
8669 {
8670 Name = Name,
8671 Value = TP
8672 });
8673 break;
8674
8675 case "eqDT":
8676 if (!XML.TryParse(E2.InnerText, out TP))
8677 {
8678 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8679 return false;
8680 }
8681
8682 Filters.Add(new DateTimeParameterEqFilter()
8683 {
8684 Name = Name,
8685 Value = TP
8686 });
8687 break;
8688
8689 case "neqDT":
8690 if (!XML.TryParse(E2.InnerText, out TP))
8691 {
8692 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8693 return false;
8694 }
8695
8696 Filters.Add(new DateTimeParameterNEqFilter()
8697 {
8698 Name = Name,
8699 Value = TP
8700 });
8701 break;
8702
8703 case "gtDT":
8704 if (!XML.TryParse(E2.InnerText, out TP))
8705 {
8706 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8707 return false;
8708 }
8709
8710 Filters.Add(new DateTimeParameterGtFilter()
8711 {
8712 Name = Name,
8713 Value = TP
8714 });
8715 break;
8716
8717 case "gteDT":
8718 if (!XML.TryParse(E2.InnerText, out TP))
8719 {
8720 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8721 return false;
8722 }
8723
8724 Filters.Add(new DateTimeParameterGteFilter()
8725 {
8726 Name = Name,
8727 Value = TP
8728 });
8729 break;
8730
8731 case "ltDT":
8732 if (!XML.TryParse(E2.InnerText, out TP))
8733 {
8734 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8735 return false;
8736 }
8737
8738 Filters.Add(new DateTimeParameterLtFilter()
8739 {
8740 Name = Name,
8741 Value = TP
8742 });
8743 break;
8744
8745 case "lteDT":
8746 if (!XML.TryParse(E2.InnerText, out TP))
8747 {
8748 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8749 return false;
8750 }
8751
8752 Filters.Add(new DateTimeParameterLteFilter()
8753 {
8754 Name = Name,
8755 Value = TP
8756 });
8757 break;
8758
8759 case "eqT":
8760 if (!TimeSpan.TryParse(E2.InnerText, out TimeSpan TS))
8761 {
8762 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8763 return false;
8764 }
8765
8766 Filters.Add(new TimeParameterEqFilter()
8767 {
8768 Name = Name,
8769 Value = TS
8770 });
8771 break;
8772
8773 case "neqT":
8774 if (!TimeSpan.TryParse(E2.InnerText, out TS))
8775 {
8776 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8777 return false;
8778 }
8779
8780 Filters.Add(new TimeParameterNEqFilter()
8781 {
8782 Name = Name,
8783 Value = TS
8784 });
8785 break;
8786
8787 case "gtT":
8788 if (!TimeSpan.TryParse(E2.InnerText, out TS))
8789 {
8790 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8791 return false;
8792 }
8793
8794 Filters.Add(new TimeParameterGtFilter()
8795 {
8796 Name = Name,
8797 Value = TS
8798 });
8799 break;
8800
8801 case "gteT":
8802 if (!TimeSpan.TryParse(E2.InnerText, out TS))
8803 {
8804 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8805 return false;
8806 }
8807
8808 Filters.Add(new TimeParameterGteFilter()
8809 {
8810 Name = Name,
8811 Value = TS
8812 });
8813 break;
8814
8815 case "ltT":
8816 if (!TimeSpan.TryParse(E2.InnerText, out TS))
8817 {
8818 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8819 return false;
8820 }
8821
8822 Filters.Add(new TimeParameterLtFilter()
8823 {
8824 Name = Name,
8825 Value = TS
8826 });
8827 break;
8828
8829 case "lteT":
8830 if (!TimeSpan.TryParse(E2.InnerText, out TS))
8831 {
8832 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8833 return false;
8834 }
8835
8836 Filters.Add(new TimeParameterLteFilter()
8837 {
8838 Name = Name,
8839 Value = TS
8840 });
8841 break;
8842
8843 case "eqDr":
8844 if (!Duration.TryParse(E2.InnerText, out Duration Dr))
8845 {
8846 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8847 return false;
8848 }
8849
8850 Filters.Add(new DurationParameterEqFilter()
8851 {
8852 Name = Name,
8853 Value = Dr
8854 });
8855 break;
8856
8857 case "neqDr":
8858 if (!Duration.TryParse(E2.InnerText, out Dr))
8859 {
8860 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8861 return false;
8862 }
8863
8864 Filters.Add(new DurationParameterNEqFilter()
8865 {
8866 Name = Name,
8867 Value = Dr
8868 });
8869 break;
8870
8871 case "gtDr":
8872 if (!Duration.TryParse(E2.InnerText, out Dr))
8873 {
8874 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8875 return false;
8876 }
8877
8878 Filters.Add(new DurationParameterGtFilter()
8879 {
8880 Name = Name,
8881 Value = Dr
8882 });
8883 break;
8884
8885 case "gteDr":
8886 if (!Duration.TryParse(E2.InnerText, out Dr))
8887 {
8888 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8889 return false;
8890 }
8891
8892 Filters.Add(new DurationParameterGteFilter()
8893 {
8894 Name = Name,
8895 Value = Dr
8896 });
8897 break;
8898
8899 case "ltDr":
8900 if (!Duration.TryParse(E2.InnerText, out Dr))
8901 {
8902 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8903 return false;
8904 }
8905
8906 Filters.Add(new DurationParameterLtFilter()
8907 {
8908 Name = Name,
8909 Value = Dr
8910 });
8911 break;
8912
8913 case "lteDr":
8914 if (!Duration.TryParse(E2.InnerText, out Dr))
8915 {
8916 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8917 return false;
8918 }
8919
8920 Filters.Add(new DurationParameterLteFilter()
8921 {
8922 Name = Name,
8923 Value = Dr
8924 });
8925 break;
8926
8927 default:
8928 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8929 return false;
8930 }
8931 }
8932 }
8933
8934 return true;
8935 }
8936
8937 private async Task<bool> AddDateTimeFilter(XmlElement E, IqEventArgs e, List<Filter> Filters, string Field)
8938 {
8939 foreach (XmlNode N2 in E.ChildNodes)
8940 {
8941 if (N2 is XmlElement E2)
8942 {
8943 if (!XML.TryParse(E2.InnerText, out DateTime TP))
8944 {
8945 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8946 return false;
8947 }
8948
8949 switch (E2.LocalName)
8950 {
8951 case "eq":
8952 Filters.Add(new FilterFieldEqualTo(Field, TP));
8953 break;
8954
8955 case "neq":
8956 Filters.Add(new FilterFieldNotEqualTo(Field, TP));
8957 break;
8958
8959 case "gt":
8960 Filters.Add(new FilterFieldGreaterThan(Field, TP));
8961 break;
8962
8963 case "gte":
8964 Filters.Add(new FilterFieldGreaterOrEqualTo(Field, TP));
8965 break;
8966
8967 case "lt":
8968 Filters.Add(new FilterFieldLesserThan(Field, TP));
8969 break;
8970
8971 case "lte":
8972 Filters.Add(new FilterFieldLesserOrEqualTo(Field, TP));
8973 break;
8974
8975 default:
8976 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8977 return false;
8978 }
8979 }
8980 }
8981
8982 return true;
8983 }
8984
8985 private async Task<bool> AddDurationFilter(XmlElement E, IqEventArgs e, List<CustomFilter> Filters)
8986 {
8987 foreach (XmlNode N2 in E.ChildNodes)
8988 {
8989 if (N2 is XmlElement E2)
8990 {
8991 if (!Duration.TryParse(E2.InnerText, out Duration Value))
8992 {
8993 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
8994 return false;
8995 }
8996
8997 switch (E2.LocalName)
8998 {
8999 case "eq":
9000 Filters.Add(new DurationEqFilter()
9001 {
9002 Value = Value
9003 });
9004 break;
9005
9006 case "neq":
9007 Filters.Add(new DurationNEqFilter()
9008 {
9009 Value = Value
9010 });
9011 break;
9012
9013 case "gt":
9014 Filters.Add(new DurationGtFilter()
9015 {
9016 Value = Value
9017 });
9018 break;
9019
9020 case "gte":
9021 Filters.Add(new DurationGteFilter()
9022 {
9023 Value = Value
9024 });
9025 break;
9026
9027 case "lt":
9028 Filters.Add(new DurationLtFilter()
9029 {
9030 Value = Value
9031 });
9032 break;
9033
9034 case "lte":
9035 Filters.Add(new DurationLteFilter()
9036 {
9037 Value = Value
9038 });
9039 break;
9040
9041 default:
9042 await e.IqErrorBadRequest(e.To, "Invalid query.", "en");
9043 return false;
9044 }
9045 }
9046 }
9047
9048 return true;
9049 }
9050
9051 private async Task AddContractAttachmentHandler(object Sender, IqEventArgs e)
9052 {
9053 try
9054 {
9055 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "contractId");
9056 string GetUrl = XML.Attribute(e.Query, "getUrl");
9057 byte[] Signature = Convert.FromBase64String(XML.Attribute(e.Query, "s"));
9058
9059 if (!this.Server.IsServerDomain(e.From.Domain, true))
9060 {
9061 await e.IqErrorForbidden(e.To, "Only accounts on the broker can add attachments.", "en");
9062 return;
9063 }
9064
9065 if (!Uri.TryCreate(GetUrl, UriKind.Absolute, out Uri GetUri))
9066 {
9067 await e.IqErrorBadRequest(e.To, "Invalid Get URL.", "en");
9068 return;
9069 }
9070
9071 using (Semaphore Semaphore = await Semaphores.BeginWrite("iotsc:" + ContractId.LowerCase))
9072 {
9073 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", ContractId), "Created");
9074 if (Contract is null)
9075 {
9076 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9077 return;
9078 }
9079
9080 if (!Contract.IsCreator(e.From, this.Server))
9081 {
9082 await e.IqErrorForbidden(e.To, "Only allowed to add attachments to your own contracts.", "en");
9083 return;
9084 }
9085
9086 if (Contract.State != ContractState.Proposed && Contract.State != ContractState.Approved)
9087 {
9088 await e.IqErrorForbidden(e.To, "Attachments can only be added to proposed or approved contracts, before they are signed.", "en");
9089 return;
9090 }
9091
9092 if (!(Contract.Attachments is null))
9093 {
9094 string s = Convert.ToBase64String(Signature);
9095
9096 foreach (AttachmentReference Ref in Contract.Attachments)
9097 {
9098 if (Convert.ToBase64String(Ref.Signature) == s)
9099 {
9100 await e.IqErrorForbidden(e.To, "Attachment already assigned to contract.", "en");
9101 return;
9102 }
9103 }
9104 }
9105
9106 KeyValuePair<string, TemporaryStream> P = await InternetContent.GetTempStreamAsync(GetUri);
9107 string ContentType = P.Key;
9108
9109 using (TemporaryStream File = P.Value)
9110 {
9111 File.Position = 0;
9112
9113 LegalIdentity UploadingIdentity = await this.ValidateLocalSenderSignature(e.From, null, DateTime.Now, File, Signature);
9114 if (UploadingIdentity is null)
9115 {
9116 await e.IqErrorForbidden(e.To, "Attachment signature is invalid.", "en");
9117 return;
9118 }
9119
9120 KeyValuePair<Attachment, AttachmentReference> A = await this.CreateAttachment(GetUri,
9121 UploadingIdentity, Signature, File, ContentType, e.From.Account, ContractId);
9122 List<AttachmentReference> References = new List<AttachmentReference>();
9123
9124 if (!(Contract.Attachments is null))
9125 References.AddRange(Contract.Attachments);
9126
9127 References.Add(A.Value);
9128
9129 Contract.Attachments = References.ToArray();
9130 Contract.Sign(this);
9131
9132 await Database.Update(Contract);
9133
9134 StringBuilder Xml = new StringBuilder();
9135 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9136
9137 await e.IqResult(Xml.ToString(), e.To);
9138 }
9139 }
9140 }
9141 catch (Exception ex)
9142 {
9143 await e.IqError(ex, e.To);
9144 }
9145 }
9146
9147 private async Task RemoveContractAttachmentHandler(object Sender, IqEventArgs e)
9148 {
9149 try
9150 {
9151 CaseInsensitiveString AttachmentId = XML.Attribute(e.Query, "attachmentId");
9152
9153 if (!this.Server.IsServerDomain(e.From.Domain, true))
9154 {
9155 await e.IqErrorForbidden(e.To, "Only accounts on the broker can remove attachments.", "en");
9156 return;
9157 }
9158
9159 Attachment Attachment = await Database.FindFirstDeleteRest<Attachment>(new FilterFieldEqualTo("Id", AttachmentId));
9160 if (Attachment is null)
9161 {
9162 await e.IqErrorItemNotFound(e.To, "Attachment not found.", "en");
9163 return;
9164 }
9165
9167 {
9168 await e.IqErrorItemNotFound(e.To, "Attachment not assigned to contract.", "en");
9169 return;
9170 }
9171
9173 {
9174 Contract Contract = await Database.FindFirstDeleteRest<Contract>(new FilterFieldEqualTo("ContractId", Attachment.ContractId), "Created");
9175 if (Contract is null)
9176 {
9177 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9178 return;
9179 }
9180
9181 if (!Contract.IsCreator(e.From, this.Server))
9182 {
9183 await e.IqErrorForbidden(e.To, "Only allowed to remove attachments to your own contracts.", "en");
9184 return;
9185 }
9186
9187 if (Contract.State != ContractState.Proposed && Contract.State != ContractState.Approved)
9188 {
9189 await e.IqErrorForbidden(e.To, "Attachments can only be removed to proposed or approved contracts, before they are signed.", "en");
9190 return;
9191 }
9192
9193 if (File.Exists(Attachment.LocalFileName))
9194 File.Delete(Attachment.LocalFileName);
9195
9196 if (!(Contract.Attachments is null))
9197 {
9198 List<AttachmentReference> Attachments = new List<AttachmentReference>();
9199
9200 foreach (AttachmentReference Ref in Contract.Attachments)
9201 {
9202 if (Ref.Id != AttachmentId)
9203 Attachments.Add(Ref);
9204 }
9205
9206 Contract.Attachments = Attachments.ToArray();
9207 Contract.Sign(this);
9208
9209 await Database.Update(Contract);
9210 }
9211
9212 await Database.Delete(Attachment);
9213
9214 StringBuilder Xml = new StringBuilder();
9215 Contract.Serialize(Xml, true, true, true, true, true, true, true, null, this);
9216
9217 await e.IqResult(Xml.ToString(), e.To);
9218 }
9219 }
9220 catch (Exception ex)
9221 {
9222 await e.IqError(ex, e.To);
9223 }
9224 }
9225
9226 private async Task AuthorizeAccessToContractHandler(object Sender, IqEventArgs e)
9227 {
9228 try
9229 {
9230 CaseInsensitiveString ContractId = XML.Attribute(e.Query, "id");
9231
9232 if (CaseInsensitiveString.IsNullOrEmpty(ContractId))
9233 {
9234 await e.IqErrorBadRequest(e.To, "No Contract ID specified.", "en");
9235 return;
9236 }
9237
9238 XmppAddress ContractIdAddress = new XmppAddress(ContractId);
9239
9240 if (!ContractIdAddress.IsBareJID)
9241 {
9242 await e.IqErrorBadRequest(e.To, "Invalid Contract ID.", "en");
9243 return;
9244 }
9245
9246 if (!this.IsComponentDomain(ContractIdAddress.Domain, true))
9247 {
9248 await e.IqErrorBadRequest(e.To, "Not a local Contract ID.", "en");
9249 return;
9250 }
9251
9252 CaseInsensitiveString RemoteId = XML.Attribute(e.Query, "remoteId");
9253 bool Authorized = XML.Attribute(e.Query, "auth", true);
9254
9256 {
9257 await e.IqErrorBadRequest(e.To, "No Remote ID specified.", "en");
9258 return;
9259 }
9260
9261 XmppAddress RemoteAddress = new XmppAddress(RemoteId);
9262 if (!RemoteAddress.IsBareJID)
9263 {
9264 await e.IqErrorBadRequest(e.To, "Invalid Remote ID.", "en");
9265 return;
9266 }
9267
9268 using (Semaphore Semaphore = await Semaphores.BeginRead("iotsc:" + ContractId.LowerCase))
9269 {
9270 Contract Contract = await this.GetContract(ContractId);
9271 if (Contract is null)
9272 {
9273 await e.IqErrorItemNotFound(e.To, "Contract not found.", "en");
9274 return;
9275 }
9276
9277 if (!await Contract.CanRead(e.From, this.Server, this))
9278 {
9279 await e.IqErrorForbidden(e.To, "You do not have access rights to contract.", "en");
9280 return;
9281 }
9282
9283 ClientInformation ClientInfo = await this.GetNetworkIdentity(RemoteId, true, true, Contract.Version);
9284 CaseInsensitiveString RemoteJid = ClientInfo.Jid;
9285
9286 this.ContractAuthorization(RemoteJid, e.From.BareJid, ContractId, Authorized);
9287 }
9288
9289 await e.IqResult(string.Empty, e.To);
9290 }
9291 catch (Exception ex)
9292 {
9293 await e.IqError(ex, e.To);
9294 }
9295 }
9296
9297 #endregion
9298
9299 #region Third-party peer reviews
9300
9301 private async Task GetReviewIdProvidersHandler(object Sender, IqEventArgs e)
9302 {
9303 if (!this.Server.IsServerDomain(e.From.Domain, true) || !e.From.HasAccount)
9304 {
9305 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
9306 return;
9307 }
9308
9309 CaseInsensitiveString AccountName = e.From.Account;
9310 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
9311 if (Account is null)
9312 {
9313 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
9314 return;
9315 }
9316
9317 if (!Account.Enabled)
9318 {
9319 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
9320 return;
9321 }
9322
9323 LegalIdentity IdentityApplication = await Database.FindFirstIgnoreRest<LegalIdentity>(new FilterAnd(
9324 new FilterFieldEqualTo("Account", AccountName),
9325 new FilterFieldEqualTo("State", IdentityState.Created)), "-Created");
9326
9327 if (IdentityApplication is null)
9328 {
9329 await e.IqErrorForbidden(e.To, "No Identity Application found.", "en");
9330 return;
9331 }
9332
9333 KeyValuePair<string, object>[] Tags = IdentityApplication.GetTags();
9334 StringBuilder Xml = new StringBuilder();
9335
9336 Xml.Append("<providers xmlns='");
9337 Xml.Append(e.Query.NamespaceURI);
9338 Xml.Append("'>");
9339
9340 foreach (IPeerReviewService Service in await this.GetPeerReviewServices(Tags, true, true))
9341 {
9342 Xml.Append("<provider id='");
9343 Xml.Append(XML.Encode(Service.Id));
9344 Xml.Append("' type='");
9345 Xml.Append(XML.Encode(Service.PeerReviewServiceProvider.GetType().FullName));
9346 Xml.Append("' name='");
9347 Xml.Append(XML.Encode(Service.Name));
9348 Xml.Append("' legalId='");
9349 Xml.Append(XML.Encode(Service.PeerReviewerLegalId));
9350 Xml.Append("' external='");
9351 Xml.Append(CommonTypes.Encode(Service.External));
9352
9353 if (!string.IsNullOrEmpty(Service.IconUrl))
9354 {
9355 Xml.Append("' iconUrl='");
9356 Xml.Append(XML.Encode(Service.IconUrl));
9357 Xml.Append("' iconWidth='");
9358 Xml.Append(Service.IconWidth.ToString());
9359 Xml.Append("' iconHeight='");
9360 Xml.Append(Service.IconHeight.ToString());
9361 }
9362
9363 Xml.Append("'/>");
9364 }
9365
9366 Xml.Append("</providers>");
9367
9368 await e.IqResult(Xml.ToString(), e.To);
9369 }
9370
9371 private async Task SelectReviewServiceHandler(object Sender, IqEventArgs e)
9372 {
9373 if (!this.Server.IsServerDomain(e.From.Domain, true) || !e.From.HasAccount)
9374 {
9375 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
9376 return;
9377 }
9378
9379 CaseInsensitiveString AccountName = e.From.Account;
9380 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
9381 if (Account is null)
9382 {
9383 await e.IqErrorForbidden(e.To, "Access to service providers only granted to accounts on broker.", "en");
9384 return;
9385 }
9386
9387 if (!Account.Enabled)
9388 {
9389 await e.IqErrorForbidden(e.To, "Account has been disabled.", "en");
9390 return;
9391 }
9392
9393 string ServiceProvider = XML.Attribute(e.Query, "provider");
9394 string ServiceId = XML.Attribute(e.Query, "serviceId");
9395
9396 Type T = Types.GetType(ServiceProvider);
9397
9398 if (T is null)
9399 {
9400 await e.IqErrorItemNotFound(e.To, "Service provider not found.", "en");
9401 return;
9402 }
9403
9404 if (!typeof(IPeerReviewServiceProvider).IsAssignableFrom(T))
9405 {
9406 await e.IqErrorBadRequest(e.To, "Invalid service provider.", "en");
9407 return;
9408 }
9409
9410 LegalIdentity IdentityApplication = await Database.FindFirstIgnoreRest<LegalIdentity>(new FilterAnd(
9411 new FilterFieldEqualTo("Account", AccountName),
9412 new FilterFieldEqualTo("State", IdentityState.Created)), "-Created");
9413
9414 if (IdentityApplication is null)
9415 {
9416 await e.IqErrorForbidden(e.To, "No Identity Application found.", "en");
9417 return;
9418 }
9419
9420 KeyValuePair<string, object>[] Tags = IdentityApplication.GetTags();
9422 IPeerReviewService Service = await Provider.GetServiceForPeerReview(ServiceId, Tags);
9423
9424 if (Service is null)
9425 {
9426 await e.IqErrorItemNotFound(e.To, "Peer-review service not found.", "en");
9427 return;
9428 }
9429
9430 this.SelectServiceProvider(AccountName, Service);
9431
9432 await e.IqResult(string.Empty, e.To);
9433 }
9434
9435 internal void SelectServiceProvider(string AccountName, IPeerReviewService Service)
9436 {
9437 lock (this.selectedServicePerAccount)
9438 {
9439 this.selectedServicePerAccount[AccountName] = Service;
9440 }
9441 }
9442
9443 private readonly Dictionary<CaseInsensitiveString, IPeerReviewService> selectedServicePerAccount = new Dictionary<CaseInsensitiveString, IPeerReviewService>();
9444
9445 internal async Task<IPeerReviewService[]> GetPeerReviewServices(KeyValuePair<string, object>[] Tags, bool IncludeInternal, bool IncludeExternal)
9446 {
9447 List<IPeerReviewService> Result = new List<IPeerReviewService>();
9448 Type[] ServiceTypes = Types.GetTypesImplementingInterface(typeof(IPeerReviewServiceProvider));
9449
9450 foreach (Type T in ServiceTypes)
9451 {
9452 ConstructorInfo CI = Types.GetDefaultConstructor(T);
9453 if (CI is null)
9454 continue;
9455
9457 IPeerReviewService[] Services = await ServiceProvider.GetServicesForPeerReview(Tags);
9458
9459 foreach (IPeerReviewService Service in Services)
9460 {
9461 if (Service.External)
9462 {
9463 if (IncludeExternal)
9464 Result.Add(Service);
9465 }
9466 else
9467 {
9468 if (IncludeInternal)
9469 Result.Add(Service);
9470 }
9471 }
9472 }
9473
9474 return Result.ToArray();
9475 }
9476
9477 private async Task ContractsClient_PetitionForPeerReviewIDReceived(object Sender,
9478 Networking.XMPP.Contracts.EventArguments.SignaturePetitionEventArgs e)
9479 {
9480 try
9481 {
9482 if (Gateway.ContractsClient is null)
9483 return;
9484
9485 KeyValuePair<string, object>[] Tags = e.RequestorIdentity.GetTags();
9486 XmppAddress From = new XmppAddress(e.RequestorFullJid);
9487
9488 if (!From.HasAccount || !this.Server.IsServerDomain(From.Domain, true))
9489 {
9490 Log.Warning("Peer-review request denied. Access to service providers only granted to accounts on broker.",
9491 e.RequestorIdentity.Id, e.From, Tags);
9492
9493 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9494 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9495
9496 return;
9497 }
9498
9499 CaseInsensitiveString AccountName = From.Account;
9500 IAccount Account = await XmppServerModule.GetAccountAsync(AccountName);
9501
9502 if (Account is null)
9503 {
9504 Log.Warning("Peer-review request denied. Access to service providers only granted to accounts on broker.",
9505 e.RequestorIdentity.Id, e.From, Tags);
9506
9507 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9508 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9509
9510 return;
9511 }
9512
9513 if (!Account.Enabled)
9514 {
9515 Log.Warning("Peer-review request denied. Account has been disabled.",
9516 e.RequestorIdentity.Id, e.From, Tags);
9517
9518 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9519 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9520
9521 return;
9522 }
9523
9524 XmppAddress RequestorIdentityId = new XmppAddress(e.RequestorIdentity.Id);
9525 if (!this.IsComponentDomain(RequestorIdentityId.Domain, true))
9526 {
9527 Log.Warning("Peer-review request denied. Requestor legal identity not on the server.",
9528 e.RequestorIdentity.Id, e.From, Tags);
9529
9530 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9531 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9532
9533 return;
9534 }
9535
9536 LegalIdentity LocalRequestorIdentity = await Database.FindFirstIgnoreRest<LegalIdentity>(
9537 new FilterFieldEqualTo("Id", RequestorIdentityId));
9538
9539 if (LocalRequestorIdentity is null)
9540 {
9541 Log.Warning("Peer-review request denied. Proposed identity did not exist on server.",
9542 e.RequestorIdentity.Id, e.From, Tags);
9543
9544 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9545 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9546
9547 return;
9548 }
9549
9550 StringBuilder Xml1 = new StringBuilder();
9551 StringBuilder Xml2 = new StringBuilder();
9552
9553 LocalRequestorIdentity.Serialize(Xml1, true, true, true, true, true, true, false, null, this);
9554 e.RequestorIdentity.Serialize(Xml2, true, true, true, true, true, true, false);
9555
9556 if (Xml1.ToString() != Xml2.ToString())
9557 {
9558 Log.Warning("Peer-review request denied. Requestor legal identity representation not the same as local version.",
9559 e.RequestorIdentity.Id, e.From, Tags);
9560
9561 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9562 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9563
9564 return;
9565 }
9566
9567 IPeerReviewService[] PeerReviewServices = await this.GetPeerReviewServices(Tags, true, false);
9568
9569 if (PeerReviewServices.Length == 0)
9570 {
9571 Log.Warning("Peer-review request denied. No local peer review services found.",
9572 e.RequestorIdentity.Id, e.From, Tags);
9573
9574 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9575 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9576
9577 return;
9578 }
9579
9580 IPeerReviewService PeerReviewService;
9581
9582 lock (this.selectedServicePerAccount)
9583 {
9584 if (!this.selectedServicePerAccount.TryGetValue(AccountName, out PeerReviewService))
9585 PeerReviewService = null;
9586 }
9587
9588 if (PeerReviewService is null)
9589 {
9590 Log.Warning("Peer-review request denied. No selected peer review service provider.",
9591 e.RequestorIdentity.Id, e.From, Tags);
9592
9593 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9594 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9595
9596 return;
9597 }
9598
9599 IAuthenticationResult Result = await PeerReviewService.IsValid(e, async (sender, e2) =>
9600 {
9601 StringBuilder Message = new StringBuilder();
9602
9603 Message.Append("<petitionClientUrl xmlns='");
9604 Message.Append(NamespaceLegalIdentity(LocalRequestorIdentity.Version));
9605 Message.Append("' pid='");
9606 Message.Append(XML.Encode(e.PetitionId));
9607 Message.Append("' url='");
9608 Message.Append(XML.Encode(e2.Url));
9609 Message.Append("'/>");
9610
9611 await this.Server.SendMessage(string.Empty, string.Empty, e.To, From.BareJid, string.Empty, Message.ToString());
9612 // Note: Client may have a new XMPP connection at this point.
9613 }, null);
9614
9615 if (!string.IsNullOrEmpty(Result.ErrorMessage))
9616 {
9617 if (Result.ErrorType == global::Paiwise.ErrorType.Server || Result.ErrorType == global::Paiwise.ErrorType.Service)
9618 Log.Error(Result.ErrorMessage, string.Empty, PeerReviewService.GetType().Namespace, Result.ErrorCode, Result.Tags);
9619 else
9620 Log.Warning(Result.ErrorMessage, string.Empty, PeerReviewService.GetType().Namespace, Result.ErrorCode, Result.Tags);
9621
9622 StringBuilder Xml = new StringBuilder();
9623
9624 Xml.Append("<body>");
9625 Xml.Append(XML.Encode(Result.ErrorMessage));
9626 Xml.Append("</body>");
9627 Xml.Append("<clientMessage xmlns='");
9628 Xml.Append(NamespaceLegalIdentity(LocalRequestorIdentity.Version));
9629 Xml.Append("' code='");
9630 Xml.Append(XML.Encode(Result.ErrorCode));
9631 Xml.Append("' type='");
9632 Xml.Append(Result.ErrorType.ToString());
9633 Xml.Append("'/>");
9634
9635 await this.Server.SendMessage(string.Empty, string.Empty, e.To, e.RequestorFullJid,
9636 Result.ErrorLanguage, Xml.ToString());
9637 }
9638
9639 if (Result.Result.HasValue && Result.Result.Value)
9640 {
9641 byte[] Signature = await Gateway.ContractsClient.SignAsync(e.ContentToSign,
9642 Networking.XMPP.Contracts.SignWith.LatestApprovedId);
9643
9644 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9645 Signature, e.PetitionId, e.RequestorFullJid, true);
9646 }
9647 else
9648 {
9649 await Gateway.ContractsClient.PetitionSignatureResponseAsync(e.SignatoryIdentityId, e.ContentToSign,
9650 new byte[0], e.PetitionId, e.RequestorFullJid, false);
9651 }
9652 }
9653 catch (Exception ex)
9654 {
9655 Log.Exception(ex);
9656 }
9657 }
9658
9659 #endregion
9660
9661 // TODO: Delete Account => Delete bareJid-portion of legal identities registered for
9662 // account, and delete legal identities not referenced in contracts.
9663 }
9664}
Contains information about a service provider.
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:594
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
Static class managing encoding and decoding of internet content.
static Task< object > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
static bool CanGet(Uri Uri, out Grade Grade, out IContentGetter Getter)
If a resource can be gotten, given its URI.
static Task< KeyValuePair< string, TemporaryStream > > GetTempStreamAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a (possibly big) resource, given its URI.
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:18
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:16
const string DefaultContentType
Default content type for XML documents.
Definition: XmlCodec.cs:27
const string SchemaContentType
Default content type for XML schema documents.
Definition: XmlCodec.cs:32
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Definition: XML.cs:744
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
Definition: XML.cs:1588
Class representing an event.
Definition: Event.cs:10
override string ToString()
Definition: Event.cs:169
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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:1647
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:566
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:682
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:334
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:2354
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:3534
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
Definition: Gateway.cs:3826
static string GetUrl(string LocalResource)
Gets a URL for a resource.
Definition: Gateway.cs:4167
static ContractsClient ContractsClient
XMPP Contracts Client, if such a compoent is available on the XMPP broker.
Definition: Gateway.cs:4375
static XmppClient XmppClient
XMPP Client connection of gateway.
Definition: Gateway.cs:3187
Implements an HTTP server.
Definition: HttpServer.cs:36
Static class managing editable parameters in objects. Editable parameters are defined by using the at...
Definition: Parameters.cs:25
Abstract base class for XMPP client connections
abstract string RemoteEndpoint
Remote endpoint
Base class for components.
Definition: Component.cs:16
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
Definition: Component.cs:297
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
Definition: Component.cs:161
CaseInsensitiveString Subdomain
Subdomain name.
Definition: Component.cs:76
CaseInsensitiveString SubdomainSuffixed
Subdomain name, suffixed with a period (.).
Definition: Component.cs:81
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: Component.cs:149
XmppAddress MainDomain
Main/principal domain address
Definition: Component.cs:86
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
Definition: Component.cs:123
XmppServer Server
XMPP Server.
Definition: Component.cs:96
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: Component.cs:249
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Definition: Component.cs:262
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
Definition: Component.cs:190
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:243
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:173
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
Definition: IqEventArgs.cs:201
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:137
Task IqErrorServiceUnavailable(XmppAddress From, string ErrorText, string Language)
Returns a service-unavailable error.
Definition: IqEventArgs.cs:215
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Definition: IqEventArgs.cs:159
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Definition: IqEventArgs.cs:229
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:114
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.
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:3412
bool TryGetClientConnections(string BareJID, out IClientConnection[] Connections)
Tries to get available connections for a given client.
Definition: XmppServer.cs:818
bool TryGetClientConnection(string FullJID, out IClientConnection Connection)
Tries to get an active client connection.
Definition: XmppServer.cs:807
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:3317
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
Definition: XmppServer.cs:861
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Definition: XmppServer.cs:1712
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
Definition: XmppServer.cs:1647
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:882
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:19
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:879
static Task< string[]> GetCollections()
Gets an array of available collections.
Definition: Database.cs:1560
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
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:15
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:451
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
Definition: Cache.cs:338
Event arguments for cache item removal events.
KeyType Key
Key of item that was removed.
RemovedReason Reason
Reason for removing the item.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:548
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:1353
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:84
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1630
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
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:16
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:53
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:90
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static byte[] ComputeHash(HashFunction Function, byte[] Data)
Computes a hash of a block of binary data.
Definition: Hashes.cs:214
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:248
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static async void Success(string Message, string UserName, string RemoteEndpoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
Login state information relating to a remote endpoint
RemoteEndpoint()
Login state information relating to a remote endpoint
Contains information about a broker account.
Definition: Account.cs:28
CaseInsensitiveString EMail
E-mail address associated with account.
Definition: Account.cs:129
bool Enabled
If account is enabled
Definition: Account.cs:287
CaseInsensitiveString PhoneNr
Phone number associated with account.
Definition: Account.cs:138
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 configuration options regarding peer-review of new legal identities.
static PeerReviewConfiguration Instance
Current instance of configuration.
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 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 authentication results
KeyValuePair< string, object >[] Tags
Tags annotating the error message.
ErrorType ErrorType
Type of error.
string ErrorLanguage
Optional language of error message
string ErrorMessage
Optional Error message
string ErrorCode
Optional Machine-readable error code.
bool? Result
true = authentication successful false = authentication rejected null = unable to perform authenticat...
Interface for identity applications.
Interface for currency converter services
Task< IAuthenticationResult > IsValid(KeyValuePair< string, object >[] Identity, IEnumerable< IPhoto > Photos)
Checks the veracity of identity claims.
Interface for currency converter service providers
IPeerReviewServiceProvider PeerReviewServiceProvider
Reference to service provider.
Task< IAuthenticationResult > IsValid(Waher.Networking.XMPP.Contracts.EventArguments.SignaturePetitionEventArgs PeerReivewRequest, ClientUrlEventHandler ClientUrlCallback, object State)
Checks the veracity of identity claims.
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 photos.
Definition: IPhoto.cs:7
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...
string RemoteEndpoint
Remote endpoint.
Abstract base class for End-to-End encryption schemes.
Definition: IE2eEndpoint.cs:12
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.
class Photo(byte[] Binary, int Rotation)
Class containing information about a photo.
Definition: Photo.cs:8
RemovedReason
Reason for removing the item.
Grade
Grade enumeration
Definition: Grade.cs:7
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11
ContentType
DTLS Record content type.
Definition: Enumerations.cs:11
HashFunction
Hash method enumeration.
Definition: Hashes.cs:28
NamespaceSet
Namespace versions
Definition: NamespaceSet.cs:7
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:13
int Years
Number of years.
Definition: Duration.cs:130
static bool TryParse(string s, out Duration Result)
Tries to parse a duration value.
Definition: Duration.cs:85
static readonly Duration Zero
Zero value
Definition: Duration.cs:532
static Duration GetDurationBetween(System.DateTime From, System.DateTime To)
Calculates the duration between two dates.
Definition: Duration.cs:649