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