AmendHub

Download:

jcs

/

subtext

/

amendments

/

451

folder: Implement file editing (only description/notes)

Move data gathering loop out of upload and into edit, and allow
sysops to re-checksum existing files now that the checksumming is
fixed

jcs made amendment 451 about 1 year ago
--- folder.c Mon Mar 27 11:06:25 2023 +++ folder.c Mon Mar 27 14:46:55 2023 @@ -112,8 +112,9 @@ bool folder_file_save(struct folder *folder, struct fo char *temp_path); bool folder_file_valid_filename(struct session *session, struct folder *folder, struct folder_file *file, char **error); -void folder_edit_file(struct session *s, struct folder *folder, - struct folder_file *file); +bool folder_edit_file(struct session *s, struct folder *folder, + struct folder_file *file, char *file_path); +bool folder_file_checksum(struct folder_file *file, char *file_path); void folder_list(struct session *s) @@ -298,7 +299,7 @@ folder_list_files(struct session *s, struct folder *fo session_printf(s, "{{B}}%s: %s (Page %ld of %ld){{/B}}\r\n", folder->name, folder->description, page, pages); session_printf(s, - "%s# Date File Description%s\r\n", + "%s # Date File Description%s\r\n", ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END)); session_flush(s); @@ -339,11 +340,9 @@ folder_upload(struct session *s, struct folder *folder char *initial_filename, char *initial_description) { struct folder_file file = { 0 }; - FILE *fp; - SHA1_CTX sha1; struct stat sb; struct zmodem_session *zs; - char *upload_path = NULL, *data = NULL, *tmp = NULL, *errorstr = NULL; + char *upload_path = NULL, *tmp = NULL; size_t size, rsize; short c, n, error; @@ -461,177 +460,23 @@ folder_upload(struct session *s, struct folder *folder ZDestroy(zs); zs = NULL; - data = xmalloc(1024); - if (data == NULL) - goto file_upload_error; - session_printf(s, "Calculating SHA1 checksum of uploaded file..."); session_flush(s); - SHA1Init(&sha1); - fp = fopen(upload_path, "rb"); - rsize = 0; - for (n = 1; fp && !feof(fp); n++) { - size = fread(data, 1, 1024, fp); - rsize += size; - SHA1Update(&sha1, (const u_int8_t *)data, size); - if (n % 2 == 0) - uthread_yield(); - } - SHA1End(&sha1, (char *)&file.sha1_checksum); - if (fp) - fclose(fp); - xfree(&data); - - if (rsize != file.size) { - session_printf(s, "failed reading file! (%lu vs %lu)\r\n", - rsize, file.size); + if (!folder_file_checksum(&file, upload_path)) { + session_printf(s, "failed reading file!\r\n"); session_flush(s); goto file_upload_cancel; } - session_printf(s, "done.\r\n\r\n"); session_flush(s); - session_printf(s, "{{B}}Uploaded By:{{/B}} %s\r\n", - s->user->username); - session_printf(s, "{{B}}Folder:{{/B}} %s\r\n", - folder->name); - session_printf(s, "{{B}}SHA1 Checksum:{{/B}} %s\r\n", - file.sha1_checksum); - session_printf(s, "{{B}}File Size:{{/B}} %ld\r\n", file.size); - session_flush(s); -file_upload_annotate: - for (;;) { - session_printf(s, "{{B}}File Name:{{/B}} "); + if (folder_edit_file(s, folder, &file, upload_path)) + goto file_upload_done; + else { + session_printf(s, "Failed saving file!\r\n"); session_flush(s); - - tmp = session_field_input(s, FOLDER_FILE_FILENAME_LENGTH, - FOLDER_FILE_FILENAME_LENGTH - 1, file.filename, false, 0); - if (tmp == NULL) - goto file_upload_cancel; - strlcpy(file.filename, tmp, sizeof(file.filename)); - xfree(&tmp); - session_output(s, "\r\n", 2); - session_flush(s); - - rtrim(file.filename, "\r\n\t "); - - if (!folder_file_valid_filename(s, folder, &file, &errorstr)) { - session_printf(s, "{{B}}Error:{{/B}} %s\r\n", errorstr); - xfree(&errorstr); - continue; - } - - break; } - for (;;) { - session_printf(s, "{{B}}Short Description:{{/B}} "); - session_flush(s); - - tmp = session_field_input(s, FOLDER_FILE_DESCR_LENGTH, - FOLDER_FILE_DESCR_LENGTH - 1, file.description, false, 0); - if (tmp == NULL) - goto file_upload_cancel; - strlcpy(file.description, tmp, sizeof(file.description)); - xfree(&tmp); - session_output(s, "\r\n", 2); - session_flush(s); - - rtrim(file.description, "\r\n\t "); - - if (file.description[0] == '\0') { - session_printf(s, "{{B}}Error:{{/B}} File " - "description cannot be blank (^C to cancel)\r\n"); - session_flush(s); - continue; - } - - break; - } - - for (;;) { - session_printf(s, - "{{B}}Notes (Optional, ^D when finished):{{/B}}\r\n"); - session_flush(s); - - tmp = session_field_input(s, 2048, s->terminal_columns - 1, - file.notes, true, 0); - if (file.notes != NULL) - xfree(&file.notes); - file.notes = tmp; - session_output(s, "\r\n", 2); - session_flush(s); - if (file.notes == NULL) { - file.notes_size = 0; - break; - } - - rtrim(file.notes, "\r\n\t "); - - if (file.notes[0] == '\0') { - xfree(&file.notes); - file.notes = NULL; - file.notes_size = 0; - break; - } - file.notes_size = strlen(file.notes) + 1; - break; - } - - for (;;) { - session_printf(s, "\r\n{{B}}(S){{/B}}ave file, " - "{{B}}(E){{/B}}dit again, or {{B}}(C){{/B}}ancel? "); - session_flush(s); - - c = session_input_char(s); - if (c == 0 || s->ending) - goto file_upload_done; - - switch (c) { - case 's': - case 'S': - case 'y': - session_printf(s, "%c\r\n", c); - session_flush(s); - /* FALLTHROUGH */ - case '\n': - case '\r': - /* save */ - session_printf(s, "Saving file... "); - session_flush(s); - - if (folder_file_save(folder, &file, upload_path)) { - session_logf(s, "[%s] Saved file %s", folder->name, - file.filename); - session_printf(s, "done\r\n"); - session_flush(s); - goto file_upload_done; - } else { - session_printf(s, "failed!\r\n"); - session_flush(s); - goto file_upload_cancel; - } - break; - case 'e': - case 'E': - session_printf(s, "%c\r\n", c); - session_flush(s); - goto file_upload_annotate; - case 'c': - case 'C': - session_printf(s, "%c\r\n", c); - session_flush(s); - /* FALLTHROUGH */ - case CONTROL_C: - goto file_upload_cancel; - } - } - -file_upload_error: - session_printf(s, "Failed saving file!\r\n"); - session_flush(s); - file_upload_cancel: session_printf(s, "\r\n"); session_flush(s); @@ -710,9 +555,12 @@ folder_file_view(struct session *s, struct folder *fol strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", localtime(&file.time)); - session_printf(s, "{{B}}Folder:{{/B}} %s\r\n", folder->name); - session_printf(s, "{{B}}File Name:{{/B}} %s\r\n", file.filename); - session_printf(s, "{{B}}Description:{{/B}} %s\r\n", file.description); + session_printf(s, "{{B}}Folder:{{/B}}{{#}} %s\r\n", + folder->name); + session_printf(s, "{{B}}File Name:{{/B}}{{#}} %s\r\n", + file.filename); + session_printf(s, "{{B}}Description:{{/B}}{{#}} %s\r\n", + file.description); session_printf(s, "{{B}}File Size:{{/B}} %lu\r\n", file.size); session_printf(s, "{{B}}File SHA1:{{/B}} %s\r\n", file.sha1_checksum); session_printf(s, "{{B}}Uploaded:{{/B}} %s %s\r\n", time, @@ -811,7 +659,15 @@ folder_file_view(struct session *s, struct folder *fol session_flush(s); break; } - folder_edit_file(s, folder, &file); + path = xmalloc(FILENAME_MAX); + if (path == NULL) { + done = true; + break; + } + snprintf(path, FILENAME_MAX, "%s:%s", folder->path, + file.filename); + folder_edit_file(s, folder, &file, path); + xfree(&path); ret = FILE_VIEW_RETURN_FIND; done = true; break; @@ -938,9 +794,14 @@ folder_file_save(struct folder *folder, struct folder_ short ret; char *data; size_t size; + bool need_move = false; - file->id = bile_next_id(folder->bile, FOLDER_FILE_RTYPE); - file->time = Time; + if (!file->id) { + need_move = true; + file->id = bile_next_id(folder->bile, FOLDER_FILE_RTYPE); + } + if (!file->time) + file->time = Time; ret = bile_marshall_object(folder->bile, folder_file_object_fields, nfolder_file_object_fields, file, &data, &size); @@ -960,26 +821,30 @@ folder_file_save(struct folder *folder, struct folder_ bile_flush(folder->bile, true); - new_name = xmalloc(FILENAME_MAX); - if (new_name == NULL) - return false; - snprintf(new_name, FILENAME_MAX, "%s:%s", folder->path, - file->filename); - CtoPstr(temp_path); - CtoPstr(new_name); - ret = Rename(temp_path, 0, new_name); - PtoCstr(temp_path); - PtoCstr(new_name); - if (ret != 0) { - warn("FSRename(%s, 0, %s) failed: %d", temp_path, new_name, ret); - bile_delete(folder->bile, FOLDER_FILE_RTYPE, file->id, - BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE); - file->id = 0; + if (need_move) { + new_name = xmalloc(FILENAME_MAX); + if (new_name == NULL) + return false; + snprintf(new_name, FILENAME_MAX, "%s:%s", folder->path, + file->filename); + CtoPstr(temp_path); + CtoPstr(new_name); + ret = Rename(temp_path, 0, new_name); + PtoCstr(temp_path); + PtoCstr(new_name); + if (ret != 0) { + warn("FSRename(%s, 0, %s) failed: %d", temp_path, new_name, + ret); + bile_delete(folder->bile, FOLDER_FILE_RTYPE, file->id, + BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE); + file->id = 0; + xfree(&new_name); + return false; + } + xfree(&new_name); - return false; } - xfree(&new_name); return true; } @@ -1094,9 +959,216 @@ folder_file_valid_filename(struct session *session, return true; } -void +bool folder_edit_file(struct session *s, struct folder *folder, - struct folder_file *file) + struct folder_file *file, char *file_path) { - /* TODO */ -} + struct username_cache *uploader; + char *tmp, *errorstr; + unsigned short c; + + uploader = user_username(file->uploader_user_id); + + session_printf(s, "{{B}}Uploaded By:{{/B}} %s\r\n", + uploader ? uploader->username : "(unknown)"); + session_printf(s, "{{B}}Folder:{{/B}} %s\r\n", + folder->name); + session_printf(s, "{{B}}SHA1 Checksum:{{/B}} %s\r\n", + file->sha1_checksum); + session_printf(s, "{{B}}File Size:{{/B}} %ld\r\n", + file->size); + session_flush(s); + +file_annotate: + for (;;) { + session_printf(s, "{{B}}File Name:{{/B}} "); + session_flush(s); + + if (file->id) { + /* TODO: allow renaming and do FSRename */ + session_printf(s, "{{#}}%s\r\n", file->filename); + } else { + tmp = session_field_input(s, FOLDER_FILE_FILENAME_LENGTH, + FOLDER_FILE_FILENAME_LENGTH - 1, file->filename, false, 0); + if (tmp == NULL) + return false; + strlcpy(file->filename, tmp, sizeof(file->filename)); + xfree(&tmp); + session_output(s, "\r\n", 2); + session_flush(s); + + rtrim(file->filename, "\r\n\t "); + + if (!folder_file_valid_filename(s, folder, file, &errorstr)) { + session_printf(s, "{{B}}Error:{{/B}} %s\r\n", errorstr); + xfree(&errorstr); + continue; + } + } + + break; + } + + for (;;) { + session_printf(s, "{{B}}Short Description:{{/B}} "); + session_flush(s); + + tmp = session_field_input(s, FOLDER_FILE_DESCR_LENGTH, + FOLDER_FILE_DESCR_LENGTH - 1, file->description, false, 0); + if (tmp == NULL) + return false; + strlcpy(file->description, tmp, sizeof(file->description)); + xfree(&tmp); + session_output(s, "\r\n", 2); + session_flush(s); + + rtrim(file->description, "\r\n\t "); + + if (file->description[0] == '\0') { + session_printf(s, "{{B}}Error:{{/B}} File " + "description cannot be blank (^C to cancel)\r\n"); + session_flush(s); + continue; + } + + break; + } + + for (;;) { + session_printf(s, + "{{B}}Notes (Optional, ^D when finished):{{/B}}\r\n"); + session_flush(s); + + tmp = session_field_input(s, 2048, s->terminal_columns - 1, + file->notes, true, 0); + if (file->notes != NULL) + xfree(&file->notes); + file->notes = tmp; + session_output(s, "\r\n", 2); + session_flush(s); + if (file->notes == NULL) { + file->notes_size = 0; + break; + } + + rtrim(file->notes, "\r\n\t "); + + if (file->notes[0] == '\0') { + xfree(&file->notes); + file->notes = NULL; + file->notes_size = 0; + break; + } + file->notes_size = strlen(file->notes) + 1; + break; + } + + for (;;) { + session_printf(s, "\r\n{{B}}(S){{/B}}ave, " + "{{B}}(E){{/B}}dit again, "); + if (file->id && s->user->is_sysop) + session_printf(s, "Re-c{{B}}(h){{/B}}ecksum, "); + session_printf(s, "or {{B}}(C){{/B}}ancel? "); + session_flush(s); + + c = session_input_char(s); + if (c == 0 || s->ending) + return false; + + switch (c) { + case 'h': + case 'H': + if (s->user->is_sysop) { + session_printf(s, "%c\r\n", c); + session_flush(s); + + session_printf(s, "Calculating SHA1 checksum of file..."); + session_flush(s); + + if (!folder_file_checksum(file, file_path)) { + session_printf(s, "failed reading %s!\r\n", file_path); + session_flush(s); + return false; + } + session_printf(s, "done:\r\n%s\r\n", file->sha1_checksum); + session_flush(s); + } + break; + case 's': + case 'S': + case 'y': + session_printf(s, "%c\r\n", c); + session_flush(s); + /* FALLTHROUGH */ + case '\n': + case '\r': + /* save */ + session_printf(s, "Saving file... "); + session_flush(s); + + if (folder_file_save(folder, file, file_path)) { + session_logf(s, "[%s] Saved file %s", folder->name, + file->filename); + session_printf(s, "done\r\n"); + session_flush(s); + return true; + } + + session_printf(s, "failed!\r\n"); + session_flush(s); + return false; + case 'e': + case 'E': + session_printf(s, "%c\r\n", c); + session_flush(s); + goto file_annotate; + case 'c': + case 'C': + session_printf(s, "%c\r\n", c); + session_flush(s); + return false; + case CONTROL_C: + return false; + } + } + + return false; +} + +bool +folder_file_checksum(struct folder_file *file, char *file_path) +{ + SHA1_CTX sha1; + FILE *fp; + char *data; + size_t rsize, size, n; + + data = xmalloc(1024); + if (data == NULL) + return false; + + SHA1Init(&sha1); + fp = fopen(file_path, "rb"); + if (fp == NULL) { + xfree(&data); + return false; + } + + rsize = 0; + for (n = 1; fp && !feof(fp); n++) { + size = fread(data, 1, 1024, fp); + rsize += size; + SHA1Update(&sha1, (const u_int8_t *)data, size); + if (n % 2 == 0) + uthread_yield(); + } + fclose(fp); + xfree(&data); + + SHA1End(&sha1, (char *)&file->sha1_checksum); + + if (rsize != file->size) + return false; + + return true; +}