Add support for tab-completion when selecting by rule
[alpine.git] / alpine / xoauth2conf.c
blob5bf3e18526bd535fa331e19f4f3dd06e22cae00c
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 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"
25 #include "xoauth2info.c"
27 typedef enum {Xname = 0, Xid, Xsecret, Xtenant, Xuser, XFlow, Xend} XTYPES;
29 typedef struct xoauh2_info_val_s {
30 char *screen_name;
31 char *pinerc_name;
32 } XOAUTH2_INFO_VAL_S;
34 /* the order here must match the order in XTYPES above */
35 XOAUTH2_INFO_VAL_S x_default[] = {
36 {NULL, XNAME},
37 {XOAUTH2_CLIENT_ID, XID},
38 {XOAUTH2_CLIENT_SECRET, XSECRET},
39 {XOAUTH2_TENANT, XTENANT},
40 {XOAUTH2_USERS, XUSER},
41 {XOAUTH2_FLOW, XFLOW},
42 {NULL, NULL}
45 char *list_to_array(char **);
46 char **array_to_list(char *);
47 void write_xoauth_configuration(struct variable *, struct variable **, EditWhich);
48 char **xoauth2_conf_dedup_and_merge(char ***);
49 XOAUTH2_INFO_S *xoauth_info_choice(XOAUTH2_INFO_S **, char *);
50 int xoauth2_info_tool(struct pine *, int, CONF_S **, unsigned int);
51 char *xoauth2_extra_text(XOAUTH2_INFO_S **, int);
52 XOAUTH2_INFO_S **xoauth2_configured_servers(void);
53 XOAUTH2_INFO_S **parse_xoauth2_info_list(char **lval, int *totalp);
55 char *
56 list_to_array(char **list)
58 char *rv;
59 int i;
60 size_t n;
62 if(list == NULL || *list == NULL) return NULL;
64 for(i = 0, n = 0; list[i] != NULL; i++)
65 n += strlen(list[i]) + 1;
67 rv = fs_get(n*sizeof(char));
68 *rv = '\0';
69 for(i = 0; list[i] != NULL; i++){
70 strcat(rv, list[i]);
71 if(list[i+1] != NULL) strcat(rv, ",");
73 return rv;
77 char **array_to_list(char *array)
79 int i;
80 char *u;
82 if(array == NULL || *array == '\0') return NULL;
84 for(u = array, i = 0; u && *u; u++)
85 if(*u == ',') i++;
87 return parse_list(array, i+1, 0,NULL);
90 char *
91 xoauth_config_line(XOAUTH2_INFO_S *x)
93 size_t n;
94 char *rv;
96 if(x == NULL) return NULL;
98 n = strlen(XNAME) + strlen((char *) x->name) + strlen(XID) + strlen(x->client_id)
99 + strlen(x->client_secret ? XSECRET : "") + strlen(x->client_secret ? x->client_secret : "")
100 + strlen(x->tenant ? XTENANT : "") + strlen(x->tenant ? x->tenant : "")
101 + strlen(XUSER) + strlen(x->users ? x->users : "")
102 + strlen(XFLOW) + strlen(x->flow ? x->flow : "")
103 + 2 + 3 + (x->client_secret ? 3 : 0) + (x->tenant ? 3 : 0)
104 + 3 + (x->flow ? 3 : 0) + 1;
105 rv = fs_get(n*sizeof(char));
106 sprintf(rv, "%s\"%s\" %s\"%s\"", XNAME, x->name, XID, x->client_id);
107 if(x->client_secret)
108 sprintf(rv + strlen(rv), " %s\"%s\"", XSECRET, x->client_secret);
109 if(x->tenant)
110 sprintf(rv + strlen(rv), " %s\"%s\"", XTENANT, x->tenant);
111 sprintf(rv + strlen(rv), " %s\"%s\"", XUSER, x->users ? x->users : "");
112 if(x->flow)
113 sprintf(rv + strlen(rv), " %s\"%s\"", XFLOW, x->flow ? x->flow : "");
114 return rv;
118 xoauth2_info_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
120 int rv = 0;
122 switch(cmd){
123 case MC_CHOICE:
124 *((*cl)->d.x.selected) = (*cl)->d.x.pat;
125 rv = simple_exit_cmd(flags);
127 case MC_EXIT:
128 rv = simple_exit_cmd(flags);
129 break;
131 default:
132 rv = -1;
135 if(rv > 0)
136 ps->mangled_body = 1;
138 return rv;
141 char *
142 xoauth2_extra_text(XOAUTH2_INFO_S **xinfo, int i)
144 int methodcount;
145 int j;
146 XOAUTH2_INFO_S *x;
147 char *rv = NULL;
149 if(xinfo == NULL)
150 return NULL;
152 for(j = 0; xinfo[j] != NULL; j++);
153 if(i < 0 || i >= j) return NULL;
155 x = xinfo[i];
156 if(x->flow){
157 for(methodcount = 1, j = 0; xinfo[j] != NULL; j++)
158 if(j != i && xinfo[j]->flow && !strucmp(xinfo[j]->flow, x->flow))
159 methodcount++;
160 if(methodcount == 1) return cpystr(x->flow);
163 /* we either do not have flow, or have more than one flow, use user */
164 if(x->users){
165 char **lval = array_to_list(xinfo[i]->users);
166 if(lval && lval[0]){ /* use the first one */
167 rv = cpystr(lval[0]);
168 free_list_array(&lval);
172 if(rv == NULL) rv = cpystr(_("client-id unused"));
173 return rv;
176 XOAUTH2_INFO_S *
177 xoauth_info_choice(XOAUTH2_INFO_S **xinfo, char *user)
179 int i, n, rv;
180 char *extra;
182 dprint((9, "xoauth_info_choice()"));
184 for(i = 0; xinfo && xinfo[i]; i++);
185 if(i == 0) return NULL;
187 if(strucmp((char *) xinfo[0]->name, (char *) GMAIL_NAME) == 0){
188 for(i = 0; xinfo && xinfo[i] && strcmp(xinfo[i]->client_id, (char *) GMAIL_ID) != 0; i++);
189 if(!xinfo || xinfo[i] != NULL){
190 int j, offset = 0;
191 XOAUTH2_INFO_S *x;
193 for(j = 0; xinfo[j] != NULL; j++){
194 if(j == i){
195 offset++;
196 continue;
198 x = xinfo[j] ? copy_xoauth2_info(xinfo[j]) : NULL;
199 free_xoauth2_info(&xinfo[j - offset]);
200 xinfo[j - offset] = x;
202 xinfo[j - offset] = NULL;
203 for(i = 0; xinfo && xinfo[i]; i++);
207 if(i == 1) return copy_xoauth2_info(xinfo[0]);
209 if(!ps_global->ttyo){
210 char *s;
211 char prompt[1024];
212 char reply[1024];
213 int sel;
214 for(i = n = 0; xinfo[i] != NULL; i++)
215 n += strlen(xinfo[i]->client_id) + strlen(xinfo[i]->flow ? xinfo[i]->flow : "")
216 + strlen(xinfo[i]->users ? xinfo[i]->users : "") + 8; /* number, parenthesis, space */
217 n += strlen((char *) xinfo[0]->name) + strlen(user);
218 n += 1024; /* large enough to display lines of 80 characters in UTF-8 */
219 s = fs_get(n*sizeof(char));
220 sprintf(s, _("Alpine cannot determine which client-id to use for the username <%s> for your %s account. "), user, xinfo[0]->name);
221 sprintf(s + strlen(s), _("Please select the client-id to use from the following list.\n\n"));
222 for(i = 0; xinfo[i]; i++){
223 extra = xoauth2_extra_text(xinfo, i);
224 sprintf(s + strlen(s), " %d) %.70s%s%s\n", i+1, xinfo[i]->client_id,
225 extra ? " - " : "", extra ? extra : "");
226 if(extra) fs_give((void **) &extra);
228 sprintf(s + strlen(s), "%s", "\n\n");
230 display_init_err(s, 0);
232 strncpy(prompt, _("Enter your selection number: "), sizeof(prompt));
233 prompt[sizeof(prompt)-1] = '\0';
236 rv = optionally_enter(reply, 0, 0, sizeof(reply), prompt, NULL, NO_HELP, 0);
237 sel = atoi(reply) - 1;
238 rv = (sel >= 0 && sel < i) ? 0 : -1;
239 } while (rv != 0);
240 return copy_xoauth2_info(xinfo[sel]);
242 else{
243 CONF_S *ctmp = NULL, *first_line = NULL;
244 XOAUTH2_INFO_S *x_sel = NULL;
245 OPT_SCREEN_S screen;
246 char tmp[1024];
248 ps_global->next_screen = SCREEN_FUN_NULL;
250 memset(&screen, 0, sizeof(screen));
252 for(i = 0; i < sizeof(tmp) && i < ps_global->ttyo->screen_cols; i++)
253 tmp[i] = '-';
254 tmp[i] = '\0';
256 new_confline(&ctmp);
257 ctmp->flags |= CF_NOSELECT;
258 ctmp->value = cpystr(tmp);
260 new_confline(&ctmp);
261 ctmp->flags |= CF_NOSELECT;
262 ctmp->value = cpystr(_("Select a Client-ID to use with this account"));
264 new_confline(&ctmp);
265 ctmp->flags |= CF_NOSELECT;
266 ctmp->value = cpystr(tmp);
268 new_confline(&ctmp);
269 ctmp->flags |= CF_NOSELECT | CF_B_LINE;
271 sprintf(tmp, _("Alpine cannot determine which client-id to use for the username <%s>"), user);
272 new_confline(&ctmp);
273 ctmp->flags |= CF_NOSELECT;
274 ctmp->value = cpystr(tmp);
276 sprintf(tmp, _("for your %s account. Please select the client-id to use from the following list.\n\n"), xinfo[0]->name);
277 new_confline(&ctmp);
278 ctmp->flags |= CF_NOSELECT;
279 ctmp->value = cpystr(tmp);
281 for(i = 0; xinfo[i] != NULL; i++){
282 new_confline(&ctmp);
283 ctmp->flags |= CF_NOSELECT | CF_B_LINE;
285 if((extra = xoauth2_extra_text(xinfo, i)) != NULL){
286 new_confline(&ctmp);
287 ctmp->flags |= CF_NOSELECT;
288 ctmp->value = cpystr(extra);
289 ctmp->valoffset = 4;
292 new_confline(&ctmp);
293 if(!first_line)
294 first_line = ctmp;
296 ctmp->value = cpystr(xinfo[i]->client_id);
297 ctmp->d.x.selected = &x_sel;
298 ctmp->d.x.pat = copy_xoauth2_info(xinfo[i]);
299 ctmp->keymenu = &xoauth2_id_select_km;
300 ctmp->help = NO_HELP;
301 ctmp->help_title = NULL;
302 ctmp->tool = xoauth2_info_tool;
303 ctmp->flags = CF_STARTITEM;
304 ctmp->valoffset = 4 + (extra ? 3 : 0);
305 if(extra) fs_give((void **) &extra);
307 (void)conf_scroll_screen(ps_global, &screen, first_line, _("SELECT CLIENT_ID"),
308 _("xoauth2"), 0, NULL);
309 return x_sel;
311 return NULL;
314 XOAUTH2_INFO_S **
315 parse_xoauth2_info_list(char **lval, int *totalp)
317 int i, total;
318 XOAUTH2_INFO_S **rv = NULL;
320 for(total = 0; lval && lval[total]; total++);
321 if(total){
322 rv = fs_get((total + 1)*sizeof(XOAUTH2_INFO_S *));
323 for(i = 0; i < total; i++)
324 rv[i] = xoauth_parse_client_info(lval[i]);
325 rv[i] = NULL;
327 *totalp = total;
328 return rv;
332 XOAUTH2_INFO_S **
333 xoauth2_configured_servers(void)
335 XOAUTH2_INFO_S **cv, **muv, **rv = NULL;
336 int cvt, muvt, rvt, i, j, total = 0;
338 cv = parse_xoauth2_info_list(ps_global->vars[V_XOAUTH2_INFO].current_val.l, &cvt);
339 muv = parse_xoauth2_info_list(ps_global->vars[V_XOAUTH2_INFO].main_user_val.l, &muvt);
341 rvt = cvt + muvt;
342 if(rvt){
343 rv = fs_get((rvt + 1)*sizeof(XOAUTH2_INFO_S *));
344 memset((void *) rv, 0, (rvt + 1)*sizeof(XOAUTH2_INFO_S *));
346 for(i = 0; cv && cv[i]; i++){
347 for(j = 0; rv[j] && !same_xoauth2_info(*cv[i], *rv[j]); j++);
348 if(!rv[j]) rv[total++] = copy_xoauth2_info(cv[i]);
350 free_xoauth2_info_list(&cv);
352 for(i = 0; muv && muv[i]; i++){
353 for(j = 0; rv[j] && !same_xoauth2_info(*muv[i], *rv[j]); j++);
354 if(!rv[j]) rv[total++] = copy_xoauth2_info(muv[i]);
356 free_xoauth2_info_list(&muv);
358 return rv;
361 /* Get the client-id, etc. for server "name" associated to user "user" */
362 XOAUTH2_INFO_S *
363 oauth2_get_client_info(unsigned char *name, char *user)
365 int i, j, matches, len;
366 XOAUTH2_INFO_S *x, **xinfo, **lval;
368 if(name == NULL || *name == '\0' || user == NULL || *user == '\0')
369 return NULL;
371 matches = 0;
372 /* first count how many servers */
373 lval = xoauth2_configured_servers();
374 for(i = 0; lval && lval[i]; i++){
375 x = lval[i];
376 if(x && x->name && name && !strcmp((char *) x->name, (char *) name))
377 matches++;
380 /* if nothing, use the default value */
381 for(i = 0; xoauth_default[i].name != NULL && strcmp((char *) xoauth_default[i].name, (char *) name); i++);
382 if(xoauth_default[i].name) matches++;
384 if(matches <= 1){
385 free_xoauth2_info_list(&lval);
386 return matches == 1 ? copy_xoauth2_info(&xoauth_default[i]) : NULL;
389 /* more than one match, see if it is a duplicate client-id entry */
390 xinfo = fs_get((matches + 1)*sizeof(XOAUTH2_INFO_S *));
391 memset((void *)xinfo, 0, (matches + 1)*sizeof(XOAUTH2_INFO_S *));
392 matches = 0; /* restart the recount, it might go lower! */
393 for(i = 0; lval && lval[i]; i++){
394 x = lval[i];
395 if(x && x->name && name && !strcmp((char *) x->name, (char *) name)){
396 for(j = 0; xinfo && xinfo[j] && !same_xoauth2_info(*x, *xinfo[j]); j++);
397 if(!xinfo[j]) xinfo[matches++] = copy_xoauth2_info(x);
400 for(i = 0; xoauth_default[i].name != NULL && strcmp((char *) xoauth_default[i].name, (char *) name); i++);
401 for(j = 0; xinfo && xinfo[j] && !same_xoauth2_info(xoauth_default[i], *xinfo[j]); j++);
402 if(!xinfo[j]) xinfo[matches++] = copy_xoauth2_info(&xoauth_default[i]);
404 /* if after removing the duplicate entries, we only have one, use it */
405 if(matches == 1){
406 x = copy_xoauth2_info(xinfo[0]);
407 free_xoauth2_info_list(&lval);
408 free_xoauth2_info_list(&xinfo);
409 return x;
412 x = NULL; /* reset x to null so it will not be double freed */
413 /* we have more than one match, now check if any of them matches the given user */
414 matches = 0;
415 for(i = 0; xinfo && xinfo[i]; i++){
416 char **alval = array_to_list(xinfo[i]->users);
417 for(j = 0; alval && alval[j] && strucmp(alval[j], user); j++);
418 if(alval && alval[j]){
419 matches++;
420 free_xoauth2_info(&x);
421 x = copy_xoauth2_info(xinfo[i]);
423 if(alval) free_list_array(&alval);
426 /* only one server matches the username */
427 if(matches == 1){
428 free_xoauth2_info_list(&xinfo);
429 free_xoauth2_info_list(&lval);
430 return x;
433 free_xoauth2_info(&x);
435 /* We either have no matches, or have more than one match!
436 * in either case, let the user pick what they want */
437 x = xoauth_info_choice(xinfo, user);
438 free_xoauth2_info_list(&xinfo);
439 free_xoauth2_info_list(&lval);
441 /* Once the user chose a client-id, save it so we do not ask again */
442 if(x != NULL){
443 int n = x->users ? strlen(x->users) + 1 : 0;
444 char ***alval, **lvalp;
446 fs_resize((void **) &x->users, (n + strlen(user) + 1)*sizeof(char));
447 x->users[n > 0 ? n - 1 : 0] = '\0';
448 if(n > 0) strcat(x->users, ",");
449 strcat(x->users, user);
450 alval = ALVAL(&ps_global->vars[V_XOAUTH2_INFO], Main);
451 lvalp = *alval;
453 for(n = 0; lvalp && lvalp[n]; n++);
454 fs_resize((void **) &lvalp, (n+2)*sizeof(char *));
455 lvalp[n] = xoauth_config_line(x);
456 lvalp[n+1] = NULL;
457 *alval = xoauth2_conf_dedup_and_merge(&lvalp);
458 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
459 write_pinerc(ps_global, Main, WRP_NONE);
462 return x;
465 /* write vlist to v
466 * Each vlist member is of type "p", while "v" is of type "l", so we
467 * each entry in "l" by using each of the "p" entries.
469 void
470 write_xoauth_configuration(struct variable *v, struct variable **vlist, EditWhich ew)
472 int i, k, m, n;
473 XOAUTH2_INFO_S *x = NULL;
474 char ***alval, **lval, **l;
475 char *p;
477 for (i = 0, n = 0; vlist[i] != NULL; i++) /* count number of lines we need */
478 if(!strcmp(vlist[i]->name, XOAUTH2_USERS))
479 n++;
480 lval = fs_get((n+1)*sizeof(char *));
481 memset((void *) lval, 0, (n+1)*sizeof(char *));
483 m = -1;
484 alval = ALVAL(v, ew);
485 for (i = 0, k = 0; vlist[i] != NULL; i++){
486 if(x == NULL){
487 x = new_xoauth2_info();
488 x->name = (unsigned char *) cpystr(vlist[i]->descrip); /* hack! but makes life so much easier! */
489 for(m = 0; xoauth_default[m].name != NULL
490 && strcmp((char *) xoauth_default[m].name, (char *) x->name); m++);
492 if (x->client_id == NULL && !strcmp(vlist[i]->name, XOAUTH2_CLIENT_ID)){
493 p = PVAL(vlist[i], ew);
494 if (p == NULL) p = vlist[i]->current_val.p;
495 if(p != NULL)
496 x->client_id = cpystr(p);
497 continue;
499 if (x->client_secret == NULL
500 && m >= 0
501 && xoauth_default[m].client_secret
502 && !strcmp(vlist[i]->name, XOAUTH2_CLIENT_SECRET)){
503 p = PVAL(vlist[i], ew);
504 if (p == NULL) p = vlist[i]->current_val.p;
505 if(p != NULL)
506 x->client_secret = cpystr(p);
507 continue;
509 if (x->tenant == NULL
510 && m >= 0
511 && xoauth_default[m].tenant
512 && !strcmp(vlist[i]->name, XOAUTH2_TENANT)){
513 p = PVAL(vlist[i], ew);
514 if (p == NULL) p = vlist[i]->current_val.p;
515 if(p != NULL)
516 x->tenant = cpystr(p);
517 continue;
519 if (x->flow == NULL && !strcmp(vlist[i]->name, XOAUTH2_FLOW)){
520 p = PVAL(vlist[i], ew);
521 if (p == NULL) p = vlist[i]->current_val.p;
522 if(p != NULL)
523 x->flow = cpystr(p);
524 continue;
526 if (x->users == NULL && !strcmp(vlist[i]->name, XOAUTH2_USERS)){
527 l = LVAL(vlist[i], ew);
528 x->users = list_to_array(l);
530 /* don't let it get to here until we are done! */
531 lval[k++] = xoauth_config_line(x);
532 /* get ready for next run */
533 free_xoauth2_info(&x);
534 m = -1;
536 if(k > 0){
537 lval[k] = NULL;
538 if(*alval) free_list_array(alval);
539 *alval = lval;
541 else
542 *alval = NULL;
543 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
547 /* parse line of the form
548 /NAME="text" /ID="text" /TENANT="text" /SECRET="text" /USER="text"
550 XOAUTH2_INFO_S *
551 xoauth_parse_client_info(char *lvalp)
553 char *s, *t, c;
554 XOAUTH2_INFO_S *x;
556 if (lvalp == NULL) return NULL;
558 x = new_xoauth2_info();
559 if((s = strstr(lvalp, XNAME)) != NULL){
560 s += strlen(XNAME);
561 if(*s == '"') s++;
562 for(t = s; *t && *t != '"' && *t != ' '; t++);
563 c = *t;
564 *t = '\0';
565 if (*s) x->name = (unsigned char *) cpystr(s);
566 *t = c;
567 } else x->name = NULL;
569 if((s = strstr(lvalp, XID)) != NULL){
570 s += strlen(XID);
571 if(*s == '"') s++;
572 for(t = s; *t && *t != '"' && *t != ' '; t++);
573 c = *t;
574 *t = '\0';
575 if (*s) x->client_id = cpystr(s);
576 *t = c;
577 } else x->client_id = NULL;
579 if((s = strstr(lvalp, XTENANT)) != NULL){
580 s += strlen(XTENANT);
581 if(*s == '"') s++;
582 for(t = s; *t && *t != '"' && *t != ' '; t++);
583 c = *t;
584 *t = '\0';
585 if (*s) x->tenant = cpystr(s);
586 *t = c;
587 } else x->tenant = NULL;
589 if((s = strstr(lvalp, XSECRET)) != NULL){
590 s += strlen(XSECRET);
591 if(*s == '"') s++;
592 for(t = s; *t && *t != '"' && *t != ' '; t++);
593 c = *t;
594 *t = '\0';
595 if (*s) x->client_secret = cpystr(s);
596 *t = c;
597 } else x->client_secret = NULL;
599 if((s = strstr(lvalp, XFLOW)) != NULL){
600 s += strlen(XFLOW);
601 if(*s == '"') s++;
602 for(t = s; *t && *t != '"' && *t != ' '; t++);
603 c = *t;
604 *t = '\0';
605 if(*s) x->flow = cpystr(s);
606 *t = c;
607 } else x->flow = NULL;
609 if((s = strstr(lvalp, XUSER)) != NULL){
610 s += strlen(XUSER);
611 if(*s == '"') s++;
612 for(t = s; *t && *t != '"' && *t != ' '; t++);
613 c = *t;
614 *t = '\0';
615 if(*s) x->users = cpystr(s);
616 *t = c;
617 } else x->users = NULL;
619 return x;
622 char **
623 xoauth2_conf_dedup_and_merge(char ***alval)
625 int i, j, l, m;
626 char **lval, **rv;
627 XOAUTH2_INFO_S *x, *y;
629 if(alval == NULL || *alval == NULL || **alval == NULL)
630 return NULL;
632 lval = *alval;
633 for(i = 0; lval[i] != NULL; i++); /* count how many entries */
635 rv = fs_get((i+1)*sizeof(char *));
636 memset((void *)rv, 0, (i+1)*sizeof(char *));
638 for (i = 0; lval[i] != NULL; i++){
639 x = xoauth_parse_client_info(lval[i]);
640 for (j = 0; rv[j] != NULL; j++){
641 y = xoauth_parse_client_info(rv[j]);
642 /* check if this is the same data. If so, merge the users into one and discard the old data */
643 if(same_xoauth2_info(*x, *y)){
644 char **l1, **l2, **l3;
645 int k, n;
646 /* merge user1 with user2, save in x->users */
647 l1 = array_to_list(x->users);
648 l2 = array_to_list(y->users);
650 for(n = 0; l1 && l1[n]; n++);
651 for(m = 0; l2 && l2[m]; m++, n++);
652 l3 = fs_get((n+1)*sizeof(char*));
653 memset((void *) l3, 0, (n+1)*sizeof(char *));
655 for(l = 0, n = 0; l1 && l1[l]; l++){
656 for(k = 0; l1 && l1[j] && l3[k] && strucmp(l1[l], l3[k]); k++);
657 if(l3[k] == NULL && l1 && l1[l] != NULL)
658 l3[n++] = cpystr(l1[l]);
660 for(l = 0; l2 && l2[l]; l++){
661 for(k = 0; l2 && l2[l] && l3[k] && strucmp(l2[l], l3[k]); k++);
662 if(l3[k] == NULL && l2 && l2[l] != NULL)
663 l3[n++] = cpystr(l2[l]);
665 l3[n++] = NULL;
666 if(x->users) fs_give((void **) &x->users);
667 x->users = list_to_array(l3);
668 fs_give((void **) &rv[j]);
669 rv[j] = xoauth_config_line(x);
671 if(l1) free_list_array(&l1);
672 if(l2) free_list_array(&l2);
673 if(l3) free_list_array(&l3);
674 free_xoauth2_info(&y);
675 break;
677 free_xoauth2_info(&y);
679 if(rv[j] == NULL) rv[j] = cpystr(lval[i]);
681 return rv;
685 * X = new value, Y = default configuration
687 void
688 write_xoauth_conf_entry(XOAUTH2_INFO_S *x, XOAUTH2_INFO_S *y, CONF_S **cl, CONF_S **clb, CONF_S **fline,
689 struct variable ***varlistp, int *pp, int ln, int key)
691 CONF_S *ctmpb = *clb;
692 struct variable **varlist;
693 int p = *pp;
694 char tmp[1024], tmp2[16];
696 sprintf(tmp2, "%d", key);
697 varlist = *varlistp;
699 new_confline(cl)->var = NULL;
700 if(fline && !*fline) *fline = *cl;
701 (*cl)->flags |= CF_NOSELECT;
702 (*cl)->help = NO_HELP;
703 (*cl)->valoffset = 1;
704 (*cl)->value = cpystr((char *) x->name);
705 (*cl)->varname = NULL;
706 (*cl)->varnamep = ctmpb = *cl;
708 /* Setup client-id variable */
709 varlist[p] = fs_get(sizeof(struct variable));
710 memset((void *) varlist[p], 0, sizeof(struct variable));
711 varlist[p]->name = cpystr(XOAUTH2_CLIENT_ID);
712 varlist[p]->is_used = 1;
713 varlist[p]->is_user = 1;
714 varlist[p]->main_user_val.p = x->client_id && y->client_id
715 && strcmp(x->client_id, y->client_id) ? cpystr(x->client_id) : NULL;
716 varlist[p]->global_val.p = y->client_id ? cpystr(y->client_id) : NULL;
717 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
718 varlist[p]->descrip = cpystr((char *) x->name); /* hack, but makes life easier! */
719 set_current_val(varlist[p], FALSE, FALSE);
721 /* Write client-id variable */
722 new_confline(cl)->var = varlist[p];
723 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_CLIENT_ID);
724 tmp[sizeof(tmp)-1] = '\0';
725 (*cl)->varname = cpystr(tmp);
726 (*cl)->varmem = p++;
727 (*cl)->valoffset = ln + 3 + 3;
728 (*cl)->value = pretty_value(ps_global, *cl);
729 (*cl)->keymenu = &config_xoauth2_text_keymenu;
730 (*cl)->help = h_config_xoauth2_client_id;
731 (*cl)->tool = text_tool;
732 (*cl)->varnamep = ctmpb;
734 /* Set up client-secret variable */
735 if(x->client_secret){
736 varlist[p] = fs_get(sizeof(struct variable));
737 memset((void *) varlist[p], 0, sizeof(struct variable));
738 varlist[p]->name = cpystr(XOAUTH2_CLIENT_SECRET);
739 varlist[p]->is_used = 1;
740 varlist[p]->is_user = 1;
741 varlist[p]->main_user_val.p = y->client_secret
742 && strcmp(x->client_secret, y->client_secret)
743 ? cpystr(x->client_secret) : NULL;
744 varlist[p]->global_val.p = y->client_secret ? cpystr(y->client_secret) : NULL;
745 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
746 varlist[p]->descrip = cpystr((char *) x->name); /* hack, but makes life easier! */
747 set_current_val(varlist[p], FALSE, FALSE);
749 /* Write client-secret variable */
750 new_confline(cl)->var = varlist[p];
751 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_CLIENT_SECRET);
752 tmp[sizeof(tmp)-1] = '\0';
753 (*cl)->varname = cpystr(tmp);
754 (*cl)->varmem = p++;
755 (*cl)->valoffset = ln + 3 + 3;
756 (*cl)->value = pretty_value(ps_global, *cl);
757 (*cl)->keymenu = &config_xoauth2_text_keymenu;
758 (*cl)->help = h_config_xoauth2_client_secret;
759 (*cl)->tool = text_tool;
760 (*cl)->varnamep = ctmpb;
763 /* Set up tenant variable */
764 if(x->tenant){
765 varlist[p] = fs_get(sizeof(struct variable));
766 memset((void *) varlist[p], 0, sizeof(struct variable));
767 varlist[p]->name = cpystr(XOAUTH2_TENANT);
768 varlist[p]->is_used = 1;
769 varlist[p]->is_user = 1;
770 varlist[p]->main_user_val.p = y->tenant && strcmp(x->tenant, y->tenant)
771 ? cpystr(x->tenant) : NULL;
772 varlist[p]->global_val.p = y->tenant ? cpystr(y->tenant) : NULL;
773 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
774 varlist[p]->descrip = cpystr((char *) x->name); /* hack, but makes life easier! */
775 set_current_val(varlist[p], FALSE, FALSE);
777 /* Write client-secret variable */
778 new_confline(cl)->var = varlist[p];
779 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_TENANT);
780 tmp[sizeof(tmp)-1] = '\0';
781 (*cl)->varname = cpystr(tmp);
782 (*cl)->varmem = p++;
783 (*cl)->valoffset = ln + 3 + 3;
784 (*cl)->value = pretty_value(ps_global, *cl);
785 (*cl)->keymenu = &config_xoauth2_text_keymenu;
786 (*cl)->help = h_config_xoauth2_tenant;
787 (*cl)->tool = text_tool;
788 (*cl)->varnamep = ctmpb;
791 /* Set up flow variable */
792 if(x->flow){
793 varlist[p] = fs_get(sizeof(struct variable));
794 memset((void *) varlist[p], 0, sizeof(struct variable));
795 varlist[p]->name = cpystr(XOAUTH2_FLOW);
796 varlist[p]->is_used = 1;
797 varlist[p]->is_user = 1;
798 varlist[p]->main_user_val.p = cpystr(x->flow);
799 varlist[p]->global_val.p = cpystr(x->flow);
800 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
801 varlist[p]->descrip = cpystr((char *) x->name); /* hack, but makes life easier! */
802 set_current_val(varlist[p], FALSE, FALSE);
804 /* Write client-secret variable */
805 new_confline(cl)->var = varlist[p];
806 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_FLOW);
807 tmp[sizeof(tmp)-1] = '\0';
808 (*cl)->varname = cpystr(tmp);
809 (*cl)->varmem = p++;
810 (*cl)->valoffset = ln + 3 + 3;
811 (*cl)->value = pretty_value(ps_global, *cl);
812 (*cl)->keymenu = &config_xoauth2_text_keymenu;
813 (*cl)->help = h_config_xoauth2_flow;
814 (*cl)->tool = text_tool;
815 (*cl)->varnamep = ctmpb;
818 /* Setup users variable */
819 varlist[p] = fs_get(sizeof(struct variable));
820 memset((void *) varlist[p], 0, sizeof(struct variable));
821 varlist[p]->name = cpystr(XOAUTH2_USERS);
822 varlist[p]->is_used = 1;
823 varlist[p]->is_user = 1;
824 varlist[p]->is_list = 1;
825 varlist[p]->main_user_val.l = x->users ? array_to_list(x->users) : NULL;
826 varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */
827 varlist[p]->descrip = cpystr((char *) x->name); /* hack, but makes life easier! */
828 set_current_val(varlist[p], FALSE, FALSE);
830 /* Write user variable */
831 new_confline(cl)->var = varlist[p];
832 utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_USERS);
833 tmp[sizeof(tmp)-1] = '\0';
834 (*cl)->varname = cpystr(tmp);
835 (*cl)->valoffset = ln + 3 + 3;
836 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
837 (*cl)->help = h_config_xoauth2_username;
838 if(x->users){
839 int z;
840 for(z = 0; varlist[p]->main_user_val.l[z]; z++){
841 if(z) new_confline(cl);
842 (*cl)->var = varlist[p];
843 (*cl)->varmem = z;
844 (*cl)->valoffset = ln + 3 + 3;
845 (*cl)->value = pretty_value(ps_global, *cl);
846 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
847 (*cl)->tool = text_tool;
848 (*cl)->varnamep = ctmpb = *cl;
851 else {
852 (*cl)->varmem = 0;
853 (*cl)->value = pretty_value(ps_global, *cl);
854 (*cl)->keymenu = &config_xoauth2_wshuf_keymenu;
855 (*cl)->tool = text_tool;
856 (*cl)->varnamep = ctmpb = *cl;
858 p++;
859 *pp = p;
860 *varlistp = varlist;
861 *clb = ctmpb;
863 /* Separate servers with a blank line */
864 new_confline(cl);
865 (*cl)->flags |= CF_NOSELECT | CF_B_LINE;
868 /*----------------------------------------------------------------------
869 Screen to add client_id and client_secret for a service
871 ---*/
872 void
873 alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions)
875 struct variable **varlist = NULL;
876 char **lval, ***alval;
877 int i, j, k, p, ln = 0, readonly_warning = 0, pos, count_vars;
878 XTYPES m;
879 CONF_S *ctmpa = NULL, *ctmpb, *first_line;
880 PINERC_S *prc = NULL;
881 OPT_SCREEN_S screen;
882 int expose_hidden_config;
883 SAVED_CONFIG_S *vsave;
884 XOAUTH2_INFO_S *y;
886 dprint((3, "-- alpine_xoauth2_configuration --\n"));
888 expose_hidden_config = F_ON(F_EXPOSE_HIDDEN_CONFIG, ps_global);
889 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
891 if(ps->restricted)
892 readonly_warning = 1;
893 else{
894 switch(ew){
895 case Main:
896 prc = ps->prc;
897 break;
898 case Post:
899 prc = ps->post_prc;
900 break;
901 default:
902 break;
905 readonly_warning = prc ? prc->readonly : 1;
908 ps->next_screen = SCREEN_FUN_NULL;
910 mailcap_free(); /* free resources we won't be using for a while */
912 pos = -1;
913 for(ln = 0, m = Xid; m < Xend; m++){
914 i = strlen(x_default[m].screen_name);
915 if(ln < i) ln = i;
918 alval = ALVAL(&ps->vars[V_XOAUTH2_INFO], ew);
919 lval = *alval = xoauth2_conf_dedup_and_merge(alval);
920 set_current_val(&ps_global->vars[V_XOAUTH2_INFO], FALSE, FALSE);
921 write_pinerc(ps_global, ew, WRP_NONE);
923 do {
924 ctmpa = first_line = NULL;
926 for(i = 0, count_vars = 0; xoauth_default[i].name != NULL; i++){
927 /* always start with the default configuration */
928 for(k = 0; lval && lval[k]; k++){
929 y = xoauth_parse_client_info(lval[k]);
930 if(same_xoauth2_info(xoauth_default[i], *y))
931 break;
932 free_xoauth2_info(&y);
934 if(lval == NULL || lval[k] == NULL){
935 count_vars += 3;
936 if(xoauth_default[i].client_secret) count_vars++;
937 if(xoauth_default[i].tenant) count_vars++;
939 for(k = 0; lval && lval[k]; k++){
940 y = xoauth_parse_client_info(lval[k]);
941 if(y && (!y->name || strcmp((char *) y->name, (char *) xoauth_default[i].name))){
942 free_xoauth2_info(&y);
943 continue;
945 count_vars += 3;
946 if(xoauth_default[i].client_secret != NULL) count_vars++;
947 if(xoauth_default[i].tenant != NULL) count_vars++;
948 free_xoauth2_info(&y);
952 for(i = 0; varlist && varlist[i]; i++){
953 free_variable_values(varlist[i]);
954 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
955 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
956 fs_give((void **) &varlist[i]);
958 if(varlist) fs_give((void **) varlist);
960 varlist = fs_get((count_vars + 1)*sizeof(struct variable *));
961 memset((void *) varlist, 0, (count_vars +1)*sizeof(struct variable *));
963 for(i = 0, p = 0; xoauth_default[i].name != NULL; i++){
964 /* always start with the default configuration */
965 for(k = 0; lval && lval[k]; k++){
966 y = xoauth_parse_client_info(lval[k]);
967 if(same_xoauth2_info(xoauth_default[i], *y))
968 break;
969 free_xoauth2_info(&y);
971 if(lval == NULL || lval[k] == NULL){
972 OAUTH2_S *oa2list;
973 for(oa2list = alpine_oauth2_list; oa2list && oa2list->name; oa2list++){
974 if(oa2list->hide) continue;
975 if(!strcmp((char *) oa2list->name, (char *) xoauth_default[i].name)){
976 xoauth_default[i].flow = cpystr(oa2list->server_mthd[0].name ? "Authorize"
977 : (oa2list->server_mthd[1].name ? "Device" : "Unknown"));
978 write_xoauth_conf_entry(&xoauth_default[i], &xoauth_default[i], &ctmpa, &ctmpb,
979 &first_line, &varlist, &p, ln, -i-1);
980 fs_give((void **) &xoauth_default[i].flow);
981 break; /* just one entry, set the default to the first entry */
985 for(k = 0; lval && lval[k]; k++){
986 OAUTH2_S *oa2list, *oa2;
988 y = xoauth_parse_client_info(lval[k]);
989 if(y && (!y->name || strcmp((char *) y->name, (char *) xoauth_default[i].name))){
990 free_xoauth2_info(&y);
991 continue;
993 if(y->client_id == NULL)
994 y->client_id = cpystr(xoauth_default[i].client_id);
995 if(y->client_secret == NULL && xoauth_default[i].client_secret != NULL)
996 y->client_secret = cpystr(xoauth_default[i].client_secret);
997 if(y->tenant == NULL && xoauth_default[i].tenant != NULL)
998 y->tenant = cpystr(xoauth_default[i].tenant);
999 for(oa2 = NULL, oa2list = alpine_oauth2_list; oa2 == NULL && oa2list; oa2list++)
1000 if(!strcmp((char *) oa2list->name, (char *) y->name)) oa2 = oa2list;
1001 if(oa2 && y->flow == NULL)
1002 y->flow = cpystr(oa2->server_mthd[0].name ? "Authorize"
1003 : (oa2->server_mthd[1].name ? "Device" : "Unknown"));
1004 if(oa2 && !oa2->hide)
1005 write_xoauth_conf_entry(y, &xoauth_default[i], &ctmpa, &ctmpb, &first_line, &varlist, &p, ln, k);
1006 free_xoauth2_info(&y);
1010 vsave = save_config_vars(ps, expose_hidden_config);
1011 first_line = pos < 0 ? first_sel_confline(first_line) : set_confline_number(first_line, pos);
1012 pos = -1;
1013 memset(&screen, 0, sizeof(screen));
1014 screen.ro_warning = readonly_warning;
1015 /* TRANSLATORS: Print something1 using something2.
1016 "configuration" is something1 */
1017 switch(conf_scroll_screen(ps, &screen, first_line, "XOAUTH2 Alpine Info",
1018 _("configuration"), 0, &pos)){
1019 case 0:
1020 break;
1022 case 1:
1023 write_xoauth_configuration(&ps->vars[V_XOAUTH2_INFO], varlist, ew);
1024 write_pinerc(ps, ew, WRP_NONE);
1025 break;
1027 case 4: /* add a service */
1028 {char service[MAILTMPLEN+1];
1029 char prompt[MAILTMPLEN+1];
1030 int flags = OE_DISALLOW_HELP;
1031 strncpy(prompt, _("Enter service name: "), sizeof(prompt));
1032 prompt[sizeof(prompt) - 1] = '\0';
1033 service[0] = '\0';
1034 if(optionally_enter(service,
1035 -(ps_global->ttyo ? FOOTER_ROWS(ps_global) : 3),
1036 0, sizeof(service), prompt, NULL, NO_HELP, &flags) == 0){
1037 for(i = 0; xoauth_default[i].name != NULL && strucmp((char *) xoauth_default[i].name, service); i++);
1038 if(xoauth_default[i].name == NULL)
1039 q_status_message1(SM_ORDER, 3, 3, _("Service %s not known"), service);
1040 else{
1041 char **list;
1042 ClearScreen();
1043 ps_global->mangled_screen = 1;
1044 for(j = 0; lval && lval[j]; j++);
1045 list = fs_get((j+2)*sizeof(char *));
1046 memset((void *)list, 0, (j+2)*sizeof(char *));
1047 list[0] = xoauth_config_line(&xoauth_default[i]);
1048 for(i = 0; lval && lval[i]; i++)
1049 list[i+1] = cpystr(lval[i]);
1050 if(lval) free_list_array(&lval);
1051 *alval = lval = list;
1052 for(i = 0; varlist && varlist[i]; i++){
1053 free_variable_values(varlist[i]);
1054 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
1055 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
1056 fs_give((void **) &varlist[i]);
1058 if(varlist) fs_give((void **) varlist);
1062 break;
1064 case 5: /* delete a service */
1065 { int m, key;
1066 XOAUTH2_INFO_S *x;
1067 char question[MAILTMPLEN];
1069 for(i = 0, m = 1, j = 0; varlist[i] && m < pos;)
1070 if(!varlist[i]->is_list){
1071 i++; m++;
1072 } else {
1073 if(varlist[i]
1074 && varlist[i]->current_val.l
1075 && varlist[i]->current_val.l[j++])
1076 m++;
1077 else{
1078 j = 0; m += 2; i++;
1081 key = atoi(varlist[i]->dname); /* this hack avoids we rebuild varlist again */
1082 if(key >= 0){
1083 x = xoauth_parse_client_info(lval[key]);
1084 snprintf(question, sizeof(question), _("Delete this configuration for %s "), x->name);
1085 free_xoauth2_info(&x);
1086 if(want_to(question, 'n', 'n', NO_HELP, WT_NORM) != 'y')
1087 break;
1088 for(i = key; lval && lval[i] && lval[i+1]; i++){
1089 fs_give((void **) &lval[i]);
1090 lval[i] = cpystr(lval[i+1]);
1092 fs_give((void **) &lval[i]);
1094 else {
1095 q_status_message(SM_ORDER, 3, 3, _("Cannot delete default configuration"));
1096 break;
1098 if(lval && lval[0] == NULL)
1099 free_list_array(&lval);
1100 *alval = lval;
1101 pos = 1; /* reset at the top */
1102 for(i = 0; varlist && varlist[i]; i++){
1103 free_variable_values(varlist[i]);
1104 if(varlist[i]->descrip) fs_give((void **) &varlist[i]->descrip);
1105 if(varlist[i]->dname) fs_give((void **) &varlist[i]->dname);
1106 fs_give((void **) &varlist[i]);
1108 if(varlist) fs_give((void **) varlist);
1110 break;
1112 case 10:
1113 revert_to_saved_config(ps, vsave, expose_hidden_config);
1114 if(prc)
1115 prc->outstanding_pinerc_changes = 0;
1116 break;
1118 default:
1119 q_status_message(SM_ORDER,7,10,
1120 "conf_scroll_screen bad ret, not supposed to happen");
1121 break;
1123 } while (pos >= 0);
1125 #ifdef _WINDOWS
1126 mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
1127 #endif