]> diplodocus.org Git - nmh/blob - uip/mhlogin.c
8179a21fdb56492d297503548a45cdb694eb19c3
[nmh] / uip / mhlogin.c
1 /* mhlogin.c -- login to external (OAuth) services
2 *
3 * This code is Copyright (c) 2014, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "h/mh.h"
13 #include "sbr/print_help.h"
14 #include "sbr/error.h"
15 #include "h/done.h"
16 #include "h/utils.h"
17 #include "h/oauth.h"
18 #include "sbr/lock_file.h"
19
20 #define MHLOGIN_SWITCHES \
21 X("user username", 0, USERSW) \
22 X("saslmech", 0, SASLMECHSW) \
23 X("authservice", 0, AUTHSERVICESW) \
24 X("browser", 0, BROWSERSW) \
25 X("snoop", 0, SNOOPSW) \
26 X("help", 0, HELPSW) \
27 X("version", 0, VERSIONSW) \
28
29 #define X(sw, minchars, id) id,
30 DEFINE_SWITCH_ENUM(MHLOGIN);
31 #undef X
32
33 #define X(sw, minchars, id) { sw, minchars, id },
34 DEFINE_SWITCH_ARRAY(MHLOGIN, switches);
35 #undef X
36
37 #ifdef OAUTH_SUPPORT
38 /* XXX copied from install-mh.c */
39 static char *
40 geta (void)
41 {
42 static char line[BUFSIZ];
43
44 if (fgets(line, sizeof(line), stdin) == NULL)
45 done (1);
46 trim_suffix_c(line, '\n');
47
48 return line;
49 }
50
51 static int
52 do_login(const char *svc, const char *user, const char *browser, int snoop)
53 {
54 char *fn, *code;
55 mh_oauth_ctx *ctx;
56 mh_oauth_cred *cred;
57 FILE *cred_file;
58 int failed_to_lock = 0;
59 const char *url;
60
61 if (svc == NULL) {
62 die("missing -authservice switch");
63 }
64
65 if (user == NULL) {
66 die("missing -user switch");
67 }
68
69 if (!mh_oauth_new(&ctx, svc)) {
70 die("%s", mh_oauth_get_err_string(ctx));
71 }
72
73 if (snoop) {
74 mh_oauth_log_to(stderr, ctx);
75 }
76
77 fn = mh_oauth_cred_fn(svc);
78
79 if ((url = mh_oauth_get_authorize_url(ctx)) == NULL) {
80 die("%s", mh_oauth_get_err_string(ctx));
81 }
82
83 if (browser) {
84 char *command = concat(browser, " '", url, "'", NULL);
85 int status = OK;
86
87 printf("Follow the prompts in your browser to authorize nmh"
88 " to access %s.\n",
89 mh_oauth_svc_display_name(ctx));
90 sleep(1);
91
92 status = system(command);
93 free(command);
94
95 if (status != OK) {
96 adios ((char *) browser, "SYSTEM");
97 }
98 } else {
99 printf("Load the following URL in your browser and authorize nmh"
100 " to access %s:\n\n%s\n\n",
101 mh_oauth_svc_display_name(ctx), url);
102 }
103 fputs("Enter the authorization code: ", stdout);
104 fflush(stdout);
105 code = geta();
106
107 while (!*code ||
108 ((cred = mh_oauth_authorize(code, ctx)) == NULL
109 && mh_oauth_get_err_code(ctx) == MH_OAUTH_BAD_GRANT)) {
110 printf(!*code ? "Empty code; try again? " : "Code rejected; try again? ");
111 fflush(stdout);
112 code = geta();
113 }
114 if (cred == NULL) {
115 inform("error exchanging code for OAuth2 token");
116 die("%s", mh_oauth_get_err_string(ctx));
117 }
118
119 cred_file = lkfopendata(fn, "r+", &failed_to_lock);
120 if (cred_file == NULL && errno == ENOENT) {
121 cred_file = lkfopendata(fn, "w+", &failed_to_lock);
122 }
123 if (cred_file == NULL || failed_to_lock) {
124 adios(fn, "oops");
125 }
126 if (!mh_oauth_cred_save(cred_file, cred, user)) {
127 die("%s", mh_oauth_get_err_string(ctx));
128 }
129 if (lkfclosedata(cred_file, fn) != 0) {
130 adios (fn, "oops");
131 }
132
133 free(fn);
134 mh_oauth_cred_free(cred);
135 mh_oauth_free(ctx);
136
137 return 0;
138 }
139 #endif
140
141 int
142 main(int argc, char **argv)
143 {
144 char *cp, **argp, **arguments;
145 const char *user = NULL, *saslmech = NULL, *svc = NULL, *browser = NULL;
146 int snoop = 0;
147
148 if (nmh_init(argv[0], true, true)) { return 1; }
149
150 arguments = getarguments (invo_name, argc, argv, 1);
151 argp = arguments;
152
153 while ((cp = *argp++)) {
154 if (*cp == '-') {
155 char help[BUFSIZ];
156 switch (smatch (++cp, switches)) {
157 case AMBIGSW:
158 ambigsw (cp, switches);
159 done (1);
160 case UNKWNSW:
161 die("-%s unknown", cp);
162
163 case HELPSW:
164 snprintf(help, sizeof(help), "%s [switches]",
165 invo_name);
166 print_help (help, switches, 1);
167 done (0);
168 case VERSIONSW:
169 print_version(invo_name);
170 done (0);
171
172 case USERSW:
173 if (!(user = *argp++) || *user == '-')
174 die("missing argument to %s", argp[-2]);
175 continue;
176
177 case SASLMECHSW:
178 if (!(saslmech = *argp++) || *saslmech == '-')
179 die("missing argument to %s", argp[-2]);
180 continue;
181
182 case AUTHSERVICESW:
183 if (!(svc = *argp++) || *svc == '-')
184 die("missing argument to %s", argp[-2]);
185 continue;
186
187 case BROWSERSW:
188 if (!(browser = *argp++) || *browser == '-')
189 die("missing argument to %s", argp[-2]);
190 continue;
191
192 case SNOOPSW:
193 snoop++;
194 continue;
195 }
196 }
197 die("extraneous arguments");
198 }
199
200 if (saslmech && strcasecmp(saslmech, "xoauth2")) {
201 /* xoauth is assumed */
202 die("only -saslmech xoauth2 is supported");
203 }
204 free(arguments);
205
206 #ifdef OAUTH_SUPPORT
207 return do_login(svc, user, browser, snoop);
208 #else
209 NMH_UNUSED(svc);
210 NMH_UNUSED(browser);
211 NMH_UNUSED(snoop);
212 die("not built with OAuth support");
213 return 1;
214 #endif
215 }