/// <summary> /// Perform backup on the specified media /// </summary> public void Backup(BackupMedia media, String password = null) { // Make a determination that the user is allowed to perform this action if (AuthenticationContext.Current.Principal != AuthenticationContext.SystemPrincipal) { new PolicyPermission(System.Security.Permissions.PermissionState.Unrestricted, PermissionPolicyIdentifiers.ExportClinicalData).Demand(); } // Get the output medium var directoryName = this.GetBackupDirectory(media); string fileName = Path.Combine(directoryName, $"sdbdc-{DateTime.Now.ToString("yyyy-MM-dd-HH-mm")}.tar"); // Confirm if the user really really wants to backup if (String.IsNullOrEmpty(password) && !ApplicationContext.Current.Confirm(Strings.locale_backup_confirm)) { return; } // TODO: Audit the backup to the data to the central server AuditUtil.AuditDataExport(); // Try to backup the data try { this.m_tracer.TraceInfo("Beginning backup to {0}..", fileName); ApplicationContext.Current?.SetProgress(Strings.locale_backup, 0.25f); // Backup folders first var sourceDirectory = ApplicationContext.Current.ConfigurationPersister.ApplicationDataDirectory; try { using (var fs = File.Create(fileName)) using (var writer = new SharpCompress.Writers.Tar.TarWriter(fs, new TarWriterOptions(SharpCompress.Common.CompressionType.None, true))) { this.BackupDirectory(writer, sourceDirectory, sourceDirectory); var appInfo = new DiagnosticReport() { ApplicationInfo = new DiagnosticApplicationInfo(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(o => o.DefinedTypes.Any(t => t.Name == "Program" || t.Name == "SplashActivity")) ?? typeof(SanteDBConfiguration).Assembly) }; // Output appInfo using (var ms = new MemoryStream()) { XmlSerializer xsz = XmlModelSerializerFactory.Current.CreateSerializer(appInfo.GetType()); xsz.Serialize(ms, appInfo); ms.Flush(); ms.Seek(0, SeekOrigin.Begin); writer.Write(".appinfo.xml", ms, DateTime.Now); } // Output declaration statement using (var ms = new MemoryStream(Encoding.UTF8.GetBytes($"User {(AuthenticationContext.Current?.Principal?.Identity?.Name ?? "SYSTEM")} created this backup on {DateTime.Now}. The end user was asked to confirm this decision to backup and acknolwedges all responsibilities for guarding this file."))) writer.Write("DISCLAIMER.TXT", ms, DateTime.Now); // Output databases var dcc = ApplicationServiceContext.Current.GetService <IDataManagementService>(); var dbBackupLocation = dcc.Backup(""); this.BackupDirectory(writer, dbBackupLocation, dbBackupLocation); try { Directory.Delete(dbBackupLocation, true); } catch { } // Now include the configuration file // HACK: This is a total hack to support restore in place if (!String.IsNullOrEmpty(password)) { using (var ms = new MemoryStream()) { ApplicationServiceContext.Current.GetService <IConfigurationManager>().Configuration.Save(ms); ms.Seek(0, SeekOrigin.Begin); var config = SanteDBConfiguration.Load(ms); /// Load a new copy off the stream // Set the security system not to encrypt the device secret config.GetSection <SecurityConfigurationSection>().PlainTextSecret = true; using (var oms = new MemoryStream()) { config.Save(oms); oms.Flush(); oms.Seek(0, SeekOrigin.Begin); writer.Write("santedb.config", oms, DateTime.Now); } } } } this.m_tracer.TraceInfo("Beginning compression {0}..", fileName); using (var fileStream = File.Create(Path.ChangeExtension(fileName, "sdbk"))) { Stream outStream = fileStream; try { // Write header fileStream.Write(MAGIC, 0, MAGIC.Length); // Encrypt if (!String.IsNullOrEmpty(password)) { fileStream.WriteByte(1); var cryptProvider = AesCryptoServiceProvider.Create(); var passKey = ASCIIEncoding.ASCII.GetBytes(password); passKey = Enumerable.Range(0, 32).Select(o => passKey.Length > o ? passKey[o] : (byte)0).ToArray(); cryptProvider.Key = passKey; cryptProvider.GenerateIV(); fileStream.Write(BitConverter.GetBytes(cryptProvider.IV.Length), 0, 4); fileStream.Write(cryptProvider.IV, 0, cryptProvider.IV.Length); outStream = new CryptoStream(fileStream, cryptProvider.CreateEncryptor(), CryptoStreamMode.Write); } else { fileStream.WriteByte(0); } using (var fs = File.OpenRead(fileName)) using (var gzs = new GZipStream(outStream, CompressionMode.Compress)) { fs.CopyTo(gzs); } } finally { if (outStream != fileStream) { outStream.Close(); } } } } finally { File.Delete(fileName); } } catch (Exception ex) { this.m_tracer.TraceError("Error backing up to {0}: {1}", fileName, ex); throw; } }