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