]> diplodocus.org Git - nmh/blobdiff - uip/mhlogin.c
lock_file.c: close(2) file descriptor on failure, avoiding leak.
[nmh] / uip / mhlogin.c
index 4fa10e1e68fd7541d96cf4a9166b33f4116f8265..e78ae68f263991a9d7faff77ac52be5ef33df461 100644 (file)
@@ -1,22 +1,27 @@
-/*
- * mhlogin.c -- login to external (OAuth) services
+/* mhlogin.c -- login to external (OAuth) services
  *
  * This code is Copyright (c) 2014, by the authors of nmh.  See the
  * COPYRIGHT file in the root directory of the nmh distribution for
  * complete copyright information.
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 
 #include <h/mh.h>
+#include <h/utils.h>
 #include <h/oauth.h>
+#include "sbr/lock_file.h"
 
 #define MHLOGIN_SWITCHES \
-    X("oauth", 1, OAUTHSW) \
-    X("snoop", 1, SNOOPSW) \
-    X("help", 1, HELPSW) \
-    X("version", 1, VERSIONSW) \
+    X("user username", 0, USERSW) \
+    X("saslmech", 0, SASLMECHSW) \
+    X("authservice", 0, AUTHSERVICESW) \
+    X("browser", 0, BROWSERSW) \
+    X("snoop", 0, SNOOPSW) \
+    X("help", 0, HELPSW) \
+    X("version", 0, VERSIONSW) \
 
 #define X(sw, minchars, id) id,
 DEFINE_SWITCH_ENUM(MHLOGIN);
@@ -31,18 +36,17 @@ DEFINE_SWITCH_ARRAY(MHLOGIN, switches);
 static char *
 geta (void)
 {
-    char *cp;
     static char line[BUFSIZ];
 
     if (fgets(line, sizeof(line), stdin) == NULL)
        done (1);
-    if ((cp = strchr(line, '\n')))
-       *cp = 0;
+    trim_suffix_c(line, '\n');
+
     return line;
 }
 
 static int
-do_login(const char *svc, int snoop)
+do_login(const char *svc, const char *user, const char *browser, int snoop)
 {
     char *fn, *code;
     mh_oauth_ctx *ctx;
@@ -52,53 +56,78 @@ do_login(const char *svc, int snoop)
     const char *url;
 
     if (svc == NULL) {
-        adios(NULL, "only support -oauth gmail");
+        adios(NULL, "missing -authservice switch");
+    }
+
+    if (user == NULL) {
+        adios(NULL, "missing -user switch");
     }
 
     if (!mh_oauth_new(&ctx, svc)) {
-        adios(NULL, mh_oauth_get_err_string(ctx));
+        adios(NULL, "%s", mh_oauth_get_err_string(ctx));
     }
 
     if (snoop) {
         mh_oauth_log_to(stderr, ctx);
     }
 
-    fn = getcpy(mh_oauth_cred_fn(ctx));
+    fn = mh_xstrdup(mh_oauth_cred_fn(svc));
 
     if ((url = mh_oauth_get_authorize_url(ctx)) == NULL) {
-      adios(NULL, mh_oauth_get_err_string(ctx));
+      adios(NULL, "%s", mh_oauth_get_err_string(ctx));
     }
 
-    printf("Load the following URL in your browser and authorize nmh"
-           " to access %s:\n"
-           "\n%s\n\n"
-           "Enter the authorization code: ",
-           mh_oauth_svc_display_name(ctx), url);
+    if (browser) {
+        char *command = concat(browser, " '", url, "'", NULL);
+        int status = OK;
+
+        printf("Follow the prompts in your browser to authorize nmh"
+               " to access %s.\n",
+               mh_oauth_svc_display_name(ctx));
+        sleep(1);
+
+        status = system(command);
+        free(command);
+
+        if (status != OK) {
+            adios ((char *) browser, "SYSTEM");
+        }
+    } else {
+        printf("Load the following URL in your browser and authorize nmh"
+               " to access %s:\n\n%s\n\n",
+               mh_oauth_svc_display_name(ctx), url);
+    }
+    printf("Enter the authorization code: ");
     fflush(stdout);
     code = geta();
 
-    while ((cred = mh_oauth_authorize(code, ctx)) == NULL
-           && mh_oauth_get_err_code(ctx) == MH_OAUTH_BAD_GRANT) {
-      printf("Code rejected; try again? ");
+    while (!*code ||
+           ((cred = mh_oauth_authorize(code, ctx)) == NULL
+            && mh_oauth_get_err_code(ctx) == MH_OAUTH_BAD_GRANT)) {
+      printf(!*code  ?  "Empty code; try again? "  :  "Code rejected; try again? ");
       fflush(stdout);
       code = geta();
     }
     if (cred == NULL) {
-      advise(NULL, "error exchanging code for OAuth2 token");
-      adios(NULL, mh_oauth_get_err_string(ctx));
+      inform("error exchanging code for OAuth2 token");
+      adios(NULL, "%s", mh_oauth_get_err_string(ctx));
     }
 
-    cred_file = lkfopendata(fn, "w", &failed_to_lock);
+    cred_file = lkfopendata(fn, "r+", &failed_to_lock);
+    if (cred_file == NULL && errno == ENOENT) {
+        cred_file = lkfopendata(fn, "w+", &failed_to_lock);
+    }
     if (cred_file == NULL || failed_to_lock) {
       adios(fn, "oops");
     }
-    if (!mh_oauth_cred_save(cred_file, cred)) {
-      adios(NULL, mh_oauth_get_err_string(ctx));
+    if (!mh_oauth_cred_save(cred_file, cred, user)) {
+      adios(NULL, "%s", mh_oauth_get_err_string(ctx));
     }
     if (lkfclosedata(cred_file, fn) != 0) {
       adios (fn, "oops");
     }
 
+    free(fn);
     mh_oauth_cred_free(cred);
     mh_oauth_free(ctx);
 
@@ -110,7 +139,7 @@ int
 main(int argc, char **argv)
 {
     char *cp, **argp, **arguments;
-    char *svc = NULL;
+    const char *user = NULL, *saslmech = NULL, *svc = NULL, *browser = NULL;
     int snoop = 0;
 
     if (nmh_init(argv[0], 1)) { return 1; }
@@ -129,7 +158,7 @@ main(int argc, char **argv)
                adios (NULL, "-%s unknown", cp);
 
            case HELPSW:
-               snprintf(help, sizeof(help), "%s -oauth gmail [switches]",
+               snprintf(help, sizeof(help), "%s [switches]",
                          invo_name);
                print_help (help, switches, 1);
                done (0);
@@ -137,10 +166,24 @@ main(int argc, char **argv)
                print_version(invo_name);
                done (0);
 
-            case OAUTHSW:
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                    svc = cp;
+           case USERSW:
+               if (!(user = *argp++) || *user == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case SASLMECHSW:
+               if (!(saslmech = *argp++) || *saslmech == '-')
+                   adios (NULL, "missing argument to %s", argp[-2]);
+               continue;
+
+           case AUTHSERVICESW:
+                if (!(svc = *argp++) || *svc == '-')
+                    adios (NULL, "missing argument to %s", argp[-2]);
+                continue;
+
+           case BROWSERSW:
+                if (!(browser = *argp++) || *browser == '-')
+                    adios (NULL, "missing argument to %s", argp[-2]);
                 continue;
 
             case SNOOPSW:
@@ -151,10 +194,17 @@ main(int argc, char **argv)
         adios(NULL, "extraneous arguments");
     }
 
+    if (saslmech  &&  strcasecmp(saslmech, "xoauth2")) {
+        /* xoauth is assumed */
+        adios(NULL, "only -saslmech xoauth2 is supported");
+    }
+    free(arguments);
+
 #ifdef OAUTH_SUPPORT
-    return do_login(svc, snoop);
+    return do_login(svc, user, browser, snoop);
 #else
     NMH_UNUSED(svc);
+    NMH_UNUSED(browser);
     NMH_UNUSED(snoop);
     adios(NULL, "not built with OAuth support");
     return 1;