diary

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

commit d4902f6a3a2adc9e5950e6b9b8d21bcbca4dc0ca
parent c437923fc2ea3462ae92f3a368978400020ac539
Author: Andreas Gruhler <agruhl@gmx.ch>
Date:   Thu, 23 Jun 2022 12:11:08 +0200

fix(#3): Only write tokenfile on CURLE_OK

Diffstat:
Msrc/caldav.c | 73++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/caldav.h | 1+
2 files changed, 43 insertions(+), 31 deletions(-)

diff --git a/src/caldav.c b/src/caldav.c @@ -72,6 +72,7 @@ char* extract_oauth_code(char* http_header) { return res; } +/* Read access token from file and refresh global token vars */ char* read_tokenfile() { FILE* token_file; char* token_buf; @@ -95,7 +96,18 @@ char* read_tokenfile() { fread(token_buf, sizeof(char), token_bytes, token_file); token_buf[token_bytes] = '\0'; - char* new_access_token = extract_json_value(token_buf, "access_token", true); + update_global_token_vars(token_buf); + } else { + perror("malloc failed"); + return NULL; + } + fclose(token_file); + return token_buf; +} + +/* Refresh global token variables from input buffer */ +void update_global_token_vars(char* input) { + char* new_access_token = extract_json_value(input, "access_token", true); //fprintf(stderr, "Info - New access token: %s\n", new_access_token); if (new_access_token != NULL) { strncpy(access_token, new_access_token, 200); @@ -103,7 +115,7 @@ char* read_tokenfile() { } // program segfaults if NULL value is provided to atoi - char* token_ttl_str = extract_json_value(token_buf, "expires_in", false); + char* token_ttl_str = extract_json_value(input, "expires_in", false); if (token_ttl_str == NULL) { token_ttl = 0; } else { @@ -114,25 +126,20 @@ char* read_tokenfile() { // 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); + char* new_refresh_token = extract_json_value(input, "refresh_token", true); if (new_refresh_token != NULL) { strncpy(refresh_token, new_refresh_token, 200); free(new_refresh_token); } - } else { - perror("malloc failed"); - return NULL; - } - fclose(token_file); - return token_buf; } -void write_tokenfile() { +/* Persist contents from global token file variables to tokenfile */ +void update_tokenfile_from_global_vars() { char* tokenfile_path = expand_path(CONFIG.google_tokenfile); FILE* tokenfile = fopen(tokenfile_path, "wb"); if (tokenfile == NULL) { - perror("Warning - failed to open tokenfile in write_tokenfile()"); + perror("Warning - failed to open tokenfile in update_tokenfile_from_global_vars()"); } else { char contents[1000]; char* tokenfile_contents = "{\n" @@ -143,6 +150,10 @@ void write_tokenfile() { sprintf(contents, tokenfile_contents, access_token, token_ttl, + // Make sure the refresh token is re-written and persistet + // to the tokenfile for further requests, because this token + // is not returned by the refresh_token call: + // https://developers.google.com/identity/protocols/oauth2/native-app#offline refresh_token); fputs(contents, tokenfile); } @@ -152,15 +163,12 @@ void write_tokenfile() { perror("chmod"); } - char* tokfile = read_tokenfile(); - free(tokfile); free(tokenfile_path); } +/* Get access token with authorization code or refresh token */ void get_access_token(char* code, char* verifier, bool refresh) { CURLcode res; - char* tokfile; - 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; @@ -205,26 +213,26 @@ void get_access_token(char* code, char* verifier, bool refresh) { curl_easy_cleanup(curl); - fputs(token_resp.memory, stderr); + // Only write tokenfile if the token in token_resp.memory looks valid. + // Assume it's valid, when the curl request succeeds (no HTTP errors). + // Otherwise, a valid token in the possisbly already existing file would + // be overwritten. This happens usually during syncing (s/S) while offline. + if (res == CURLE_OK) { + // fputs(token_resp.memory, stderr); - if (res != CURLE_OK) { - fprintf(stderr, "Error - curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + // update global token variables + update_global_token_vars(token_resp.memory); + update_tokenfile_from_global_vars(); + } else { + fprintf(stderr, "Error - curl_easy_perform() failed in get_access_token(): %s\n", curl_easy_strerror(res)); return; } - // update global variables from tokenfile - // this will also init the access_token var the first time - tokfile = read_tokenfile(); - free(tokfile); - // Make sure the refresh token is re-written and persistet - // to the tokenfile for further requests, because this token - // is not returned by the refresh_token call: - // https://developers.google.com/identity/protocols/oauth2/native-app#offline - write_tokenfile(); } free(postfields); } +/* Perform challenge/request to fetch authorization code */ char* get_oauth_code(const char* verifier, WINDOW* header) { struct addrinfo hints, *addr_res; @@ -383,6 +391,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { return oauth_code; } +/* Make a CalDAV request */ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields, int depth) { // only support depths 0 or 1 if (depth < 0 || depth > 1) { @@ -407,6 +416,7 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method); + fprintf(stderr, "caldav_req(): %s\n", url); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_mem_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&caldav_resp); @@ -434,7 +444,7 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields curl_easy_cleanup(curl); if (res != CURLE_OK) { - fprintf(stderr, "Error - curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + fprintf(stderr, "Error - curl_easy_perform() failed in caldav_req(): %s\n", curl_easy_strerror(res)); free(caldav_resp.memory); return NULL; } @@ -443,7 +453,7 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields return caldav_resp.memory; } -// return current user principal from CalDAV XML response +/* Return current user principal from CalDAV XML response */ char* parse_caldav_current_user_principal(char* xml) { char* xml_key_pos = strstr(xml, "<D:current-user-principal>"); // this XML does not contain a user principal at all @@ -465,7 +475,7 @@ char* parse_caldav_current_user_principal(char* xml) { return tok; } -// return calendar uri from CalDAV home set XML response +/* Return calendar uri from CalDAV home set XML response */ 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); @@ -500,6 +510,7 @@ char* parse_caldav_calendar(char* xml, char* calendar) { return NULL; } +/* Upload event to CalDAV server */ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar_uri) { // get entry path char path[100]; @@ -587,7 +598,7 @@ int caldav_sync(struct tm* date, bool confirm) { pthread_t progress_tid; - // fetch existing API tokens + // fetch existing API tokens from file char* tokfile = read_tokenfile(); free(tokfile); diff --git a/src/caldav.h b/src/caldav.h @@ -41,6 +41,7 @@ int caldav_sync(struct tm* date, const char* dir, size_t dir_size, bool confirm); +void update_global_token_vars(char*); struct curl_mem_chunk { char* memory;