+
+ if (success) {
+ *result = user_creds;
+ } else {
+ free_user_creds(user_creds);
+ }
+
+ return success;
+}
+
+static boolean
+save_user(FILE *fp, const char *user, const char *access, const char *refresh,
+ long expires_at)
+{
+ if (access != NULL) {
+ if (fprintf(fp, "access-%s: %s\n", user, access) < 0) return FALSE;
+ }
+ if (refresh != NULL) {
+ if (fprintf(fp, "refresh-%s: %s\n", user, refresh) < 0) return FALSE;
+ }
+ if (expires_at > 0) {
+ if (fprintf(fp, "expire-%s: %ld\n", user, (long)expires_at) < 0) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+boolean
+mh_oauth_cred_save(FILE *fp, mh_oauth_cred *cred, const char *user)
+{
+ struct user_creds *user_creds;
+ int fd = fileno(fp);
+ size_t i;
+
+ /* Load existing creds if any. */
+ if (!load_creds(&user_creds, fp, cred->ctx)) {
+ return FALSE;
+ }
+
+ if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) goto err;
+ if (ftruncate(fd, 0) < 0) goto err;
+ if (fseek(fp, 0, SEEK_SET) < 0) goto err;
+
+ /* Write all creds except for this user. */
+ for (i = 0; i < user_creds->len; i++) {
+ mh_oauth_cred *c = &user_creds->creds[i];
+ if (strcmp(c->user, user) == 0) continue;
+ if (!save_user(fp, c->user, c->access_token, c->refresh_token,
+ c->expires_at)) {
+ goto err;
+ }
+ }
+
+ /* Write updated creds for this user. */
+ if (!save_user(fp, user, cred->access_token, cred->refresh_token,
+ cred->expires_at)) {
+ goto err;
+ }
+
+ free_user_creds(user_creds);
+
+ return TRUE;
+
+ err:
+ free_user_creds(user_creds);
+ set_err(cred->ctx, MH_OAUTH_CRED_FILE);
+ return FALSE;