4using System.IO.Compression;
7using System.Threading.Tasks;
33 internal static Aes aes = GetCryptoProvider();
39 : base(
"/StartExport")
43 private static Aes GetCryptoProvider()
45 Aes Result = Aes.Create();
47 Result.BlockSize = 128;
49 Result.Mode = CipherMode.CBC;
50 Result.Padding = PaddingMode.None;
89 if (Content.
HasError || !(Content.
Decoded is Dictionary<string, object> RequestObj))
95 if (!RequestObj.TryGetValue(
"TypeOfFile", out
object Obj) || !(Obj is
string TypeOfFile))
101 if (!RequestObj.TryGetValue(
"Database", out Obj) || !(Obj is
bool Database))
107 if (!RequestObj.TryGetValue(
"Ledger", out Obj) || !(Obj is
bool Ledger))
110 if (!RequestObj.TryGetValue(
"WebContent", out Obj) || !(Obj is
bool WebContent))
116 if (!RequestObj.TryGetValue(
"OnlySelectedCollections", out Obj) || !(Obj is
bool OnlySelectedCollections))
122 if (!RequestObj.TryGetValue(
"selectedCollections", out Obj) || !(Obj is Array SelectedCollections))
128 if (!RequestObj.TryGetValue(
"exportOnly", out Obj) || !(Obj is
bool ExportOnly))
134 ExportInfo ExportInfo = await GetExporter(TypeOfFile, OnlySelectedCollections, SelectedCollections);
141 Response.StatusCode = 409;
142 Response.StatusMessage =
"Conflict";
144 T = Response.
Write(
"Export is underway.");
161 Export.ExportType = TypeOfFile;
163 Export.ExportLedger =
Ledger;
164 Export.ExportWebContent = WebContent;
167 List<string> Folders =
new List<string>();
171 if (RequestObj.TryGetValue(FolderCategory.CategoryId, out Obj) && Obj is
bool b)
177 Folders.AddRange(FolderCategory.Folders);
181 Task
_ = DoExport(ExportInfo,
Database,
Ledger, WebContent, Folders.ToArray());
183 Response.StatusCode = 200;
184 Response.StatusMessage =
"OK";
187 await Response.
Write(ExportInfo.LocalBackupFileName);
195 private static bool exporting =
false;
196 private static readonly
object synchObject =
new object();
198 internal class ExportInfo
200 public string LocalBackupFileName;
201 public string LocalKeyFileName;
202 public string FullBackupFileName;
203 public string FullKeyFileName;
204 public string ContentType;
208 internal static async Task<ExportInfo> GetExporter(
string TypeOfFile,
bool OnlySelectedCollections, Array SelectedCollections)
210 ExportInfo Result =
new ExportInfo();
213 if (!Directory.Exists(BasePath))
214 Directory.CreateDirectory(BasePath);
216 BasePath += Path.DirectorySeparatorChar;
218 Result.FullBackupFileName = BasePath + DateTime.Now.ToString(
"yyyy-MM-dd HH_mm_ss");
223 Result.FullBackupFileName = await
GetUniqueFileName(Result.FullBackupFileName,
".xml");
225 FileStream fs =
new FileStream(Result.FullBackupFileName, FileMode.Create, FileAccess.Write);
226 DateTime Created = File.GetCreationTime(Result.FullBackupFileName);
228 Settings.Async =
true;
229 XmlWriter XmlOutput = XmlWriter.Create(fs, Settings);
230 Result.LocalBackupFileName = Result.FullBackupFileName[BasePath.Length..];
231 Result.Exporter =
new XmlExportFormat(Result.LocalBackupFileName, Created, XmlOutput, fs, OnlySelectedCollections, SelectedCollections);
235 Result.FullBackupFileName = await
GetUniqueFileName(Result.FullBackupFileName,
".bin");
237 fs =
new FileStream(Result.FullBackupFileName, FileMode.Create, FileAccess.Write);
238 Created = File.GetCreationTime(Result.FullBackupFileName);
239 Result.LocalBackupFileName = Result.FullBackupFileName[BasePath.Length..];
240 Result.Exporter =
new BinaryExportFormat(Result.LocalBackupFileName, Created, fs, fs, OnlySelectedCollections, SelectedCollections);
244 Result.FullBackupFileName = await
GetUniqueFileName(Result.FullBackupFileName,
".gz");
245 Result.ContentType =
"application/gzip";
246 fs =
new FileStream(Result.FullBackupFileName, FileMode.Create, FileAccess.Write);
247 Created = File.GetCreationTime(Result.FullBackupFileName);
248 Result.LocalBackupFileName = Result.FullBackupFileName[BasePath.Length..];
249 GZipStream gz =
new GZipStream(fs, CompressionLevel.Optimal,
false);
250 Result.Exporter =
new BinaryExportFormat(Result.LocalBackupFileName, Created, gz, fs, OnlySelectedCollections, SelectedCollections);
254 Result.FullBackupFileName = await
GetUniqueFileName(Result.FullBackupFileName,
".bak");
256 fs =
new FileStream(Result.FullBackupFileName, FileMode.Create, FileAccess.Write);
257 Created = File.GetCreationTime(Result.FullBackupFileName);
258 Result.LocalBackupFileName = Result.FullBackupFileName[BasePath.Length..];
263 ICryptoTransform AesTransform = aes.CreateEncryptor(Key, IV);
264 CryptoStream cs =
new CryptoStream(fs, AesTransform, CryptoStreamMode.Write);
266 gz =
new GZipStream(cs, CompressionLevel.Optimal,
false);
267 Result.Exporter =
new BinaryExportFormat(Result.LocalBackupFileName, Created, gz, fs, cs, 32, OnlySelectedCollections, SelectedCollections);
271 if (!Directory.Exists(BasePath2))
272 Directory.CreateDirectory(BasePath2);
274 BasePath2 += Path.DirectorySeparatorChar;
275 Result.LocalKeyFileName = Result.LocalBackupFileName.Replace(
".bak",
".key");
276 Result.FullKeyFileName = BasePath2 + Result.LocalKeyFileName;
278 using (XmlOutput = XmlWriter.Create(Result.FullKeyFileName,
XML.
WriterSettings(
true,
false)))
280 XmlOutput.WriteStartDocument();
282 XmlOutput.WriteAttributeString(
"key", Convert.ToBase64String(Key));
283 XmlOutput.WriteAttributeString(
"iv", Convert.ToBase64String(IV));
284 XmlOutput.WriteEndElement();
285 XmlOutput.WriteEndDocument();
292 using (fs = File.OpenRead(Result.FullKeyFileName))
303 Created = File.GetCreationTime(Result.FullKeyFileName);
309 throw new NotSupportedException(
"Unsupported file type.");
324 string Suffix =
string.Empty;
330 s = Base + Suffix + Extension;
335 Suffix =
" (" + i.ToString() +
")";
347 internal static async Task DoExport(ExportInfo ExportInfo,
bool Database,
bool Ledger,
bool WebContent,
string[] Folders)
355 List<KeyValuePair<string, object>> Tags =
new List<KeyValuePair<string, object>>()
357 new KeyValuePair<string, object>(
"Database",
Database),
358 new KeyValuePair<string, object>(
"Ledger",
Ledger)
361 foreach (
string Folder
in Folders)
362 Tags.Add(
new KeyValuePair<string, object>(Folder,
true));
364 Log.
Informational(
"Starting export.", ExportInfo.Exporter.FileName, Tags.ToArray());
366 await ExportInfo.Exporter.Start();
372 StringBuilder Temp =
new StringBuilder();
373 string[] RepairedCollections;
378 "Transforms",
"DbStatXmlToHtml.xslt"), Path.Combine(
Gateway.
RootFolder,
"Data"),
false,
true,
382 if (RepairedCollections.Length > 0)
384 string Xml = Temp.ToString();
385 string ReportFileName = Path.Combine(Path.GetDirectoryName(ExportInfo.FullBackupFileName),
386 "AutoRepair " + DateTime.Now.ToString(
"yyyy-MM-ddTHH.mm.ss.ffffff") +
".xml");
390 SortedDictionary<string, bool> CollectionsToExport =
new SortedDictionary<string, bool>();
392 if (ExportInfo.Exporter.CollectionNames is
null)
394 foreach (
string Collection
in await Persistence.Database.GetCollections())
395 CollectionsToExport[Collection] =
true;
399 foreach (
string Collection
in ExportInfo.Exporter.CollectionNames)
400 CollectionsToExport[Collection] =
true;
403 foreach (
string Collection
in Persistence.Database.GetExcludedCollections())
404 CollectionsToExport.Remove(Collection);
406 string[] ToExport =
new string[CollectionsToExport.Count];
407 CollectionsToExport.Keys.CopyTo(ToExport, 0);
409 await Persistence.Database.Export(ExportInfo.Exporter, ToExport,
419 CollectionNames = ExportInfo.Exporter.CollectionNames
422 await Persistence.Ledger.Export(ExportInfo.Exporter, Restricion,
426 if (WebContent || Folders.Length > 0)
430 await ExportInfo.Exporter.StartFiles();
439 if (File.Exists(ConfigFileName))
440 await ExportFile(ConfigFileName, ExportInfo.Exporter);
442 FileNames = Directory.GetFiles(
Gateway.
RootFolder,
"*.*", SearchOption.TopDirectoryOnly);
444 foreach (
string FileName
in FileNames)
445 await ExportFile(FileName, ExportInfo.Exporter);
449 foreach (
string Folder
in Directory.GetDirectories(
Gateway.
RootFolder,
"*.*", SearchOption.TopDirectoryOnly))
451 bool IsWebContent =
true;
455 foreach (
string Folder3
in FolderCategory.Folders)
457 if (
string.Compare(Folder3, Folder,
true) == 0)
459 IsWebContent =
false;
470 FileNames = Directory.GetFiles(Folder,
"*.*", SearchOption.AllDirectories);
472 foreach (
string FileName
in FileNames)
473 await ExportFile(FileName, ExportInfo.Exporter);
478 foreach (
string Folder
in Folders)
482 FileNames = Directory.GetFiles(Folder2,
"*.*", SearchOption.AllDirectories);
484 foreach (
string FileName
in FileNames)
485 await ExportFile(FileName, ExportInfo.Exporter);
491 await ExportInfo.Exporter.EndFiles();
495 Log.
Informational(
"Export successfully completed.", ExportInfo.Exporter.FileName);
513 await ExportInfo.Exporter.End();
514 ExportInfo.Exporter.Dispose();
531 await UploadBackupFile(ExportInfo.LocalBackupFileName,
532 ExportInfo.FullBackupFileName, ExportInfo.ContentType,
535 if (!
string.IsNullOrEmpty(ExportInfo.FullKeyFileName))
537 await UploadBackupFile(ExportInfo.LocalKeyFileName,
538 ExportInfo.FullKeyFileName, ExportInfo.ContentType,
559 private static async Task<bool> ExportFile(
string FileName,
IExportFormat Output)
561 using (FileStream fs = File.OpenRead(FileName))
573 private static Task UploadBackupFile(
string LocalFileName,
string FullFileName,
576 return UploadBackupFile(
new BackupInfo()
578 LocalFileName = LocalFileName,
579 FullFileName = FullFileName,
587 private class BackupInfo
589 public string LocalFileName;
590 public string FullFileName;
593 public bool Rescheduled;
594 public Dictionary<string, bool> Recipients =
null;
598 private static async Task UploadBackupFile(
object State)
600 BackupInfo BackupInfo = (BackupInfo)State;
601 bool Reschedule =
false;
606 if (!File.Exists(BackupInfo.FullFileName))
608 Log.
Warning(Msg =
"Backup file has been removed. Upload cancelled.", BackupInfo.LocalFileName);
610 if (BackupInfo.Rescheduled)
619 if (BackupInfo.Recipients is
null)
621 BackupInfo.Recipients =
new Dictionary<string, bool>();
625 if (BackupInfo.IsKey && !(Hosts is
null))
627 foreach (
string Host
in Hosts)
628 BackupInfo.Recipients[Host] =
false;
633 if (!BackupInfo.IsKey && !(Hosts is
null))
635 foreach (
string Host
in Hosts)
636 BackupInfo.Recipients[Host] =
false;
640 LinkedList<string> Recipients =
new LinkedList<string>();
643 foreach (KeyValuePair<string, bool> P
in BackupInfo.Recipients)
646 Recipients.AddLast(P.Key);
649 while (!((Recipient = Recipients.First?.Value) is
null))
651 Recipients.RemoveFirst();
653 if (Recipient.IndexOf(
'@') >= 0)
668 BackupInfo.Thread?.NewState(
"Discover_" + Recipient);
672 await UploadClient.DiscoverAsync(Recipient);
674 if (UploadClient.HasSupport)
676 using FileStream fs = File.OpenRead(BackupInfo.FullFileName);
677 long FileSize = fs.Length;
679 BackupInfo.Thread?.NewState(
"Prepare_" + Recipient);
681 await UploadClient.PrepareFileUpload(BackupInfo.LocalFileName,
682 BackupInfo.ContentType, FileSize,
FilePurpose.Backup);
684 BackupInfo.Thread?.NewState(
"Get_Slot_" + Recipient);
687 BackupInfo.ContentType, FileSize,
false);
690 throw e2.StanzaError ??
new XmppException(
"Unable to get HTTP upload slot for backup file.");
692 BackupInfo.Thread?.NewState(
"Upload_" + Recipient);
694 if (BackupInfo.IsKey)
695 Log.
Informational(
"Uploading key file to " + Recipient +
".", BackupInfo.LocalFileName);
697 Log.
Informational(
"Uploading backup file to " + Recipient +
".", BackupInfo.LocalFileName);
699 await e2.
PUT(fs, BackupInfo.ContentType, 60 * 60 * 1000);
701 if (BackupInfo.IsKey)
702 Log.
Informational(
"Key file uploaded to " + Recipient +
".", BackupInfo.LocalFileName);
704 Log.
Informational(
"Backup file uploaded to " + Recipient +
".", BackupInfo.LocalFileName);
709 BackupInfo.Thread?.Exception(ex, BackupInfo.LocalFileName);
721 BackupInfo.Thread?.Exception(ex);
729 BackupInfo.Thread?.NewState(
"Reschedule");
730 BackupInfo.Rescheduled =
true;
734 else if (BackupInfo.Rescheduled)
736 await
Gateway.
SendNotification(
"Backup file has been successfully been uploaded. The initial attempt to upload the backup file failed, but a sequent rescheduled upload succeeded.");
737 BackupInfo.Rescheduled =
false;
740 BackupInfo.Thread =
null;
const string DefaultContentType
text/plain
Helps with parsing of commong data types.
static string JsonStringEncode(string s)
Encodes a string for inclusion in JSON.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
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 ...
Plain text encoder/decoder.
const string DefaultContentType
text/plain
const string DefaultContentType
Default content type for XML documents.
Helps with common XML-related tasks.
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
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.
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
static string[] GetTabIDsForLocation(string Location)
Gets the Tab IDs of all tabs that display a particular resource.
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Information about an exportable folder category
Static class managing data export.
static async Task< string[]> GetKeyHostsAsync()
Secondary key hosts.
static async Task< string > GetFullExportFolderAsync()
Full path to export folder.
static FolderCategory[] GetRegisteredFolders()
Gets registered exportable folders.
static async Task< string > GetFullKeyExportFolderAsync()
Full path to key folder.
static async Task< string[]> GetBackupHostsAsync()
Secondary backup hosts.
static async Task SetExportFolderAsync(string Value)
Export folder.
Static class managing the runtime environment of the IoT Gateway.
static IUser AssertUserAuthenticated(HttpRequest Request, string Privilege)
Makes sure a request is being made from a session with a successful user login.
static string ConfigFilePath
Full path to Gateway.config file.
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
static string AppDataFolder
Application data folder.
static Task SendNotification(Graph Graph)
Sends a graph as a notification message to configured notification recipients.
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
static XmppClient XmppClient
XMPP Client connection of gateway.
static string RootFolder
Web root folder.
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
StartExport()
Starts data export
override bool UserSessions
If the resource uses user sessions.
static async Task< string > GetUniqueFileName(string Base, string Extension)
Gets a unique filename.
override bool HandlesSubPaths
If the resource handles sub-paths.
bool AllowsPOST
If the POST method is allowed.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Represents an HTTP request.
bool HasData
If the request has data.
async Task< ContentResponse > DecodeDataAsync()
Decodes data sent in request.
Represets a response of an HTTP client request.
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Task Write(byte[] Data)
Returns binary data in the response.
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
bool Ok
If the response is an OK result response (true), or an error response (false).
Class managing HTTP File uploads, as defined in XEP-0363.
Event arguments for HTTP File Upload callback methods.
Task PUT(byte[] Content, string ContentType, int Timeout)
Uploads file content to the server.
Maintains information about an item in the roster.
bool HasLastPresence
If the roster item has received presence from an online resource having the given bare JID.
PresenceEventArgs LastPresence
Last presence received from a resource having this bare JID.
XmppState State
Current state of connection.
Base class of XMPP exceptions
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Contains basic ledger export restrictions.
Static interface for ledger persistence. In order to work, a ledger provider has to be assigned to it...
static bool HasProvider
If a ledger provider is registered.
Simple ledger that records anything that happens in the database to XML files in the program data fol...
const string Namespace
http://waher.se/Schema/Export.xsd
static Task WriteAllTextAsync(string FileName, string Text)
Creates a text file asynchronously.
Class that keeps track of events and timing.
void Stop()
Stops measuring time.
ProfilerThread CreateThread(string Name, ProfilerThreadType Type)
Creates a new profiler thread.
void NewState(string State)
Main Thread changes state.
void Exception(System.Exception Exception)
Event occurred on main thread
void Start()
Starts measuring time.
Class that keeps track of events and timing for one thread.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Static class of application-wide semaphores that can be used to order access to editable objects.
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...
POST Interface for HTTP resources.
FilePurpose
Purpose of file uploaded
XmppState
State of XMPP connection.
ProfilerThreadType
Type of profiler thread.
ContentType
DTLS Record content type.