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