Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
UploadSignature.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Runtime.ExceptionServices;
5using System.Threading.Tasks;
6using System.Xml;
7using Waher.Content;
14
16{
18 {
19 private static readonly Dictionary<string, UploadPackage.UploadRec> signatureFilePerSession = new Dictionary<string, UploadPackage.UploadRec>();
20 private static int expectedBlockSignature = 0;
21
22 public UploadSignature()
23 : base("/UploadSignature")
24 {
25 }
26
27 public override bool HandlesSubPaths => false;
28 public override bool UserSessions => true;
29 public bool AllowsPOST => true;
30
31 public async Task POST(HttpRequest Request, HttpResponse Response)
32 {
33 KeyValuePair<bool, int> P = await UploadPackage.Upload(Request, Response, expectedBlockSignature, signatureFilePerSession, "signature", false);
34 expectedBlockSignature = P.Value;
35
36 if (P.Key)
37 {
38 UploadPackage.UploadRec PackageRec;
39 UploadPackage.UploadRec SignatureRec;
40 string TabID;
41 string HttpSessionID;
42
43 if (!Request.Header.TryGetHeaderField("X-TabID", out HttpField F) || string.IsNullOrEmpty(TabID = F.Value) ||
44 string.IsNullOrEmpty(HttpSessionID = GetSessionId(Request, Response)))
45 {
46 throw new BadRequestException();
47 }
48
49 PackageRec = GetAndRemoveFile(HttpSessionID, UploadPackage.packageFilePerSession);
50 SignatureRec = GetAndRemoveFile(HttpSessionID, signatureFilePerSession);
51
52 CopyPackage(PackageRec, SignatureRec, TabID, Request.Session["packageFileName"]?.ToString(), Request.RemoteEndPoint);
53 }
54 }
55
56 private static UploadPackage.UploadRec GetAndRemoveFile(string SessionID, Dictionary<string, UploadPackage.UploadRec> Files)
57 {
58 lock (Files)
59 {
60 if (Files.TryGetValue(SessionID, out UploadPackage.UploadRec Rec))
61 {
62 Files.Remove(SessionID);
63 return Rec;
64 }
65 else
66 return null;
67 }
68 }
69
70 private static async void CopyPackage(UploadPackage.UploadRec PackageRec, UploadPackage.UploadRec SignatureRec, string TabID,
71 string PackageFileName, string RemoteEndpoint)
72 {
73 try
74 {
75 if (PackageRec.File.Length > int.MaxValue)
76 throw new Exception("Package file too large.");
77
78 XmlDocument Doc = new XmlDocument()
79 {
80 PreserveWhitespace = true
81 };
82 SignatureRec.File.Position = 0;
83 Doc.Load(SignatureRec.File);
84
85 if (Doc.DocumentElement is null ||
86 Doc.DocumentElement.LocalName != "Signatures" ||
87 Doc.DocumentElement.NamespaceURI != "http://waher.se/Schema/Signatures.xsd")
88 {
89 throw new Exception("Invalid signature file.");
90 }
91
92 byte[] Signature = null;
93
94 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
95 {
96 if (N is XmlElement E && E.LocalName == "Signature" && XML.Attribute(E, "fileName") == PackageFileName)
97 {
98 try
99 {
100 Signature = Convert.FromBase64String(E.InnerText);
101 }
102 catch (Exception)
103 {
104 throw new Exception("Invalid signature.");
105 }
106 break;
107 }
108 }
109
110 if (Signature is null)
111 throw new Exception("Signature for corresponding package file not included in uploaded signature file.");
112
113 await CopyPackage(PackageRec, Signature, TabID, PackageFileName, RemoteEndpoint);
114 }
115 catch (Exception ex)
116 {
117 await ClientEvents.PushEvent(new string[] { TabID }, "UploadFailed",
118 "{\"fileName\":\"" + CommonTypes.JsonStringEncode(PackageFileName) +
119 "\", \"message\": \"" + CommonTypes.JsonStringEncode(ex.Message) + "\", \"remove\": true}", true, "User");
120 }
121 finally
122 {
123 PackageRec.File.Dispose();
124 SignatureRec.File.Dispose();
125 }
126 }
127
128 internal static async Task CopyPackage(UploadPackage.UploadRec PackageRec, byte[] Signature, string TabID,
129 string PackageFileName, string RemoteEndpoint)
130 {
131 DateTime Now = DateTime.UtcNow;
132 Package Package = await Provisioning.ProvisioningComponent.GetPackage(PackageFileName);
133 long Size = PackageRec.File.Length;
134 bool PrevDownloadable;
135 bool Downloadable = PackageRec.MakeDownloadable;
136
137 if (Package is null)
138 {
139 PrevDownloadable = false;
140
141 Package = new Package()
142 {
143 FileName = PackageFileName,
144 Signature = Signature,
145 RemoteEndpoint = RemoteEndpoint,
146 Published = Now,
147 Supersedes = DateTime.MinValue,
148 Created = Now,
149 Bytes = Size,
150 AesKey = null,
151 Installed = DateTime.MinValue,
152 PublicKey = null,
153 Downloadable = false
154 };
155
156 await Database.Insert(Package);
157 }
158 else
159 {
160 PrevDownloadable = Package.Downloadable;
161
162 Package.Signature = Signature;
163 Package.RemoteEndpoint = RemoteEndpoint;
164 Package.Supersedes = Package.Published;
165 Package.Published = Now;
166 Package.Bytes = Size;
167 Package.Downloadable = Downloadable;
168
169 if (!(Package.AesKey is null))
170 Package.ContentOnly = XmppServerModule.IsContentPackage(Package);
171
172 await Database.Update(Package);
173 }
174
175 bool NewSoftware = string.Compare(PackageFileName, BrokerPackage.FileName, true) == 0;
176 bool InstallPackage = Package.Installed > DateTime.MinValue;
177
178 try
179 {
180 if (NewSoftware)
181 {
182 PackageRec.File.Position = 0;
183 if (!XmppServerModule.ValidateIoTBrokerPackage(PackageRec.File, Signature))
184 throw new Exception("Invalid IoT Broker package. Signature invalid.");
185 }
186
187 using (FileStream f = File.Create(Path.Combine(XmppServerModule.PackagesFolder, PackageFileName)))
188 {
189 PackageRec.File.Position = 0;
190 await PackageRec.File.CopyToAsync(f);
191 }
192
193 if (PackageRec.MakeDownloadable)
194 {
195 string DownloadsFolder = XmppServerModule.DownloadsFolder;
196
197 if (!Directory.Exists(DownloadsFolder))
198 Directory.CreateDirectory(DownloadsFolder);
199
200 using (FileStream f = File.Create(Path.Combine(DownloadsFolder, PackageFileName)))
201 {
202 PackageRec.File.Position = 0;
203 await PackageRec.File.CopyToAsync(f);
204 }
205 }
206 }
207 catch (Exception ex)
208 {
209 await Database.Delete(Package);
210 ExceptionDispatchInfo.Capture(ex).Throw();
211 }
212
213 if (PrevDownloadable && !Downloadable)
214 {
215 string FullPath = Path.Combine(XmppServerModule.DownloadsFolder, Package.FileName);
216
217 if (File.Exists(FullPath))
218 File.Delete(FullPath);
219 }
220
221 string s = Convert.ToBase64String(Package.Signature);
222 await ClientEvents.PushEvent(new string[] { TabID }, "UploadDone",
223 "{\"fileName\":\"" + CommonTypes.JsonStringEncode(PackageFileName) +
224 "\", \"relativeUrl\": \"" + CommonTypes.JsonStringEncode(Package.RelativeUrl) +
225 "\", \"bytes\": \"" + Export.FormatBytes(Package.Bytes) +
226 "\", \"created\": \"" + Package.Created.ToString() +
227 "\", \"supersedes\": \"" + (Package.Supersedes == DateTime.MinValue ? string.Empty : Package.Supersedes.ToString()) +
228 "\", \"published\": \"" + Package.Published.ToString() +
229 "\", \"remoteEndpoint\": \"" + CommonTypes.JsonStringEncode(Package.RemoteEndpoint.ToString()) +
230 "\", \"signature\": \"<a href='javascript:window.alert(\\\"" + s + "\\\")'>" + s.Substring(0, 10) + "...</a>" +
231 "\", \"button\": \"<button class='negButtonSm' onclick='DeletePackage(\\\"" + Package.FileName.Replace("\"", "\\\"") + "\\\")'>Delete</button>" +
232 "\", \"message\": \"Package successfully uploaded.\"}", true, "User");
233
234 await XmppServerModule.Provisioning.NewPackage(Package);
235
236 if (NewSoftware || InstallPackage)
237 await XmppServerModule.Instance.NewSoftwareAvailable(Package, RemoteEndpoint);
238 }
239
240 }
241}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string JsonStringEncode(string s)
Encodes a string for inclusion in JSON.
Definition: CommonTypes.cs:803
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
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Static class managing data export.
Definition: Export.cs:19
static string FormatBytes(double Bytes)
Formats a file size using appropriate unit.
Definition: Export.cs:126
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Base class for all HTTP fields.
Definition: HttpField.cs:7
bool TryGetHeaderField(string FieldName, out HttpField Field)
Tries to get a named header field.
Definition: HttpHeader.cs:227
Represents an HTTP request.
Definition: HttpRequest.cs:18
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:134
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:195
Variables Session
Contains session states, if the resource requires sessions, or null otherwise.
Definition: HttpRequest.cs:164
static string GetSessionId(HttpRequest Request, HttpResponse Response)
Gets the session ID used for a request.
const string HttpSessionID
The Cookie Key for HTTP Session Identifiers: "HttpSessionID"
Definition: HttpResource.cs:27
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:21
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
CaseInsensitiveString Replace(CaseInsensitiveString oldValue, CaseInsensitiveString newValue)
Returns a new string in which all occurrences of a specified string in the current instance are repla...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
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 async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
Identity of the IoT Broker package.
Definition: BrokerPackage.cs:7
const string FileName
IoTBroker.package
Contains information about a software package.
Definition: Package.cs:18
bool Downloadable
If package should be made downloadable via web interface.
Definition: Package.cs:106
byte[] Signature
Cryptographic signature of package, as calculated by the issuer of the package.
Definition: Package.cs:46
CaseInsensitiveString FileName
Filename of package.
Definition: Package.cs:40
DateTime Published
When package was published.
Definition: Package.cs:76
byte[] AesKey
Symmetric cipher used to encrypt package file.
Definition: Package.cs:58
DateTime Supersedes
Timestamp of superceded package.
Definition: Package.cs:82
string RemoteEndpoint
Remote Endpoint from where the package was downloaded or uploaded.
Definition: Package.cs:70
DateTime Created
When package record was created
Definition: Package.cs:88
bool AllowsPOST
If the POST method is allowed.
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
Service Module hosting the XMPP broker and its components.
POST Interface for HTTP resources.