diary

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

commit 08f6fdb8f0c4c100cb46ded1b7ad7ec36fa080a8
parent 04ec397be3d57c9eea510b5f7b7f6af937056677
Author: Andreas Gruhler <agruhl@gmx.ch>
Date:   Sat, 22 May 2021 00:52:43 +0200

calculate timediff and download remote

Diffstat:
Mcaldav.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mcaldav.h | 3+++
2 files changed, 126 insertions(+), 25 deletions(-)

diff --git a/caldav.c b/caldav.c @@ -85,7 +85,7 @@ char* read_tokenfile() { char* tokenfile_path = expand_path(CONFIG.google_tokenfile); token_file = fopen(tokenfile_path, "r"); if (token_file == NULL) { - perror("Failed to open tokenfile:"); + perror("Failed to open tokenfile"); } fseek(token_file, 0, SEEK_END); @@ -126,7 +126,7 @@ void write_tokenfile() { char* tokenfile_path = expand_path(CONFIG.google_tokenfile); FILE* tokenfile = fopen(tokenfile_path, "wb"); if (tokenfile == NULL) { - perror("Failed to open tokenfile:"); + perror("Failed to open tokenfile"); } else { char contents[1000]; char* tokenfile_contents = "{\n" @@ -177,12 +177,12 @@ void get_access_token(char* code, char* verifier, bool refresh) { curl_easy_setopt(curl, CURLOPT_URL, GOOGLE_OAUTH_TOKEN_URL); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); tokenfile_path = expand_path(CONFIG.google_tokenfile); tokenfile = fopen(tokenfile_path, "wb"); if (tokenfile == NULL) { - perror("Failed to open tokenfile:"); + perror("Failed to open tokenfile"); } else { curl_easy_setopt(curl, CURLOPT_WRITEDATA, tokenfile); res = curl_easy_perform(curl); @@ -255,6 +255,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { int col; col = getmaxx(header); wresize(header, LINES, col); + // todo: make it a pad for the long link to be clickable? mvwprintw(header, 0, 0, "Go to Google OAuth2 authorization URI. Use 'q' or 'Ctrl+c' to quit authorization process.\n%s", uri); wrefresh(header); @@ -307,7 +308,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { int poll_count = poll(pfds, fd_count, -1); if (poll_count == -1) { - perror("poll"); + perror("Erro in poll"); break; } @@ -362,7 +363,7 @@ char* get_oauth_code(const char* verifier, WINDOW* header) { return code; } -char* caldav_req(struct tm* date, char* url, char* postfields, int depth) { +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) { @@ -381,8 +382,8 @@ char* caldav_req(struct tm* date, char* url, char* postfields, int depth) { if (curl) { // fail if not authenticated, !CURLE_OK curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND"); + //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); @@ -513,7 +514,7 @@ void caldav_sync(struct tm* date, WINDOW* header) { // check if we can use the token from the tokenfile - char* user_principal = caldav_req(date, GOOGLE_CALDAV_URI, principal_postfields, 0); + char* user_principal = caldav_req(date, GOOGLE_CALDAV_URI, "PROPFIND", principal_postfields, 0); if (user_principal == NULL) { fprintf(stderr, "Unable to fetch principal, refreshing API token.\n"); @@ -521,34 +522,131 @@ void caldav_sync(struct tm* date, WINDOW* header) { // get new acess 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, principal_postfields, 0); + user_principal = caldav_req(date, GOOGLE_CALDAV_URI, "PROPFIND", principal_postfields, 0); } - //fprintf(stderr, "\nUser Principal: %s\n\n", user_principal); user_principal = parse_caldav_current_user_principal(user_principal); - fprintf(stderr, "\nUser Principal: %s\n\n", user_principal); + fprintf(stderr, "\nUser Principal: %s\n", 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\n", uri); - char* home_set = caldav_req(date, uri, "", 1); - fprintf(stderr, "\nHome Set: %s\n\n", home_set); + fprintf(stderr, "\nHome Set URI: %s\n", uri); + 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 = parse_caldav_calendar(home_set, CONFIG.google_calendar); - fprintf(stderr, "\nCalendar href: %s\n\n", calendar); + char* calendar_href = parse_caldav_calendar(home_set, CONFIG.google_calendar); + fprintf(stderr, "\nCalendar href: %s\n", calendar_href); // get cursor date - char dstr[16]; - mktime(date); - get_date_str(date, dstr, sizeof dstr, CONFIG.fmt); - fprintf(stderr, "\nCursor date: %s\n\n", dstr); + //char dstr[16]; + //mktime(date); + //strftime(date, dstr, sizeof dstr, CONFIG.fmt); + //fprintf(stderr, "\nCursor date: %s\n\n", dstr); + + char* xml_filter = "<c:calendar-query xmlns:d='DAV:' xmlns:c='urn:ietf:params:xml:ns:caldav'>" + "<d:prop><c:calendar-data/></d:prop>" + "<c:filter><c:comp-filter name='VCALENDAR'>" + "<c:comp-filter name='VEVENT'>" + "<c:time-range start='%s' end='%s'/></c:comp-filter>" + "</c:comp-filter></c:filter></c:calendar-query>"; + + // construct next day from date+1 + time_t date_time = mktime(date); + struct tm* next_day = localtime(&date_time); + next_day->tm_mday++; + mktime(next_day); + + char dstr_cursor[30]; + char dstr_next_day[30]; + + char* format = "%Y%m%dT%H%M%SZ"; + strftime(dstr_cursor, sizeof dstr_cursor, format, date); + strftime(dstr_next_day, sizeof dstr_next_day, format, next_day); + + char caldata_postfields[strlen(xml_filter)+50]; + 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); + char* event = caldav_req(date, uri, "REPORT", caldata_postfields, 0); + fprintf(stderr, "Event:\n%s", event); + + // get path of entry + 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; + + // check last modification time of local time + struct stat attr; + if (stat(path, &attr) != 0) { + perror("Stat failed"); + local_file_exists = false; + } + fprintf(stderr, "File last modified time: %s\n", ctime(&attr.st_mtime)); + + struct tm remote_datetime; + time_t remote_date; + + // check remote LAST-MODIFIED:20210521T212441Z + char* remote_last_mod = strstr(event, "LAST-MODIFIED:"); + if (remote_last_mod == NULL) { + remote_file_exists = false; + } else { + remote_last_mod = strtok(remote_last_mod, ":"); + remote_last_mod = strtok(NULL, "\n"); + strptime(remote_last_mod, format, &remote_datetime); + remote_date = mktime(&remote_datetime); + fprintf(stderr, "Remote last modified: %s\n", ctime(&remote_date)); + } + + if (! (local_file_exists || remote_file_exists)) { + fprintf(stderr, "Neither local nor remote file exists, giving up.\n"); + return; + } + + double timediff = difftime(attr.st_mtime, remote_date); + fprintf(stderr, "Time diff between local and remote mod time:%e\n", timediff); + + + // todo: find out if no remote file exists + // find out when no local file exists, 1970 + // File last modified time: Thu Jan 1 01:00:00 1970 + // Remote last modified: Mon Feb 13 06:31:36 89854 + + if (timediff > 0 && local_file_exists) { + // local time > remote time + // if local file mod time more recent than LAST-MODIFIED + fprintf(stderr, "Local file is newer, uploading to remote...\n"); + //put_event(date); + } + + if (timediff < 0 && remote_file_exists) { + //todo: Warn - really sync? remote is more recent and will overwrite + fprintf(stderr, "Remote file is newer, extracting description from remote...\n"); + char* remote_desc = strstr(event, "DESCRIPTION:"); + remote_desc = strtok(remote_desc, ":"); + remote_desc = strtok(NULL, "\n"); + fprintf(stderr, "Remote event description:%s\n", remote_desc); + + // else persist downloaded buffer to local file + FILE* cursordate_file = fopen(path, "wb"); + if (cursordate_file == NULL) { + perror("Failed to open cursor date file"); + } else { + fprintf(cursordate_file, remote_desc); + } + fclose(cursordate_file); + } - // check LAST-MODIFIED - // if local file mod time more recent than LAST-MODIFIED - //put_event(date); - // else persist downloaded buffer to local file + // todo: update bold face cursor date } diff --git a/caldav.h b/caldav.h @@ -1,11 +1,14 @@ #ifndef DIARY_CALDAV_H #define DIARY_CALDAV_H +#define __USE_XOPEN +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> +#include <sys/stat.h> #include <sys/socket.h> #include <sys/poll.h> #include <netdb.h>