private void AddBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { var str = request.Form["data"].Value; if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "Missing backup object"); return; } AddOrUpdateBackupData data = null; try { data = Serializer.Deserialize<AddOrUpdateBackupData>(new StringReader(str)); if (data.Backup == null) { ReportError(response, bw, "Data object had no backup entry"); return; } data.Backup.ID = null; if (Duplicati.Library.Utility.Utility.ParseBool(request.Form["temporary"].Value, false)) { Program.DataConnection.RegisterTemporaryBackup(data.Backup); bw.OutputOK(new { status = "OK", ID = data.Backup.ID }); } else { if (Library.Utility.Utility.ParseBool(request.Form["existing_db"].Value, false)) { data.Backup.DBPath = Library.Main.DatabaseLocator.GetDatabasePath(data.Backup.TargetURL, null, false, false); if (string.IsNullOrWhiteSpace(data.Backup.DBPath)) throw new Exception("Unable to find remote db path?"); } lock(Program.DataConnection.m_lock) { if (Program.DataConnection.Backups.Where(x => x.Name.Equals(data.Backup.Name, StringComparison.InvariantCultureIgnoreCase)).Any()) { ReportError(response, bw, "There already exists a backup with the name: " + data.Backup.Name); return; } Program.DataConnection.AddOrUpdateBackupAndSchedule(data.Backup, data.Schedule); } bw.OutputOK(new { status = "OK", ID = data.Backup.ID }); } } catch (Exception ex) { if (data == null) ReportError(response, bw, string.Format("Unable to parse backup or schedule object: {0}", ex.Message)); else ReportError(response, bw, string.Format("Unable to save schedule or backup object: {0}", ex.Message)); } }
private void ReadLogData(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var backupid = input["id"].Value; if (string.IsNullOrWhiteSpace(backupid)) { List <Dictionary <string, object> > res = null; Program.DataConnection.ExecuteWithCommand(x => { res = DumpTable(x, "ErrorLog", "Timestamp", input["offset"].Value, input["pagesize"].Value); }); bw.OutputOK(res); } else { var backup = Program.DataConnection.GetBackup(backupid); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } using (var con = (System.Data.IDbConnection)Activator.CreateInstance(Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType)) { con.ConnectionString = "Data Source=" + backup.DBPath; con.Open(); using (var cmd = con.CreateCommand()) { if (Duplicati.Library.Utility.Utility.ParseBool(input["remotelog"].Value, false)) { var dt = DumpTable(cmd, "RemoteOperation", "ID", input["offset"].Value, input["pagesize"].Value); // Unwrap raw data to a string foreach (var n in dt) { try { n["Data"] = System.Text.Encoding.UTF8.GetString((byte[])n["Data"]); } catch { } } bw.OutputOK(dt); } else { var dt = DumpTable(cmd, "LogData", "ID", input["offset"].Value, input["pagesize"].Value); bw.OutputOK(dt); } } } } }
private void ReadLogData(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var backupid = input["id"].Value; if (string.IsNullOrWhiteSpace(backupid)) { List<Dictionary<string, object>> res = null; Program.DataConnection.ExecuteWithCommand(x => { res = DumpTable(x, "ErrorLog", "Timestamp", input["offset"].Value, input["pagesize"].Value); }); bw.OutputOK(res); } else { var backup = Program.DataConnection.GetBackup(backupid); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } using(var con = (System.Data.IDbConnection)Activator.CreateInstance(Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType)) { con.ConnectionString = "Data Source=" + backup.DBPath; con.Open(); using(var cmd = con.CreateCommand()) { if (Duplicati.Library.Utility.Utility.ParseBool(input["remotelog"].Value, false)) { var dt = DumpTable(cmd, "RemoteOperation", "ID", input["offset"].Value, input["pagesize"].Value); // Unwrap raw data to a string foreach(var n in dt) try { n["Data"] = System.Text.Encoding.UTF8.GetString((byte[])n["Data"]); } catch { } bw.OutputOK(dt); } else { var dt = DumpTable(cmd, "LogData", "ID", input["offset"].Value, input["pagesize"].Value); bw.OutputOK(dt); } } } } }
private void GetBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) { ReportError(response, bw, "Invalid or missing backup id"); } else { var scheduleId = Program.DataConnection.GetScheduleIDsFromTags(new string[] { "ID=" + bk.ID }); var schedule = scheduleId.Any() ? Program.DataConnection.GetSchedule(scheduleId.First()) : null; var sourcenames = SpecialFolders.GetSourceNames(bk); //TODO: Filter out the password in both settings and the target url bw.OutputOK(new { success = true, data = new { Schedule = schedule, Backup = bk, DisplayNames = sourcenames } }); } }
private void GetBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) ReportError(response, bw, "Invalid or missing backup id"); else { var scheduleId = Program.DataConnection.GetScheduleIDsFromTags(new string[] { "ID=" + bk.ID }); var schedule = scheduleId.Any() ? Program.DataConnection.GetSchedule(scheduleId.First()) : null; var sourcenames = GetSourceNames(bk); //TODO: Filter out the password in both settings and the target url bw.OutputOK(new { success = true, data = new { Schedule = schedule, Backup = bk, DisplayNames = sourcenames } }); } }
private void SearchBackupFiles(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var filter = (input["filter"].Value ?? "").Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries); var timestring = input["time"].Value; var allversion = Duplicati.Library.Utility.Utility.ParseBool(input["all-versions"].Value, false); if (string.IsNullOrWhiteSpace(timestring) && !allversion) { ReportError(response, bw, "Invalid or missing time"); return; } var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } var prefixonly = Duplicati.Library.Utility.Utility.ParseBool(input["prefix-only"].Value, false); var foldercontents = Duplicati.Library.Utility.Utility.ParseBool(input["folder-contents"].Value, false); var time = new DateTime(); if (!allversion) { time = Duplicati.Library.Utility.Timeparser.ParseTimeInterval(timestring, DateTime.Now); } var r = Runner.Run(Runner.CreateListTask(bk, filter, prefixonly, allversion, foldercontents, time), false) as Duplicati.Library.Interface.IListResults; var result = new Dictionary <string, object>(); foreach (HttpServer.HttpInputItem n in input) { result[n.Name] = n.Value; } result["Filesets"] = r.Filesets; result["Files"] = r.Files; bw.OutputOK(result); }
private void SearchBackupFiles(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var filter = (input["filter"].Value ?? "").Split(new string[] { System.IO.Path.PathSeparator.ToString() }, StringSplitOptions.RemoveEmptyEntries); var timestring = input["time"].Value; var allversion = Duplicati.Library.Utility.Utility.ParseBool(input["all-versions"].Value, false); if (string.IsNullOrWhiteSpace(timestring) && !allversion) { ReportError(response, bw, "Invalid or missing time"); return; } var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } var prefixonly = Duplicati.Library.Utility.Utility.ParseBool(input["prefix-only"].Value, false); var foldercontents = Duplicati.Library.Utility.Utility.ParseBool(input["folder-contents"].Value, false); var time = new DateTime(); if (!allversion) time = Duplicati.Library.Utility.Timeparser.ParseTimeInterval(timestring, DateTime.Now); var r = Runner.Run(Runner.CreateListTask(bk, filter, prefixonly, allversion, foldercontents, time), false) as Duplicati.Library.Interface.IListResults; var result = new Dictionary<string, object>(); foreach(HttpServer.HttpInputItem n in input) result[n.Name] = n.Value; result["Filesets"] = r.Filesets; result["Files"] = r.Files; bw.OutputOK(result); }
private void GetLicenseData(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(Duplicati.License.LicenseReader.ReadLicenses(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location), "licenses"))); }
private void ListSupportedActions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(new { Version = 1, Methods = SUPPORTED_METHODS.Keys }); }
private void UpdateBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { string str = request.Form["data"].Value; if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "Missing backup object"); return; } AddOrUpdateBackupData data = null; try { data = Serializer.Deserialize<AddOrUpdateBackupData>(new StringReader(str)); if (data.Backup == null) { ReportError(response, bw, "Data object had no backup entry"); return; } if (data.Backup.ID == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (data.Backup.IsTemporary) { var backup = Program.DataConnection.GetBackup(data.Backup.ID); if (backup.IsTemporary) throw new InvalidDataException("External is temporary but internal is not?"); Program.DataConnection.UpdateTemporaryBackup(backup); bw.OutputOK(); } else { lock(Program.DataConnection.m_lock) { var backup = Program.DataConnection.GetBackup(data.Backup.ID); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (Program.DataConnection.Backups.Where(x => x.Name.Equals(data.Backup.Name, StringComparison.InvariantCultureIgnoreCase) && x.ID != data.Backup.ID).Any()) { ReportError(response, bw, "There already exists a backup with the name: " + data.Backup.Name); return; } //TODO: Merge in real passwords where the placeholder is found Program.DataConnection.AddOrUpdateBackupAndSchedule(data.Backup, data.Schedule); } bw.OutputOK(); } } catch (Exception ex) { if (data == null) ReportError(response, bw, string.Format("Unable to parse backup or schedule object: {0}", ex.Message)); else ReportError(response, bw, string.Format("Unable to save backup or schedule: {0}", ex.Message)); } }
private void UpdateBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { string str = request.Form["data"].Value; if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "Missing backup object"); return; } AddOrUpdateBackupData data = null; try { data = Serializer.Deserialize <AddOrUpdateBackupData>(new StringReader(str)); if (data.Backup == null) { ReportError(response, bw, "Data object had no backup entry"); return; } if (data.Backup.ID == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (data.Backup.IsTemporary) { var backup = Program.DataConnection.GetBackup(data.Backup.ID); if (backup.IsTemporary) { throw new InvalidDataException("External is temporary but internal is not?"); } Program.DataConnection.UpdateTemporaryBackup(backup); bw.OutputOK(); } else { lock (Program.DataConnection.m_lock) { var backup = Program.DataConnection.GetBackup(data.Backup.ID); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (Program.DataConnection.Backups.Where(x => x.Name.Equals(data.Backup.Name, StringComparison.InvariantCultureIgnoreCase) && x.ID != data.Backup.ID).Any()) { ReportError(response, bw, "There already exists a backup with the name: " + data.Backup.Name); return; } //TODO: Merge in real passwords where the placeholder is found Program.DataConnection.AddOrUpdateBackupAndSchedule(data.Backup, data.Schedule); } bw.OutputOK(); } } catch (Exception ex) { if (data == null) { ReportError(response, bw, string.Format("Unable to parse backup or schedule object: {0}", ex.Message)); } else { ReportError(response, bw, string.Format("Unable to save backup or schedule: {0}", ex.Message)); } } }
private void DeleteBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var backup = Program.DataConnection.GetBackup(input["id"].Value); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (Program.WorkThread.Active) { try { //TODO: It's not safe to access the values like this, //because the runner thread might interfere var nt = Program.WorkThread.CurrentTask; if (backup.Equals(nt == null ? null : nt.Backup)) { bool force; if (!bool.TryParse(input["force"].Value, out force)) { force = false; } if (!force) { bw.WriteJsonObject(new { status = "failed", reason = "backup-in-progress" }); return; } bool hasPaused = Program.LiveControl.State == LiveControls.LiveControlState.Paused; Program.LiveControl.Pause(); try { for (int i = 0; i < 10; i++) { if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) { System.Threading.Thread.Sleep(1000); } else { break; } } else { break; } } } finally { } if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) { if (hasPaused) { Program.LiveControl.Resume(); } bw.WriteJsonObject(new { status = "failed", reason = "backup-unstoppable" }); return; } } if (hasPaused) { Program.LiveControl.Resume(); } } } catch (Exception ex) { bw.WriteJsonObject(new { status = "error", message = ex.Message }); return; } } //var dbpath = backup.DBPath; Program.DataConnection.DeleteBackup(backup); // TODO: Before we activate this, // we need some strategy to figure out // if the db is shared with something else // like the commandline or another backup /*try * { * if (System.IO.File.Exists(dbpath)) * System.IO.File.Delete(dbpath); * } * catch (Exception ex) * { * Program.DataConnection.LogError(null, string.Format("Failed to delete database: {0}", dbpath), ex); * }*/ //We have fiddled with the schedules Program.Scheduler.Reschedule(); bw.OutputOK(); }
public override bool Process(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var authcookie = request.Cookies[AUTH_COOKIE_NAME] ?? request.Cookies[Library.Utility.Uri.UrlEncode(AUTH_COOKIE_NAME)]; var authinput = input["auth-token"] ?? input[Library.Utility.Uri.UrlEncode("auth-token")]; var auth_token = authcookie == null || string.IsNullOrWhiteSpace(authcookie.Value) ? null : authcookie.Value; if (authinput != null && !string.IsNullOrWhiteSpace(authinput.Value)) { auth_token = input["auth-token"].Value; } if (request.Uri.AbsolutePath == "/logout.cgi") { if (!string.IsNullOrWhiteSpace(auth_token)) { if (m_activeTokens.ContainsKey(auth_token)) { m_activeTokens.Remove(auth_token); } } response.Status = System.Net.HttpStatusCode.NoContent; response.Reason = "OK"; return(true); } else if (request.Uri.AbsolutePath == "/login.cgi") { foreach (var k in (from n in m_activeNonces where DateTime.UtcNow > n.Value.Item1 select n.Key).ToList()) { m_activeNonces.Remove(k); } if (input["get-nonce"] != null && !string.IsNullOrWhiteSpace(input["get-nonce"].Value)) { if (m_activeNonces.Count > 50) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too many active login attempts"; return(true); } var buf = new byte[32]; var expires = DateTime.UtcNow.AddMinutes(10); m_prng.GetBytes(buf); var nonce = Convert.ToBase64String(buf); var sha256 = System.Security.Cryptography.SHA256.Create(); sha256.TransformBlock(buf, 0, buf.Length, buf, 0); buf = Convert.FromBase64String(Program.DataConnection.ApplicationSettings.WebserverPassword); sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); m_activeNonces.Add(nonce, new Tuple <DateTime, string>(expires, pwd)); response.Cookies.Add(new HttpServer.ResponseCookie(NONCE_COOKIE_NAME, nonce, expires)); using (var bw = new BodyWriter(response)) { bw.OutputOK(new { Status = "OK", Nonce = nonce, Salt = Program.DataConnection.ApplicationSettings.WebserverPasswordSalt }); } return(true); } else { if (input["password"] != null && !string.IsNullOrWhiteSpace(input["password"].Value)) { var nonce_el = request.Cookies[NONCE_COOKIE_NAME] ?? request.Cookies[Library.Utility.Uri.UrlEncode(NONCE_COOKIE_NAME)]; var nonce = nonce_el == null || string.IsNullOrWhiteSpace(nonce_el.Value) ? "" : nonce_el.Value; var urldecoded = nonce == null ? "" : Duplicati.Library.Utility.Uri.UrlDecode(nonce); if (m_activeNonces.ContainsKey(urldecoded)) { nonce = urldecoded; } if (!m_activeNonces.ContainsKey(nonce)) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var pwd = m_activeNonces[nonce].Item2; m_activeNonces.Remove(nonce); if (pwd != input["password"].Value) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var buf = new byte[32]; var expires = DateTime.UtcNow.AddHours(1); m_prng.GetBytes(buf); var token = Duplicati.Library.Utility.Utility.Base64UrlEncode(buf); while (token.Length > 0 && token.EndsWith("=")) { token = token.Substring(0, token.Length - 1); } m_activeTokens.Add(token, expires); response.Cookies.Add(new HttpServer.ResponseCookie(AUTH_COOKIE_NAME, token, expires)); using (var bw = new BodyWriter(response)) bw.OutputOK(); return(true); } } } if (string.IsNullOrWhiteSpace(Program.DataConnection.ApplicationSettings.WebserverPassword)) { return(false); } foreach (var k in (from n in m_activeTokens where DateTime.UtcNow > n.Value select n.Key).ToList()) { m_activeTokens.Remove(k); } // If we have a valid token, proceeed if (!string.IsNullOrWhiteSpace(auth_token)) { DateTime expires; var found = m_activeTokens.TryGetValue(auth_token, out expires); if (!found) { auth_token = Duplicati.Library.Utility.Uri.UrlDecode(auth_token); found = m_activeTokens.TryGetValue(auth_token, out expires); } if (found && DateTime.UtcNow < expires) { expires = DateTime.UtcNow.AddHours(1); m_activeTokens[auth_token] = expires; response.Cookies.Add(new ResponseCookie(AUTH_COOKIE_NAME, auth_token, expires)); return(false); } } if (request.Uri.AbsolutePath == "/" || request.Uri.AbsolutePath == "/index.html") { response.Redirect("/login.html"); return(true); } if (request.Uri.AbsolutePath == "/control.cgi") { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Not logged in"; response.AddHeader("Location", "login.html"); return(true); } return(false); }
private void GetNotifications(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(Program.DataConnection.GetNotifications()); }
public override bool Process(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var auth_token = FindAuthCookie(request); var xsrf_token = FindXSRFToken(request); if (!HasXSRFCookie(request)) { var cookieAdded = AddXSRFTokenToRespone(response); if (!cookieAdded) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too Many Concurrent Request, try again later"; return(true); } } if (LOGOUT_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { if (!string.IsNullOrWhiteSpace(auth_token)) { if (m_activeTokens.ContainsKey(auth_token)) { m_activeTokens.Remove(auth_token); } } response.Status = System.Net.HttpStatusCode.NoContent; response.Reason = "OK"; return(true); } else if (LOGIN_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { foreach (var k in (from n in m_activeNonces where DateTime.UtcNow > n.Value.Item1 select n.Key).ToList()) { m_activeNonces.Remove(k); } if (input["get-nonce"] != null && !string.IsNullOrWhiteSpace(input["get-nonce"].Value)) { if (m_activeNonces.Count > 50) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too many active login attempts"; return(true); } var buf = new byte[32]; var expires = DateTime.UtcNow.AddMinutes(AUTH_TIMEOUT_MINUTES); m_prng.GetBytes(buf); var nonce = Convert.ToBase64String(buf); var sha256 = System.Security.Cryptography.SHA256.Create(); sha256.TransformBlock(buf, 0, buf.Length, buf, 0); buf = Convert.FromBase64String(Program.DataConnection.ApplicationSettings.WebserverPassword); sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); m_activeNonces.Add(nonce, new Tuple <DateTime, string>(expires, pwd)); response.Cookies.Add(new HttpServer.ResponseCookie(NONCE_COOKIE_NAME, nonce, expires)); using (var bw = new BodyWriter(response, request)) { bw.OutputOK(new { Status = "OK", Nonce = nonce, Salt = Program.DataConnection.ApplicationSettings.WebserverPasswordSalt }); } return(true); } else { if (input["password"] != null && !string.IsNullOrWhiteSpace(input["password"].Value)) { var nonce_el = request.Cookies[NONCE_COOKIE_NAME] ?? request.Cookies[Library.Utility.Uri.UrlEncode(NONCE_COOKIE_NAME)]; var nonce = nonce_el == null || string.IsNullOrWhiteSpace(nonce_el.Value) ? "" : nonce_el.Value; var urldecoded = nonce == null ? "" : Duplicati.Library.Utility.Uri.UrlDecode(nonce); if (m_activeNonces.ContainsKey(urldecoded)) { nonce = urldecoded; } if (!m_activeNonces.ContainsKey(nonce)) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var pwd = m_activeNonces[nonce].Item2; m_activeNonces.Remove(nonce); if (pwd != input["password"].Value) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var buf = new byte[32]; var expires = DateTime.UtcNow.AddHours(1); m_prng.GetBytes(buf); var token = Duplicati.Library.Utility.Utility.Base64UrlEncode(buf); while (token.Length > 0 && token.EndsWith("=")) { token = token.Substring(0, token.Length - 1); } m_activeTokens.Add(token, expires); response.Cookies.Add(new HttpServer.ResponseCookie(AUTH_COOKIE_NAME, token, expires)); using (var bw = new BodyWriter(response, request)) bw.OutputOK(); return(true); } } } var limitedAccess = ControlHandler.CONTROL_HANDLER_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase) || request.Uri.AbsolutePath.StartsWith(RESTHandler.API_URI_PATH, StringComparison.InvariantCultureIgnoreCase) ; if (limitedAccess) { if (xsrf_token != null && m_activexsrf.ContainsKey(xsrf_token)) { var expires = DateTime.UtcNow.AddMinutes(XSRF_TIMEOUT_MINUTES); m_activexsrf[xsrf_token] = expires; response.Cookies.Add(new ResponseCookie(XSRF_COOKIE_NAME, xsrf_token, expires)); } else { response.Status = System.Net.HttpStatusCode.BadRequest; response.Reason = "Missing XSRF Token"; return(true); } } if (string.IsNullOrWhiteSpace(Program.DataConnection.ApplicationSettings.WebserverPassword)) { return(false); } foreach (var k in (from n in m_activeTokens where DateTime.UtcNow > n.Value select n.Key).ToList()) { m_activeTokens.Remove(k); } // If we have a valid token, proceeed if (!string.IsNullOrWhiteSpace(auth_token)) { DateTime expires; var found = m_activeTokens.TryGetValue(auth_token, out expires); if (!found) { auth_token = Duplicati.Library.Utility.Uri.UrlDecode(auth_token); found = m_activeTokens.TryGetValue(auth_token, out expires); } if (found && DateTime.UtcNow < expires) { expires = DateTime.UtcNow.AddHours(1); m_activeTokens[auth_token] = expires; response.Cookies.Add(new ResponseCookie(AUTH_COOKIE_NAME, auth_token, expires)); return(false); } } if ("/".Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase) || "/index.html".Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { response.Redirect("/login.html"); return(true); } if (ControlHandler.CONTROL_HANDLER_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Not logged in"; response.AddHeader("Location", "login.html"); return(true); } return(false); }
private void SetServerOptions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { string str = request.Form["data"].Value; if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "Missing data object"); return; } Dictionary<string, string> data = null; try { data = Serializer.Deserialize<Dictionary<string, string>>(new StringReader(str)); if (data == null) { ReportError(response, bw, "Data object had no backup entry"); return; } Program.DataConnection.ApplicationSettings.UpdateSettings(data); bw.OutputOK(); } catch (Exception ex) { if (data == null) ReportError(response, bw, string.Format("Unable to parse data object: {0}", ex.Message)); else ReportError(response, bw, string.Format("Unable to save settings: {0}", ex.Message)); } }
private void ExportBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } var cmdline = Library.Utility.Utility.ParseBool(input["cmdline"].Value, false); if (cmdline) { bw.OutputOK(new { Command = Runner.GetCommandLine(Runner.CreateTask(DuplicatiOperation.Backup, bk)) }); } else { var passphrase = input["passphrase"].Value; var scheduleId = Program.DataConnection.GetScheduleIDsFromTags(new string[] { "ID=" + bk.ID }); var ipx = new ImportExportStructure() { CreatedByVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), Backup = (Database.Backup)bk, Schedule = (Database.Schedule)(scheduleId.Any() ? Program.DataConnection.GetSchedule(scheduleId.First()) : null), DisplayNames = GetSourceNames(bk) }; byte[] data; using(var ms = new System.IO.MemoryStream()) using(var sw = new System.IO.StreamWriter(ms)) { Serializer.SerializeJson(sw, ipx, true); if (!string.IsNullOrWhiteSpace(passphrase)) { ms.Position = 0; using(var ms2 = new System.IO.MemoryStream()) using(var m = new Duplicati.Library.Encryption.AESEncryption(passphrase, new Dictionary<string, string>())) { m.Encrypt(ms, ms2); data = ms2.ToArray(); } } else data = ms.ToArray(); } var filename = Library.Utility.Uri.UrlEncode(bk.Name) + "-duplicati-config.json"; if (!string.IsNullOrWhiteSpace(passphrase)) filename += ".aes"; response.ContentLength = data.Length; response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", filename)); response.ContentType = "application/octet-stream"; bw.SetOK(); response.SendHeaders(); response.SendBody(data); } }
private void GetServerOptions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { var adv_props = from n in Program.DataConnection.GetSettings(Database.Connection.APP_SETTINGS_ID) select new KeyValuePair<string, string>(n.Name, n.Value); bw.OutputOK(adv_props.ToDictionary(x => x.Key, x => x.Value)); }
private void DeleteLocalData(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var bk = Program.DataConnection.GetBackup(input["id"].Value); if (bk == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } System.IO.File.Delete(bk.DBPath); bw.OutputOK(); }
private void ListApplicationSettings(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(Program.DataConnection.ApplicationSettings); }
private void ListCoreOptions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(new Duplicati.Library.Main.Options(new Dictionary<string, string>()).SupportedCommands); }
private void DismissNotification(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var str = input["id"].Value; long id; long.TryParse(str, out id); if (Program.DataConnection.DismissNotification(id)) bw.OutputOK(); else ReportError(response, bw, "No such notification"); }
private void GetProgressState(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { if (Program.GenerateProgressState == null) { ReportError(response, bw, "No active backup"); } else { var ev = Program.GenerateProgressState(); bw.OutputOK(ev); } }
private void GetUISettings(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var scheme = input["scheme"].Value; if (string.IsNullOrWhiteSpace(scheme)) { ReportError(response, bw, "No scheme supplied"); return; } bw.OutputOK(Program.DataConnection.GetUISettings(scheme)); }
private void ListSupportedActions(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(new { Version = 1, Methods = SUPPORTED_METHODS.Keys }); }
private void SetUISettings(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var scheme = input["scheme"].Value; var str = input["data"].Value; if (string.IsNullOrWhiteSpace(scheme)) { ReportError(response, bw, "No scheme supplied"); return; } if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "No data supplied"); return; } IDictionary<string, string> data; try { data = Serializer.Deserialize<Dictionary<string, string>>(new StringReader(str)); } catch (Exception ex) { ReportError(response, bw, string.Format("Unable to parse settings object: {0}", ex.Message)); return; } if (data == null) { ReportError(response, bw, string.Format("Unable to parse settings object")); return; } Program.DataConnection.SetUISettings(scheme, data); bw.OutputOK(); }
private void ValidatePath(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; if (input["path"] == null || input["path"].Value == null) { ReportError(response, bw, "The path parameter was not set"); return; } try { string path = SpecialFolders.ExpandEnvironmentVariables(input["path"].Value); if (System.IO.Path.IsPathRooted(path) && System.IO.Directory.Exists(path)) { bw.OutputOK(); return; } } catch { } ReportError(response, bw, "File or folder not found"); return; }
private void GetUISettingSchemes(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(Program.DataConnection.GetUISettingsSchemes()); }
private void ListTags(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { var r = from n in Serializable.ServerSettings.CompressionModules .Union(Serializable.ServerSettings.EncryptionModules) .Union(Serializable.ServerSettings.BackendModules) .Union(Serializable.ServerSettings.GenericModules) select n.Key.ToLower(); // Append all known tags r = r.Union(from n in Program.DataConnection.Backups select n.Tags into p from x in p select x.ToLower()); bw.OutputOK(r); }
private void GetFolderContents(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; if (input["path"] == null || input["path"].Value == null) { ReportError(response, bw, "The path parameter was not set"); return; } bool skipFiles = Library.Utility.Utility.ParseBool(input["onlyfolders"].Value, false); var path = input["path"].Value; string specialpath = null; string specialtoken = null; if (path.StartsWith("%")) { var ix = path.IndexOf("%", 1); if (ix > 0) { var tk = path.Substring(0, ix + 1); var node = SpecialFolders.Nodes.Where(x => x.id.Equals(tk, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (node != null) { specialpath = node.resolvedpath; specialtoken = node.id; } } } path = SpecialFolders.ExpandEnvironmentVariables(path); if (Duplicati.Library.Utility.Utility.IsClientLinux && !path.StartsWith("/")) { ReportError(response, bw, "The path parameter must start with a forward-slash"); return; } try { if (path != "" && path != "/") path = Duplicati.Library.Utility.Utility.AppendDirSeparator(path); IEnumerable<Serializable.TreeNode> res; if (!Library.Utility.Utility.IsClientLinux && (path.Equals("/") || path.Equals(""))) { res = from di in System.IO.DriveInfo.GetDrives() where di.DriveType == DriveType.Fixed || di.DriveType == DriveType.Network || di.DriveType == DriveType.Removable select new Serializable.TreeNode() { id = di.RootDirectory.FullName, text = di.RootDirectory.FullName.Replace('\\', ' ') + "(" + di.DriveType + ")", iconCls = "x-tree-icon-drive" }; } else { res = ListFolderAsNodes(path, skipFiles); } if ((path.Equals("/") || path.Equals("")) && specialtoken == null) { // Prepend special folders res = SpecialFolders.Nodes.Union(res); } if (specialtoken != null) { res = from n in res select new Serializable.TreeNode() { id = specialtoken + n.id.Substring(specialpath.Length), text = n.text, iconCls = n.iconCls, leaf = n.leaf, resolvedpath = n.id }; } bw.OutputOK(res); } catch (Exception ex) { ReportError(response, bw, "Failed to process the path: " + ex.Message); } }
private void AddBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { var str = request.Form["data"].Value; if (string.IsNullOrWhiteSpace(str)) { ReportError(response, bw, "Missing backup object"); return; } AddOrUpdateBackupData data = null; try { data = Serializer.Deserialize <AddOrUpdateBackupData>(new StringReader(str)); if (data.Backup == null) { ReportError(response, bw, "Data object had no backup entry"); return; } data.Backup.ID = null; if (Duplicati.Library.Utility.Utility.ParseBool(request.Form["temporary"].Value, false)) { Program.DataConnection.RegisterTemporaryBackup(data.Backup); bw.OutputOK(new { status = "OK", ID = data.Backup.ID }); } else { if (Library.Utility.Utility.ParseBool(request.Form["existing_db"].Value, false)) { data.Backup.DBPath = Library.Main.DatabaseLocator.GetDatabasePath(data.Backup.TargetURL, null, false, false); if (string.IsNullOrWhiteSpace(data.Backup.DBPath)) { throw new Exception("Unable to find remote db path?"); } } lock (Program.DataConnection.m_lock) { if (Program.DataConnection.Backups.Where(x => x.Name.Equals(data.Backup.Name, StringComparison.InvariantCultureIgnoreCase)).Any()) { ReportError(response, bw, "There already exists a backup with the name: " + data.Backup.Name); return; } Program.DataConnection.AddOrUpdateBackupAndSchedule(data.Backup, data.Schedule); } bw.OutputOK(new { status = "OK", ID = data.Backup.ID }); } } catch (Exception ex) { if (data == null) { ReportError(response, bw, string.Format("Unable to parse backup or schedule object: {0}", ex.Message)); } else { ReportError(response, bw, string.Format("Unable to save schedule or backup object: {0}", ex.Message)); } } }
public override bool Process(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var auth_token = FindAuthCookie(request); var xsrf_token = FindXSRFToken(request); if (!HasXSRFCookie(request)) { var cookieAdded = AddXSRFTokenToRespone(response); if (!cookieAdded) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too Many Concurrent Request, try again later"; return(true); } } Tuple <DateTime, string> tmpTuple; DateTime tmpDateTime; if (LOGOUT_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrWhiteSpace(auth_token)) { // Remove the active auth token m_activeTokens.TryRemove(auth_token, out tmpDateTime); } response.Status = System.Net.HttpStatusCode.NoContent; response.Reason = "OK"; return(true); } else if (LOGIN_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.OrdinalIgnoreCase)) { // Remove expired nonces foreach (var k in (from n in m_activeNonces where DateTime.UtcNow > n.Value.Item1 select n.Key)) { m_activeNonces.TryRemove(k, out tmpTuple); } if (input["get-nonce"] != null && !string.IsNullOrWhiteSpace(input["get-nonce"].Value)) { if (m_activeNonces.Count > 50) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too many active login attempts"; return(true); } var password = Program.DataConnection.ApplicationSettings.WebserverPassword; if (request.Headers[TRAYICONPASSWORDSOURCE_HEADER] == "database") { password = Program.DataConnection.ApplicationSettings.WebserverPasswordTrayIconHash; } var buf = new byte[32]; var expires = DateTime.UtcNow.AddMinutes(AUTH_TIMEOUT_MINUTES); m_prng.GetBytes(buf); var nonce = Convert.ToBase64String(buf); var sha256 = System.Security.Cryptography.SHA256.Create(); sha256.TransformBlock(buf, 0, buf.Length, buf, 0); buf = Convert.FromBase64String(password); sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); m_activeNonces.AddOrUpdate(nonce, key => new Tuple <DateTime, string>(expires, pwd), (key, existingValue) => { // Simulate the original behavior => if the nonce, against all odds, is already used // we throw an ArgumentException throw new ArgumentException("An element with the same key already exists in the dictionary."); }); response.Cookies.Add(new HttpServer.ResponseCookie(NONCE_COOKIE_NAME, nonce, expires)); using (var bw = new BodyWriter(response, request)) { bw.OutputOK(new { Status = "OK", Nonce = nonce, Salt = Program.DataConnection.ApplicationSettings.WebserverPasswordSalt }); } return(true); } else { if (input["password"] != null && !string.IsNullOrWhiteSpace(input["password"].Value)) { var nonce_el = request.Cookies[NONCE_COOKIE_NAME] ?? request.Cookies[Library.Utility.Uri.UrlEncode(NONCE_COOKIE_NAME)]; var nonce = nonce_el == null || string.IsNullOrWhiteSpace(nonce_el.Value) ? "" : nonce_el.Value; var urldecoded = nonce == null ? "" : Duplicati.Library.Utility.Uri.UrlDecode(nonce); if (m_activeNonces.ContainsKey(urldecoded)) { nonce = urldecoded; } if (!m_activeNonces.ContainsKey(nonce)) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var pwd = m_activeNonces[nonce].Item2; // Remove the nonce m_activeNonces.TryRemove(nonce, out tmpTuple); if (pwd != input["password"].Value) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return(true); } var buf = new byte[32]; var expires = DateTime.UtcNow.AddHours(1); m_prng.GetBytes(buf); var token = Duplicati.Library.Utility.Utility.Base64UrlEncode(buf); while (token.Length > 0 && token.EndsWith("=", StringComparison.Ordinal)) { token = token.Substring(0, token.Length - 1); } m_activeTokens.AddOrUpdate(token, key => expires, (key, existingValue) => { // Simulate the original behavior => if the token, against all odds, is already used // we throw an ArgumentException throw new ArgumentException("An element with the same key already exists in the dictionary."); }); response.Cookies.Add(new HttpServer.ResponseCookie(AUTH_COOKIE_NAME, token, expires)); using (var bw = new BodyWriter(response, request)) bw.OutputOK(); return(true); } } } var limitedAccess = request.Uri.AbsolutePath.StartsWith(RESTHandler.API_URI_PATH, StringComparison.OrdinalIgnoreCase) ; // Override to allow the CAPTCHA call to go through if (request.Uri.AbsolutePath.StartsWith(CAPTCHA_IMAGE_URI, StringComparison.OrdinalIgnoreCase) && request.Method == "GET") { limitedAccess = false; } if (limitedAccess) { if (xsrf_token != null && m_activexsrf.ContainsKey(xsrf_token)) { var expires = DateTime.UtcNow.AddMinutes(XSRF_TIMEOUT_MINUTES); m_activexsrf[xsrf_token] = expires; response.Cookies.Add(new ResponseCookie(XSRF_COOKIE_NAME, xsrf_token, expires)); } else { response.Status = System.Net.HttpStatusCode.BadRequest; response.Reason = "Missing XSRF Token. Please reload the page"; return(true); } } if (string.IsNullOrWhiteSpace(Program.DataConnection.ApplicationSettings.WebserverPassword)) { return(false); } foreach (var k in (from n in m_activeTokens where DateTime.UtcNow > n.Value select n.Key)) { m_activeTokens.TryRemove(k, out tmpDateTime); } // If we have a valid token, proceed if (!string.IsNullOrWhiteSpace(auth_token)) { DateTime expires; var found = m_activeTokens.TryGetValue(auth_token, out expires); if (!found) { auth_token = Duplicati.Library.Utility.Uri.UrlDecode(auth_token); found = m_activeTokens.TryGetValue(auth_token, out expires); } if (found && DateTime.UtcNow < expires) { expires = DateTime.UtcNow.AddHours(1); m_activeTokens[auth_token] = expires; response.Cookies.Add(new ResponseCookie(AUTH_COOKIE_NAME, auth_token, expires)); return(false); } } if ("/".Equals(request.Uri.AbsolutePath, StringComparison.OrdinalIgnoreCase) || "/index.html".Equals(request.Uri.AbsolutePath, StringComparison.OrdinalIgnoreCase)) { response.Redirect("/login.html"); return(true); } if (limitedAccess) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Not logged in"; response.AddHeader("Location", "login.html"); return(true); } return(false); }
private void ListBackups (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { var schedules = Program.DataConnection.Schedules; var backups = Program.DataConnection.Backups; var all = from n in backups select new AddOrUpdateBackupData() { Backup = (Database.Backup)n, Schedule = (from x in schedules where x.Tags != null && x.Tags.Contains("ID=" + n.ID) select (Database.Schedule)x).FirstOrDefault() }; bw.OutputOK(all.ToArray()); }
private void TestBackend(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; if (input["url"] == null || input["url"].Value == null) { ReportError(response, bw, "The url parameter was not set"); return; } var modules = (from n in Library.DynamicLoader.GenericLoader.Modules where n is Library.Interface.IConnectionModule select n).ToArray(); try { var url = input["url"].Value; var uri = new Library.Utility.Uri(url); var qp = uri.QueryParameters; var opts = new Dictionary<string, string>(); foreach(var k in qp.Keys.Cast<string>()) opts[k] = qp[k]; foreach(var n in modules) n.Configure(opts); using(var b = Duplicati.Library.DynamicLoader.BackendLoader.GetBackend(url, new Dictionary<string, string>())) b.Test(); bw.OutputOK(); } catch (Duplicati.Library.Interface.FolderMissingException) { ReportError(response, bw, "missing-folder"); } catch (Duplicati.Library.Utility.SslCertificateValidator.InvalidCertificateException icex) { if (string.IsNullOrWhiteSpace(icex.Certificate)) ReportError(response, bw, icex.Message); else ReportError(response, bw, "incorrect-cert:" + icex.Certificate); } catch (Exception ex) { ReportError(response, bw, ex.Message); } finally { foreach(var n in modules) if (n is IDisposable) ((IDisposable)n).Dispose(); } }
private void ListSystemInfo(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bw.OutputOK(new { APIVersion = 1, PasswordPlaceholder = Duplicati.Server.WebServer.Server.PASSWORD_PLACEHOLDER, ServerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), ServerVersionName = Duplicati.License.VersionNumbers.Version, ServerTime = DateTime.Now, OSType = Library.Utility.Utility.IsClientLinux ? (Library.Utility.Utility.IsClientOSX ? "OSX" : "Linux") : "Windows", DirectorySeparator = System.IO.Path.DirectorySeparatorChar, PathSeparator = System.IO.Path.PathSeparator, CaseSensitiveFilesystem = Duplicati.Library.Utility.Utility.IsFSCaseSensitive, MonoVersion = Duplicati.Library.Utility.Utility.IsMono ? Duplicati.Library.Utility.Utility.MonoVersion.ToString() : null, MachineName = System.Environment.MachineName, NewLine = System.Environment.NewLine, CLRVersion = System.Environment.Version.ToString(), CLROSInfo = new { Platform = System.Environment.OSVersion.Platform.ToString(), ServicePack = System.Environment.OSVersion.ServicePack, Version = System.Environment.OSVersion.Version.ToString(), VersionString = System.Environment.OSVersion.VersionString }, Options = Serializable.ServerSettings.Options, CompressionModules = Serializable.ServerSettings.CompressionModules, EncryptionModules = Serializable.ServerSettings.EncryptionModules, BackendModules = Serializable.ServerSettings.BackendModules, GenericModules = Serializable.ServerSettings.GenericModules, WebModules = Serializable.ServerSettings.WebModules, ConnectionModules = Serializable.ServerSettings.ConnectionModules, UsingAlternateUpdateURLs = Duplicati.Library.AutoUpdater.AutoUpdateSettings.UsesAlternateURLs, LogLevels = Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType)) }); }
private void DeleteBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var backup = Program.DataConnection.GetBackup(input["id"].Value); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (Program.WorkThread.Active) { try { //TODO: It's not safe to access the values like this, //because the runner thread might interfere var nt = Program.WorkThread.CurrentTask; if (backup.Equals(nt == null ? null : nt.Backup)) { bool force; if (!bool.TryParse(input["force"].Value, out force)) { force = false; } if (!force) { bw.WriteJsonObject(new { status = "failed", reason = "backup-in-progress" }); return; } bool hasPaused = Program.LiveControl.State == LiveControls.LiveControlState.Paused; Program.LiveControl.Pause(); try { for (int i = 0; i < 10; i++) { if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) { System.Threading.Thread.Sleep(1000); } else { break; } } else { break; } } } finally { } if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) { if (hasPaused) { Program.LiveControl.Resume(); } bw.WriteJsonObject(new { status = "failed", reason = "backup-unstoppable" }); return; } } if (hasPaused) { Program.LiveControl.Resume(); } } } catch (Exception ex) { bw.WriteJsonObject(new { status = "error", message = ex.Message }); return; } } Program.DataConnection.DeleteBackup(backup); //We have fiddled with the schedules Program.Scheduler.Reschedule(); bw.OutputOK(); }
private void GetBackupDefaults(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { // Start with a scratch object var o = new Newtonsoft.Json.Linq.JObject(); // Add application wide settings o.Add("ApplicationOptions", new Newtonsoft.Json.Linq.JArray(Program.DataConnection.Settings)); try { // Add built-in defaults Newtonsoft.Json.Linq.JObject n; using(var s = new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".newbackup.json"))) n = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(s.ReadToEnd()); MergeJsonObjects(o, n); } catch { } try { // Add install defaults/overrides, if present var path = System.IO.Path.Combine(Duplicati.Library.AutoUpdater.UpdaterManager.InstalledBaseDir, "newbackup.json"); if (System.IO.File.Exists(path)) { Newtonsoft.Json.Linq.JObject n; n = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(System.IO.File.ReadAllText(path)); MergeJsonObjects(o, n); } } catch { } bw.OutputOK(new { success = true, data = o }); }
private void GetCurrentState (HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { bool isError; long id = 0; if (LongPollCheck(request, response, bw, Program.StatusEventNotifyer, ref id, out isError)) { //Make sure we do not report a higher number than the eventnotifyer says var st = new Serializable.ServerStatus(); st.LastEventID = id; bw.OutputOK(st); } else if (!isError) { bw.OutputOK(new Serializable.ServerStatus()); } }
private void DeleteBackup(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var backup = Program.DataConnection.GetBackup(input["id"].Value); if (backup == null) { ReportError(response, bw, "Invalid or missing backup id"); return; } if (Program.WorkThread.Active) { try { //TODO: It's not safe to access the values like this, //because the runner thread might interfere var nt = Program.WorkThread.CurrentTask; if (backup.Equals(nt == null ? null : nt.Backup)) { bool force; if (!bool.TryParse(input["force"].Value, out force)) force = false; if (!force) { bw.WriteJsonObject(new { status = "failed", reason = "backup-in-progress" }); return; } bool hasPaused = Program.LiveControl.State == LiveControls.LiveControlState.Paused; Program.LiveControl.Pause(); try { for (int i = 0; i < 10; i++) if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) System.Threading.Thread.Sleep(1000); else break; } else break; } finally { } if (Program.WorkThread.Active) { var t = Program.WorkThread.CurrentTask; if (backup.Equals(t == null ? null : t.Backup)) { if (hasPaused) Program.LiveControl.Resume(); bw.WriteJsonObject(new { status = "failed", reason = "backup-unstoppable" }); return; } } if (hasPaused) Program.LiveControl.Resume(); } } catch (Exception ex) { bw.WriteJsonObject(new { status = "error", message = ex.Message }); return; } } Program.DataConnection.DeleteBackup(backup); //We have fiddled with the schedules Program.Scheduler.Reschedule(); bw.OutputOK(); }
private void PollLogMessages(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var level_str = input["level"].Value ?? ""; var id_str = input["id"].Value ?? ""; Library.Logging.LogMessageType level; long id; long.TryParse(id_str, out id); Enum.TryParse(level_str, true, out level); bw.OutputOK(Program.LogHandler.AfterID(id, level)); }
public override bool Process(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; var auth_token = FindAuthCookie(request); var xsrf_token = FindXSRFToken(request); if (!HasXSRFCookie(request)) { var cookieAdded = AddXSRFTokenToRespone(response); if (!cookieAdded) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too Many Concurrent Request, try again later"; return true; } } Tuple<DateTime, string> tmpTuple; DateTime tmpDateTime; if (LOGOUT_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { if (!string.IsNullOrWhiteSpace(auth_token)) { // Remove the active auth token m_activeTokens.TryRemove(auth_token, out tmpDateTime); } response.Status = System.Net.HttpStatusCode.NoContent; response.Reason = "OK"; return true; } else if (LOGIN_SCRIPT_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { // Remove expired nonces foreach(var k in (from n in m_activeNonces where DateTime.UtcNow > n.Value.Item1 select n.Key)) m_activeNonces.TryRemove(k, out tmpTuple); if (input["get-nonce"] != null && !string.IsNullOrWhiteSpace(input["get-nonce"].Value)) { if (m_activeNonces.Count > 50) { response.Status = System.Net.HttpStatusCode.ServiceUnavailable; response.Reason = "Too many active login attempts"; return true; } var buf = new byte[32]; var expires = DateTime.UtcNow.AddMinutes(AUTH_TIMEOUT_MINUTES); m_prng.GetBytes(buf); var nonce = Convert.ToBase64String(buf); var sha256 = System.Security.Cryptography.SHA256.Create(); sha256.TransformBlock(buf, 0, buf.Length, buf, 0); buf = Convert.FromBase64String(Program.DataConnection.ApplicationSettings.WebserverPassword); sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); m_activeNonces.AddOrUpdate(nonce, key => new Tuple<DateTime, string>(expires, pwd), (key, existingValue) => { // Simulate the original behavior => if the nonce, against all odds, is already used // we throw an ArgumentException throw new ArgumentException("An element with the same key already exists in the dictionary."); }); response.Cookies.Add(new HttpServer.ResponseCookie(NONCE_COOKIE_NAME, nonce, expires)); using(var bw = new BodyWriter(response, request)) { bw.OutputOK(new { Status = "OK", Nonce = nonce, Salt = Program.DataConnection.ApplicationSettings.WebserverPasswordSalt }); } return true; } else { if (input["password"] != null && !string.IsNullOrWhiteSpace(input["password"].Value)) { var nonce_el = request.Cookies[NONCE_COOKIE_NAME] ?? request.Cookies[Library.Utility.Uri.UrlEncode(NONCE_COOKIE_NAME)]; var nonce = nonce_el == null || string.IsNullOrWhiteSpace(nonce_el.Value) ? "" : nonce_el.Value; var urldecoded = nonce == null ? "" : Duplicati.Library.Utility.Uri.UrlDecode(nonce); if (m_activeNonces.ContainsKey(urldecoded)) nonce = urldecoded; if (!m_activeNonces.ContainsKey(nonce)) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return true; } var pwd = m_activeNonces[nonce].Item2; // Remove the nonce m_activeNonces.TryRemove(nonce, out tmpTuple); if (pwd != input["password"].Value) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Unauthorized"; response.ContentType = "application/json"; return true; } var buf = new byte[32]; var expires = DateTime.UtcNow.AddHours(1); m_prng.GetBytes(buf); var token = Duplicati.Library.Utility.Utility.Base64UrlEncode(buf); while (token.Length > 0 && token.EndsWith("=")) token = token.Substring(0, token.Length - 1); m_activeTokens.AddOrUpdate(token, key => expires, (key, existingValue) => { // Simulate the original behavior => if the token, against all odds, is already used // we throw an ArgumentException throw new ArgumentException("An element with the same key already exists in the dictionary."); }); response.Cookies.Add(new HttpServer.ResponseCookie(AUTH_COOKIE_NAME, token, expires)); using(var bw = new BodyWriter(response, request)) bw.OutputOK(); return true; } } } var limitedAccess = ControlHandler.CONTROL_HANDLER_URI.Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase) || request.Uri.AbsolutePath.StartsWith(RESTHandler.API_URI_PATH, StringComparison.InvariantCultureIgnoreCase) ; if (limitedAccess) { if (xsrf_token != null && m_activexsrf.ContainsKey(xsrf_token)) { var expires = DateTime.UtcNow.AddMinutes(XSRF_TIMEOUT_MINUTES); m_activexsrf[xsrf_token] = expires; response.Cookies.Add(new ResponseCookie(XSRF_COOKIE_NAME, xsrf_token, expires)); } else { response.Status = System.Net.HttpStatusCode.BadRequest; response.Reason = "Missing XSRF Token"; return true; } } if (string.IsNullOrWhiteSpace(Program.DataConnection.ApplicationSettings.WebserverPassword)) return false; foreach(var k in (from n in m_activeTokens where DateTime.UtcNow > n.Value select n.Key)) m_activeTokens.TryRemove(k, out tmpDateTime); // If we have a valid token, proceed if (!string.IsNullOrWhiteSpace(auth_token)) { DateTime expires; var found = m_activeTokens.TryGetValue(auth_token, out expires); if (!found) { auth_token = Duplicati.Library.Utility.Uri.UrlDecode(auth_token); found = m_activeTokens.TryGetValue(auth_token, out expires); } if (found && DateTime.UtcNow < expires) { expires = DateTime.UtcNow.AddHours(1); m_activeTokens[auth_token] = expires; response.Cookies.Add(new ResponseCookie(AUTH_COOKIE_NAME, auth_token, expires)); return false; } } if ("/".Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase) || "/index.html".Equals(request.Uri.AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { response.Redirect("/login.html"); return true; } if (limitedAccess) { response.Status = System.Net.HttpStatusCode.Unauthorized; response.Reason = "Not logged in"; response.AddHeader("Location", "login.html"); return true; } return false; }
private void GetFolderContents(HttpServer.IHttpRequest request, HttpServer.IHttpResponse response, HttpServer.Sessions.IHttpSession session, BodyWriter bw) { HttpServer.HttpInput input = request.Method.ToUpper() == "POST" ? request.Form : request.QueryString; if (input["path"] == null || input["path"].Value == null) { ReportError(response, bw, "The path parameter was not set"); return; } bool skipFiles = Library.Utility.Utility.ParseBool(input["onlyfolders"].Value, false); var path = input["path"].Value; string specialpath = null; string specialtoken = null; if (path.StartsWith("%")) { var ix = path.IndexOf("%", 1); if (ix > 0) { var tk = path.Substring(0, ix + 1); var node = SpecialFolders.Nodes.Where(x => x.id.Equals(tk, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (node != null) { specialpath = node.resolvedpath; specialtoken = node.id; } } } path = SpecialFolders.ExpandEnvironmentVariables(path); if (Duplicati.Library.Utility.Utility.IsClientLinux && !path.StartsWith("/")) { ReportError(response, bw, "The path parameter must start with a forward-slash"); return; } try { if (path != "" && path != "/") { path = Duplicati.Library.Utility.Utility.AppendDirSeparator(path); } IEnumerable <Serializable.TreeNode> res; if (!Library.Utility.Utility.IsClientLinux && (path.Equals("/") || path.Equals(""))) { res = from di in System.IO.DriveInfo.GetDrives() where di.DriveType == DriveType.Fixed || di.DriveType == DriveType.Network || di.DriveType == DriveType.Removable select new Serializable.TreeNode() { id = di.RootDirectory.FullName, text = di.RootDirectory.FullName.Replace('\\', ' ') + "(" + di.DriveType + ")", iconCls = "x-tree-icon-drive" }; } else { res = ListFolderAsNodes(path, skipFiles); } if ((path.Equals("/") || path.Equals("")) && specialtoken == null) { // Prepend special folders res = SpecialFolders.Nodes.Union(res); } if (specialtoken != null) { res = from n in res select new Serializable.TreeNode() { id = specialtoken + n.id.Substring(specialpath.Length), text = n.text, iconCls = n.iconCls, leaf = n.leaf, resolvedpath = n.id }; } bw.OutputOK(res); } catch (Exception ex) { ReportError(response, bw, "Failed to process the path: " + ex.Message); } }