diary

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

commit 9c1f1bf60cd3a4c272172ed0bdf12e0d6ed3d3d7
parent 1bb32c4927860dd655f2f65ad1ebc3bcdb06162e
Author: Andreas Gruhler <agruhl@gmx.ch>
Date:   Mon, 10 Jan 2022 00:39:16 +0100

caldav improvements

Diffstat:
Msrc/caldav.c | 185++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/utils.c | 6------
2 files changed, 93 insertions(+), 98 deletions(-)

diff --git a/src/caldav.c b/src/caldav.c @@ -1,13 +1,13 @@ #include "caldav.h" CURL *curl; -char* access_token; -char* refresh_token; -int token_ttl; +char access_token[200] = ""; +char refresh_token[200] = ""; +int token_ttl = 0; // Local bind address for receiving OAuth callbacks. // Reserve 2 chars for the ipv6 square brackets. -char ip[INET6_ADDRSTRLEN], ipstr[INET6_ADDRSTRLEN+2]; +char ip[INET6_ADDRSTRLEN], ipstr[INET6_ADDRSTRLEN + 2]; /* Write a random code challenge of size len to dest */ void random_code_challenge(size_t len, char* dest) { @@ -22,7 +22,7 @@ void random_code_challenge(size_t len, char* dest) { for (i = 0; i < len; i++) { dest[i] = alphabet[rand() % alphabet_size]; } - dest[len-1] = '\0'; + dest[len] = '\0'; } static size_t curl_write_mem_callback(void * contents, size_t size, size_t nmemb, void *userp) { @@ -70,7 +70,6 @@ char* extract_oauth_code(char* http_header) { if (strstr(res, "code") != NULL) { res = strtok(res, "="); // code key res = strtok(NULL, "&"); // code value - fprintf(stderr, "Code: %s\n", res); break; } res = strtok(NULL, " "); @@ -88,7 +87,7 @@ char* read_tokenfile() { free(tokenfile_path); if (token_file == NULL) { - perror("Failed to open tokenfile"); + perror("Warning - failed to open tokenfile in extract_oauth_code"); return NULL; } @@ -101,7 +100,9 @@ char* read_tokenfile() { fread(token_buf, sizeof(char), token_bytes, token_file); token_buf[token_bytes] = '\0'; - access_token = extract_json_value(token_buf, "access_token", true); + char* new_access_token = extract_json_value(token_buf, "access_token", true); + strncpy(access_token, new_access_token, 200); + free(new_access_token); // program segfaults if NULL value is provided to atoi char* token_ttl_str = extract_json_value(token_buf, "expires_in", false); @@ -110,18 +111,16 @@ char* read_tokenfile() { } else { token_ttl = atoi(token_ttl_str); } + free(token_ttl_str); // only update the existing refresh token if the request actually // contained a valid refresh_token, i.e, if it was the initial // interactive authZ request from token code confirmed by the user char* new_refresh_token = extract_json_value(token_buf, "refresh_token", true); if (new_refresh_token != NULL) { - refresh_token = new_refresh_token; + strncpy(refresh_token, new_refresh_token, 200); + free(new_refresh_token); } - - fprintf(stderr, "Access token: %s\n", access_token); - fprintf(stderr, "Token TTL: %i\n", token_ttl); - fprintf(stderr, "Refresh token: %s\n", refresh_token); } else { perror("malloc failed"); return NULL; @@ -133,10 +132,9 @@ char* read_tokenfile() { void write_tokenfile() { char* tokenfile_path = expand_path(CONFIG.google_tokenfile); FILE* tokenfile = fopen(tokenfile_path, "wb"); - free(tokenfile_path); if (tokenfile == NULL) { - perror("Failed to open tokenfile"); + perror("Warning - failed to open tokenfile in write_tokenfile()"); } else { char contents[1000]; char* tokenfile_contents = "{\n" @@ -155,27 +153,30 @@ void write_tokenfile() { chmod(tokenfile_path, S_IRUSR|S_IWUSR); perror("chmod"); - char* tokfile = read_tokenfile(); - fprintf(stderr, "New tokenfile contents: %s\n", tokfile); - fprintf(stderr, "New Access token: %s\n", access_token); - fprintf(stderr, "New Token TTL: %i\n", token_ttl); - fprintf(stderr, "Refresh token: %s\n", refresh_token); free(tokfile); + free(tokenfile_path); } void get_access_token(char* code, char* verifier, bool refresh) { CURLcode res; char* tokfile; - char postfields[500]; + char* postfields_refresh = "client_id=%s&client_secret=%s&grant_type=refresh_token&refresh_token=%s"; + char* postfields_code = "client_id=%s&client_secret=%s&code=%s&code_verifier=%s&grant_type=authorization_code&redirect_uri=http://%s:%i"; + char* postfields; + if (refresh) { - sprintf(postfields, "client_id=%s&client_secret=%s&grant_type=refresh_token&refresh_token=%s", + // work with refresh token, subtract 3 placholders + postfields = calloc(strlen(postfields_refresh) - 6 + strlen(CONFIG.google_clientid) + strlen(CONFIG.google_secretid) + strlen(refresh_token) + 1, sizeof(char)); + sprintf(postfields, postfields_refresh, CONFIG.google_clientid, CONFIG.google_secretid, refresh_token); } else { - sprintf(postfields, "client_id=%s&client_secret=%s&code=%s&code_verifier=%s&grant_type=authorization_code&redirect_uri=http://%s:%i", + // work with auth code, subtract 6 placeholders + postfields = calloc(strlen(postfields_code) - 12 + strlen(CONFIG.google_clientid) + strlen(CONFIG.google_secretid) + strlen(code) + strlen(verifier) + strlen(ipstr) + 1, sizeof(char)); + sprintf(postfields, postfields_code, CONFIG.google_clientid, CONFIG.google_secretid, code, @@ -201,7 +202,7 @@ void get_access_token(char* code, char* verifier, bool refresh) { free(tokenfile_path); if (tokenfile == NULL) { - perror("Failed to open tokenfile"); + perror("Warning - failed to open tokenfile in get_access_token()"); return; } curl_easy_setopt(curl, CURLOPT_WRITEDATA, tokenfile); @@ -224,6 +225,8 @@ void get_access_token(char* code, char* verifier, bool refresh) { // https://developers.google.com/identity/protocols/oauth2/native-app#offline write_tokenfile(); } + + free(postfields); } char* get_oauth_code(const char* verifier, WINDOW* header) { @@ -267,7 +270,6 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { ipstr, GOOGLE_OAUTH_REDIRECT_PORT, CONFIG.google_clientid); - fprintf(stderr, "Google OAuth2 authorization URI: %s\n", uri); // Show the Google OAuth2 authorization URI in the header wclear(header); @@ -303,7 +305,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { pfds[1].events = POLLIN; int fd_count = 2; - int connfd, bytes_rec, bytes_sent; + int connfd, bytes_rec = 0, bytes_sent = 0; char http_header[8*1024]; char* reply = "HTTP/1.1 200 OK\n" @@ -337,8 +339,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { // Ctrl+c: ^C 0x03 // q : q 0x71 if (ch == 0x03 || ch == 0x71) { - fprintf(stderr, "Escape char: %x\n", ch); - fprintf(stderr, "Hanging up, closing server socket\n"); + fprintf(stderr, "Warning - hanging up, closing server socket\n"); break; } } @@ -355,13 +356,11 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { perror("Error reading stream message"); break; } - fprintf(stderr, "Received http header: %s\n", http_header); bytes_sent = send(connfd, reply, strlen(reply), 0); if (bytes_sent < 0) { perror("Error sending"); } - fprintf(stderr, "Bytes sent: %i\n", bytes_sent); close(connfd); break; @@ -371,12 +370,16 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { // close server socket close(pfds[1].fd); + if (bytes_rec == 0) { + fprintf(stderr, "Error - No OAuth header found.\n"); + return NULL; + } + char* code = extract_oauth_code(http_header); if (code == NULL) { - fprintf(stderr, "Found no OAuth code in http header.\n"); + fprintf(stderr, "Error - No OAuth code found in http header.\n"); return NULL; } - fprintf(stderr, "OAuth code: %s\n", code); return code; } @@ -387,11 +390,11 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields return NULL; } - // if access_token is NULL, the program segfaults - // while construcing the bearer_token below - if (access_token == NULL) { - return NULL; - } + // // if access_token is NULL, the program segfaults + // // while construcing the bearer_token below + // if (access_token == NULL) { + // return NULL; + // } CURLcode res; @@ -412,7 +415,6 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method); curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_mem_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&caldav_resp); @@ -433,13 +435,12 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields res = curl_easy_perform(curl); - curl_easy_cleanup(curl); + // free the memory used for the curl slist + curl_slist_free_all(header); - fprintf(stderr, "Curl retrieved %lu bytes\n", (unsigned long)caldav_resp.size); - fprintf(stderr, "Curl content: %s\n", caldav_resp.memory); + curl_easy_cleanup(curl); if (res != CURLE_OK) { - fprintf(stderr, "Curl response: %s\n", caldav_resp.memory); fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return NULL; } @@ -456,14 +457,11 @@ char* parse_caldav_current_user_principal(char* xml) { return NULL; } - fprintf(stderr, "Found current-user-principal at position: %i\n", *xml_key_pos); - //<D:current-user-principal> //<D:href>/caldav/v2/diary.in0rdr%40gmail.com/user</D:href> char* tok = strtok(xml_key_pos, "<"); // D:current-user-principal> if (tok != NULL) { tok = strtok(NULL, "<"); // D:href>/caldav/v2/test%40gmail.com/user - fprintf(stderr, "First token: %s\n", tok); tok = strstr(tok, ">"); // >/caldav/v2/test%40gmail.com/user tok++; // cut > char* tok_end = strrchr(tok, '/'); @@ -477,7 +475,7 @@ char* parse_caldav_current_user_principal(char* xml) { char* parse_caldav_calendar(char* xml, char* calendar) { char displayname_needle[strlen(calendar) + strlen("<D:displayname></D:displayname>")]; sprintf(displayname_needle, "<D:displayname>%s</D:displayname>", calendar); - fprintf(stderr, "Displayname needle: %s\n", displayname_needle); + char* displayname_pos = strstr(xml, displayname_needle); // this XML multistatus response does not contain the users calendar if (displayname_pos == NULL) { @@ -498,12 +496,10 @@ char* parse_caldav_calendar(char* xml, char* calendar) { *displayname_pos= '\0'; char* href = strrstr(xml, "<D:href>"); if (href != NULL) { - fprintf(stderr, "Found calendar href: %s\n", href); href = strtok(href, "<"); // :href>/caldav/v2/aaa%40group.calendar.google.com/events/ if (href != NULL) { href = strchr(href, '>'); href++; // cut > - fprintf(stderr, "Found calendar href: %s\n", href); } return href; } @@ -584,6 +580,9 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar if (response == NULL) { fprintf(stderr, "PUT event failed.\n"); } + + // free memory allocated to store curl response + free(response); } /* @@ -605,11 +604,10 @@ int caldav_sync(struct tm* date, char* tokfile = read_tokenfile(); free(tokfile); - if (access_token == NULL && refresh_token == NULL) { + if (strcmp(access_token, "") == 0) { // no access token exists yet, create new verifier - char challenge[GOOGLE_OAUTH_CODE_VERIFIER_LENGTH]; + char challenge[GOOGLE_OAUTH_CODE_VERIFIER_LENGTH + 1]; random_code_challenge(GOOGLE_OAUTH_CODE_VERIFIER_LENGTH, challenge); - fprintf(stderr, "Challenge/Verifier: %s\n", challenge); // fetch new code with verifier char* code = get_oauth_code(challenge, header); @@ -632,9 +630,9 @@ 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); - fprintf(stderr, "User principal: %s\n", user_principal); if (user_principal == NULL) { + free(user_principal); fprintf(stderr, "Unable to fetch principal, refreshing API token.\n"); // The principal could not be fetched, // get new acess token with refresh token @@ -658,28 +656,22 @@ int caldav_sync(struct tm* date, wgetch(header); echo(); - free(access_token); - access_token = NULL; - free(refresh_token); - refresh_token = NULL; pthread_cancel(progress_tid); wclear(header); return -1; } - user_principal = parse_caldav_current_user_principal(user_principal); - fprintf(stderr, "\nUser Principal: %s\n", user_principal); + char* current_user_principal = parse_caldav_current_user_principal(user_principal); // get the home-set of the user char uri[300]; - sprintf(uri, "%s%s", GOOGLE_API_URI, user_principal); - fprintf(stderr, "\nHome Set URI: %s\n", uri); + 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); - fprintf(stderr, "\nHome Set: %s\n", home_set); // get calendar URI from the home-set char* calendar_href = parse_caldav_calendar(home_set, CONFIG.google_calendar); - fprintf(stderr, "\nCalendar href: %s\n", calendar_href); char* xml_filter = "<c:calendar-query xmlns:d='DAV:' xmlns:c='urn:ietf:params:xml:ns:caldav'>" "<d:prop><c:calendar-data/></d:prop>" @@ -705,13 +697,13 @@ int caldav_sync(struct tm* date, sprintf(caldata_postfields, xml_filter, dstr_cursor, dstr_next_day); - fprintf(stderr, "Calendar data postfields:\n%s\n", caldata_postfields); // fetch event for the cursor date sprintf(uri, "%s%s", GOOGLE_API_URI, calendar_href); - fprintf(stderr, "\nCalendar URI: %s\n", uri); + // free memory allocated to store curl response + free(home_set); + char* event = caldav_req(date, uri, "REPORT", caldata_postfields, 0); - fprintf(stderr, "Event:\n%s", event); // todo: warn if multiple events, // multistatus has more than just one caldav:calendar-data elements @@ -719,18 +711,22 @@ int caldav_sync(struct tm* date, char path[100]; char* ppath = path; fpath(CONFIG.dir, strlen(CONFIG.dir), date, &ppath, sizeof path); - fprintf(stderr, "Cursor date file path: %s\n", path); - bool local_file_exists = true; - bool remote_file_exists = true; + struct tm* localfile_time; + + time_t epoch_zero = 0; // check last modification time of local time struct stat attr; if (stat(path, &attr) != 0) { perror("Stat failed"); - local_file_exists = false; + // local file does probably not exist + localfile_time = localtime(&epoch_zero); + } else { + // remember utc time of file on disk + localfile_time = localtime(&attr.st_mtime); } - struct tm* localfile_time = gmtime(&attr.st_mtime); + fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst); // set to negative value, so mktime uses timezone information and system databases // to attempt to determine whether DST is in effect at the specified time @@ -738,7 +734,7 @@ int caldav_sync(struct tm* date, time_t localfile_date = mktime(localfile_time); fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst); fprintf(stderr, "Local file last modified time: %s\n", ctime(&localfile_date)); - fprintf(stderr, "Local file last modified time: %s\n", ctime(&attr.st_mtime)); + // fprintf(stderr, "Local file last modified time: %s\n", ctime(&attr.st_mtime)); struct tm remote_datetime; time_t remote_date; @@ -749,7 +745,8 @@ int caldav_sync(struct tm* date, char* remote_uid = extract_ical_field(event, "UID", &search_pos, false); if (remote_last_mod == NULL || remote_uid == NULL) { - remote_file_exists = false; + // remote file does probably not exist, set remote date to 1970 + remote_date = epoch_zero; } else { fprintf(stderr, "Remote last modified: %s\n", remote_last_mod); fprintf(stderr, "Remote UID: %s\n", remote_uid); @@ -760,29 +757,35 @@ int caldav_sync(struct tm* date, fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst); fprintf(stderr, "Remote last modified: %s\n", ctime(&remote_date)); free(remote_last_mod); + free(remote_uid); } - if (! (local_file_exists || remote_file_exists)) { - fprintf(stderr, "Neither local nor remote file exists, giving up.\n"); + double timediff = difftime(localfile_date, remote_date); + fprintf(stderr, "Info - time diff between local and remote mod time:%e\n", timediff); + + + char* rmt_desc; + char dstr[16]; + int conf_ch = 0; + + if (timediff == 0) { + fprintf(stderr, "Warning - local and remote files have equal timestamp or don't exist, giving up.\n"); pthread_cancel(progress_tid); wclear(header); + // free memory allocated to store curl response + free(event); return 0; - } - - double timediff = difftime(localfile_date, remote_date); - fprintf(stderr, "Time diff between local and remote mod time:%e\n", timediff); - - if (local_file_exists && (timediff > 0 || !remote_file_exists)) { + } else if (timediff > 0) { // local time > remote time // if local file mod time more recent than LAST-MODIFIED - if (remote_file_exists) { - // purge any existing daily calendar entries on the remote side - char event_uri[300]; - sprintf(event_uri, "%s%s%s.ics", GOOGLE_API_URI, calendar_href, remote_uid); + // if (remote_last_mod == NULL || remote_uid == NULL) { + // // purge any existing daily calendar entries on the remote side + // char event_uri[300]; + // sprintf(event_uri, "%s%s%s.ics", GOOGLE_API_URI, calendar_href, remote_uid); - caldav_req(date, event_uri, "DELETE", "", 0); - } + // caldav_req(date, event_uri, "DELETE", "", 0); + // } fputs("Local file is newer, uploading to remote...\n", stderr); put_event(date, dir, dir_size, uri); @@ -790,12 +793,7 @@ int caldav_sync(struct tm* date, pthread_cancel(progress_tid); wclear(header); - } - - char* rmt_desc; - char dstr[16]; - int conf_ch = 0; - if (remote_file_exists && (timediff < 0 || !local_file_exists)) { + } else if (timediff < 0) { rmt_desc = extract_ical_field(event, "DESCRIPTION", &search_pos, true); fprintf(stderr, "Remote event description:%s\n", rmt_desc); @@ -857,5 +855,8 @@ int caldav_sync(struct tm* date, curs_set(0); free(rmt_desc); } + + // free memory allocated to store curl response + free(event); return conf_ch; } diff --git a/src/utils.c b/src/utils.c @@ -94,12 +94,6 @@ char* fold(const char* str) { bool esc = false; while(*i != '\0' || esc) { - fprintf(stderr, "strlen(buf): %i\n", bufl); - fprintf(stderr, "*i: %c\n", *i); - fprintf(stderr, "escch: %c\n", escch); - fprintf(stderr, "esc: %i\n", esc); - // fprintf(stderr, "buffer: %s\n\n", buf); - newbuf = realloc(buf, ++bufl); if (newbuf == NULL) { perror("realloc failed");