diary

Text-based journaling program
git clone https://git.in0rdr.ch/diary.git
Log | Files | Refs | README | LICENSE

commit 33495a785319543c1036da68d781ea31e705ef9e
parent 480e24766f4d10b6fd73d23e2aa0c10c4ae64729
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Sun, 12 Nov 2023 17:06:37 +0100

draft: caldav server with basicauth

Diffstat:
Mconfig/diary.cfg | 11+++++++++--
Msrc/caldav.c | 89++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/caldav.h | 1-
Msrc/diary.c | 15++++++++++++---
Msrc/utils.c | 5++++-
Msrc/utils.h | 10++++++++--
6 files changed, 87 insertions(+), 44 deletions(-)

diff --git a/config/diary.cfg b/config/diary.cfg @@ -14,10 +14,17 @@ fmt_cmd = #no_mouse = false # Editor to open journal files with editor = -# Google calendar name for CalDAV sync -#google_calendar = +# CalDAV server URI +# For example, Google calendar URI: "https://apidata.googleusercontent.com/caldav/v2" +#caldav_server = +# Calendar name for CalDAV sync +#caldav_calendar = # Google OAuth2 clientid and secretid #google_clientid = #google_secretid = # Google OAuth2 tokenfile #google_tokenfile = ~/.diary-token +# CalDAV server username +#caldav_username = +# CalDAV server password +#caldav_password = diff --git a/src/caldav.c b/src/caldav.c @@ -407,7 +407,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { } /* Make a CalDAV request */ -char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields, int depth) { +char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields, int depth, bool basicauth) { // only support depths 0 or 1 if (depth < 0 || depth > 1) { return NULL; @@ -435,6 +435,13 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_mem_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&caldav_resp); + // default to basic auth when Google credentials are not set + if (basicauth) { + char basicauth [strlen(CONFIG.caldav_username) + strlen(CONFIG.caldav_username) + 1]; + sprintf(basicauth, "%s:%s", CONFIG.caldav_username, CONFIG.caldav_password); + curl_easy_setopt(curl, CURLOPT_USERPWD, basicauth); + } + // construct header fields struct curl_slist *header = NULL; char bearer_token[strlen("Authorization: Bearer ") + strlen(access_token) + 1]; @@ -525,7 +532,7 @@ char* parse_caldav_calendar(char* xml, char* calendar) { } /* Upload event to CalDAV server */ -void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar_uri) { +void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar_uri, bool basicauth) { // get entry path char path[100]; char* ppath = path; @@ -584,7 +591,7 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar strcat(calendar_uri, uid); strcat(calendar_uri, ".ics"); - char* response = caldav_req(date, calendar_uri, "PUT", postfields, 0); + char* response = caldav_req(date, calendar_uri, "PUT", postfields, 0, basicauth); fclose(fp); free(folded_descr); free(descr); @@ -614,10 +621,17 @@ int caldav_sync(struct tm* date, char* info_txt; - if (strcmp(CONFIG.google_clientid, "") == 0 || strcmp(CONFIG.google_secretid, "") == 0 || strcmp(CONFIG.google_calendar, "") == 0 ) { + bool google_oath_enabled = !(strcmp(CONFIG.google_clientid, "") == 0 || strcmp(CONFIG.google_clientid, "") == 0); + bool basicauth_enabled = !(strcmp(CONFIG.caldav_username, "") == 0 || strcmp(CONFIG.caldav_password, "") == 0); + + if (strcmp(CONFIG.caldav_server, "") == 0 || strcmp(CONFIG.caldav_calendar, "") == 0 || !(google_oath_enabled && basicauth_enabled)) { + tracepoint(diary, debug_string, "CONFIG.caldav_server", CONFIG.caldav_server); + tracepoint(diary, debug_string, "CONFIG.caldav_calendar", CONFIG.caldav_calendar); + tracepoint(diary, debug_string, "CONFIG.caldav_username", CONFIG.caldav_username); + tracepoint(diary, debug_string, "CONFIG.caldav_password", CONFIG.caldav_password); wclear(header); wresize(header, LINES, getmaxx(header)); - info_txt = "Missing sync parameters. Set Google Client ID, secret and remote calendar name.\n" + info_txt = "Missing sync parameters. Configure CalDAV server and credentials.\n" "Press any key to continue."; mvwaddstr(header, 0, 0, info_txt); wrefresh(header); @@ -629,27 +643,29 @@ int caldav_sync(struct tm* date, wclear(header); } - // fetch existing API tokens from file - char* tokfile = read_tokenfile(); - free(tokfile); + if (google_oath_enabled) { + // fetch existing API tokens from file + char* tokfile = read_tokenfile(); + free(tokfile); - if (strcmp(access_token, "") == 0) { - // no access token exists yet, create new verifier - char challenge[GOOGLE_OAUTH_CODE_VERIFIER_LENGTH + 1]; - random_code_challenge(GOOGLE_OAUTH_CODE_VERIFIER_LENGTH, challenge); + if (strcmp(access_token, "") == 0) { + // no access token exists yet, create new verifier + char challenge[GOOGLE_OAUTH_CODE_VERIFIER_LENGTH + 1]; + random_code_challenge(GOOGLE_OAUTH_CODE_VERIFIER_LENGTH, challenge); - // fetch new code with verifier - char* code = get_oauth_code(challenge, header); - if (code == NULL) { - tracepoint(diary, error, "Error retrieving oauth code in caldav_sync()"); - return -1; - } + // fetch new code with verifier + char* code = get_oauth_code(challenge, header); + if (code == NULL) { + tracepoint(diary, error, "Error retrieving oauth code in caldav_sync()"); + return -1; + } - // get acess token using code and verifier - tracepoint(diary, debug, "Fetching access token with code challenge"); - get_access_token(code, challenge, false); + // get acess token using code and verifier + tracepoint(diary, debug, "Fetching access token with code challenge"); + get_access_token(code, challenge, false); - free(code); + free(code); + } } pthread_create(&progress_tid, NULL, show_progress, (void*)header); @@ -661,22 +677,25 @@ int caldav_sync(struct tm* date, // check if we can use the token from the tokenfile - char* user_principal = caldav_req(date, GOOGLE_CALDAV_URI, "PROPFIND", principal_postfields, 0); + char* user_principal = caldav_req(date, CONFIG.caldav_server, "PROPFIND", principal_postfields, 0, basicauth_enabled); if (user_principal == NULL) { - // The principal could not be fetched, - // get new acess token with refresh token - tracepoint(diary, debug, "Unable to fetch principal, refreshing API token"); - tracepoint(diary, debug, "Fetching access token with refresh token"); - get_access_token(NULL, NULL, true); - // Retry request for event with new token - user_principal = caldav_req(date, GOOGLE_CALDAV_URI, "PROPFIND", principal_postfields, 0); + // The principal could not be fetched + tracepoint(diary, debug, "Unable to fetch principal"); + if (google_oath_enabled) { + // get new acess token with refresh token + tracepoint(diary, debug, "Fetching access token with refresh token"); + get_access_token(NULL, NULL, true); + // Retry request for event with new token + user_principal = caldav_req(date, CONFIG.caldav_server, "PROPFIND", principal_postfields, 0, basicauth_enabled); + } } if (user_principal == NULL) { pthread_cancel(progress_tid); wclear(header); wresize(header, LINES, getmaxx(header)); + // TODO: this could also be wrong basicauth credentials info_txt = "Offline, corrupted or otherwise invalid OAuth2 credential tokenfile.\n" "Go online or delete tokenfile '%s' and restart diary to retry.\n" "Press any key to continue."; @@ -699,10 +718,10 @@ int caldav_sync(struct tm* date, sprintf(uri, "%s%s", GOOGLE_API_URI, current_user_principal); // free memory allocated by curl request free(user_principal); - char* home_set = caldav_req(date, uri, "PROPFIND", "", 1); + char* home_set = caldav_req(date, uri, "PROPFIND", "", 1, basicauth_enabled); // get calendar URI from the home-set - char* calendar_href = parse_caldav_calendar(home_set, CONFIG.google_calendar); + char* calendar_href = parse_caldav_calendar(home_set, CONFIG.caldav_calendar); char* xml_filter = "<c:calendar-query xmlns:d='DAV:' xmlns:c='urn:ietf:params:xml:ns:caldav'>" "<d:prop><c:calendar-data/></d:prop>" @@ -732,7 +751,7 @@ int caldav_sync(struct tm* date, // fetch event for the cursor date sprintf(uri, "%s%s", GOOGLE_API_URI, calendar_href); - char* event = caldav_req(date, uri, "REPORT", caldata_postfields, 0); + char* event = caldav_req(date, uri, "REPORT", caldata_postfields, 0, basicauth_enabled); // todo: warn if multiple events, // multistatus has more than just one caldav:calendar-data elements // currently, the code below will just extract the first occurance of @@ -812,11 +831,11 @@ int caldav_sync(struct tm* date, char event_uri[300]; sprintf(event_uri, "%s%s%s.ics", GOOGLE_API_URI, calendar_href, remote_uid); tracepoint(diary, debug_string, "Event URI for DELETE request", event_uri); - char* response = caldav_req(date, event_uri, "DELETE", NULL, 0); + char* response = caldav_req(date, event_uri, "DELETE", NULL, 0, basicauth_enabled); free(response); } - put_event(date, dir, dir_size, uri); + put_event(date, dir, dir_size, uri, basicauth_enabled); pthread_cancel(progress_tid); wclear(header); diff --git a/src/caldav.h b/src/caldav.h @@ -32,7 +32,6 @@ #define GOOGLE_OAUTH_REDIRECT_PORT 9004 #define GOOGLE_OAUTH_REDIRECT_SOCKET_BACKLOG 10 #define GOOGLE_API_URI "https://apidata.googleusercontent.com" -#define GOOGLE_CALDAV_URI GOOGLE_API_URI "/caldav/v2" int caldav_sync(struct tm* date, WINDOW* header, diff --git a/src/diary.c b/src/diary.c @@ -363,9 +363,18 @@ bool read_config(const char* file_path) { } else if (strcmp("google_secretid", key_buf) == 0) { CONFIG.google_secretid = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); strcpy(CONFIG.google_secretid, value_buf); - } else if (strcmp("google_calendar", key_buf) == 0) { - CONFIG.google_calendar = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); - strcpy(CONFIG.google_calendar, value_buf); + } else if (strcmp("caldav_server", key_buf) == 0) { + CONFIG.caldav_server = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); + strcpy(CONFIG.caldav_server, value_buf); + } else if (strcmp("caldav_calendar", key_buf) == 0) { + CONFIG.caldav_calendar = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); + strcpy(CONFIG.caldav_calendar, value_buf); + } else if (strcmp("caldav_username", key_buf) == 0) { + CONFIG.caldav_username = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); + strcpy(CONFIG.caldav_username, value_buf); + } else if (strcmp("caldav_password", key_buf) == 0) { + CONFIG.caldav_password = (char *) malloc(strlen(value_buf) + 1 * sizeof(char)); + strcpy(CONFIG.caldav_password, value_buf); } } } diff --git a/src/utils.c b/src/utils.c @@ -405,5 +405,8 @@ config CONFIG = { .google_tokenfile = GOOGLE_OAUTH_TOKEN_FILE, .google_clientid = GOOGLE_OAUTH_CLIENT_ID, .google_secretid = GOOGLE_OAUTH_CLIENT_SECRET, - .google_calendar = "" + .caldav_calendar = "", + .caldav_server = "", + .caldav_username = "", + .caldav_password = "" }; diff --git a/src/utils.h b/src/utils.h @@ -62,8 +62,14 @@ typedef struct char* google_clientid; // Google secret id char* google_secretid; - // Google calendar to synchronize - char* google_calendar; + // CalDAV calendar to synchronize + char* caldav_calendar; + // CalDAV server URI + char* caldav_server; + // CalDAV username + char* caldav_username; + // CalDAV password + char* caldav_password; } config; extern config CONFIG;