* Alpine might offer more than one client-id when trying to use XOAUTH2
[alpine.git] / alpine / xoauth2conf.c
blob0058a3e137f80986e5251439caba15eae3b9c514
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2020 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "xoauth2conf.h"
17 #include "keymenu.h"
18 #include "status.h"
19 #include "confscroll.h"
20 #include "init.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/list.h"
24 #include "../pith/mailcmd.h"
26 extern OAUTH2_S alpine_oauth2_list[];
28 XOAUTH2_INFO_S xoauth_default[] = {
29 { GMAIL_NAME, GMAIL_ID, GMAIL_SECRET, GMAIL_TENANT, NULL},
30 { OUTLOOK_NAME, OUTLOOK_ID, OUTLOOK_SECRET, OUTLOOK_TENANT, NULL},
31 { NULL, NULL, NULL, NULL, NULL}
34 typedef enum {Xname = 0, Xid, Xsecret, Xtenant, Xuser, Xend} XTYPES;
36 typedef struct xoauh2_info_val_s {
37 char *screen_name;
38 char *pinerc_name;
39 } XOAUTH2_INFO_VAL_S;
41 /* the order here must match the order in XTYPES above */
42 XOAUTH2_INFO_VAL_S x_default[] = {
43 {NULL, "/NAME="},
44 {"Client-Id", "/ID="},
45 {"Client-Secret", "/SECRET="},
46 {"Tenant", "/TENANT="},
47 {"Username", "/USER="},
48 {NULL, NULL}
51 #define XNAME x_default[Xname].pinerc_name
52 #define XID x_default[Xid].pinerc_name
53 #define XSECRET x_default[Xsecret].pinerc_name
54 #define XTENANT x_default[Xtenant].pinerc_name
55 #define XUSER x_default[Xuser].pinerc_name
57 #define XOAUTH2_CLIENT_ID x_default[Xid].screen_name
58 #define XOAUTH2_CLIENT_SECRET x_default[Xsecret].screen_name
59 #define XOAUTH2_TENANT x_default[Xtenant].screen_name
60 #define XOAUTH2_USERS x_default[Xuser].screen_name
62 char *list_to_array(char **);
63 char **array_to_list(char *);
64 void write_xoauth_configuration(struct variable *, struct variable **, EditWhich);
65 char **xoauth2_conf_dedup_and_merge(char ***);
66 int same_xoauth2_info(XOAUTH2_INFO_S, XOAUTH2_INFO_S);
67 XOAUTH2_INFO_S *xoauth_info_choice(XOAUTH2_INFO_S **, char *);
68 int xoauth2_info_tool(struct pine *, int, CONF_S **, unsigned int);
70 int
71 same_xoauth2_info(XOAUTH2_INFO_S x, XOAUTH2_INFO_S y)
73 int rv = 0;
74 if(x.name && y.name && !strcmp(x.name, y.name)
75 && x.client_id && y.client_id && !strcmp(x.client_id, y.client_id)
76 && ((!x.client_secret && !y.client_secret)
77 || (x.client_secret && y.client_secret && !strcmp(x.client_secret, y.client_secret)))
78 && ((!x.tenant && !y.tenant) || (x.tenant && y.tenant && !strcmp(x.tenant, y.tenant))))
79 rv = 1;
80 return rv;
83 char *
84 list_to_array(char **list)
86 char *rv;
87 int i;
88 size_t n;
90 if(list == NULL || *list == NULL) return NULL;
92 for(i = 0, n = 0; list[i] != NULL; i++)
93 n += strlen(list[i]) + 1;
95 rv = fs_get(n*sizeof(char));
96 *rv = '\0';
97 for(i = 0; list[i] != NULL; i++){
98 strcat(rv, list[i]);
99 if(list[i+1] != NULL) strcat(rv, ",");
101 return rv;
105 char **array_to_list(char *array)
107 int i;
108 char *u;
110 if(array == NULL || *array == '\0') return NULL;
112 for(u = array, i = 0; u && *u; u++)
113 if(*u == ',') i++;
115 return parse_list(array, i+1, 0,NULL);
118 char *
119 xoauth_config_line(XOAUTH2_INFO_S *x)
121 size_t n;
122 char *rv;
123 int i;
125 if(x == NULL) return NULL;
127 n = strlen(XNAME) + strlen(x->name) + strlen(XID) + strlen(x->client_id)
128 + strlen(x->client_secret ? XSECRET : "") + strlen(x->client_secret ? x->client_secret : "")
129 + strlen(x->tenant ? XTENANT : "") + strlen(x->tenant ? x->tenant : "")
130 + strlen(XUSER) + strlen(x->users ? x->users : "")
131 + 2 + 3 + (x->client_secret ? 3 : 0) + (x->tenant ? 3 : 0) + 3 + 1;
132 rv = fs_get(n*sizeof(char));
133 sprintf(rv, "%s\"%s\" %s\"%s\"", XNAME, x->name, XID, x->client_id);
134 if(x->client_secret)
135 sprintf(rv + strlen(rv), " %s\"%s\"", XSECRET, x->client_secret);
136 if(x->tenant)
137 sprintf(rv + strlen(rv), " %s\"%s\"", XTENANT, x->tenant);
138 sprintf(rv + strlen(rv), " %s\"%s\"", XUSER, x->users ? x->users : "");
139 return rv;
143 xoauth2_info_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
145 int rv = 0;
147 switch(cmd){
148 case MC_CHOICE:
149 *((*cl)->d.x.selected) = (*cl)->d.x.pat;
150 rv = simple_exit_cmd(flags);
152 case MC_EXIT:
153 rv = simple_exit_cmd(flags);
154 break;
156 default:
157 rv = -1;
160 if(rv > 0)
161 ps->mangled_body = 1;
163 return rv;
166 XOAUTH2_INFO_S *
167 xoauth_info_choice(XOAUTH2_INFO_S **xinfo, char *user)
169 int i, n, rv;
170 if(!ps_global->ttyo){
171 char *s;
172 char prompt[1024];
173 char reply[1024];
174 int sel;
175 for(i = n = 0; xinfo[i] != NULL; i++)
176 n += strlen(xinfo[i]->client_id); + 5; /* number, parenthesis, space */
177 n += strlen(xinfo[0]->name) + strlen(user);
178 n += 1024; /* large enough to display to lines of 80 characters in UTF-8 */
179 s = fs_get(n*sizeof(char));
180 sprintf(s, _("Alpine cannot determine which client-id to use for the username <%s> for your %s account. "), user, xinfo[0]->name);
181 sprintf(s + strlen(s), _("Please select the client-id to use from the following list.\n\n"));
182 for(i = 0; xinfo[i]; i++)
183 sprintf(s + strlen(s), " %d) %.70s\n", i+1, xinfo[i]->client_id);
184 sprintf(s + strlen(s), "%s", "\n\n");
186 display_init_err(s, 0);
188 strncpy(prompt, _("Enter your selection number: "), sizeof(prompt));
189 prompt[sizeof(prompt)-1] = '\0';
192 rv = optionally_enter(reply, 0, 0, sizeof(reply), prompt, NULL, NO_HELP, 0);
193 sel = atoi(reply) - 1;
194 rv = (sel >= 0 && sel < i) ? 0 : -1;
195 } while (rv != 0);
196 return copy_xoauth2_info(xinfo[rv]);
198 else{
199 CONF_S *ctmp = NULL, *first_line = NULL;
200 XOAUTH2_INFO_S *x_sel = NULL;
201 OPT_SCREEN_S screen;
202 char tmp[1024];
204 dprint((9, "xoauth2 select client-id screen"));
205 ps_global->next_screen = SCREEN_FUN_NULL;
207 memset(&screen, 0, sizeof(screen));
209 for(i = 0; i < sizeof(tmp) && i < ps_global->ttyo->screen_cols; i++)
210 tmp[i] = '-';
211 tmp[i] = '\0';
213 new_confline(&ctmp);
214 ctmp->flags |= CF_NOSELECT;
215 ctmp->value = cpystr(tmp);
217 new_confline(&ctmp);
218 ctmp->flags |= CF_NOSELECT;
219 ctmp->value = cpystr(_("Select a Client-ID to use with this account"));
221 new_confline(&ctmp);
222 ctmp->flags |= CF_NOSELECT;
223 ctmp->value = cpystr(tmp);
225 new_confline(&ctmp);
226 ctmp->flags |= CF_NOSELECT | CF_B_LINE;
228 sprintf(tmp, _("Alpine cannot determine which client-id to use for the username <%s>"), user);
229 new_confline(&ctmp);
230 ctmp->flags |= CF_NOSELECT;
231 ctmp->value = cpystr(tmp);
233 sprintf(tmp, _("for your %s account. Please select the client-id to use from the following list.\n\n"), xinfo[0]->name);
234 new_confline(&ctmp);
235 ctmp->flags |= CF_NOSELECT;
236 ctmp->value = cpystr(tmp);
238 new_confline(&ctmp);
239 ctmp->flags |= CF_NOSELECT | CF_B_LINE;
241 for(i = 0; xinfo[i] != NULL; i++){
242 new_confline(&ctmp);
243 if(!first_line)
244 first_line = ctmp;
246 ctmp->value = cpystr(xinfo[i]->client_id);
247 ctmp->d.x.selected = &x_sel;
248 ctmp->d.x.pat = copy_xoauth2_info(xinfo[i]);
249 ctmp->keymenu = &xoauth2_id_select_km;
250 ctmp->help = NO_HELP;
251 ctmp->help_title = NULL;
252 ctmp->tool = xoauth2_info_tool;
253 ctmp->flags = CF_STARTITEM;
254 ctmp->valoffset = 4;
256 (void)conf_scroll_screen(ps_global, &screen, first_line, _("SELECT CLIENT_ID"),
257 _("xoauth2"), 0, NULL);
258 return x_sel;
260 return NULL;
263 /* Get the client-id, etc. for server "name" associated to user "user" */
264 XOAUTH2_INFO_S *
265 oauth2_get_client_info(unsigned char *name, char *user)
267 int i, j, matches;
268 char ***alval, **lval;
269 XOAUTH2_INFO_S *x, **xinfo;
271 if(name == NULL || *name == '\0' || user == NULL || *user == '\0')
272 return NULL;
274 matches = 0;
275 /* first count how many servers */
276 lval = ps_global->vars[V_XOAUTH2_INFO].current_val.l;
277 for(i = 0; lval && lval[i]; i++){
278 x = xoauth_parse_client_info(lval[i]);
279 if(x && x->name && name && !strcmp(x->name, name))
280 matches++;
281 free_xoauth2_info(&x);
284 /* if nothing, use the default value */
285 for(i = 0; xoauth_default[i].name != NULL && strcmp(xoauth_default[i].name, name); i++);
286 if(xoauth_default[i].name) matches++;
288 if(matches == 0) return NULL;
289 if(matches == 1) return copy_xoauth2_info(&xoauth_default[i]);
291 /* more than one match, see if it is a duplicate client-id entry */
292 xinfo = fs_get((matches + 1)*sizeof(XOAUTH2_INFO_S *));
293 memset((void *)xinfo, 0, (matches + 1)*sizeof(XOAUTH2_INFO_S *));
294 matches = 0; /* restart the recount, it might go lower! */
295 for(i = 0; lval && lval[i]; i++){
296 x = xoauth_parse_client_info(lval[i]);
297 if(x && x->name && name && !strcmp(x->name, name)){
298 for(j = 0; xinfo && xinfo[j] && !same_xoauth2_info(*x, *xinfo[j]); j++);
299 if(!xinfo[j]) xinfo[matches++] = copy_xoauth2_info(x);
301 free_xoauth2_info(&x);
303 for(i = 0; xoauth_default[i].name != NULL && strcmp(xoauth_default[i].name, name); i++);
304 for(j = 0; xinfo && xinfo[j] && !same_xoauth2_info(xoauth_default[i], *xinfo[j]); j++);
305 if(!xinfo[j]) xinfo[matches++] = copy_xoauth2_info(&xoauth_default[i]);
307 /* if after removing the duplicate entries, we only have one, use it */
308 if(matches == 1){
309 x = copy_xoauth2_info(xinfo[0]);
310 free_xoauth2_info(&xinfo[0]);
311 fs_give((void **) xinfo);
312 return x;
315 /* we have more than one match, now check if any of them matches the given user */
316 matches = 0;
317 for(i = 0; xinfo && xinfo[i]; i++){
318 lval = array_to_list(xinfo[i]->users);
319 for(j = 0; lval && lval[j] && strucmp(lval[j], user); j++);
320 if(lval && lval[j]){
321 matches++;
322 free_xoauth2_info(&x);
323 x = copy_xoauth2_info(xinfo[i]);
325 if(lval) free_list_array(&lval);
328 /* only one server matches the username */
329 if(matches == 1){
330 for(i = 0; xinfo[i] != NULL; i++)
331 free_xoauth2_info(&xinfo[i]);
332 fs_give((void **) xinfo);
333 return x;
336 free_xoauth2_info(&x);
337 /* We either have no matches, or have more than one match!
338 * in either case, let the user pick what they want */
339 x = xoauth_info_choice(xinfo, user);
340 for(i = 0; xinfo[i] != NULL; i++)
341 free_xoauth2_info(&xinfo[i]);
342 fs_give((void **) xinfo);
344 /* Once the user chose a client-id, save it so we do not ask again */
345 if(x != NULL){
346 int n = x->users ? strlen(x->users) + 1 : 0;
347 char ***alval, **l;
349 fs_resize((void **) &x->users, (n + strlen(user) + 1)*sizeof(char));
350 x->users[n > 0 ? n - 1 : 0] = '\0';
351 if(n > 0) strcat(x->users, ",");
352 strcat(x->users, user);
353 alval = ALVAL(&ps_global->vars[V_XOAUTH2_INFO], Main);
354 lval = *alval;
356 for(n = 0; lval && lval[n]; n++);
357 fs_resize((void **) &lval, (n+2)*sizeof(char *));
358 lval[n] = xoauth_config_line(x);
359 lval[n+1] = NULL;
360 *alval = xoauth2_conf_dedup_and_merge(&lval);
361 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
362 write_pinerc(ps_global, Main, WRP_NONE);
365 return x;
368 /* write vlist to v
369 * Each vlist member is of type "p", while "v" is of type "l", so we
370 * each entry in "l" by using each of the "p" entries.
372 void
373 write_xoauth_configuration(struct variable *v, struct variable **vlist, EditWhich ew)
375 int i, k, m, n;
376 XOAUTH2_INFO_S *x = NULL, *y;
377 char ***alval, **lval, **l;
378 char *p;
380 for (i = 0, n = 0; vlist[i] != NULL; i++) /* count number of lines we need */
381 if(!strcmp(vlist[i]->name, XOAUTH2_USERS))
382 n++;
383 lval = fs_get((n+1)*sizeof(char *));
384 memset((void *) lval, 0, (n+1)*sizeof(char *));
386 m = -1;
387 alval = ALVAL(v, ew);
388 for (i = 0, k = 0; vlist[i] != NULL; i++){
389 if(x == NULL){
390 x = new_xoauth2_info();
391 x->name = cpystr(vlist[i]->descrip); /* hack! but makes life so much easier! */
392 for(m = 0; xoauth_default[m].name != NULL
393 && strcmp(xoauth_default[m].name, x->name); m++);
395 if (x->client_id == NULL && !strcmp(vlist[i]->name, XOAUTH2_CLIENT_ID)){
396 p = PVAL(vlist[i], ew);
397 if (p == NULL) p = vlist[i]->current_val.p;
398 if(p != NULL)
399 x->client_id = cpystr(p);
400 continue;
402 if (x->client_secret == NULL
403 && m >= 0
404 && xoauth_default[m].client_secret
405 && !strcmp(vlist[i]->name, XOAUTH2_CLIENT_SECRET)){
406 p = PVAL(vlist[i], ew);
407 if (p == NULL) p = vlist[i]->current_val.p;
408 if(p != NULL)
409 x->client_secret = cpystr(p);
410 continue;
412 if (x->tenant == NULL
413 && m >= 0
414 && xoauth_default[m].tenant
415 && !strcmp(vlist[i]->name, XOAUTH2_TENANT)){
416 p = PVAL(vlist[i], ew);
417 if (p == NULL) p = vlist[i]->current_val.p;
418 if(p != NULL)
419 x->tenant = cpystr(p);
420 continue;
422 if (x->users == NULL && !strcmp(vlist[i]->name, XOAUTH2_USERS)){
423 l = LVAL(vlist[i], ew);
424 x->users = list_to_array(l);
426 /* don't let it get to here until we are done! */
427 lval[k++] = xoauth_config_line(x);
428 /* get ready for next run */
429 free_xoauth2_info(&x);
430 m = -1;
432 if(k > 0){
433 lval[k] = NULL;
434 if(*alval) free_list_array(alval);
435 *alval = lval;
437 else
438 *alval = NULL;
439 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
443 /* parse line of the form
444 /NAME="text" /ID="text" /TENANT="text" /SECRET="text" /USER="text"
446 XOAUTH2_INFO_S *
447 xoauth_parse_client_info(char *lvalp)
449 char *s, *t, c;
450 XOAUTH2_INFO_S *x;
452 if (lvalp == NULL) return NULL;
454 x = new_xoauth2_info();
455 if((s = strstr(lvalp, XNAME)) != NULL){
456 s += strlen(XNAME);
457 if(*s == '"') s++;
458 for(t = s; *t && *t != '"' && *t != ' '; t++);
459 c = *t;
460 *t = '\0';
461 if (*s) x->name = cpystr(s);
462 *t = c;
463 } else x->name = NULL;
465 if((s = strstr(lvalp, XID)) != NULL){
466 s += strlen(XID);
467 if(*s == '"') s++;
468 for(t = s; *t && *t != '"' && *t != ' '; t++);
469 c = *t;
470 *t = '\0';
471 if (*s) x->client_id = cpystr(s);
472 *t = c;
473 } else x->client_id = NULL;
475 if((s = strstr(lvalp, XTENANT)) != NULL){
476 s += strlen(XTENANT);
477 if(*s == '"') s++;
478 for(t = s; *t && *t != '"' && *t != ' '; t++);
479 c = *t;
480 *t = '\0';
481 if (*s) x->tenant = cpystr(s);
482 *t = c;
483 } else x->tenant = NULL;
485 if((s = strstr(lvalp, XSECRET)) != NULL){
486 s += strlen(XSECRET);
487 if(*s == '"') s++;
488 for(t = s; *t && *t != '"' && *t != ' '; t++);
489 c = *t;
490 *t = '\0';
491 if (*s) x->client_secret = cpystr(s);
492 *t = c;
493 } else x->client_secret = NULL;
495 if((s = strstr(lvalp, XUSER)) != NULL){
496 s += strlen(XUSER);
497 if(*s == '"') s++;
498 for(t = s; *t && *t != '"' && *t != ' '; t++);
499 c = *t;
500 *t = '\0';
501 if(*s) x->users = cpystr(s);
502 *t = c;
503 } else x->users = NULL;
505 return x;
508 char **
509 xoauth2_conf_dedup_and_merge(char ***alval)
511 int i, j, k, l, n, m;
512 char **lval, **rv;
513 XOAUTH2_INFO_S *x, *y;
515 if(alval == NULL || *alval == NULL || **alval == NULL)
516 return NULL;
518 lval = *alval;
519 for(i = 0; lval[i] != NULL; i++); /* count how many entries */
521 rv = fs_get((i+1)*sizeof(char *));
522 memset((void *)rv, 0, (i+1)*sizeof(char *));
524 for (i = 0; lval[i] != NULL; i++){
525 x = xoauth_parse_client_info(lval[i]);
526 for (j = 0; rv[j] != NULL; j++){
527 y = xoauth_parse_client_info(rv[j]);
528 /* check if this is the same data. If so, merge the users into one and discard the old data */
529 if(same_xoauth2_info(*x, *y)){
530 char **l1, **l2, **l3;
531 int k, n;
532 /* merge user1 with user2, save in x->users */
533 l1 = array_to_list(x->users);
534 l2 = array_to_list(y->users);
536 for(n = 0; l1 && l1[n]; n++);
537 for(m = 0; l2 && l2[m]; m++, n++);
538 l3 = fs_get((n+1)*sizeof(char*));
539 memset((void *) l3, 0, (n+1)*sizeof(char *));
541 for(l = 0, n = 0; l1 && l1[l]; l++){
542 for(k = 0; l1 && l1[j] && l3[k] && strucmp(l1[l], l3[k]); k++);
543 if(l3[k] == NULL && l1 && l1[l] != NULL)
544 l3[n++] = cpystr(l1[l]);
546 for(l = 0; l2 && l2[l]; l++){
547 for(k = 0; l2 && l2[l] && l3[k] && strucmp(l2[l], l3[k]); k++);
548 if(l3[k] == NULL && l2 && l2[l] != NULL)
549 l3[n++] = cpystr(l2[l]);
551 l3[n++] = NULL;
552 if(x->users) fs_give((void **) &x->users);
553 x->users = list_to_array(l3);
554 fs_give((void **) &rv[j]);
555 rv[j] = xoauth_config_line(x);
557 if(l1) free_list_array(&l1);
558 if(l2) free_list_array(&l2);
559 if(l3) free_list_array(&l3);
560 free_xoauth2_info(&y);
561 break;
563 free_xoauth2_info(&y);
565 if(rv[j] == NULL) rv[j] = cpystr(lval[i]);
567 return rv;
571 * X = new value, Y = default configuration
573 void
574 write_xoauth_conf_entry(XOAUTH2_INFO_S *x, XOAUTH2_INFO_S *y, CONF_S **cl, CONF_S **clb, CONF_S **fline,
575 struct variable ***varlistp, int *pp, int ln, int key)
577 CONF_S *ctmpb = *clb;
578 struct variable **varlist;
579 int i, p = *pp;
580 char tmp[1024], tmp2[16];
582 i = 2; /* client_id and users always appear */
583 if(x->client_secret) i++;
584 if(x->tenant) i++;
586 sprintf(tmp2, "%d", key);
587 fs_resize((void **) varlistp, (p + i + 1)*sizeof(struct variable **));
588 memset((void *) (*varlistp + p*sizeof(struct variable *)), 0, (i + 1)*sizeof(struct variable *));
589 varlist = *varlistp;
591 new_confline(cl)->var = NULL;
592 if(fline && !*fline) *fline = *cl;
593 (*cl)->flags |= CF_NOSELECT;
594 (*cl)->help = NO_HELP;
595 (*cl)->valoffset = 1;
596 (*cl)->value = cpystr(x->name);
597 (*cl)->varname = NULL;
598 (*cl)->varnamep = ctmpb = *cl;
600 /* Setup client-id variable */
601 varlist[p] = fs_get(sizeof(struct variable));
602 memset((void *) varlist[p], 0, sizeof(struct variable));
603 varlist[p]->name = cpystr(XOAUTH2_CLIENT_ID);
604 varlist[p]->is_used = 1;
605 varlist[p]->is_user = 1;
606 varlist[p]->main_user_val.p = x->client_id && y->client_id
607 && strcmp(x->client_id, y->client_id) ? cpystr(x->client_id) : NULL;
608 varlist[p]->global_val.p = y->client_id ? cpystr(y->client_id) : NULL;
609 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
610 varlist[p]->descrip = cpystr(x->name); /* hack, but makes life easier! */
611 set_current_val(varlist[p], FALSE, FALSE);
613 /* Write client-id variable */
614 new_confline(cl)->var = varlist[p];
615 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_CLIENT_ID);
616 tmp[sizeof(tmp)-1] = '\0';
617 (*cl)->varname = cpystr(tmp);
618 (*cl)->varmem = p++;
619 (*cl)->valoffset = ln + 3 + 3;
620 (*cl)->value = pretty_value(ps_global, *cl);
621 (*cl)->keymenu = &config_xoauth2_text_keymenu;
622 (*cl)->help = h_config_xoauth2_client_id;
623 (*cl)->tool = text_tool;
624 (*cl)->varnamep = ctmpb;
626 /* Set up client-secret variable */
627 if(x->client_secret){
628 varlist[p] = fs_get(sizeof(struct variable));
629 memset((void *) varlist[p], 0, sizeof(struct variable));
630 varlist[p]->name = cpystr(XOAUTH2_CLIENT_SECRET);
631 varlist[p]->is_used = 1;
632 varlist[p]->is_user = 1;
633 varlist[p]->main_user_val.p = y->client_secret
634 && strcmp(x->client_secret, y->client_secret)
635 ? cpystr(x->client_secret) : NULL;
636 varlist[p]->global_val.p = y->client_secret ? cpystr(y->client_secret) : NULL;
637 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
638 varlist[p]->descrip = cpystr(x->name); /* hack, but makes life easier! */
639 set_current_val(varlist[p], FALSE, FALSE);
641 /* Write client-secret variable */
642 new_confline(cl)->var = varlist[p];
643 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_CLIENT_SECRET);
644 tmp[sizeof(tmp)-1] = '\0';
645 (*cl)->varname = cpystr(tmp);
646 (*cl)->varmem = p++;
647 (*cl)->valoffset = ln + 3 + 3;
648 (*cl)->value = pretty_value(ps_global, *cl);
649 (*cl)->keymenu = &config_xoauth2_text_keymenu;
650 (*cl)->help = h_config_xoauth2_client_secret;
651 (*cl)->tool = text_tool;
652 (*cl)->varnamep = ctmpb;
655 /* Set up tenant variable */
656 if(x->tenant){
657 varlist[p] = fs_get(sizeof(struct variable));
658 memset((void *) varlist[p], 0, sizeof(struct variable));
659 varlist[p]->name = cpystr(XOAUTH2_TENANT);
660 varlist[p]->is_used = 1;
661 varlist[p]->is_user = 1;
662 varlist[p]->main_user_val.p = y->tenant && strcmp(x->tenant, y->tenant)
663 ? cpystr(x->tenant) : NULL;
664 varlist[p]->global_val.p = y->tenant ? cpystr(y->tenant) : NULL;
665 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
666 varlist[p]->descrip = cpystr(x->name); /* hack, but makes life easier! */
667 set_current_val(varlist[p], FALSE, FALSE);
669 /* Write client-secret variable */
670 new_confline(cl)->var = varlist[p];
671 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_TENANT);
672 tmp[sizeof(tmp)-1] = '\0';
673 (*cl)->varname = cpystr(tmp);
674 (*cl)->varmem = p++;
675 (*cl)->valoffset = ln + 3 + 3;
676 (*cl)->value = pretty_value(ps_global, *cl);
677 (*cl)->keymenu = &config_xoauth2_text_keymenu;
678 (*cl)->help = h_config_xoauth2_tenant;
679 (*cl)->tool = text_tool;
680 (*cl)->varnamep = ctmpb;
683 /* Setup users variable */
684 varlist[p] = fs_get(sizeof(struct variable));
685 memset((void *) varlist[p], 0, sizeof(struct variable));
686 varlist[p]->name = cpystr(XOAUTH2_USERS);
687 varlist[p]->is_used = 1;
688 varlist[p]->is_user = 1;
689 varlist[p]->is_list = 1;
690 varlist[p]->main_user_val.l = x->users ? array_to_list(x->users) : NULL;
691 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
692 varlist[p]->descrip = cpystr(x->name); /* hack, but makes life easier! */
693 set_current_val(varlist[p], FALSE, FALSE);
695 /* Write user variable */
696 new_confline(cl)->var = varlist[p];
697 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_USERS);
698 tmp[sizeof(tmp)-1] = '\0';
699 (*cl)->varname = cpystr(tmp);
700 (*cl)->valoffset = ln + 3 + 3;
701 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
702 (*cl)->help = h_config_xoauth2_username;
703 if(x->users){
704 int z;
705 for(z = 0; varlist[p]->main_user_val.l[z]; z++){
706 if(z) new_confline(cl);
707 (*cl)->var = varlist[p];
708 (*cl)->varmem = z;
709 (*cl)->valoffset = ln + 3 + 3;
710 (*cl)->value = pretty_value(ps_global, *cl);
711 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
712 (*cl)->tool = text_tool;
713 (*cl)->varnamep = ctmpb = *cl;
716 else {
717 (*cl)->varmem = 0;
718 (*cl)->value = pretty_value(ps_global, *cl);
719 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
720 (*cl)->tool = text_tool;
721 (*cl)->varnamep = ctmpb = *cl;
723 p++;
724 *pp = p;
725 *varlistp = varlist;
726 *clb = ctmpb;
728 /* Separate servers with a blank line */
729 new_confline(cl);
730 (*cl)->flags |= CF_NOSELECT | CF_B_LINE;
733 /*----------------------------------------------------------------------
734 Screen to add client_id and client_secret for a service
736 ---*/
737 void
738 alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions)
740 struct variable **varlist = NULL;
741 char tmp[MAXPATH+1], *pval, **lval, ***alval;
742 char *s, *extraname = NULL;
743 char *name, *id, *tenant, *secret, **user;
744 char *name_lval, *id_lval, *tenant_lval, *secret_lval, *user_lval,
745 *id_def, *tenant_def, *secret_def;
746 int i, j, k, l, p, q, ln = 0, readonly_warning = 0, pos;
747 CONF_S *ctmpa = NULL, *ctmpb, *first_line;
748 FEATURE_S *feature;
749 PINERC_S *prc = NULL;
750 OPT_SCREEN_S screen;
751 int expose_hidden_config, add_hidden_vars_title = 0;
752 SAVED_CONFIG_S *vsave;
753 XOAUTH2_INFO_S x, *y;
755 dprint((3, "-- alpine_xoauth2_configuration --\n"));
757 expose_hidden_config = F_ON(F_EXPOSE_HIDDEN_CONFIG, ps_global);
758 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
760 if(ps->restricted)
761 readonly_warning = 1;
762 else{
763 switch(ew){
764 case Main:
765 prc = ps->prc;
766 break;
767 case Post:
768 prc = ps->post_prc;
769 break;
770 default:
771 break;
774 readonly_warning = prc ? prc->readonly : 1;
777 ps->next_screen = SCREEN_FUN_NULL;
779 mailcap_free(); /* free resources we won't be using for a while */
781 pos = -1;
782 ln = strlen(XOAUTH2_CLIENT_ID);
783 i = strlen(XOAUTH2_CLIENT_SECRET);
784 if(ln < i) ln = i;
785 i = strlen(XOAUTH2_TENANT);
786 if(ln < i) ln = i;
787 i = strlen(XOAUTH2_USERS);
788 if(ln < i) ln = i;
790 alval = ALVAL(&ps->vars[V_XOAUTH2_INFO], ew);
791 lval = *alval = xoauth2_conf_dedup_and_merge(alval);
792 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
793 write_pinerc(ps_global, ew, WRP_NONE);
795 do {
796 ctmpa = first_line = NULL;
798 for(i = 0, p = 0; xoauth_default[i].name != NULL; i++){
799 /* always start with the default configuration */
800 for(k = 0, q = 0; lval && lval[k]; k++){
801 y = xoauth_parse_client_info(lval[k]);
802 if(same_xoauth2_info(xoauth_default[i], *y))
803 break;
804 free_xoauth2_info(&y);
806 if(lval == NULL || lval[k] == NULL)
807 write_xoauth_conf_entry(&xoauth_default[i], &xoauth_default[i], &ctmpa, &ctmpb,
808 &first_line, &varlist, &p, ln, -i-1);
809 for(k = 0, q = 0; lval && lval[k]; k++){
810 y = xoauth_parse_client_info(lval[k]);
811 if(y && (!y->name || strcmp(y->name, xoauth_default[i].name))){
812 free_xoauth2_info(&y);
813 continue;
815 if(y->client_id == NULL)
816 y->client_id = cpystr(xoauth_default[i].client_id);
817 if(y->client_secret == NULL && xoauth_default[i].client_secret != NULL)
818 y->client_secret = cpystr(xoauth_default[i].client_secret);
819 if(y->tenant == NULL && xoauth_default[i].tenant != NULL)
820 y->tenant = cpystr(xoauth_default[i].tenant);
821 write_xoauth_conf_entry(y, &xoauth_default[i], &ctmpa, &ctmpb, &first_line, &varlist, &p, ln, k);
822 free_xoauth2_info(&y);
826 vsave = save_config_vars(ps, expose_hidden_config);
827 first_line = pos < 0 ? first_sel_confline(first_line) : set_confline_number(first_line, pos);
828 pos = -1;
829 memset(&screen, 0, sizeof(screen));
830 screen.ro_warning = readonly_warning;
831 /* TRANSLATORS: Print something1 using something2.
832 "configuration" is something1 */
833 switch(conf_scroll_screen(ps, &screen, first_line, "XOAUTH2 Alpine Info",
834 _("configuration"), 0, &pos)){
835 case 0:
836 break;
838 case 1:
839 write_xoauth_configuration(&ps->vars[V_XOAUTH2_INFO], varlist, ew);
840 write_pinerc(ps, ew, WRP_NONE);
841 break;
843 case 4: /* add a service */
844 {char service[MAILTMPLEN+1];
845 char prompt[MAILTMPLEN+1];
846 int flags = OE_DISALLOW_HELP;
847 strncpy(prompt, _("Enter service name: "), sizeof(prompt));
848 prompt[sizeof(prompt) - 1] = '\0';
849 service[0] = '\0';
850 if(optionally_enter(service,
851 -(ps_global->ttyo ? FOOTER_ROWS(ps_global) : 3),
852 0, sizeof(service), prompt, NULL, NO_HELP, &flags) == 0){
853 for(i = 0; xoauth_default[i].name != NULL && strucmp(xoauth_default[i].name, service); i++);
854 if(xoauth_default[i].name == NULL)
855 q_status_message1(SM_ORDER, 3, 3, _("Service %s not known"), service);
856 else{
857 char **list;
858 ClearScreen();
859 ps_global->mangled_screen = 1;
860 for(j = 0; lval && lval[j]; j++);
861 list = fs_get((j+2)*sizeof(char *));
862 memset((void *)list, 0, (j+2)*sizeof(char *));
863 list[0] = xoauth_config_line(&xoauth_default[i]);
864 for(i = 0; lval && lval[i]; i++)
865 list[i+1] = cpystr(lval[i]);
866 if(lval) free_list_array(&lval);
867 *alval = lval = list;
868 for(i = 0; varlist && varlist[i]; i++){
869 free_variable_values(varlist[i]);
870 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
871 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
872 fs_give((void **) &varlist[i]);
874 if(varlist) fs_give((void **) varlist);
878 break;
880 case 5: /* delete a service */
881 { int m, key;
882 XOAUTH2_INFO_S *x;
883 char question[MAILTMPLEN];
885 for(i = 0, m = 1, j = 0; varlist[i] && m < pos;)
886 if(!varlist[i]->is_list){
887 i++; m++;
888 } else {
889 if(varlist[i]->current_val.l[j++]) m++;
890 else{
891 j = 0; m += 2; i++;
894 key = atoi(varlist[i]->dname); /* this hack avoids we rebuild varlist again */
895 if(key >= 0){
896 x = xoauth_parse_client_info(lval[key]);
897 snprintf(question, sizeof(question), _("Delete this configuration for %s "), x->name);
898 free_xoauth2_info(&x);
899 if(want_to(question, 'n', 'n', NO_HELP, WT_NORM) != 'y')
900 break;
901 for(i = key; lval && lval[i] && lval[i+1]; i++){
902 fs_give((void **) &lval[i]);
903 lval[i] = cpystr(lval[i+1]);
905 fs_give((void **) &lval[i]);
907 else {
908 q_status_message(SM_ORDER, 3, 3, _("Cannot delete default configuration"));
909 break;
911 if(lval && lval[0] == NULL)
912 free_list_array(&lval);
913 *alval = lval;
914 pos = 1; /* reset at the top */
915 for(i = 0; varlist && varlist[i]; i++){
916 free_variable_values(varlist[i]);
917 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
918 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
919 fs_give((void **) &varlist[i]);
921 if(varlist) fs_give((void **) varlist);
923 break;
925 case 10:
926 revert_to_saved_config(ps, vsave, expose_hidden_config);
927 if(prc)
928 prc->outstanding_pinerc_changes = 0;
929 break;
931 default:
932 q_status_message(SM_ORDER,7,10,
933 "conf_scroll_screen bad ret, not supposed to happen");
934 break;
936 } while (pos >= 0);
938 for(i = 0; varlist && varlist[i]; i++){
939 free_variable_values(varlist[i]);
940 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
941 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
942 fs_give((void **) &varlist[i]);
944 if(varlist) fs_give((void **) varlist);
946 #ifdef _WINDOWS
947 mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
948 #endif