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;
+}