/// <summary> /// Helper method used to send data stored as a resource under the webres folder (namespace). /// </summary> /// <param name="target">The name of the resource in the webres folder/namespace.</param> /// <param name="state">Pass-through parameter.</param> /// <param name="request">Pass-through parameter.</param> /// <param name="body">Pass-through parameter.</param> /// <param name="encoder">Pass-through parameter.</param> /// <returns>A task object which may or may not be completed already. This also may need to be returned as a dependency of the handler completion.</returns> private static async Task <Task> StaticRoute( string target, ServerHandler state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await Util.ReadStreamUntilEndAndDiscardDataAsync(body); #if USE_SOURCE_DIRECTORY_WEBRES var strm = File.OpenRead( Path.Combine( @"/home/kmcguire/extra/old/source/repos/MDACSDatabase/MDACSDatabase/webres", target ) ); #else var strm = Assembly.GetExecutingAssembly().GetManifestResourceStream($"MDACSDatabase.webres.{target}"); if (strm == null) { return(await encoder.Response(404, "Not Found") .CacheControl("no-cache, no-store, must-revalidate") .SendNothing()); } #endif return(await encoder.Response(200, "OK") .ContentType_GuessFromFileName(target) .CacheControl("public, max-age=0") .SendStream(strm)); }
public async Task <Task> ServeData(string dataName, IProxyHTTPEncoder encoder) { var dataNameSanitized = SanitizeDataName(dataName); var fullPath = Path.Combine(cfg.web_resources_path, dataNameSanitized); FileStream fp; try { fp = File.OpenRead(fullPath); } catch (FileNotFoundException) { return(encoder.Response(404, "Not Found") .ContentType("text/plain") .CacheControlDoNotCache() .SendString("Not Found")); } catch (Exception ex) { return(encoder.Response(500, "Internal Error") .ContentType("text/plain") .CacheControlDoNotCache() .SendString(ex.ToString())); } return(await encoder.Response(200, "OK") .ContentType_GuessFromFileName(dataName) .CacheControlDoNotCache() .SendStream(fp)); }
public static async Task <Task> UserList( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var msg = await Util.ReadJsonObjectFromStreamAsync <Msg>(body, 1024); var(user, req) = state.AuthenticateMessage <JObject>(msg); if (user == null) { return(await encoder.Response(403, "The user list request was denied due to an authentication failure.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } var users = state.GetUserList(); return(await encoder.Response(200, "OK") .ContentType_JSON() .SendJsonFromObject(state.GetUserList())); }
public QuickResponse(int code, string text, IProxyHTTPEncoder proxy) { response_code = code; response_text = text; header = new Dictionary <string, string>(); this.proxy = proxy; }
public static async Task <Task> Utility( ServerHandler state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await Util.ReadStreamUntilEndAndDiscardDataAsync(body); return(await StaticRoute(request.query_string, state, request, body, encoder)); }
public static async Task <Task> Index( ServerHandler state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await Util.ReadStreamUntilEndAndDiscardDataAsync(body); return(await StaticRoute("index.html", state, request, body, encoder)); }
public static async Task <Task> Challenge( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await Util.ReadStreamUntilEndAndDiscardDataAsync(body); var challenge = state.GetChallenge(); return(await encoder.Response(200, "OK") .ContentType_JSON() .SendString( JsonConvert.SerializeObject(new AuthChallengeResponse() { challenge = challenge, } ) )); }
public static async Task <Task> UserDelete( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var msg = await Util.ReadJsonObjectFromStreamAsync <Msg>(body, 1024); var(user, req) = state.AuthenticateMessage <AuthUserDeleteRequest>(msg); if (user == null) { return(await encoder.Response(403, "Authentication failed for the user used.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } if (!user.admin) { return(await encoder.Response(403, "Disallowed delete of user by non-administrator.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } if (!await state.DeleteUser(req.username)) { return(await encoder.Response(500, "The delete user command failed on the server.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } return(await encoder.Response(200, "OK") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); }
public static async Task <Task> UserSet( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var msg = await Util.ReadJsonObjectFromStreamAsync <Msg>(body, 1024); var(user, req) = state.AuthenticateMessage <AuthUserSetRequest>(msg); if (user == null) { return(await encoder.Response(403, "Authentication based on user failed.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } if (!user.admin && user.user != req.user.user) { return(await encoder.Response(403, "Disallowed modification of another user.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } if (!await state.SetUser(req.user)) { return(await encoder.Response(500, "The set user command failed to execute.") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); } return(await encoder.Response(200, "OK") .ContentType("text/plain") .CacheControlDoNotCache() .SendNothing()); }
public static async Task <Task> VerifyPayload( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var req = await Util.ReadJsonObjectFromStreamAsync <AuthVerifyPayloadRequest>(body, 1024 * 1024); var user = state.VerifyPayload(req.challenge, req.chash, req.phash); if (user == null) { return(await encoder.Response(403, "Authentication based on user failed.").SendNothing()); } var resp = new AuthCheckResponse() { payload = "", success = true, user = user, }; return(await encoder.Response(200, "OK").ContentType_JSON().SendJsonFromObject(resp)); }
public static async Task <Task> Utility(ServerCore core, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { return(await core.ServeData(request.query_string, encoder)); }
public static async Task <Task> Index(ServerCore core, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { return(await core.ServeData("index.html", encoder)); }
/// <summary> /// Performs higher-level interpretation of application layer data and provides that in a higher level form /// for consumption by the upper layers. /// </summary> /// <param name="header"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> /// <remarks>This implementation needs cleaning on variable names for query string processing.</remarks> public override async Task <Task> HandleRequest(Dictionary <String, String> header, Stream body, IProxyHTTPEncoder encoder) { var outheader = new Dictionary <String, String>(); // Ensure the URL parameter actually exists instead of crashing. if (!header.ContainsKey("$url")) { outheader.Add("$response_code", "500"); outheader.Add("$response_text", "ERROR"); await encoder.WriteHeader(outheader); await encoder.BodyWriteSingleChunk("The request did not specify a URL."); return(Task.CompletedTask); } var url = header["$url"]; var url_absolute = url; var query_string = new Dictionary <String, String>(); String query_as_string = null; // Break down any query string into its key and value parts. if (url.IndexOf("?") > -1) { var qsndx = url.IndexOf("?"); query_as_string = url.Substring(qsndx + 1); var qstring_parts = query_as_string.Split('&'); foreach (var part in qstring_parts) { var eqndx = part.IndexOf("="); if (eqndx < 0) { query_string.Add(part, part); } else { query_string.Add(part.Substring(0, eqndx), part.Substring(eqndx + 1)); } } url_absolute = url.Substring(0, qsndx); } HTTPRequest request; switch (header["$method"].ToLower()) { case "get": request.method = HTTPRequestMethod.GET; break; case "post": request.method = HTTPRequestMethod.POST; break; default: request.method = HTTPRequestMethod.UNKNOWN; break; } // Package everything up nice. Hide away the messy implementation details. request.internal_headers = header; request.query = query_string; request.url = url; request.url_absolute = url_absolute; request.query_string = query_as_string; return(await HandleRequest2(request, body, encoder)); }
/// <summary> /// Handles returning information about space/bytes usage of the database. /// </summary> /// <param name="shandler"></param> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var buf = new byte[512]; int cnt; do { cnt = await body.ReadAsync(buf, 0, buf.Length); } while (cnt > 0); InternalVersonInfo ver_info; using (var strm = Assembly.GetExecutingAssembly().GetManifestResourceStream("MDACSDatabase.buildinfo.json")) { var json_data = await new StreamReader(strm).ReadToEndAsync(); ver_info = JsonConvert.DeserializeObject <InternalVersonInfo>(json_data); } var resp = new VersionResponse() { version = ver_info.version, }; await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
/// <summary> /// The function responsible for handling each request. This function can be implemented by subclassing /// of this class and using override. The function is provided with the request header, request body, and /// the response encoder which allows setting of headers and data if any. /// </summary> /// <param name="header"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public virtual async Task <Task> HandleRequest(Dictionary <String, String> header, Stream body, IProxyHTTPEncoder encoder) { var outheader = new Dictionary <String, String>(); outheader.Add("$response_code", "200"); outheader.Add("$response_text", "OK"); await encoder.WriteHeader(outheader); MemoryStream ms = new MemoryStream(); byte[] something = Encoding.UTF8.GetBytes("hello world\n"); ms.Write(something, 0, something.Length); ms.Write(something, 0, something.Length); ms.Write(something, 0, something.Length); ms.Write(something, 0, something.Length); ms.Write(something, 0, something.Length); ms.Position = 0; //await encoder.BodyWriteSingleChunk("response test body"); await encoder.BodyWriteStream(ms); return(Task.CompletedTask); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await MDACS.Server.Util.ReadStreamUntilEndAndDiscardDataAsync(body); var resp = new JObject(); resp["dbUrl"] = "."; resp["authUrl"] = shandler.auth_url; await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
/// <summary> /// Handles deleting existing data files. Requires authentication and a special privilege. /// </summary> /// <param name="shandler"></param> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth_resp = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth_resp.success) { return(encoder.Response(403, "Denied").SendNothing()); } if (!auth_resp.user.can_delete) { return(encoder.Response(403, "Denied").SendNothing()); } var sreq = JsonConvert.DeserializeObject <DeleteRequest>(auth_resp.payload); var sid = sreq.sid; if (shandler.items.ContainsKey(sid)) { var item = shandler.items[sid]; try { File.Delete(item.fqpath); } catch (Exception) { await encoder.WriteQuickHeaderAndStringBody( 500, "Error", JsonConvert.SerializeObject(new DeleteResponse() { success = false, }) ); return(Task.CompletedTask); } if (item.fqpath != null && item.fqpath.Length > 0) { shandler.UsedSpaceSubtract((long)item.datasize); } item.fqpath = null; await shandler.WriteItemToJournal(item); } await encoder.WriteQuickHeaderAndStringBody( 200, "Deleted", JsonConvert.SerializeObject(new DeleteResponse() { success = true, }) ); return(Task.CompletedTask); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth.success) { return(encoder.Response(403, "Denied").SendNothing()); } var req = JsonConvert.DeserializeObject <HandleBatchSingleOpsRequest>(auth.payload); var failed = new List <BatchSingleOp>(); var tasks = new List <Task <bool> >(); foreach (var op in req.ops) { var sid = op.sid; var field_name = op.field_name; if (!await shandler.FieldModificationValidForUser(auth.user, field_name)) { return(encoder.Response(403, $"Denied Change On Field {field_name}").SendNothing()); } } foreach (var op in req.ops) { var sid = op.sid; var field_name = op.field_name; var value = op.value; lock (shandler.items) { if (shandler.items.ContainsKey(sid)) { try { var tmp = shandler.items[sid]; var field = tmp.GetType().GetField(field_name); field.SetValue(tmp, value.ToObject(field.FieldType)); tasks.Add(shandler.WriteItemToJournal(tmp)); } catch (Exception ex) { //Logger.WriteDebugString( // $"Failed during batch single operation. The SID was {sid}. The field name was {field_name}. The value was {value}. The error was:\n{ex}" //); failed.Add(new BatchSingleOp() { field_name = field_name, sid = sid, value = value, }); } } else { failed.Add(new BatchSingleOp() { field_name = field_name, sid = sid, value = value, }); } } } Task.WaitAll(tasks.ToArray()); var resp = new HandleBatchSingleOpsResponse(); resp.success = true; resp.failed = failed.ToArray(); await encoder.Response(200, "OK") .CacheControlDoNotCache() .SendJsonFromObject(resp); return(Task.CompletedTask); }
/// <summary> /// Handles returning information about space/bytes usage of the database. /// </summary> /// <param name="shandler"></param> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth_resp = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth_resp.success) { throw new UnauthorizedException(); } var resp = new JObject(); resp["success"] = true; resp["used_bytes"] = shandler.GetUsedSpace(); resp["max_bytes"] = shandler.GetMaxSpace(); await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
/// <summary> /// The entry point for route handling. Provides common error response from exception propogation. /// </summary> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns>Asynchronous task object.</returns> public override async Task <Task> HandleRequest2(HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { try { Debug.WriteLine($"url={request.url}"); if (!this.handlers.ContainsKey(request.url_absolute)) { await encoder.WriteQuickHeader(404, "Not Found"); await encoder.BodyWriteSingleChunk("The request resource is not avaliable."); return(Task.CompletedTask); } return(await this.handlers[request.url_absolute](this.user_argument, request, body, encoder)); } catch (Exception ex) { Console.WriteLine("==== EXCEPTION ===="); Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); throw; } }
public static async Task <Task> IsLoginValid( ServerState state, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var msg = await Util.ReadJsonObjectFromStreamAsync <Msg>(body, 1024); bool valid = false; User user; if (msg.payload == null || msg.auth.hash == null) { // Ensure the payload can never be accidentally used since this // authentication is without a payload hash. msg.payload = null; user = state.Verify(msg.auth.challenge, msg.auth.chash); if (user != null) { valid = true; } } else { var payload_hash = BitConverter.ToString( new SHA512Managed().ComputeHash( Encoding.UTF8.GetBytes(msg.payload) ) ).Replace("-", "").ToLower(); user = state.VerifyPayload( msg.auth.challenge, msg.auth.chash, payload_hash /* recompute it */ ); if (user != null) { valid = true; } } if (valid) { return(await encoder.Response(200, "Login Valid") .CacheControlDoNotCache() .ContentType_JSON() .SendJsonFromObject(new AuthLoginValidResponse() { success = true, user = user, })); } else { return(await encoder.Response(403, "The login was not valid.") .CacheControlDoNotCache() .ContentType_JSON() .SendJsonFromObject(new AuthLoginValidResponse() { success = false, user = null, })); } }
public static async Task <Task> GetConfig(ServerCore core, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { await MDACS.Server.Util.ReadStreamUntilEndAndDiscardDataAsync(body); var resp = new JObject(); resp["dbUrl"] = core.GetDatabaseUrl(); resp["authUrl"] = core.GetAuthUrl(); await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
/// <summary> /// Must be implemented by sub-class. /// </summary> /// <remarks>May come back and refactor this to use interfaces instead of subclases.</remarks> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public virtual async Task <Task> HandleRequest2(HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { throw new Exception("Not Implemented"); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var buf = new byte[4096]; int ndx = 0; int cnt; while ((cnt = await body.ReadAsync(buf, ndx, buf.Length - ndx)) > 0) { ndx += cnt; } var buf_utf8 = Encoding.UTF8.GetString(buf, 0, ndx); //Logger.WriteDebugString($"buf_utf8={buf_utf8}"); var req = JsonConvert.DeserializeObject <DeviceConfigRequest>(buf_utf8); var path = Path.Combine(shandler.config_path, $"config_{req.deviceid}.data"); if (!File.Exists(path)) { var _fp = File.OpenWrite( path ); var _tmp = new JObject(); _tmp["userid"] = null; _tmp["config_data"] = req.current_config_data; var _config_bytes_utf8 = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_tmp)); await _fp.WriteAsync(_config_bytes_utf8, 0, _config_bytes_utf8.Length); _fp.Dispose(); } //Logger.WriteDebugString($"The config path is {path}."); var fp = File.OpenRead( path ); var config_bytes_utf8 = new byte[fp.Length]; await fp.ReadAsync(config_bytes_utf8, 0, config_bytes_utf8.Length); fp.Dispose(); var config_data = Encoding.UTF8.GetString(config_bytes_utf8); JObject tmp = JsonConvert.DeserializeObject <JObject>(config_data); tmp["config_data"] = JsonConvert.SerializeObject( JsonConvert.DeserializeObject <JObject>(tmp["config_data"].Value <string>()), Formatting.Indented ); tmp["config_data"] = tmp["config_data"].Value <string>().Replace("\n", "\r\n"); var resp = new DeviceConfigResponse(); resp.success = true; resp.config_data = JsonConvert.SerializeObject(tmp); Debug.WriteLine($"@@@@@ {resp.config_data}"); await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth.success) { await encoder.WriteQuickHeader(403, "Must be authenticated."); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(new CommitConfigurationResponse() { success = false, })); return(Task.CompletedTask); } if (!auth.user.admin) { await encoder.WriteQuickHeader(403, "Must be admin."); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(new CommitConfigurationResponse() { success = false, })); return(Task.CompletedTask); } var req = JsonConvert.DeserializeObject <CommitConfigurationRequest>(auth.payload); var fp = File.OpenWrite( Path.Combine(shandler.config_path, $"config_{req.deviceid}.data") ); var file_data = new ConfigFileData() { userid = req.userid, config_data = req.config_data, }; // TODO: *think* reliable operation and atomic as possible var config_bytes_utf8 = Encoding.UTF8.GetBytes( JsonConvert.SerializeObject(file_data) ); await fp.WriteAsync(config_bytes_utf8, 0, config_bytes_utf8.Length); fp.Dispose(); await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(new CommitConfigurationResponse() { success = true, })); return(Task.CompletedTask); }
/// <summary> /// Handles reading header and data for data upload. A critical routine that employs as many checks as needed /// to ensure that written data is verified as written and correct. /// </summary> /// <param name="shandler"></param> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <returns></returns> public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var buf = new byte[1024 * 32]; int bufndx = 0; int cnt; int tndx; do { cnt = await body.ReadAsync(buf, bufndx, buf.Length - bufndx); if (cnt > 0) { bufndx += cnt; } tndx = Array.IndexOf(buf, (byte)'\n'); if (bufndx >= buf.Length && tndx < 0) { throw new ProgramException("On receiving upload header. The header size exceeded 4096-bytes."); } } while (cnt > 0 && tndx < 0); var hdrstr = Encoding.UTF8.GetString(buf, 0, tndx).Trim(); Debug.WriteLine(hdrstr); var auth_package = JsonConvert.DeserializeObject <MDACS.API.Auth.Msg>(hdrstr); var payload = auth_package.payload; /*var info = await MDACS.API.Auth.AuthenticateMessageAsync(shandler.auth_url, auth_package); * * if (!info.success) * { * throw new UnauthorizedException(); * } */ var hdr = JsonConvert.DeserializeObject <MDACS.API.Requests.UploadHeader>(payload); Array.Copy(buf, tndx + 1, buf, 0, bufndx - (tndx + 1)); // Quasi-repurpose the variable `bufndx` to mark end of the slack data. bufndx = bufndx - (tndx + 1); var data_node = String.Format("{0}_{1}_{2}_{3}.{4}", hdr.datestr, hdr.userstr, hdr.devicestr, hdr.timestr, hdr.datatype ); var data_node_path = Path.Combine(shandler.data_path, data_node); // Make the name unique and keep pertinent information in the event something fails. var temp_data_node_path = Path.Combine( shandler.data_path, $"temp_{DateTime.Now.ToFileTime().ToString()}_{data_node}" ); // TODO: hash data then rehash data after writing to storage, maybe? SHA512 hasher; var fhash_sha512 = new byte[512 / 8]; FileStream fp = null; try { #if DEBUG //Logger.WriteDebugString($"Opening {temp_data_node_path} as temporary output for upload."); #endif fp = File.Open(temp_data_node_path, FileMode.Create); await fp.WriteAsync(buf, 0, bufndx); long total = 0; hasher = SHA512Managed.Create(); hasher.Initialize(); while (true) { var _cnt = await body.ReadAsync(buf, 0, buf.Length); if (_cnt < 1) { break; } Debug.WriteLine($"buf={buf} _cnt={_cnt}"); // https://stackoverflow.com/questions/20634827/how-to-compute-hash-of-a-large-file-chunk hasher.TransformBlock(buf, 0, _cnt, null, 0); total += _cnt; await fp.WriteAsync(buf, 0, _cnt); } hasher.TransformFinalBlock(buf, 0, 0); fhash_sha512 = hasher.Hash; #if DEBUG //Logger.WriteDebugString($"Wrote {total} bytes to {temp_data_node_path} as temporary output for upload."); #endif await body.CopyToAsync(fp); await fp.FlushAsync(); fp.Dispose(); } catch (Exception ex) { if (fp != null) { fp.Dispose(); } #if DEBUG //Logger.WriteDebugString($"Exception on {temp_data_node_path} with:\n{ex}"); #endif File.Delete(temp_data_node_path); await encoder.WriteQuickHeader(500, "Problem"); await encoder.BodyWriteSingleChunk("Problem during write to file from body stream."); return(Task.CompletedTask); } #if DEBUG //Logger.WriteDebugString($"Upload for {temp_data_node_path} is done."); #endif if (!await WaitForFileSizeMatch(temp_data_node_path, (long)hdr.datasize, 3)) { File.Delete(temp_data_node_path); await encoder.WriteQuickHeader(504, "Timeout"); await encoder.BodyWriteSingleChunk("The upload byte length of the destination never reached the intended stream size."); return(Task.CompletedTask); } try { if (File.Exists(data_node_path)) { await CheckedFileMoveAsync(data_node_path, $"{data_node_path}.moved.{DateTime.Now.ToFileTime().ToString()}"); } await CheckedFileMoveAsync(temp_data_node_path, data_node_path); } catch (Exception) { // Delete the temporary since we should have saved the original. File.Delete(temp_data_node_path); // Move the original back to the original filename. await CheckedFileMoveAsync($"{data_node_path}.moved.{DateTime.Now.ToFileTime().ToString()}", data_node_path); await encoder.WriteQuickHeader(500, "Problem"); await encoder.BodyWriteSingleChunk("Unable to do a CheckFileMoveAsync."); return(Task.CompletedTask); } if (!await WaitForFileSizeMatch(data_node_path, (long)hdr.datasize, 3)) { await encoder.WriteQuickHeader(504, "Timeout"); await encoder.BodyWriteSingleChunk("Timeout waiting for size change."); return(Task.CompletedTask); } Item item = new Item(); hasher = new SHA512Managed(); var security_id_bytes = hasher.ComputeHash(Encoding.UTF8.GetBytes(data_node)); item.datasize = hdr.datasize; item.datatype = hdr.datatype; item.datestr = hdr.datestr; item.devicestr = hdr.devicestr; item.duration = -1.0; item.fqpath = data_node_path; item.metatime = DateTime.Now.ToFileTimeUtc(); item.node = data_node; item.note = ""; item.security_id = BitConverter.ToString(security_id_bytes).Replace("-", "").ToLower(); item.timestr = hdr.timestr; item.userstr = hdr.userstr; item.state = ""; item.manager_uuid = shandler.manager_uuid; item.data_hash_sha512 = Convert.ToBase64String(fhash_sha512); item.duration = MDACS.Database.MediaTools.MP4Info.GetDuration(item.fqpath); await shandler.WriteItemToJournal(item); var uresponse = new MDACS.API.Responses.UploadResponse(); uresponse.success = true; uresponse.fqpath = item.fqpath; uresponse.security_id = item.security_id; shandler.UsedSpaceAdd((long)hdr.datasize); await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(uresponse)); // Allow current execution to continue while spinning off the // a handler for this successful upload. shandler.HouseworkAfterUploadSuccess(item); return(Task.CompletedTask); }
/// <summary> /// /// </summary> /// <param name="request"></param> /// <param name="body"></param> /// <param name="encoder"></param> /// <exception cref="UnauthorizedException">User has insufficient priviledges to modify the item.</exception> /// <exception cref="AuthenticationException">User is not valid for access of any type.</exception> /// <exception cref="InvalidArgumentException">One of the arguments was not correct or the reason for failure.</exception> /// <exception cref="ProgramException">Anything properly handled but needs handling for acknowlegement purposes.</exception> /// <exception cref="Exception">Anything else could result in instability.</exception> public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth_resp = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth_resp.success) { throw new UnauthorizedException(); } var sreq = JsonConvert.DeserializeObject <API.Requests.CommitSetRequest>(auth_resp.payload); Monitor.Enter(shandler.items); Item item; #if DEBUG //Logger.WriteDebugString($"sreq.security_id={sreq.security_id}"); #endif try { if (!shandler.items.ContainsKey(sreq.security_id)) { await encoder.WriteQuickHeader(404, "Not Found"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } item = shandler.items[sreq.security_id]; } catch (Exception ex) { #if DEBUG //Logger.WriteDebugString($"Exception on getting item was:\n{ex}"); #endif await encoder.WriteQuickHeader(500, "Error"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } finally { Monitor.Exit(shandler.items); } if (!Helpers.CanUserModifyItem(auth_resp.user, item)) { #if DEBUG //Logger.WriteDebugString($"User was not authorized to write to item."); #endif await encoder.WriteQuickHeader(403, "Not Authorized"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } // Check fields to see if the user is authorized to modify them. foreach (var pair in sreq.meta) { if (!await shandler.FieldModificationValidForUser(auth_resp.user, pair.Key)) { await encoder.WriteQuickHeader(403, "Not Authorized"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } } try { foreach (var pair in sreq.meta) { // Reflection simplified coding time at the expense of performance. var field = item.GetType().GetField(pair.Key); field.SetValue(item, pair.Value.ToObject(field.FieldType)); #if DEBUG //Logger.WriteDebugString($"Set field {field} of {sreq.meta} to {pair.Value.ToString()}."); #endif } shandler.items[sreq.security_id] = item; if (!await shandler.WriteItemToJournal(item)) { #if DEBUG //Logger.WriteDebugString($"Error happened when writing to the journal for a commit set operation."); #endif await encoder.WriteQuickHeader(500, "Error"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } } catch (Exception) { #if DEBUG //Logger.WriteDebugString($"Error happened when writing to journal or setting item fields during commit set operation."); #endif await encoder.WriteQuickHeader(500, "Error"); await encoder.BodyWriteSingleChunk(""); return(Task.CompletedTask); } var resp = new MDACS.API.Responses.CommitSetResponse(); resp.success = true; await encoder.WriteQuickHeader(200, "OK"); await encoder.BodyWriteSingleChunk(JsonConvert.SerializeObject(resp)); return(Task.CompletedTask); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth.success) { return(encoder.Response(403, "Denied").SendNothing()); } var resp = new EnumerateConfigurationsResponse(); resp.success = true; resp.configs = new Dictionary <string, string>(); try { foreach (var node in Directory.EnumerateFiles(shandler.config_path)) { var fnode = Path.Combine(shandler.data_path, node); var fnode_filename = Path.GetFileName(fnode); if (fnode_filename.StartsWith("config_") && fnode_filename.EndsWith(".data")) { var fd = File.OpenRead(fnode); var buf = new byte[fd.Length]; int cnt = 0; while (cnt < buf.Length) { cnt += await fd.ReadAsync(buf, cnt, buf.Length - cnt); } var buf_text = Encoding.UTF8.GetString(buf); var id = fnode_filename.Substring(fnode_filename.IndexOf("_") + 1); id = id.Substring(0, id.LastIndexOf(".")); resp.configs[id] = buf_text; fd.Dispose(); } } } catch (Exception ex) { //Logger.WriteCriticalString($"Error during configuration enumeration as follows:\n{ex}"); return(encoder.Response(500, "Error").SendNothing()); } return(encoder.Response(200, "OK").SendJsonFromObject(resp)); }
public static async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { var auth_resp = await Helpers.ReadMessageFromStreamAndAuthenticate(shandler, 1024 * 16, body); if (!auth_resp.success) { return(encoder.Response(403, "Denied").SendNothing()); } var reply = new HandleDataReply(); reply.data = new Item[shandler.items.Count]; lock (shandler.items) { int x = 0; foreach (var pair in shandler.items) { if ( Helpers.CanUserSeeItem(auth_resp.user, pair.Value) ) { reply.data[x++] = pair.Value; } } } return(encoder.Response(200, "OK").SendJsonFromObject(reply)); }
static public async Task <Task> Action(ServerHandler shandler, HTTPRequest request, Stream body, IProxyHTTPEncoder encoder) { // This URL route was never implemented with user security. The security ID itself is the security, but that // could be revised. //var auth = await ReadMessageFromStreamAndAuthenticate(1024 * 16, body); //if (!auth.success) //{ // throw new UnauthorizedException(); //} String download_sid; if (request.query_string.Length > 0) { download_sid = request.query_string; } else { //req = JsonConvert.DeserializeObject<HandleDownloadRequest>(auth.payload); //download_sid = req.security_id; throw new InvalidArgumentException(); } Item item; lock (shandler.items) { if (!shandler.items.ContainsKey(download_sid)) { throw new InvalidArgumentException(); } item = shandler.items[download_sid]; } var item_data_path = Path.Combine(shandler.data_path, item.node); var fd = File.OpenRead(item_data_path); ulong offset_start = 0; // How can a stream be negative? Could this stream ever be negative? What purpose does it suit? ulong offset_size = (ulong)fd.Length; ulong total_size = (ulong)fd.Length; String response_code = "200"; if (request.internal_headers.ContainsKey("range")) { var range_str = request.internal_headers["range"]; var eqndx = range_str.IndexOf("="); if (eqndx > -1) { var range_sub_str = range_str.Substring(eqndx + 1).Trim(); var range_nums_strs = range_sub_str.Split("-"); if (range_nums_strs.Length > 1) { offset_start = ulong.Parse(range_nums_strs[0]); if (range_nums_strs[1].Equals("")) { offset_size = (ulong)fd.Length - offset_start; } else { offset_size = ulong.Parse(range_nums_strs[1]) - offset_start + 1; } response_code = "206"; } } } checked { fd.Seek((long)offset_start, SeekOrigin.Begin); } fd.Seek((long)offset_start, SeekOrigin.Begin); String mime_type = null; switch (item.datatype) { case "mp4": mime_type = "video/mp4"; break; case "jpg": mime_type = "image/jpeg"; break; } using (var de_stream = new LimitedStream(fd, offset_size)) { var header = new Dictionary <String, String>(); header.Add("$response_code", response_code); header.Add("$response_text", "Partial Content"); header.Add("content-disposition", String.Format("inline; filename=\"{0}_{1}_{2}_{3}.{4}\"", item.datestr, item.userstr, item.devicestr, item.timestr, item.datatype ) ); header.Add("accept-ranges", "bytes"); header.Add("content-range", String.Format("bytes {0}-{1}/{2}", offset_start, offset_size + offset_start - 1, total_size ) ); if (mime_type != null) { header.Add("content-type", mime_type); } await encoder.WriteHeader(header); await encoder.BodyWriteStream(de_stream); return(Task.CompletedTask); } }