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