commit a1024d94542fb39c56b1bf6422367f49dc930989
parent 8604d4895b93cf133b74b89a821db223efdbe98e
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date: Sun, 1 Sep 2024 18:59:08 +0200
feat(caldav): oauth with oauth_eval_cmd
Diffstat:
11 files changed, 225 insertions(+), 665 deletions(-)
diff --git a/Makefile b/Makefile
@@ -9,9 +9,7 @@ MANDIR := $(DESTDIR)$(PREFIX)/share/man
MAN1 = man1/diary.1
CC = gcc
-CFLAGS = -Wall \
- -DGOOGLE_OAUTH_CLIENT_ID=\"$(GOOGLE_OAUTH_CLIENT_ID)\" \
- -DGOOGLE_OAUTH_CLIENT_SECRET=\"$(GOOGLE_OAUTH_CLIENT_SECRET)\"
+CFLAGS = -Wall
UNAME = ${shell uname}
ifeq ($(UNAME),FreeBSD)
diff --git a/README.md b/README.md
@@ -75,18 +75,7 @@ Server = https://downloadcontent.opensuse.org/repositories/home:/in0rdr/Arch/$ar
```
## Build from Source
-1. Define [OAuth2 application credentials](https://developers.google.com/identity/protocols/oauth2)
- if CalDAV sync should be effective:
-
- ```
- export GOOGLE_OAUTH_CLIENT_ID=""
- export GOOGLE_OAUTH_CLIENT_SECRET=""
- ```
-
- Alternatively, leave these two variables unset and define the
- clientid/secret in the configuration file at run-time (see diary man page).
-
-2. The compilation step requires development libraries for `ncurses`, `libcurl`
+1. The compilation step requires development libraries for `ncurses`, `libcurl`
and `liblttng-ust`. These packages might have a slightly different naming
scheme depending on the distribution. To compile, run:
@@ -96,7 +85,7 @@ Server = https://downloadcontent.opensuse.org/repositories/home:/in0rdr/Arch/$ar
Note: for \*BSD users run gmake.
-3. Install the binary (optional):
+2. Install the binary (optional):
```
sudo make install
diff --git a/config/diary.cfg b/config/diary.cfg
@@ -18,12 +18,9 @@ editor =
#caldav_server =
# Calendar name for CalDAV sync
#caldav_calendar =
-# Google OAuth2 clientid and secretid
-#google_clientid =
-#google_secretid =
-# Google OAuth2 tokenfile
-#google_tokenfile = ~/.diary-token
# CalDAV server username
#caldav_username =
# CalDAV server password
#caldav_password =
+# OAuth command to fetch access token. For example, "oama access"
+#oauth_eval_cmd =
diff --git a/man1/diary.1 b/man1/diary.1
@@ -5,7 +5,7 @@
.nh
.ad l
.\" Begin generated content:
-.TH "DIARY" "1" "2024-06-22"
+.TH "DIARY" "1" "2024-09-01"
.PP
.SH NAME
diary - text-based journaling program
@@ -298,15 +298,12 @@ editor =
#caldav_server =
# Calendar name for CalDAV sync
#caldav_calendar =
-# Google OAuth2 clientid and secretid
-#google_clientid =
-#google_secretid =
-# Google OAuth2 tokenfile
-#google_tokenfile = ~/\&.diary-token
# CalDAV server username
#caldav_username =
# CalDAV server password
#caldav_password =
+# OAuth command to fetch access token\&. For example, "oama access"
+#oauth_eval_cmd =
EOF
.fi
.RE
@@ -320,8 +317,8 @@ cp config/diary\&.cfg ${XDG_CONFIG_HOME:-~/\&.config}/diary/
.RE
.PP
The file "${XDG_CONFIG_HOME:-~/.\&config}/diary/diary.\&cfg" should adhere to a
-basic "key = value" format.\& Lines can be commented with the special characters
-"#"or ";".\&
+basic "key = value" format.\& The value is read until the end of the line '\&\en'\&.\&
+Lines can be commented with the special characters "#"or ";".\&
.PP
The following configuration keys are currently supported:
.PP
@@ -339,8 +336,6 @@ l lx lx lx lx
l lx lx lx lx
l lx lx lx lx
l lx lx lx lx
-l lx lx lx lx
-l lx lx lx lx
l lx lx lx lx.
T{
\fBCommand Line Option\fR
@@ -446,11 +441,11 @@ n/a
T} T{
caldav_server
T} T{
-Google calendar "https://apidata.\&googleusercontent.\&com/caldav/v2", Yahoo calendar "https://caldav.\&calendar.\&yahoo.\&com", GMX "https://caldav.\&gmx.\&net" or other CalDAV compatible provider
+Google calendar "https://apidata.\&googleusercontent.\&com/caldav/v2", Yahoo calendar "https://caldav.\&calendar.\&yahoo.\&com", GMX "https://caldav.\&gmx.\&net" or other CalDAV compatible provider.\&
T} T{
(empty)
T} T{
-CalDAV server for \fBCALDAV SYNC\fR
+CalDAV server for \fBCALDAV SYNC\fR (required).\&
T}
T{
n/a
@@ -461,62 +456,40 @@ diary
T} T{
(empty)
T} T{
-Calendar name for \fBCALDAV SYNC\fR
+Calendar name for \fBCALDAV SYNC\fR (required).\&
T}
T{
n/a
T} T{
-google_clientid
-T} T{
-
-T} T{
-built-in at build time (see \fBGOOGLE CALENDAR OAUTH2\fR) or empty
-T} T{
-Google Calendar for \fBGOOGLE CALENDAR OAUTH2\fR clientid
-T}
-T{
-n/a
-T} T{
-google_secretid
+caldav_username
T} T{
T} T{
-built-in at build time (see \fBGOOGLE CALENDAR OAUTH2\fR) or empty
-T} T{
-Google Calendar for \fBGOOGLE CALENDAR OAUTH2\fR secretid
-T}
-T{
-n/a
-T} T{
-google_tokenfile
-T} T{
-~/.\&diary-token
-T} T{
-~/.\&diary-token
+(empty)
T} T{
-\fBGOOGLE CALENDAR OAUTH2\fR API token file location
+CalDAV server username for \fBCALDAV SYNC\fR (optional).\&
T}
T{
n/a
T} T{
-caldav_username
+caldav_password
T} T{
T} T{
(empty)
T} T{
-CalDAV server username for \fBCALDAV SYNC\fR
+CalDAV server password for \fBCALDAV SYNC\fR (optional).\&
T}
T{
n/a
T} T{
-caldav_password
+oauth_eval_cmd
T} T{
-
+"oama access <email>" or "pass"
T} T{
(empty)
T} T{
-CalDAV server password for \fBCALDAV SYNC\fR
+OAuth command to fetch access token for \fBCALDAV SYNC\fR (optional).\&
T}
.TE
.sp 1
@@ -562,9 +535,25 @@ calendar for syncing purposes and not to have multiple events for a day on the
remote calendar.\& The diary only considers the first event of a given day.\&
.PP
The calender for synchronization can be defined with the configuration key
-"caldav_calendar", see \fBCONFIGURATION FILE\fR.\& This key is empty by default.\&
+"caldav_calendar".\& This key is empty by default.\& It is required to configure at
+least following parameters in the \fBCONFIGURATION FILE\fR:
+.PP
+.PD 0
+.IP \(bu 4
+caldav_server: Calendar server API
+.IP \(bu 4
+caldav_calendar: Calendar name for CalDAV sync
+.PD
+.PP
+In addition, it is required to specifiy either "caldav_username" and
+"caldav_password" for basicauth (takes precedence) OR the "oauth_eval_cmd" to
+retreive an OAuth access token from a password manager or tool that prints the
+token to stdout.\& Leave username/password empty if you intend to use OAuth
+("oauth_eval_cmd").\&
.PP
-Read GOOGLE CALENDAR OAUTH2 to setup authorization with the Google server.\&
+Read GOOGLE CALENDAR OAUTH2 to setup authorization with the Google server.\& Other
+CalDAV servers and APIs that are secured with OAuth2 might work as well
+(untested).\&
.PP
Use the EXPORT/IMPORT functionality for more batch oriented processing.\& Making
a network request and deciding the most recently modified entry (local/remote)
@@ -580,11 +569,11 @@ The Google Calendar CalDAV API is protected with OAuth2:
.PP
https://developers.\&google.\&com/identity/protocols/oauth2
.PP
-The credentials and the consent screen can be redefined at compile time (export
-"GOOGLE_OAUTH_CLIENT_ID"/"GOOGLE_OAUTH_CLIENT_SECRET" to environment) or during
-runtime with the keys "google_clientid"/"google_secretid" in the \fBCONFIGURATION
-FILE\fR.\& The token used to authenticate with the Google API is stored in the file
-specified by "google_tokenfile" and renewed automatically.\&
+The "application", the "consent screen" and the "credentials" (client id and
+secret) need to be defined in the Google cloud console.\& Client id and secret
+should be stored in an utility application that can fetch and refresh the OAuth
+access token (e.\&g.\&, "oama").\& The command to access the access token
+("oauth_eval_cmd") should be configured in the \fBCONFIGURATION FILE\fR.\&
.PP
The application requires two OAuth2 scopes
(https://developers.\&google.\&com/calendar/api/auth) for CalDAV requests:
diff --git a/man1/diary.1.html b/man1/diary.1.html
@@ -240,15 +240,12 @@ editor =
#caldav_server =
# Calendar name for CalDAV sync
#caldav_calendar =
-# Google OAuth2 clientid and secretid
-#google_clientid =
-#google_secretid =
-# Google OAuth2 tokenfile
-#google_tokenfile = ~/.diary-token
# CalDAV server username
#caldav_username =
# CalDAV server password
#caldav_password =
+# OAuth command to fetch access token. For example, "oama access"
+#oauth_eval_cmd =
EOF</pre>
</div>
<p class="Pp">To copy the sample file from the source repository:</p>
@@ -257,8 +254,9 @@ EOF</pre>
<pre>cp config/diary.cfg ${XDG_CONFIG_HOME:-~/.config}/diary/</pre>
</div>
<p class="Pp">The file "${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg"
- should adhere to a basic "key = value" format. Lines can be
- commented with the special characters "#"or ";".</p>
+ should adhere to a basic "key = value" format. The value is read
+ until the end of the line '\n'. Lines can be commented with the special
+ characters "#"or ";".</p>
<p class="Pp">The following configuration keys are currently supported:</p>
<p class="Pp"></p>
<table class="tbl" border="1" style="border-style: solid;">
@@ -359,51 +357,38 @@ EOF</pre>
"https://apidata.googleusercontent.com/caldav/v2", Yahoo
calendar "https://caldav.calendar.yahoo.com", GMX
"https://caldav.gmx.net" or other CalDAV compatible
- provider</td>
+ provider.</td>
<td>(empty)</td>
- <td>CalDAV server for <b>CALDAV SYNC</b></td>
+ <td>CalDAV server for <b>CALDAV SYNC</b> (required).</td>
</tr>
<tr>
<td>n/a</td>
<td>caldav_calendar</td>
<td>diary </td>
<td>(empty)</td>
- <td>Calendar name for <b>CALDAV SYNC</b></td>
+ <td>Calendar name for <b>CALDAV SYNC</b> (required).</td>
</tr>
<tr>
<td>n/a</td>
- <td>google_clientid</td>
- <td></td>
- <td>built-in at build time (see <b>GOOGLE CALENDAR OAUTH2</b>) or empty</td>
- <td>Google Calendar for <b>GOOGLE CALENDAR OAUTH2</b> clientid</td>
- </tr>
- <tr>
- <td>n/a</td>
- <td>google_secretid</td>
+ <td>caldav_username</td>
<td></td>
- <td>built-in at build time (see <b>GOOGLE CALENDAR OAUTH2</b>) or empty</td>
- <td>Google Calendar for <b>GOOGLE CALENDAR OAUTH2</b> secretid</td>
- </tr>
- <tr>
- <td>n/a</td>
- <td>google_tokenfile</td>
- <td>~/.diary-token</td>
- <td>~/.diary-token</td>
- <td><b>GOOGLE CALENDAR OAUTH2</b> API token file location</td>
+ <td>(empty)</td>
+ <td>CalDAV server username for <b>CALDAV SYNC</b> (optional).</td>
</tr>
<tr>
<td>n/a</td>
- <td>caldav_username</td>
+ <td>caldav_password</td>
<td></td>
<td>(empty)</td>
- <td>CalDAV server username for <b>CALDAV SYNC</b></td>
+ <td>CalDAV server password for <b>CALDAV SYNC</b> (optional).</td>
</tr>
<tr>
<td>n/a</td>
- <td>caldav_password</td>
- <td></td>
+ <td>oauth_eval_cmd</td>
+ <td>"oama access <email>" or "pass"</td>
<td>(empty)</td>
- <td>CalDAV server password for <b>CALDAV SYNC</b></td>
+ <td>OAuth command to fetch access token for <b>CALDAV SYNC</b>
+ (optional).</td>
</tr>
</table>
<p class="Pp"></p>
@@ -459,10 +444,23 @@ EOF</pre>
day on the remote calendar. The diary only considers the first event of a
given day.</p>
<p class="Pp">The calender for synchronization can be defined with the
- configuration key "caldav_calendar", see <b>CONFIGURATION
- FILE</b>. This key is empty by default.</p>
+ configuration key "caldav_calendar". This key is empty by default.
+ It is required to configure at least following parameters in the
+ <b>CONFIGURATION FILE</b>:</p>
+<p class="Pp"></p>
+<ul class="Bl-bullet">
+ <li>caldav_server: Calendar server API</li>
+ <li>caldav_calendar: Calendar name for CalDAV sync</li>
+</ul>
+<p class="Pp">In addition, it is required to specifiy either
+ "caldav_username" and "caldav_password" for basicauth
+ (takes precedence) OR the "oauth_eval_cmd" to retreive an OAuth
+ access token from a password manager or tool that prints the token to
+ stdout. Leave username/password empty if you intend to use OAuth
+ ("oauth_eval_cmd").</p>
<p class="Pp">Read GOOGLE CALENDAR OAUTH2 to setup authorization with the Google
- server.</p>
+ server. Other CalDAV servers and APIs that are secured with OAuth2 might
+ work as well (untested).</p>
<p class="Pp">Use the EXPORT/IMPORT functionality for more batch oriented
processing. Making a network request and deciding the most recently modified
entry (local/remote) takes quite some time for large batches.</p>
@@ -476,14 +474,13 @@ EOF</pre>
<p class="Pp">https://console.cloud.google.com/apis/api/caldav.googleapis.com</p>
<p class="Pp">The Google Calendar CalDAV API is protected with OAuth2:</p>
<p class="Pp">https://developers.google.com/identity/protocols/oauth2</p>
-<p class="Pp">The credentials and the consent screen can be redefined at compile
- time (export
- "GOOGLE_OAUTH_CLIENT_ID"/"GOOGLE_OAUTH_CLIENT_SECRET" to
- environment) or during runtime with the keys
- "google_clientid"/"google_secretid" in the
- <b>CONFIGURATION</b> <b>FILE</b>. The token used to authenticate with the
- Google API is stored in the file specified by "google_tokenfile"
- and renewed automatically.</p>
+<p class="Pp">The "application", the "consent screen" and
+ the "credentials" (client id and secret) need to be defined in the
+ Google cloud console. Client id and secret should be stored in an utility
+ application that can fetch and refresh the OAuth access token (e.g.,
+ "oama"). The command to access the access token
+ ("oauth_eval_cmd") should be configured in the <b>CONFIGURATION
+ FILE</b>.</p>
<p class="Pp">The application requires two OAuth2 scopes
(https://developers.google.com/calendar/api/auth) for CalDAV requests:</p>
<p class="Pp"></p>
@@ -607,7 +604,7 @@ EOF</pre>
</div>
<table class="foot">
<tr>
- <td class="foot-date">2024-06-22</td>
+ <td class="foot-date">2024-09-01</td>
<td class="foot-os"></td>
</tr>
</table>
diff --git a/man1/diary.1.scd b/man1/diary.1.scd
@@ -180,15 +180,12 @@ editor =
#caldav_server =
# Calendar name for CalDAV sync
#caldav_calendar =
-# Google OAuth2 clientid and secretid
-#google_clientid =
-#google_secretid =
-# Google OAuth2 tokenfile
-#google_tokenfile = ~/.diary-token
# CalDAV server username
#caldav_username =
# CalDAV server password
#caldav_password =
+# OAuth command to fetch access token. For example, "oama access"
+#oauth_eval_cmd =
EOF
```
@@ -199,8 +196,8 @@ cp config/diary.cfg ${XDG_CONFIG_HOME:-~/.config}/diary/
```
The file "${XDG_CONFIG_HOME:-~/.config}/diary/diary.cfg" should adhere to a
-basic "key = value" format. Lines can be commented with the special characters
-"#"or ";".
+basic "key = value" format. The value is read until the end of the line '\\n'.
+Lines can be commented with the special characters "#"or ";".
The following configuration keys are currently supported:
@@ -274,39 +271,29 @@ The following configuration keys are currently supported:
: caldav_server
: Google calendar "https://apidata.googleusercontent.com/caldav/v2", Yahoo
calendar "https://caldav.calendar.yahoo.com", GMX "https://caldav.gmx.net" or
- other CalDAV compatible provider
+ other CalDAV compatible provider.
: (empty)
-: CalDAV server for *CALDAV SYNC*
+: CalDAV server for *CALDAV SYNC* (required).
| n/a
: caldav_calendar
: diary
: (empty)
-: Calendar name for *CALDAV SYNC*
-| n/a
-: google_clientid
-:
-: built-in at build time (see *GOOGLE CALENDAR OAUTH2*) or empty
-: Google Calendar for *GOOGLE CALENDAR OAUTH2* clientid
-| n/a
-: google_secretid
-:
-: built-in at build time (see *GOOGLE CALENDAR OAUTH2*) or empty
-: Google Calendar for *GOOGLE CALENDAR OAUTH2* secretid
-| n/a
-: google_tokenfile
-: ~/.diary-token
-: ~/.diary-token
-: *GOOGLE CALENDAR OAUTH2* API token file location
+: Calendar name for *CALDAV SYNC* (required).
| n/a
: caldav_username
:
: (empty)
-: CalDAV server username for *CALDAV SYNC*
+: CalDAV server username for *CALDAV SYNC* (optional).
| n/a
: caldav_password
:
: (empty)
-: CalDAV server password for *CALDAV SYNC*
+: CalDAV server password for *CALDAV SYNC* (optional).
+| n/a
+: oauth_eval_cmd
+: "oama access <email>" or "pass"
+: (empty)
+: OAuth command to fetch access token for *CALDAV SYNC* (optional).
# CUSTOM FORMATTING
The preview of the journal entries can be processed with a custom *--fmt-cmd*.
@@ -350,9 +337,21 @@ calendar for syncing purposes and not to have multiple events for a day on the
remote calendar. The diary only considers the first event of a given day.
The calender for synchronization can be defined with the configuration key
-"caldav_calendar", see *CONFIGURATION FILE*. This key is empty by default.
+"caldav_calendar". This key is empty by default. It is required to configure at
+least following parameters in the *CONFIGURATION FILE*:
+
+- caldav_server: Calendar server API
+- caldav_calendar: Calendar name for CalDAV sync
+
+In addition, it is required to specifiy either "caldav_username" and
+"caldav_password" for basicauth (takes precedence) OR the "oauth_eval_cmd" to
+retreive an OAuth access token from a password manager or tool that prints the
+token to stdout. Leave username/password empty if you intend to use OAuth
+("oauth_eval_cmd").
-Read GOOGLE CALENDAR OAUTH2 to setup authorization with the Google server.
+Read GOOGLE CALENDAR OAUTH2 to setup authorization with the Google server. Other
+CalDAV servers and APIs that are secured with OAuth2 might work as well
+(untested).
Use the EXPORT/IMPORT functionality for more batch oriented processing. Making
a network request and deciding the most recently modified entry (local/remote)
@@ -368,11 +367,11 @@ The Google Calendar CalDAV API is protected with OAuth2:
https://developers.google.com/identity/protocols/oauth2
-The credentials and the consent screen can be redefined at compile time (export
-"GOOGLE_OAUTH_CLIENT_ID"/"GOOGLE_OAUTH_CLIENT_SECRET" to environment) or during
-runtime with the keys "google_clientid"/"google_secretid" in the *CONFIGURATION
-FILE*. The token used to authenticate with the Google API is stored in the file
-specified by "google_tokenfile" and renewed automatically.
+The "application", the "consent screen" and the "credentials" (client id and
+secret) need to be defined in the Google cloud console. Client id and secret
+should be stored in an utility application that can fetch and refresh the OAuth
+access token (e.g., "oama"). The command to access the access token
+("oauth_eval_cmd") should be configured in the *CONFIGURATION FILE*.
The application requires two OAuth2 scopes
(https://developers.google.com/calendar/api/auth) for CalDAV requests:
diff --git a/src/caldav.c b/src/caldav.c
@@ -2,29 +2,11 @@
CURL* curl;
char access_token[sizeof(char)*2048] = "";
-char refresh_token[sizeof(char)*512] = "";
-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];
-/* Write a random code challenge of size len to dest */
-void random_code_challenge(size_t len, char* dest) {
- // https://developers.google.com/identity/protocols/oauth2/native-app#create-code-challenge
- // A code_verifier is a random string using characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
- srand(time(NULL));
-
- char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
- size_t alphabet_size = strlen(alphabet);
-
- int i = 0;
- for (i = 0; i < len; i++) {
- dest[i] = alphabet[rand() % alphabet_size];
- }
- dest[len] = '\0';
-}
-
static size_t curl_write_mem_callback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t realsize = size * nmemb;
struct curl_mem_chunk* mem = (struct curl_mem_chunk*)userp;
@@ -53,363 +35,25 @@ void* get_in_addr(struct sockaddr* sa) {
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
-/*
-* Extract OAuth2 code from http header. Reads the code from
-* the http header in http_header and writes the OAuth2 code
-* to the char string pointed to by code.
-*/
-char* extract_oauth_code(char* http_header) {
- // example token: "/?code=&scope="
- char* res = strtok(http_header, " ");
- while (res != NULL) {
- if (strstr(res, "code") != NULL) {
- res = strtok(res, "="); // code key
- res = strtok(NULL, "&"); // code value
- break;
- }
- res = strtok(NULL, " ");
- }
- return res;
-}
-
-/* Read access token from file and refresh global token vars */
-char* read_tokenfile() {
- FILE* token_file;
- char* token_buf;
- long token_bytes;
-
- char* tokenfile_path = expand_path(CONFIG.google_tokenfile);
- token_file = fopen(tokenfile_path, "r");
- free(tokenfile_path);
-
- if (token_file == NULL) {
- perror("Warning - failed to open tokenfile in read_tokenfile()");
- return NULL;
- }
-
- fseek(token_file, 0, SEEK_END);
- token_bytes = ftell(token_file);
- rewind(token_file);
-
- token_buf = malloc(token_bytes + 1);
- if (token_buf != NULL) {
- size_t items_read = fread(token_buf, sizeof(char), token_bytes, token_file);
- if (items_read != token_bytes) {
- perror("Error while reading in read_tokenfile()");
- fclose(token_file);
- return NULL;
- }
- token_buf[token_bytes] = '\0';
-
- update_global_token_vars(token_buf);
- } else {
- perror("malloc failed");
- fclose(token_file);
- 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);
- tracepoint(diary, debug_string, "New access token", new_access_token);
- if (new_access_token != NULL) {
- strncpy(access_token, new_access_token, 250);
- free(new_access_token);
- }
-
- // program segfaults if NULL value is provided to atoi
- char* token_ttl_str = extract_json_value(input, "expires_in", false);
- if (token_ttl_str == NULL) {
- token_ttl = 0;
- } 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(input, "refresh_token", true);
- tracepoint(diary, debug_string, "New refresh token", new_refresh_token);
- if (new_refresh_token != NULL) {
- strncpy(refresh_token, new_refresh_token, 250);
- free(new_refresh_token);
- }
-}
-
-/* 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 update_tokenfile_from_global_vars()");
- } else {
- char contents[sizeof(char)*2048 + sizeof(char)*512 + 200];
- char* tokenfile_contents = "{\n"
- " \"access_token\": \"%s\",\n"
- " \"expires_in\": %i,\n"
- " \"refresh_token\": \"%s\"\n"
- "}\n";
- 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);
- }
- fclose(tokenfile);
-
- if (chmod(tokenfile_path, S_IRUSR|S_IWUSR) == -1) {
- perror("chmod");
- }
-
- 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* 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;
-
- tracepoint(diary, debug, "Getting access token get_access_token()");
-
- if (refresh) {
- // work with refresh token, subtract 3 placholders
- tracepoint(diary, debug_sizet, "strlen(refresh_token)", strlen(refresh_token));
- 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 {
- // work with auth code, subtract 6 placeholders
- tracepoint(diary, debug_sizet, "strlen(postfields_code)", strlen(postfields_code));
- tracepoint(diary, debug_sizet, "strlen(CONFIG.google_clientid)", strlen(CONFIG.google_clientid));
- tracepoint(diary, debug_sizet, "strlen(CONFIG.google_secretid))", strlen(CONFIG.google_secretid));
- tracepoint(diary, debug_sizet, "strlen(code)", strlen(code));
- tracepoint(diary, debug_sizet, "strlen(verifier)", strlen(verifier));
- tracepoint(diary, debug_sizet, "strlen(ipstr)", strlen(ipstr));
- tracepoint(diary, debug_sizet, "strlen(ipstr)", strlen(MKSTR(GOOGLE_OAUTH_REDIRECT_PORT)));
- postfields = calloc(strlen(postfields_code) - 12
- + strlen(CONFIG.google_clientid)
- + strlen(CONFIG.google_secretid)
- + strlen(code)
- + strlen(verifier)
- + strlen(ipstr)
- + strlen(MKSTR(GOOGLE_OAUTH_REDIRECT_PORT)) + 1, sizeof(char));
- sprintf(postfields, postfields_code,
- CONFIG.google_clientid,
- CONFIG.google_secretid,
- code,
- verifier,
- ipstr,
- GOOGLE_OAUTH_REDIRECT_PORT);
- }
-
- curl = curl_easy_init();
-
- // https://curl.se/libcurl/c/getinmemory.html
- struct curl_mem_chunk token_resp;
- token_resp.memory = malloc(1);
- if (token_resp.memory == NULL) {
- perror("malloc failed");
- return;
- }
- token_resp.size = 0;
-
- if (curl) {
- curl_easy_setopt(curl, CURLOPT_URL, GOOGLE_OAUTH_TOKEN_URL);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_mem_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&token_resp);
-
- res = curl_easy_perform(curl);
-
- curl_easy_cleanup(curl);
-
- // 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) {
- // update global token variables
- update_global_token_vars(token_resp.memory);
- update_tokenfile_from_global_vars();
- } else {
- tracepoint(diary, error_string, "curl_easy_perform() failed in get_access_token()", curl_easy_strerror(res));
- }
- }
-
- free(token_resp.memory);
- free(postfields);
-}
-
-/* Perform challenge/request to fetch authorization code */
-char* get_oauth_code(const char* verifier, WINDOW* header) {
- struct addrinfo hints, *addr_res;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
+/* Get access token from "oauth_eval_cmd" and refresh global var */
+void get_access_token() {
+ FILE *fp;
int status;
- if ((status=getaddrinfo(NULL, MKSTR(GOOGLE_OAUTH_REDIRECT_PORT), &hints, &addr_res)) != 0) {
- tracepoint(diary, error_string, "getaddrinfo() failed in get_oauth_code()", gai_strerror(status));
- }
- void *addr;
- char *ipver;
- //todo: extract
- //addr = get_in_addr(addr_res->ai_addr);
- if (addr_res->ai_family == AF_INET) {
- struct sockaddr_in *ipv4 = (struct sockaddr_in *) addr_res->ai_addr;
- addr = &(ipv4->sin_addr);
- ipver = "IPv4";
- } else { // IPv6
- struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) addr_res->ai_addr;
- addr = &(ipv6->sin6_addr);
- ipver = "IPv6";
- }
+ fp = popen(CONFIG.oauth_eval_cmd, "r");
+ if (fp == NULL)
+ perror("Failure running oauth_eval_cmd in get_access_token()");
- inet_ntop(addr_res->ai_family, addr, ip, sizeof ip);
- if (strcmp("IPv6", ipver) == 0) {
- sprintf(ipstr, "[%s]", ip);
- }
+ while (fgets(access_token, sizeof(char)*2048, fp) != NULL)
+ printf("%s", access_token);
- // Show Google OAuth URI
- char uri[500];
- sprintf(uri, "%s?scope=%s&code_challenge=%s&response_type=%s&redirect_uri=http://%s:%i&client_id=%s",
- GOOGLE_OAUTH_AUTHZ_URL,
- GOOGLE_OAUTH_SCOPE,
- verifier,
- GOOGLE_OAUTH_RESPONSE_TYPE,
- ipstr,
- GOOGLE_OAUTH_REDIRECT_PORT,
- CONFIG.google_clientid);
-
- // Show the Google OAuth2 authorization URI in the header
- wclear(header);
- wresize(header, LINES, getmaxx(header));
- mvwprintw(header, 0, 0, "Go to Google OAuth2 authorization URI. Use 'q' or 'Ctrl+c' to quit authorization process.\n%s", uri);
- wrefresh(header);
+ // strip any newline characters
+ access_token[strcspn(access_token, "\n")] = '\0';
- int socketfd = socket(addr_res->ai_family, addr_res->ai_socktype, addr_res->ai_protocol);
- if (socketfd < 0) {
- perror("Error opening socket");
+ status = pclose(fp);
+ if (status == -1) {
+ perror("Failure during pclose in get_access_token()");
}
-
- // reuse socket address
- int yes=1;
- setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
-
- if (bind(socketfd, addr_res->ai_addr, addr_res->ai_addrlen) < 0) {
- perror("Error binding socket");
- }
-
- freeaddrinfo(addr_res);
-
- int ls = listen(socketfd, GOOGLE_OAUTH_REDIRECT_SOCKET_BACKLOG);
- if (ls < 0) {
- perror("Listen error");
- }
-
- struct pollfd pfds[2];
-
- pfds[0].fd = STDIN_FILENO;
- pfds[0].events = POLLIN;
- pfds[1].fd = socketfd;
- pfds[1].events = POLLIN;
- int fd_count = 2;
-
- int connfd, bytes_rec = 0, bytes_sent = 0;
- char http_header[8*1024];
- char* reply =
- "HTTP/1.1 200 OK\n"
- "Content-Type: text/html\n"
- "Connection: close\n\n"
- "<html>"
- "<head><title>Authorization successfull</title></head>"
- "<body>"
- "<p><b>Authorization successfull.</b></p>"
- "<p>You consented that diary can access your Google calendar.<br/>"
- "Please close this window and return to diary.</p>"
- "</body>"
- "</html>";
-
- // Handle descriptors read-to-read (POLLIN),
- // stdin or server socker, whichever is first
- for (;;) {
- int poll_count = poll(pfds, fd_count, -1);
-
- if (poll_count == -1) {
- perror("Erro in poll");
- break;
- }
-
- // Cancel through stdin
- if (pfds[0].revents & POLLIN) {
- noecho();
- int ch = wgetch(header);
- echo();
- // sudo showkey -a
- // Ctrl+c: ^C 0x03
- // q : q 0x71
- if (ch == 0x03 || ch == 0x71) {
- tracepoint(diary, debug, "Hanging up, closing server socket");
- break;
- }
- }
- if (pfds[1].revents & POLLIN) {
- // accept connections but ignore client addr
- connfd = accept(socketfd, NULL, NULL);
- if (connfd < 0) {
- perror("Error accepting connection");
- break;
- }
-
- bytes_rec = recv(connfd, http_header, sizeof http_header, 0);
- if (bytes_rec < 0) {
- perror("Error reading stream message");
- break;
- }
-
- bytes_sent = send(connfd, reply, strlen(reply), 0);
- if (bytes_sent < 0) {
- perror("Error sending");
- }
-
- close(connfd);
- break;
- }
- } // end for ;;
-
- // close server socket
- close(pfds[1].fd);
-
- if (bytes_rec == 0) {
- tracepoint(diary, error, "No OAuth header found");
- return NULL;
- }
-
- char* code = extract_oauth_code(http_header);
- if (code == NULL) {
- tracepoint(diary, error, "No OAuth code in http header");
- return NULL;
- }
-
- char* oauth_code = malloc(strlen(code) + 1);
- strcpy(oauth_code, code);
-
- return oauth_code;
}
/* Make a CalDAV request */
@@ -445,13 +89,14 @@ char* caldav_req(struct tm* date, char* url, char* http_method, char* postfields
// construct header fields
struct curl_slist *header = NULL;
- // default to basic auth when Google credentials are not set
+ // prefer basicauth credentials when set
if (basicauth) {
char basicauth [strlen(CONFIG.caldav_username) + strlen(CONFIG.caldav_password) + 2];
sprintf(basicauth, "%s:%s", CONFIG.caldav_username, CONFIG.caldav_password);
curl_easy_setopt(curl, CURLOPT_USERPWD, basicauth);
tracepoint(diary, debug_string, "curl_easy_perform() basicauth", basicauth);
} else {
+ // use OAuth credentials
char bearer_token[strlen("Authorization: Bearer ") + strlen(access_token) + 1];
sprintf(bearer_token, "Authorization: Bearer %s", access_token);
header = curl_slist_append(header, bearer_token);
@@ -522,7 +167,7 @@ char* parse_caldav_current_user_principal(char* xml) {
}
/* Return home set uri from CalDAV home set XML response */
-char* parse_home_set(char* xml) {
+char* parse_home_set(const char* xml) {
char* homeset_needle = "calendar-home-set";
char* homeset_pos = strstr(xml, homeset_needle);
@@ -541,7 +186,16 @@ char* parse_home_set(char* xml) {
href = strchr(href, '>');
href++; // cut >
}
- return href;
+
+ // return a copy of the href
+ char* res = (char*) malloc(strlen(href) * sizeof(char) + 1);
+ if (res == NULL) {
+ perror("malloc failed in parse_home_set()");
+ return NULL;
+ }
+ strcpy(res, href);
+
+ return res;
}
return NULL;
}
@@ -554,25 +208,34 @@ char* parse_caldav_calendar(char* xml, char* calendar) {
char* displayname_pos = strstr(xml, displayname_needle);
// this XML multistatus response does not contain the users calendar
if (displayname_pos == NULL) {
- tracepoint(diary, debug, "parse_caldav_calenar() displayname_pos was null");
+ tracepoint(diary, debug, "parse_caldav_calendar() displayname_pos was null");
return NULL;
}
- tracepoint(diary, debug_string, "parse_caldav_calenar() displayname_pos", displayname_pos);
+ tracepoint(diary, debug_string, "parse_caldav_calendar() displayname_pos", displayname_pos);
// shorten multistatus response and find last hyperlink
- *displayname_pos= '\0';
+ *displayname_pos = '\0';
- tracepoint(diary, debug_string, "parse_caldav_calenar() shortened multistatus response", xml);
+ tracepoint(diary, debug_string, "parse_caldav_calendar() shortened multistatus response", xml);
char* href = strrstr(xml, "href>/");
if (href != NULL) {
- tracepoint(diary, debug_string, "parse_caldav_calenar() last href", href);
+ tracepoint(diary, debug_string, "parse_caldav_calendar() last href", href);
href = strtok(href, "<"); // :href>/caldav/v2/aaa%40group.calendar.google.com/events/
- tracepoint(diary, debug_string, "parse_caldav_calenar() last href strtok", href);
+ tracepoint(diary, debug_string, "parse_caldav_calendar() last href strtok", href);
if (href != NULL) {
href = strchr(href, '>');
href++; // cut >
}
- return href;
+
+ // return a copy of the href
+ char* res = (char*) malloc(strlen(href) * sizeof(char) + 1);
+ if (res == NULL) {
+ perror("malloc failed in parse_caldav_calendar");
+ return NULL;
+ }
+ strcpy(res, href);
+
+ return res;
}
return NULL;
}
@@ -611,7 +274,7 @@ void put_event(struct tm* date, const char* dir, size_t dir_size, char* calendar
size_t items_read = fread(descr + descr_label_size, sizeof(char), descr_bytes, fp);
if (items_read != descr_bytes) {
- perror("Error while reading in read_tokenfile()");
+ perror("Error while reading in put_event()");
fclose(fp);
return;
}
@@ -672,52 +335,27 @@ int caldav_sync(struct tm* date,
bool confirm) {
pthread_t progress_tid;
- char* info_txt;
-
- bool google_oauth_enabled = !(strcmp(CONFIG.google_clientid, "") == 0 || strcmp(CONFIG.google_clientid, "") == 0);
+ bool oauth_enabled = !(strcmp(CONFIG.oauth_eval_cmd, "") == 0);
bool basicauth_enabled = !(strcmp(CONFIG.caldav_username, "") == 0 || strcmp(CONFIG.caldav_password, "") == 0);
+ tracepoint(diary, debug_int, "oauth_enabled: ", oauth_enabled);
+ tracepoint(diary, debug_int, "basicauth_enabled", basicauth_enabled);
- if (strcmp(CONFIG.caldav_server, "") == 0 || strcmp(CONFIG.caldav_calendar, "") == 0 || !(google_oauth_enabled || basicauth_enabled)) {
+ // check remote server and calendar are defined
+ if (strcmp(CONFIG.caldav_server, "") == 0 || strcmp(CONFIG.caldav_calendar, "") == 0) {
tracepoint(diary, debug_string, "CONFIG.caldav_server", CONFIG.caldav_server);
tracepoint(diary, debug_string, "CONFIG.caldav_calendar", CONFIG.caldav_calendar);
tracepoint(diary, debug_string, "CONFIG.caldav_username", CONFIG.caldav_username);
tracepoint(diary, debug_string, "CONFIG.caldav_password", CONFIG.caldav_password);
- wclear(header);
- wresize(header, LINES, getmaxx(header));
- info_txt = "Missing sync parameters. Configure CalDAV server and credentials.\n"
- "Press any key to continue.";
- mvwaddstr(header, 0, 0, info_txt);
- wrefresh(header);
-
- // accept any input to proceed
- noecho();
- wgetch(header);
- echo();
- wclear(header);
+ show_info(header, "CalDAV config incomplete, press any key to continue.", NULL);
+ return -1;
}
- if (google_oauth_enabled) {
- // fetch existing API tokens from file
- char* tokfile = read_tokenfile();
- free(tokfile);
-
+ if (oauth_enabled) {
if (strcmp(access_token, "") == 0) {
- // no access token exists yet, create new verifier
- char challenge[GOOGLE_OAUTH_CODE_VERIFIER_LENGTH + 1];
- random_code_challenge(GOOGLE_OAUTH_CODE_VERIFIER_LENGTH, challenge);
-
- // fetch new code with verifier
- char* code = get_oauth_code(challenge, header);
- if (code == NULL) {
- tracepoint(diary, error, "Error retrieving oauth code in caldav_sync()");
- return -1;
- }
-
- // get acess token using code and verifier
- tracepoint(diary, debug, "Fetching access token with code challenge");
- get_access_token(code, challenge, false);
-
- free(code);
+ // get acess token with oauth_eval_cmd
+ tracepoint(diary, debug_string, "Fetching access token using oauth_eval_cmd", CONFIG.oauth_eval_cmd);
+ get_access_token();
+ tracepoint(diary, debug_string, "OAuth access token", access_token);
}
}
@@ -729,39 +367,13 @@ int caldav_sync(struct tm* date,
"</d:propfind>";
- // check if we can use the token from the tokenfile
char* user_principal = caldav_req(date, CONFIG.caldav_server, "PROPFIND", principal_postfields, 0, basicauth_enabled, "application/xml", 1);
tracepoint(diary, debug_string, "User principal", user_principal);
if (user_principal == NULL) {
// The principal could not be fetched
- tracepoint(diary, debug, "Unable to fetch principal");
- if (google_oauth_enabled) {
- // get new acess token with refresh token
- tracepoint(diary, debug, "Fetching access token with refresh token");
- get_access_token(NULL, NULL, true);
- // Retry request for event with new token
- user_principal = caldav_req(date, CONFIG.caldav_server, "PROPFIND", principal_postfields, 0, basicauth_enabled, "application/xml", 1);
- }
- }
-
- if (user_principal == NULL) {
- pthread_cancel(progress_tid);
- wclear(header);
- wresize(header, LINES, getmaxx(header));
- // TODO: this could also be wrong basicauth credentials
- info_txt = "Offline, corrupted or otherwise invalid OAuth2 credential tokenfile.\n"
- "Go online or delete tokenfile '%s' and restart diary to retry.\n"
- "Press any key to continue.";
- mvwprintw(header, 0, 0, info_txt, CONFIG.google_tokenfile);
- wrefresh(header);
-
- // accept any input to proceed
- noecho();
- wgetch(header);
- echo();
-
- wclear(header);
+ tracepoint(diary, debug, "Unable to fetch user principal");
+ show_info(header, "Unable to fetch user principal, press any key to continue.", &progress_tid);
return -1;
}
@@ -770,9 +382,7 @@ int caldav_sync(struct tm* date,
// extract host without path as basis for further api paths
char *caldav_host; char* caldav_host_scheme;
- CURLU *h = curl_url();
- CURLUcode uc = curl_url_set(h, CURLUPART_URL, CONFIG.caldav_server, 0);
- uc = curl_url_get(h, CURLUPART_HOST, &caldav_host, 0);
+ CURLU *h = curl_url(); CURLUcode uc = curl_url_set(h, CURLUPART_URL, CONFIG.caldav_server, 0); uc = curl_url_get(h, CURLUPART_HOST, &caldav_host, 0);
uc = curl_url_get(h, CURLUPART_SCHEME, &caldav_host_scheme, 0);
tracepoint(diary, debug_string, "Caldav server host name", caldav_host);
tracepoint(diary, debug_string, "Caldav server scheme/protocol", caldav_host_scheme);
@@ -796,27 +406,17 @@ int caldav_sync(struct tm* date,
char* home_set = caldav_req(date, uri, "PROPFIND", homeset_request, 0, basicauth_enabled, "application/xml", 0);
tracepoint(diary, debug_string, "Home set xml", home_set);
- if (home_set == NULL) {
- pthread_cancel(progress_tid);
- wclear(header);
- wresize(header, LINES, getmaxx(header));
- info_txt = "Error while fetching home set in caldav_sync()\n"
- "Press any key to continue.";
- mvwaddstr(header, 0, 0, info_txt);
- wrefresh(header);
-
- // accept any input to proceed
- noecho();
- wgetch(header);
- echo();
-
- wclear(header);
- return -1;
- }
// parse home set from xml response
char* home_set_parsed = parse_home_set(home_set);
+ tracepoint(diary, debug_string, "home_set_parsed", home_set_parsed);
+ free(home_set);
+ if (home_set_parsed == NULL) {
+ show_info(header, "Error while fetching home set in caldav_sync, press any key to continue", &progress_tid);
+ return -1;
+ }
sprintf(uri, "%s://%s%s", caldav_host_scheme, caldav_host, home_set_parsed);
+ free(home_set_parsed);
char* calendar_request = "<d:propfind xmlns:d='DAV:' xmlns:cs='http://calendarserver.org/ns/' xmlns:c='urn:ietf:params:xml:ns:caldav'>"
" <d:prop>"
@@ -834,6 +434,16 @@ int caldav_sync(struct tm* date,
// get calendar URI from the home-set
char* calendar_href = parse_caldav_calendar(calendar_xml, CONFIG.caldav_calendar);
free(calendar_xml);
+ tracepoint(diary, debug_string, "calendar_href", calendar_href);
+
+ if (calendar_href == NULL) {
+ tracepoint(diary, debug_string, "Could not find CalDAV calendar", CONFIG.caldav_calendar);
+ char* msg_fmtstr = "Could not find CalDAV calendar '%s', press any key to continue.";
+ char msg[strlen(msg_fmtstr) + strlen(CONFIG.caldav_calendar)];
+ sprintf(msg, msg_fmtstr, CONFIG.caldav_calendar);
+ show_info(header, msg, &progress_tid);
+ return -1;
+ }
char* xml_filter = "<c:calendar-query xmlns:d='DAV:' xmlns:c='urn:ietf:params:xml:ns:caldav'>"
"<d:prop><d:getetag/><c:calendar-data/></d:prop>"
@@ -864,14 +474,9 @@ int caldav_sync(struct tm* date,
// DTSTAMP, UID and DESCRIPTION (from the first event)
if (event == NULL) {
- // Event not found. The curl request probably failed due to one of the missing input parameters:
- // - Google Client ID
- // - Google Secret ID
- // - Google Calendar Name
tracepoint(diary, debug, "Event not found");
pthread_cancel(progress_tid);
wclear(header);
- free(home_set);
return -1;
}
@@ -933,7 +538,6 @@ int caldav_sync(struct tm* date,
curl_free(caldav_host);
free(event);
free(remote_uid);
- free(home_set);
return 0;
} else if (timediff > 0) {
tracepoint(diary, debug, "Local file is newer. Uploading to remote.");
@@ -963,7 +567,6 @@ int caldav_sync(struct tm* date,
curl_free(caldav_host);
free(event);
free(remote_uid);
- free(home_set);
return -1;
}
@@ -1024,6 +627,8 @@ int caldav_sync(struct tm* date,
curl_free(caldav_host);
free(event);
free(remote_uid);
- free(home_set); // home_set was required for sprintfing calendar_href to event_uri
+ free(calendar_href);
+ pthread_join(progress_tid, NULL);
+ wclear(header);
return conf_ch;
}
diff --git a/src/caldav.h b/src/caldav.h
@@ -22,16 +22,6 @@
#define STR(s) #s
#define MKSTR(s) STR(s)
-// https://developers.google.com/identity/protocols/oauth2/native-app#create-code-challenge
-// A valid code_verifier has a length between 43 and 128 characters
-#define GOOGLE_OAUTH_CODE_VERIFIER_LENGTH 43
-#define GOOGLE_OAUTH_AUTHZ_URL "https://accounts.google.com/o/oauth2/auth"
-#define GOOGLE_OAUTH_TOKEN_URL "https://oauth2.googleapis.com/token"
-#define GOOGLE_OAUTH_SCOPE "https://www.googleapis.com/auth/calendar%20https://www.googleapis.com/auth/calendar.events"
-#define GOOGLE_OAUTH_RESPONSE_TYPE "code"
-#define GOOGLE_OAUTH_REDIRECT_PORT 9004
-#define GOOGLE_OAUTH_REDIRECT_SOCKET_BACKLOG 10
-
int caldav_sync(struct tm* date,
WINDOW* header,
WINDOW* cal,
@@ -39,7 +29,6 @@ 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;
diff --git a/src/diary.c b/src/diary.c
@@ -322,7 +322,7 @@ bool read_config(const char* file_path) {
// ignore comment lines
if (*line == '#' || *line == ';') continue;
- if (sscanf(line, "%79s = %79s", key_buf, value_buf) == 2) {
+ if (sscanf(line, "%79s = %79[^\n]", key_buf, value_buf) == 2) {
if (strcmp("dir", key_buf) == 0) {
expanded_value = expand_path(value_buf);
CONFIG.dir = (char *) malloc(strlen(expanded_value) + 1 * sizeof(char));
@@ -352,17 +352,6 @@ bool read_config(const char* file_path) {
} else if (strcmp("editor", key_buf) == 0) {
CONFIG.editor = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
strcpy(CONFIG.editor, value_buf);
- } else if (strcmp("google_tokenfile", key_buf) == 0) {
- expanded_value = expand_path(value_buf);
- CONFIG.google_tokenfile = (char *) malloc(strlen(expanded_value) + 1 * sizeof(char));
- strcpy(CONFIG.google_tokenfile, expanded_value);
- free(expanded_value);
- } else if (strcmp("google_clientid", key_buf) == 0) {
- CONFIG.google_clientid = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
- strcpy(CONFIG.google_clientid, value_buf);
- } else if (strcmp("google_secretid", key_buf) == 0) {
- CONFIG.google_secretid = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
- strcpy(CONFIG.google_secretid, value_buf);
} else if (strcmp("caldav_server", key_buf) == 0) {
CONFIG.caldav_server = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
strcpy(CONFIG.caldav_server, value_buf);
@@ -375,7 +364,10 @@ bool read_config(const char* file_path) {
} else if (strcmp("caldav_password", key_buf) == 0) {
CONFIG.caldav_password = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
strcpy(CONFIG.caldav_password, value_buf);
- }
+ } else if (strcmp("oauth_eval_cmd", key_buf) == 0) {
+ CONFIG.oauth_eval_cmd = (char *) malloc(strlen(value_buf) + 1 * sizeof(char));
+ strcpy(CONFIG.oauth_eval_cmd, value_buf);
+ }
}
}
fclose (pfile);
diff --git a/src/utils.c b/src/utils.c
@@ -373,7 +373,6 @@ bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos, struc
return true;
}
-
void* show_progress(void* vargp){
struct timespec timer = { 0, 70000000 };
WINDOW* header = (WINDOW*) vargp;
@@ -394,6 +393,24 @@ void* show_progress(void* vargp){
}
}
+void show_info(WINDOW* w, char* msg, pthread_t* p) {
+ if (p != NULL) {
+ // cancel any progress spinner
+ pthread_cancel(*p);
+ }
+
+ wclear(w);
+ wresize(w, LINES, getmaxx(w));
+ mvwaddstr(w, 0, 0, msg);
+ wrefresh(w);
+
+ // accept any input to proceed
+ noecho();
+ wgetch(w);
+ echo();
+ wclear(w);
+}
+
config CONFIG = {
.dir = NULL,
.range = 1,
@@ -403,11 +420,9 @@ config CONFIG = {
.no_pty = false,
.no_mouse = false,
.editor = "",
- .google_tokenfile = GOOGLE_OAUTH_TOKEN_FILE,
- .google_clientid = GOOGLE_OAUTH_CLIENT_ID,
- .google_secretid = GOOGLE_OAUTH_CLIENT_SECRET,
.caldav_calendar = "",
.caldav_server = "",
.caldav_username = "",
- .caldav_password = ""
+ .caldav_password = "",
+ .oauth_eval_cmd = ""
};
diff --git a/src/utils.h b/src/utils.h
@@ -7,20 +7,13 @@
#include <stdlib.h>
#include <time.h>
#include <string.h>
+#include <pthread.h>
#include <wordexp.h>
#include <stdbool.h>
#include <ncurses.h>
#include "diary-tp.h"
-#define GOOGLE_OAUTH_TOKEN_FILE "~/.diary-token"
-#ifndef GOOGLE_OAUTH_CLIENT_ID
- #define GOOGLE_OAUTH_CLIENT_ID ""
-#endif
-#ifndef GOOGLE_OAUTH_CLIENT_SECRET
- #define GOOGLE_OAUTH_CLIENT_SECRET ""
-#endif
-
#define CAL_WIDTH 21
#define ASIDE_WIDTH 4
#define MAX_MONTH_HEIGHT 6
@@ -36,6 +29,7 @@ char* strrstr(char *haystack, char *needle);
void fpath(const char* dir, size_t dir_size, const struct tm* date, char** rpath, size_t rpath_size);
bool go_to(WINDOW* calendar, WINDOW* aside, time_t date, int* cur_pad_pos, struct tm* curs_date, struct tm* cal_start, struct tm* cal_end);
void* show_progress(void* vargp);
+void show_info(WINDOW* w, char* msg, pthread_t* p);
typedef struct
{
@@ -56,12 +50,6 @@ typedef struct
bool no_mouse;
// Editor to open journal files with
char* editor;
- // File for Google OAuth access token
- char* google_tokenfile;
- // Google client id
- char* google_clientid;
- // Google secret id
- char* google_secretid;
// CalDAV calendar to synchronize
char* caldav_calendar;
// CalDAV server URI
@@ -70,6 +58,8 @@ typedef struct
char* caldav_username;
// CalDAV password
char* caldav_password;
+ // OAuth command to fetch access token
+ char* oauth_eval_cmd;
} config;
extern config CONFIG;