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