diary

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

commit e75cbc27e901841a9297ce4eed3100b9668e797d
parent bca4f026e08975ee6c0a9ae1bdcd81f021b1a6bc
Author: Andreas Gruhler <agruhl@gmx.ch>
Date:   Wed, 16 Jun 2021 23:29:32 +0200

fold TEXT

Diffstat:
Msrc/caldav.c | 61+++++++++++++++++++++++++++++++++++++++----------------------
Msrc/utils.c | 162++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 170 insertions(+), 53 deletions(-)

diff --git a/src/caldav.c b/src/caldav.c @@ -121,6 +121,9 @@ char* read_tokenfile() { 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; } fclose(token_file); return token_buf; @@ -395,6 +398,10 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields // https://curl.se/libcurl/c/getinmemory.html struct curl_mem_chunk caldav_resp; caldav_resp.memory = malloc(1); + if (caldav_resp.memory == NULL) { + perror("malloc failed"); + return NULL; + } caldav_resp.size = 0; if (curl) { @@ -426,11 +433,11 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields curl_easy_cleanup(curl); - //fprintf(stderr, "Curl retrieved %lu bytes\n", (unsigned long)caldav_resp.size); - //fprintf(stderr, "Curl content: %s\n", caldav_resp.memory); + fprintf(stderr, "Curl retrieved %lu bytes\n", (unsigned long)caldav_resp.size); + fprintf(stderr, "Curl content: %s\n", caldav_resp.memory); if (res != CURLE_OK) { - //fprintf(stderr, "Curl response: %s\n", caldav_resp.memory); + fprintf(stderr, "Curl response: %s\n", caldav_resp.memory); fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return NULL; } @@ -447,7 +454,7 @@ char* parse_caldav_current_user_principal(char* xml) { return NULL; } - //fprintf(stderr, "Found current-user-principal at position: %i\n", *xml_key_pos); + 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> @@ -489,12 +496,12 @@ 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); + 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); + fprintf(stderr, "Found calendar href: %s\n", href); } return href; } @@ -521,15 +528,28 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar descr_bytes = ftell(fp); rewind(fp); - descr = malloc(descr_bytes); - if (descr == NULL) perror("Error allocating space for description"); + size_t descr_labell = strlen("DESCRIPTION:"); + size_t descrl = descr_bytes + descr_labell; + descr = malloc(descrl); + if (descr == NULL) { + perror("malloc failed"); + return; + } + + descr[0] = '\0'; + strcat(descr, "DESCRIPTION:"); - fread(descr, sizeof(char), descr_bytes, fp); - descr[descr_bytes] = '\0'; + int items_read = fread(descr + descr_labell, sizeof(char), descr_bytes, fp); + if (items_read != descr_bytes) { + fprintf(stderr, "Read %i items but expected %li, aborting.", items_read, descr_bytes); + return; + } + + descr[descrl] = '\0'; fprintf(stderr, "File buffer that will be uploaded to the remote CalDAV server:\n%s\n", descr); char* folded_descr = fold(descr); - fprintf(stderr, "Folded descr:\n%s\n", folded_descr); + fprintf(stderr, "Folded descr:%s\n", folded_descr); char uid[9]; strftime(uid, sizeof uid, "%Y%m%d", date); @@ -539,7 +559,7 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar "UID:%s\n" "DTSTART;VALUE=DATE:%s\n" "SUMMARY:%s\n" - "DESCRIPTION:%s\n" + "%s\n" "END:VEVENT\n" "END:VCALENDAR"; char postfields[strlen(ics) + strlen(folded_descr) + 100]; @@ -547,7 +567,7 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar uid, uid, uid, // todo: display first few chars of DESCRIPTION as SUMMARY - folded_descr); //todo: fold multiline descriptions + folded_descr); fprintf(stderr, "PUT event postfields:\n%s\n", postfields); @@ -661,7 +681,7 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons sprintf(uri, "%s%s", GOOGLE_API_URI, user_principal); 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); + 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); @@ -720,9 +740,9 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst); //local_time->tm_isdst = -1; time_t localfile_date = mktime(localfile_time); - //fprintf(stderr, "Local dst: %i\n", localfile_time->tm_isdst); + 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; @@ -737,7 +757,7 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons } else { strptime(remote_last_mod, "%Y%m%dT%H%M%SZ", &remote_datetime); //remote_datetime.tm_isdst = -1; - //fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst); + fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst); remote_date = mktime(&remote_datetime); fprintf(stderr, "Remote dst: %i\n", remote_datetime.tm_isdst); fprintf(stderr, "Remote last modified: %s\n", ctime(&remote_date)); @@ -750,9 +770,6 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons return; } - // TODO: check SEQUENCE:24 ? - // update sequence on upload - double timediff = difftime(localfile_date, remote_date); fprintf(stderr, "Time diff between local and remote mod time:%e\n", timediff); @@ -768,7 +785,7 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons caldav_req(date, event_uri, "DELETE", "", 0); } - fprintf(stderr, "Local file is newer, uploading to remote...\n"); + fputs("Local file is newer, uploading to remote...\n", stderr); put_event(date, dir, dir_size, uri); pthread_cancel(progress_tid); @@ -784,7 +801,7 @@ void caldav_sync(struct tm* date, WINDOW* header, WINDOW* cal, int pad_pos, cons fprintf(stderr, "Remote event description:%s\n", rmt_desc); if (rmt_desc == NULL) { - fprintf(stderr, "Failed to fetch description of remote event. Might be empty.\n"); + fprintf(stderr, "Could not fetch description of remote event.\n"); pthread_cancel(progress_tid); wclear(header); return; diff --git a/src/utils.c b/src/utils.c @@ -15,7 +15,11 @@ void update_date(WINDOW* header, struct tm* curs_date) { char* extract_json_value(const char* json, char* key, bool quoted) { // work on a copy of the json - char* jsoncp = (char*) malloc(strlen(json) * sizeof(char)); + char* jsoncp = (char*) malloc(strlen(json) * sizeof(char) + 1); + if (jsoncp == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(jsoncp, json); char* tok = strtok(jsoncp, " "); @@ -37,7 +41,11 @@ char* extract_json_value(const char* json, char* key, bool quoted) { char* res = NULL; if (tok != NULL) { - res = (char*) malloc(strlen(tok) * sizeof(char)); + res = (char*) malloc(strlen(tok) * sizeof(char) + 1); + if (res == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(res, tok); } @@ -47,49 +55,128 @@ char* extract_json_value(const char* json, char* key, bool quoted) { char* fold(const char* str) { // work on a copy of the str - int strl = strlen(str); - char* strcp = (char *) malloc(strl * sizeof(char)); + char* strcp = (char *) malloc(strlen(str) * sizeof(char) + 1); + if (strcp == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(strcp, str); - if (strlen(strcp) <= 75) return strcp; - + // create buffer for escaped result TEXT char* buf = malloc(1); - buf = '\0'; - size_t bufl = 0; - - // break lines after 75 chars - for (char* i = strcp; i-strcp < strl; i += 75) { - // split between any two characters by inserting a CRLF - // immediately followed by a white space character - fprintf(stderr, "next size: %li\n", bufl + 77); - fprintf(stderr, "buf: %s\n", buf); - buf = realloc(buf, bufl + 77); - if (buf == NULL) { + if (buf == NULL) { + perror("malloc failed"); + return NULL; + } + buf[0] = '\0'; + + void* newbuf; + // bufl is the current buffer size incl. \0 + int bufl = 1; + // i is the iterator in strcp + char* i = strcp; + // escch is the char to be escaped, + // only written when esc=true + char escch; + 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"); - return buf; + free(buf); + return NULL; + } + buf = (char*) newbuf; + + if ((bufl > 1) && ((bufl % 77) == 0)) { + // break lines after 75 chars + // split between any two characters by inserting a CRLF + // immediately followed by a white space character + buf[bufl-2] = '\n'; + escch = ' '; + esc = true; + continue; + } + + if (esc) { + // only escape char, do not advance iterator i + buf[bufl-2] = escch; + esc = false; + } else { + // escape characters + // https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.11 + switch (*i) { + case 0x5c: // backslash + buf[bufl-2] = 0x5c; + escch = 0x5c; + esc = true; + break; + case ';': + buf[bufl-2] = 0x5c; + escch = ';'; + esc = true; + break; + case ',': + buf[bufl-2] = 0x5c; + escch = ','; + esc = true; + break; + case '\n': + buf[bufl-2] = 0x5c; + escch = 'n'; + esc = true; + break; + default: + // write regular character from strcp + buf[bufl-2] = *i; + break; + } + i++; } - strncat(buf, i, 75); - buf[i - strcp] = '\n'; - buf[i - strcp + 1] = ' '; - bufl = strlen(buf); + + // terminate the char string in any case (esc or not) + buf[bufl-1] = '\0'; } + fprintf(stderr, "escch: %c\n", escch); + fprintf(stderr, "end: %c\n", buf[bufl]); + free(strcp); return buf; } char* unfold(const char* str) { - if (strcmp(str, "\n")) return NULL; + fprintf(stderr, "Before unfolding: %s\n", str); + //if (strcmp(str, "")) { + // fputs("Unfold string is empty.\n", stderr); + // return NULL; + //} // work on a copy of the str - char* strcp = (char *) malloc(strlen(str) * sizeof(char)); + char* strcp = (char *) malloc(strlen(str) * sizeof(char) + 1); + if (strcp == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(strcp, str); char* res = strtok(strcp, "\n"); - char* buf = malloc(strlen(res)); + char* buf = malloc(strlen(res) + 1); + if (buf == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(buf, res); + char* newbuf; regex_t re; regmatch_t pm[1]; @@ -107,12 +194,14 @@ char* unfold(const char* str) { break; } - buf = realloc(buf, strlen(buf) + strlen(res)); - if (buf != NULL) { - strcat(buf, res + 1); - } else { + newbuf = realloc(buf, strlen(buf) + strlen(res) + 1); + if (buf == NULL) { perror("realloc failed"); + free(buf); return NULL; + } else { + buf = newbuf; + strcat(buf, res + 1); } } @@ -134,7 +223,11 @@ char* extract_ical_field(const char* ics, char* key, bool multiline) { } // work on a copy of the ical xml response - char* icscp= (char *) malloc(strlen(ics) * sizeof(char)); + char* icscp = (char *) malloc(strlen(ics) * sizeof(char) + 1); + if (icscp == NULL) { + perror("malloc failed"); + return NULL; + } strcpy(icscp, ics); // tokenize ical by newlines @@ -145,7 +238,13 @@ char* extract_ical_field(const char* ics, char* key, bool multiline) { res = strstr(res, ":"); // value res++; // strip the ":" - if (multiline) { + fprintf(stderr, "Extracted ical result value: %s\n", res); + fprintf(stderr, "Extracted ical result size: %li\n", strlen(res)); + + if (strlen(res) == 0) { + // empty remote description + res = NULL; + } else if (multiline) { res = unfold(ics + (res - icscp)); } break; @@ -153,6 +252,7 @@ char* extract_ical_field(const char* ics, char* key, bool multiline) { // key not in this line, advance line res = strtok(NULL, "\n"); } + fprintf(stderr, "Sizeof ics: %li\n", strlen(ics)); free(icscp); return res;