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