commit 9c1f1bf60cd3a4c272172ed0bdf12e0d6ed3d3d7
parent 1bb32c4927860dd655f2f65ad1ebc3bcdb06162e
Author: Andreas Gruhler <agruhl@gmx.ch>
Date: Mon, 10 Jan 2022 00:39:16 +0100
caldav improvements
Diffstat:
M | src/caldav.c | | | 185 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
M | src/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");