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