/// <summary> /// Reads query string from header and form - file data from content, sets values to request's QueryString, Form and Files properties /// </summary> internal static void ReadContent(HttpRequest request) { request.QueryString = !string.IsNullOrEmpty(request.QueryStringData) ? EncodedFormDataReader.Read(request.QueryStringData) : new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase); if (string.IsNullOrEmpty(request.ContentType)) { return; } if (request.ContentType.Equals(HttpHeaders.MULTIPART_FORM_DATA, StringComparison.InvariantCultureIgnoreCase)) { if (request.ContentLength > 0) { List <FormDataItem> data = MultipartFormDataReader.Read(request.Boundary, request.ContentStream); request.Form = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase); foreach (FormDataItem item in data.Where(x => !x.IsFile)) { string value = Encoding.UTF8.GetString(item.Stream.ToArray()); if (request.Form.ContainsKey(item.Name)) { request.Form[item.Name] += "," + value; } else { request.Form.Add(item.Name, value); } } request.Files = data.Where(x => x.IsFile).Select(x => new FormFile { Filename = x.Filename, Name = x.Name, Size = Convert.ToInt32(x.Stream.Length), Stream = x.Stream, ContentType = x.ContentType }); } else { request.Form = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase); request.Files = new IFormFile[0]; } } else if (request.ContentType.Equals(HttpHeaders.APPLICATION_FORM_URLENCODED, StringComparison.InvariantCultureIgnoreCase)) { request.Form = request.ContentLength > 0 ? EncodedFormDataReader.Read(request.ContentStream) : new Dictionary <string, string>(); } }
private async Task HandleUpload(HttpConnection p, string path) { bool responseListPage = p.ParseUrlQstr()["infoonly"] != "1"; string info = null; if (!(allow_create | allow_edit)) { info = $"{strMissingPermission("create")} and {strMissingPermission("edit")}."; goto FAIL; } var reader = new MultipartFormDataReader(p); int count = 0; string saveFileName = null; string encoding = "utf-8"; while (await reader.ReadNextPartHeader()) { if (reader.CurrentPartName == "file") { var fileName = reader.CurrentPartFileName; if (!CheckPathForWriting(path, fileName, out info, out var realPath)) { goto FAIL; } if (!TryOpenFile(path, fileName + ".uploading", out var fs, out info, out var tempPath)) { goto FAIL; } try { using (fs) { await NaiveUtils.StreamCopyAsync(reader, fs); } MoveOrReplace(tempPath, realPath); } catch (Exception e) { Logger.exception(e, Logging.Level.Warning, $"receiving file '{fileName}' from {p.myStream}."); File.Delete(realPath); if (e is DisconnectedException) { return; } info = $"IO error on '{saveFileName}'"; goto FAIL; } Logger.info($"uploaded '{fileName}' by {p.myStream}."); count++; } else if (reader.CurrentPartName.Is("textFileName").Or("fileName")) { saveFileName = await reader.ReadAllTextAsync(); } else if (reader.CurrentPartName == "textFileEncoding") { encoding = await reader.ReadAllTextAsync(); } else if (reader.CurrentPartName == "textContent") { if (!CheckPathForWriting(path, saveFileName, out info, out var realPath)) { goto FAIL; } if (!TryOpenFile(path, saveFileName + ".uploading", out var fs, out info, out var tempPath)) { goto FAIL; } try { using (fs) { if (encoding == "utf-8") { await NaiveUtils.StreamCopyAsync(reader, fs, bs : 8 * 1024); } else { using (var sr = new StreamReader(reader)) { using (var sw = new StreamWriter(fs, Encoding.GetEncoding(encoding))) { const int bufLen = 8 * 1024; var buf = new char[bufLen]; var read = await sr.ReadAsync(buf, 0, bufLen); await sw.WriteAsync(buf, 0, read); } } } } MoveOrReplace(tempPath, realPath); } catch (Exception e) { Logger.exception(e, Logging.Level.Warning, $"receiving text file '{saveFileName}' from {p.myStream}."); File.Delete(tempPath); if (e is DisconnectedException) { return; } info = $"IO error on '{saveFileName}'"; goto FAIL; } Logger.info($"uploaded text '{saveFileName}' by {p.myStream}."); count++; } else if (reader.CurrentPartName == "mkdir") { var dirName = await reader.ReadAllTextAsync(); if (!CheckPathForWriting(path, dirName, out info, out var realPath)) { goto FAIL; } try { Directory.CreateDirectory(realPath); } catch (Exception) { info = $"Failed to create directory '{dirName}'"; goto FAIL; } Logger.info($"created dir '{dirName}' by {p.myStream}."); count++; } else if (reader.CurrentPartName == "rm") { var delFile = await reader.ReadAllTextAsync(); if (!CheckPathForWriting(path, delFile, out info, out var realPath, out var r)) { goto FAIL; } try { if (r == WebSvrHelper.PathResult.Directory) { Directory.Delete(realPath); } else if (r == WebSvrHelper.PathResult.File) { File.Delete(realPath); } else { info = $"Failed to delete '{delFile}' (not found)."; goto FAIL; } } catch (Exception) { info = $"Failed to delete '{delFile}'"; goto FAIL; } Logger.info($"deleted '{delFile}' by {p.myStream}."); count++; } else if (reader.CurrentPartName == "netdl") { if (!allow_netdl) { info = $"{strMissingPermission("netdl")}."; goto FAIL; } var unparsed = await reader.ReadAllTextAsync(); var args = Naive.Console.Command.SplitArguments(unparsed); string url, name; if (args.Length == 0 || args.Length > 2) { info = $"wrong arguments."; goto FAIL; } url = args[0]; if (args.Length == 2) { name = args[1]; } else { int startIndex = url.LastIndexOf('/'); if (startIndex < 0) { info = "can not determine a filename from the given url, please specify one."; goto FAIL; } name = url.Substring(startIndex); } if (!CheckPathForWriting(path, name, out info, out var realPath, out var r)) { goto FAIL; } if (r == WebSvrHelper.PathResult.Directory) { name = Path.Combine(url.Substring(url.LastIndexOf('/')), name); if (!CheckPathForWriting(path, name, out info, out realPath)) { goto FAIL; } } if (!CheckPathForWriting(path, name + ".downloading", out info, out var realDlPath)) { goto FAIL; } var dlTask = new DownloadTask(url, realDlPath); // double checked locking, add the new task into the dict if no task already exist. downloadTasksLock.EnterReadLock(); var taskExists = downloadTasks.TryGetValue(realDlPath, out var oldTask); downloadTasksLock.ExitReadLock(); if (!taskExists) { downloadTasksLock.EnterWriteLock(); taskExists = downloadTasks.TryGetValue(realDlPath, out oldTask); if (!taskExists) { downloadTasks.Add(realDlPath, dlTask); } downloadTasksLock.ExitWriteLock(); } if (taskExists) { if (oldTask.State <= DownloadTask.States.running) { info = "a task is already running on this path."; goto FAIL; } else { info = $"override a '{oldTask.State}' task.\n"; downloadTasksLock.EnterWriteLock(); downloadTasks[realDlPath] = dlTask; downloadTasksLock.ExitWriteLock(); } } try { var t = NaiveUtils.RunAsyncTask(async() => { try { await dlTask.Start(); MoveOrReplace(realDlPath, realPath); } finally { // Whether success or not, remove the task from list after 5 minutes. AsyncHelper.SetTimeout(5 * 60 * 1000, () => { downloadTasksLock.EnterWriteLock(); downloadTasks.Remove(realDlPath); downloadTasksLock.ExitWriteLock(); }); } }); if (await t.WithTimeout(200)) { info += "downloading task is started."; } else { await t; info += "downloaded."; } } catch (Exception e) { Logger.exception(e, Logging.Level.Warning, $"downloading from '{url}' to '{realDlPath}'"); info += "downloading failed."; goto FAIL; } count++; } else if (reader.CurrentPartName.Is("mv").Or("cp")) { var unparsed = await reader.ReadAllTextAsync(); var args = Naive.Console.Command.SplitArguments(unparsed); if (args.Length < 2) { info = $"too few arguments"; goto FAIL; } string to = args[args.Length - 1]; if (!CheckPathForWriting(path, to, out info, out var toPath, out var toType)) { goto FAIL; } bool multipleFrom = args.Length > 2; if (multipleFrom && toType != WebSvrHelper.PathResult.Directory) { info = "multiple 'from' when 'to' is not a directory!"; goto FAIL; } for (int i = 0; i < args.Length - 1; i++) { string from = args[i]; if (!CheckPathForWriting(path, from, out info, out var fromPath)) { goto FAIL; } string toFilePath = toType == WebSvrHelper.PathResult.Directory ? Path.Combine(toPath, Path.GetFileName(from)) : toPath; // TODO: refine directory moving/copying try { if (reader.CurrentPartName == "mv") { MoveOrReplace(fromPath, toFilePath); } else { File.Copy(fromPath, toFilePath, true); } } catch (Exception e) { Logger.exception(e, Logging.Level.Warning, $"file mv/cp from '{fromPath}' to '{toFilePath}' by {p.myStream}."); info = "error occurred during file operation at " + from; goto FAIL; } count++; } } else if (reader.CurrentPartName == "rmm") { var unparsed = await reader.ReadAllTextAsync(); var args = Naive.Console.Command.SplitArguments(unparsed); if (args.Length == 0) { info = "no arguments."; goto FAIL; } foreach (var delFile in args) { if (!CheckPathForWriting(path, delFile, out info, out var realPath, out var r)) { goto FAIL; } try { if (r == WebSvrHelper.PathResult.Directory) { Directory.Delete(realPath); } else if (r == WebSvrHelper.PathResult.File) { File.Delete(realPath); } else { info = $"Failed to delete '{delFile}' (not found)"; goto FAIL; } } catch (Exception) { info = $"Failed to delete '{delFile}'"; goto FAIL; } Logger.info($"deleted '{delFile}' by {p.myStream}."); count++; } } else if (reader.CurrentPartName.Is("infoonly").Or("isajax")) { responseListPage = false; } else { info = $"Unknown part name '{reader.CurrentPartName}'"; goto FAIL; } } if (info == null || count > 1) { info = $"Finished {count} operation{(count > 1 ? "s" : null)}."; } FAIL: if (responseListPage) { await HandleDirList(p, path, info); } else { p.Handled = true; p.setContentTypeTextPlain(); await p.writeLineAsync(info); } }