# HG changeset patch # User Olaf Wintermann # Date 1732617490 -3600 # Node ID 2fbb3cac05a59dfaa4e9b6723c014fb8b4e72a7f # Parent e27526429d856590c43bbb397f879ee7e5b16f95 implement rename diff -r e27526429d85 -r 2fbb3cac05a5 application/application.c --- a/application/application.c Tue Nov 26 10:40:45 2024 +0100 +++ b/application/application.c Tue Nov 26 11:38:10 2024 +0100 @@ -315,6 +315,16 @@ } } +void action_rename(UiEvent *event, void *data) { + DavBrowser *browser = event->document; + UiListSelection sel = ui_list_getselection(browser->resources); + if (sel.count == 1) { + davbrowser_rename(event->obj, browser, sel); + } else if(sel.count > 1) { + fprintf(stderr, "Renaming multiple resources is not implemented yet\n"); + } +} + static void newfiledialog_result(UiEvent *event, void *data) { DavBrowser *browser = event->document; char *path = event->eventdata; diff -r e27526429d85 -r 2fbb3cac05a5 application/application.h --- a/application/application.h Tue Nov 26 10:40:45 2024 +0100 +++ b/application/application.h Tue Nov 26 11:38:10 2024 +0100 @@ -186,6 +186,8 @@ void action_selectall(UiEvent *event, void *data); +void action_rename(UiEvent *event, void *data); + void action_newfile(UiEvent *event, void *data); void action_mkcol(UiEvent *event, void *data); diff -r e27526429d85 -r 2fbb3cac05a5 application/davcontroller.c --- a/application/davcontroller.c Tue Nov 26 10:40:45 2024 +0100 +++ b/application/davcontroller.c Tue Nov 26 11:38:10 2024 +0100 @@ -517,6 +517,26 @@ char *result_contenttype; } DavPathOpResult; +typedef struct DavRenameOp { + UiObject *ui; + DavBrowser *browser; + + // clone of the browser's DavSession + DavSession *sn; + char *path; + char *newname; + int result; + char *errormsg; + + // browser->resources index + size_t index; + + // browser->current ptr when the PathOp started + // used in combination with collection_ctn to check if the browser list changed + DavResource *collection; + int64_t collection_ctn; +} DavRenameOp; + static int uithr_pathop_delete_error(void *data) { DavPathOpResult *result = data; @@ -693,6 +713,149 @@ + +static int jobthr_rename(void *data) { + DavRenameOp *op = data; + + DavResource *res = dav_get(op->sn, op->path, NULL); + if(!res) { + fprintf(stderr, "Error: Cannot get resource %s\n", op->path); + op->result = 1; + return 0; + } + + char *cryptoname = dav_get_string_property_ns(res, DAV_NS, "crypto-name"); + char *cryptokey = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); + if(cryptoname && cryptokey) { + // encrypted resource, the name is stored in the crypto-name property + // these properties are only loaded if encryption is enabled for + // this session + DavKey *key = dav_context_get_key(op->sn->context, cryptokey); + if(!key) { + cxmutstr error = cx_asprintf_a(op->sn->mp->allocator, "Cannot rename resource: crypto key %s not found.", cryptokey); + op->errormsg = error.ptr; + op->result = 1; + return 0; + } + + // check if a resource with this name already exists + char *parent = util_parent_path(res->path); + char *newpath = util_concat_path(parent, op->newname); + DavResource *testres = dav_resource_new(op->sn, newpath); + if(dav_exists(testres)) { + cxmutstr error = cx_asprintf_a(op->sn->mp->allocator, "A resource with the name %s already exists.", op->newname); + op->errormsg = error.ptr; + op->result = 1; + } else { + char *crname = aes_encrypt(op->newname, strlen(op->newname), key); + dav_set_string_property_ns(res, DAV_NS, "crypto-name", crname); + free(crname); + if(dav_store(res)) { + op->result = 1; + } + } + free(parent); + free(newpath); + } else { + // rename the resource by changing the url mapping with MOVE + char *parent = util_parent_path(res->href); + char *new_href = util_concat_path(parent, op->newname); + char *dest = util_get_url(op->sn, new_href); + free(parent); + free(new_href); + if(dav_moveto(res, dest, false)) { + op->result = 1; + } + free(dest); + } + + if(op->result && !op->errormsg) { + cxmutstr error = cx_asprintf_a(op->sn->mp->allocator, "Error: %d", op->sn->error); + op->errormsg = error.ptr; + } + + return 0; +} + +static void uithr_rename_finished(UiEvent *event, void *data) { + DavRenameOp *op = data; + + if(!op->result) { + // update name in the browser list + if (op->browser->current == op->collection && op->browser->res_counter == op->collection_ctn) { + DavResource *res = ui_list_get(op->browser->resources, op->index); + + char *parent = util_parent_path(res->path); + char *newpath = util_concat_path(parent, op->newname); + dav_session_free(res->session, res->path); + dav_session_free(res->session, res->name); + res->path = dav_session_strdup(res->session, newpath); + res->name = dav_session_strdup(res->session, op->newname); + op->browser->resources->update(op->browser->resources, 0); + free(parent); + free(newpath); + } + } else { + // error + ui_dialog(op->ui, .title = "Error", .content = op->errormsg, .closebutton_label = "OK"); + } + dav_session_destroy(op->sn); +} + +static void action_resource_rename(UiEvent *event, void *data) { + DavRenameOp *op = data; + if(event->intval == 1) { + char *newname = event->eventdata; + if(!newname || strlen(newname) == 0) { + ui_dialog(op->ui, .title = "Error", .content = "No name specified", .closebutton_label = "OK"); + dav_session_destroy(op->sn); + return; + } + + char *s = strchr(newname, '/'); + if(s) { + ui_dialog(op->ui, .title = "Error", .content = "Character '/' is not allowed", .closebutton_label = "OK"); + dav_session_destroy(op->sn); + return; + } + + op->newname = dav_session_strdup(op->sn, newname); + + ui_job(op->ui, jobthr_rename, op, uithr_rename_finished, op); + return; + } + dav_session_destroy(op->sn); +} + +void davbrowser_rename(UiObject *ui, DavBrowser *browser, UiListSelection selection) { + DavSession *sn = dav_session_clone(browser->sn); + + DavResource *res = ui_list_get(browser->resources, selection.rows[0]); + + DavRenameOp *rename = dav_session_malloc(sn, sizeof(DavRenameOp)); + memset(rename, 0, sizeof(DavRenameOp)); + rename->browser = browser; + rename->ui = ui; + rename->sn = sn; + rename->path = dav_session_strdup(sn, res->path); + rename->index = selection.rows[0]; + + rename->collection = browser->current; + rename->collection_ctn = browser->res_counter; + + ui_dialog(ui, + .title = "Rename", + .content = res->name, + .input = TRUE, + .input_value = res->name, + .result = action_resource_rename, + .resultdata = rename, + .button1_label = "Rename", + .closebutton_label = "Cancel"); +} + + + DavResourceViewer* dav_resourceviewer_create(DavSession *sn, const char *path, DavResourceViewType type) { DavResourceViewer *doc = ui_document_new(sizeof(DavResourceViewer)); UiContext *ctx = ui_document_context(doc); diff -r e27526429d85 -r 2fbb3cac05a5 application/davcontroller.h --- a/application/davcontroller.h Tue Nov 26 10:40:45 2024 +0100 +++ b/application/davcontroller.h Tue Nov 26 11:38:10 2024 +0100 @@ -85,6 +85,8 @@ void davbrowser_mkcol(UiObject *ui, DavBrowser *browser, const char *name); void davbrowser_newfile(UiObject *ui, DavBrowser *browser, const char *name); +void davbrowser_rename(UiObject *ui, DavBrowser *browser, UiListSelection selection); + DavResourceViewer* dav_resourceviewer_create(DavSession *sn, const char *path, DavResourceViewType type); diff -r e27526429d85 -r 2fbb3cac05a5 application/window.c --- a/application/window.c Tue Nov 26 10:40:45 2024 +0100 +++ b/application/window.c Tue Nov 26 11:38:10 2024 +0100 @@ -60,7 +60,7 @@ ui_menuitem(.label = "Delete", .onclick = action_delete, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); ui_menuitem(.label = "Select All", .onclick = action_selectall, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); ui_menuseparator(); - ui_menuitem(.label = "Rename", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + ui_menuitem(.label = "Rename", .onclick = action_rename, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); ui_menuseparator(); ui_menuitem("Open Properties", .onclick = action_open_properties, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); ui_menuitem("Open as Text File", .onclick = action_open_properties, .onclickdata = "text/plain", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION));