diff options
-rw-r--r-- | alpine/conftype.h | 4 | ||||
-rw-r--r-- | alpine/imap.c | 227 | ||||
-rw-r--r-- | alpine/keymenu.c | 15 | ||||
-rw-r--r-- | alpine/keymenu.h | 1 | ||||
-rw-r--r-- | alpine/xoauth2.h | 2 | ||||
-rw-r--r-- | alpine/xoauth2conf.c | 98 | ||||
-rw-r--r-- | imap/src/c-client/mail.h | 1 | ||||
-rw-r--r-- | imap/src/c-client/oauth2_aux.c | 2 | ||||
-rw-r--r-- | pith/pine.hlp | 36 |
9 files changed, 352 insertions, 34 deletions
diff --git a/alpine/conftype.h b/alpine/conftype.h index 9c25ad6b..21c7e456 100644 --- a/alpine/conftype.h +++ b/alpine/conftype.h @@ -104,6 +104,10 @@ typedef struct conf_line { XOAUTH2_INFO_S *pat; XOAUTH2_INFO_S **selected; } x; + struct xoauth2_flow { + OAUTH2_S *pat; + OAUTH2_S **selected; /* of type XOAUTH2_S */ + } xf; } d; } CONF_S; diff --git a/alpine/imap.c b/alpine/imap.c index 03685707..fe4fdcec 100644 --- a/alpine/imap.c +++ b/alpine/imap.c @@ -40,6 +40,8 @@ static char rcsid[] = "$Id: imap.c 1266 2009-07-14 18:39:12Z hubert@u.washington #include "busy.h" #include "titlebar.h" #include "xoauth2.h" +#include "xoauth2conf.h" +#include "confscroll.h" #include "init.h" #include "../pith/state.h" #include "../pith/conf.h" @@ -103,6 +105,8 @@ void mm_login_alt_cue(NETMBX *); long pine_tcptimeout_noscreen(long, long, char *); int answer_cert_failure(int, MSGNO_S *, SCROLL_S *); int oauth2_auth_answer(int, MSGNO_S *, SCROLL_S *); +OAUTH2_S *oauth2_select_flow(char *); +int xoauth2_flow_tool(struct pine *, int, CONF_S **, unsigned int); #ifdef LOCAL_PASSWD_CACHE int read_passfile(char *, MMLOGIN_S **); @@ -199,9 +203,159 @@ OAUTH2_S alpine_oauth2_list[] = 0, /* first time indicator */ 0 /* client secret required */ }, + {"Outlook", + {"outlook.office365.com", "smtp.office365.com", NULL, NULL}, + {{"client_id", NULL}, + {"client_secret", NULL}, /* not used, but needed */ + {"tenant", NULL}, /* used */ + {"code", NULL}, /* used during authorization */ + {"refresh_token", NULL}, + {"scope", "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send"}, + {"redirect_uri", "http://localhost"}, + {"grant_type", "authorization_code"}, + {"grant_type", "refresh_token"}, + {"response_type", "code"}, + {"state", NULL}, /* not used */ + {"device_code", NULL} /* not used */ + }, + {{"GET", "https://login.microsoftonline.com/\001/oauth2/v2.0/authorize", /* Get Access Code */ + {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End}}, + {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ + {"POST", "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get first Refresh Token and Access token */ + {OA2_Id, OA2_Redirect, OA2_Scope, OA2_GrantTypeforAccessToken, OA2_Secret, OA2_Code, OA2_End}}, + {"POST", "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get access token from refresh token */ + {OA2_Id, OA2_RefreshToken, OA2_Scope, OA2_GrantTypefromRefreshToken, OA2_Secret, OA2_End, OA2_End}} + }, + {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information, not used */ + NULL, /* access token */ + 0, /* expiration time */ + 0, /* first time indicator */ + 1 /* client secret required */ + }, { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, }; +int +xoauth2_flow_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags) +{ + int rv = 0; + + switch(cmd){ + case MC_CHOICE: + *((*cl)->d.xf.selected) = (*cl)->d.xf.pat; + rv = simple_exit_cmd(flags); + + case MC_EXIT: + rv = simple_exit_cmd(flags); + break; + + default: + rv = -1; + } + + if(rv > 0) + ps->mangled_body = 1; + + return rv; +} + +OAUTH2_S * +oauth2_select_flow(char *host) +{ + OAUTH2_S *oa2list, *oa2; + int i, n, rv; + char *method; + + if(ps_global->ttyo){ + CONF_S *ctmp = NULL, *first_line = NULL; + OAUTH2_S *x_sel = NULL; + OPT_SCREEN_S screen; + char tmp[1024]; + + dprint((9, "xoauth2 select flow")); + ps_global->next_screen = SCREEN_FUN_NULL; + + memset(&screen, 0, sizeof(screen)); + + for(i = 0; i < sizeof(tmp) && i < ps_global->ttyo->screen_cols; i++) + tmp[i] = '-'; + tmp[i] = '\0'; + + new_confline(&ctmp); + ctmp->flags |= CF_NOSELECT; + ctmp->value = cpystr(tmp); + + new_confline(&ctmp); + ctmp->flags |= CF_NOSELECT; + ctmp->value = cpystr(_("Please select below the authorization flow you would like to follow:")); + + new_confline(&ctmp); + ctmp->flags |= CF_NOSELECT; + ctmp->value = cpystr(tmp); + + for(oa2list = alpine_oauth2_list; oa2list && oa2list->name ;oa2list++){ + for(i = 0; oa2list && oa2list->host && oa2list->host[i] && strucmp(oa2list->host[i], host); i++); + if(oa2list && oa2list->host && i < OAUTH2_TOT_EQUIV && oa2list->host[i]){ + new_confline(&ctmp); + if(!first_line) + first_line = ctmp; + method = oa2list->server_mthd[0].name ? "Authorize" + : (oa2list->server_mthd[1].name ? "Device" : "Unknown"); + sprintf(tmp, "%s (%s)", oa2list->name, method); + ctmp->value = cpystr(tmp); + ctmp->d.xf.selected = &x_sel; + ctmp->d.xf.pat = oa2list; + ctmp->keymenu = &xoauth2_id_select_km; + ctmp->help = NO_HELP; + ctmp->help_title = NULL; + ctmp->tool = xoauth2_flow_tool; + ctmp->flags = CF_STARTITEM; + ctmp->valoffset = 4; + } + } + (void)conf_scroll_screen(ps_global, &screen, first_line, _("SELECT AUTHORIZATION FLOW"), + _("xoauth2"), 0, NULL); + oa2 = x_sel; + } + else{ + char *s; + char prompt[1024]; + char reply[1024]; + int sel, j; + + for(oa2list = alpine_oauth2_list; oa2list && oa2list->name ;oa2list++) + n += strlen(oa2list->name); + 5; /* number, parenthesis, space */ + n += 1024; /* large enough to display to lines of 80 characters in UTF-8 */ + s = fs_get(n*sizeof(char)); + strcpy(s, _("Please select below the authorization flow you would like to follow:")); + sprintf(s + strlen(s), _("Please select the client-id to use from the following list.\n\n")); + for(j = 1, oa2list = alpine_oauth2_list; oa2list && oa2list->name ;oa2list++){ + for(i = 0; oa2list && oa2list->host && oa2list->host[i] && strucmp(oa2list->host[i], host); i++); + if(oa2list && oa2list->host && i < OAUTH2_TOT_EQUIV && oa2list->host[i]) + sprintf(s + strlen(s), " %d) %.70s\n", j++, oa2list->name); + } + display_init_err(s, 0); + + strncpy(prompt, _("Enter your selection number: "), sizeof(prompt)); + prompt[sizeof(prompt)-1] = '\0'; + do{ + rv = optionally_enter(reply, 0, 0, sizeof(reply), prompt, NULL, NO_HELP, 0); + sel = atoi(reply); + rv = (sel >= 0 && sel < i) ? 0 : -1; + } while (rv != 0); + + for(j = 1, oa2list = alpine_oauth2_list; oa2list && oa2list->name ;oa2list++){ + for(i = 0; oa2list && oa2list->host && oa2list->host[i] && strucmp(oa2list->host[i], host); i++); + if(oa2list && oa2list->host && i < OAUTH2_TOT_EQUIV && oa2list->host[i]){ + if(j == sel) break; + else j++; + } + } + oa2 = oa2list; + } + return oa2; +} + typedef struct auth_code_s { char *code; int answer; @@ -512,7 +666,8 @@ oauth2_get_access_code(unsigned char *url, char *method, OAUTH2_S *oauth2, int * ps_global->painted_footer_on_startup = 0; } while (user_input.answer != 'e'); - if(!struncmp(user_input.code, "https://", 8)){ + if(!struncmp(user_input.code, "http://", 7) + || !struncmp(user_input.code, "https://", 8)){ char *s, *t; s = strstr(user_input.code, "code="); if(s != NULL){ @@ -594,7 +749,8 @@ try_wantto: rc = optionally_enter(token, q_line, 0, MAILTMPLEN, prompt, NULL, NO_HELP, &flags); } while (rc != 0 && rc != 1); - if(!struncmp(token, "https://", 8)){ + if(!struncmp(token, "http://", 7) + || !struncmp(token, "https://", 8)){ char *s, *t; s = strstr(token, "code="); if(s != NULL){ @@ -680,7 +836,9 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, int save_in_init; int registered; int ChangeAccessToken, ChangeRefreshToken, ChangeExpirationTime; - OAUTH2_S *oa2list; + OAUTH2_S *oa2list, *oa2; + XOAUTH2_INFO_S *x; + unsigned long OldExpirationTime, NewExpirationTime, SaveExpirationTime; #if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE) int preserve_password = -1; @@ -723,13 +881,25 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, } } + if(trial == 0L && !altuserforcache){ + if(*mb->user != '\0') + strncpy(user, mb->user, NETMAXUSER); + else{ + flags = OE_APPEND_CURRENT; + sprintf(prompt, "%s: %s - %s: ", hostlabel, mb->orighost, userlabel); + rc = optionally_enter(user, q_line, 0, NETMAXUSER, + prompt, NULL, NO_HELP, &flags); + } + user[NETMAXUSER-1] = '\0'; + } + /* * We check to see if the server we are going to log in to is already * registered. This gives us a list of servers with the same * credentials, so we use the same credentials for all of them. */ - for(registered = 0, oa2list = alpine_oauth2_list; + for(registered = 0, oa2list = alpine_oauth2_list; oa2list && oa2list->host != NULL && oa2list->host[0] != NULL; oa2list++){ for(i = 0; i < OAUTH2_TOT_EQUIV @@ -742,6 +912,25 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, } if(registered){ + x = oauth2_get_client_info(oa2list->name, user); + if(x && x->flow){ + for(oa2list = alpine_oauth2_list; + oa2list && oa2list->host != NULL && oa2list->host[0] != NULL; + oa2list++){ + for(i = 0; i < OAUTH2_TOT_EQUIV + && oa2list->host[i] != NULL + && strucmp(oa2list->host[i], mb->orighost) != 0; i++); + if(i < OAUTH2_TOT_EQUIV && oa2list->host[i] != NULL){ + char *flow = oa2list->server_mthd[0].name ? "Authorize" + : (oa2list->server_mthd[1].name ? "Device" : "Unknown"); + if(!strucmp(x->flow, flow)) break; /* found it */ + } + } + } + /* else use the one we found earlier, the user has to configure this better */ + } + + if(registered){ hostlist2[i = 0].name = mb->host; if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)) hostlist2[++i].name = mb->orighost; @@ -766,18 +955,6 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, * and use the access token. */ if(trial == 0L && !altuserforcache){ - int code; - - if(*mb->user != '\0') - strncpy(user, mb->user, NETMAXUSER); - else{ - flags = OE_APPEND_CURRENT; - sprintf(prompt, "%s: %s - %s: ", hostlabel, mb->orighost, userlabel); - rc = optionally_enter(user, q_line, 0, NETMAXUSER, - prompt, NULL, NO_HELP, &flags); - } - user[NETMAXUSER-1] = '\0'; - /* Search for a refresh token that is already loaded ... */ if(imap_get_passwd_auth(mm_login_list, &token, user, registered ? hostlist2 : hostlist, @@ -845,6 +1022,24 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, } else login->first_time++; + if(login->first_time){ /* count how many authorization methods we support */ + int nmethods, i, j; + + for(nmethods = 0, oa2 = alpine_oauth2_list; oa2 && oa2->name ; oa2++){ + for(j = 0; j < OAUTH2_TOT_EQUIV + && oa2 + && oa2->host[j] != NULL + && strucmp(oa2->host[j], mb->orighost) != 0; j++); + if(oa2 && oa2->host && j < OAUTH2_TOT_EQUIV && oa2->host[j]) + nmethods++; + } + + if(nmethods > 1) + oa2list = oauth2_select_flow(mb->orighost); + + if(!oa2list) registered = 0; + } + /* Default to saving what we already had saved */ SaveRefreshToken = NewRefreshToken; diff --git a/alpine/keymenu.c b/alpine/keymenu.c index 8d94e878..47350d69 100644 --- a/alpine/keymenu.c +++ b/alpine/keymenu.c @@ -1797,6 +1797,21 @@ struct key role_select_keys[] = WHEREIS_MENU}; INST_KEY_MENU(role_select_km, role_select_keys); +struct key xoauth2_flow_select_keys[] = + {HELP_MENU, + NULL_MENU, + {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE}, + {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE}, + {"P", N_("PrevFlow"), {MC_PREVITEM, 1, {'p'}}, KS_NONE}, + {"N", N_("NextFlow"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE}, + PREVPAGE_MENU, + NEXTPAGE_MENU, + HOMEKEY_MENU, + ENDKEY_MENU, + NULL_MENU, + WHEREIS_MENU}; +INST_KEY_MENU(xoauth2_flow_select_km, xoauth2_flow_select_keys); + struct key xoauth2_id_select_keys[] = {HELP_MENU, NULL_MENU, diff --git a/alpine/keymenu.h b/alpine/keymenu.h index 3cf4e2cc..4664ed90 100644 --- a/alpine/keymenu.h +++ b/alpine/keymenu.h @@ -651,6 +651,7 @@ extern struct key_menu cancel_keymenu, config_checkbox_keymenu, config_text_keymenu, xoauth2_id_select_km, + xoauth2_flow_select_km, config_xoauth2_text_keymenu, config_text_to_charsets_keymenu, config_radiobutton_keymenu, diff --git a/alpine/xoauth2.h b/alpine/xoauth2.h index a50a1e8f..739a3b71 100644 --- a/alpine/xoauth2.h +++ b/alpine/xoauth2.h @@ -22,7 +22,7 @@ #define OUTLOOK_NAME "Outlook" #define OUTLOOK_ID "f21dcaf2-8020-469b-8135-343bfc35d046" -#define OUTLOOK_SECRET NULL +#define OUTLOOK_SECRET "Tk-DAcEi13-FeSsY_Ja4Y.-MyL66I.wIPt" #define OUTLOOK_TENANT "common" #endif /* ALPINE_XOAUTH2_INCLUDED */ diff --git a/alpine/xoauth2conf.c b/alpine/xoauth2conf.c index 7af36796..f930be6b 100644 --- a/alpine/xoauth2conf.c +++ b/alpine/xoauth2conf.c @@ -26,12 +26,12 @@ extern OAUTH2_S alpine_oauth2_list[]; XOAUTH2_INFO_S xoauth_default[] = { - { GMAIL_NAME, GMAIL_ID, GMAIL_SECRET, GMAIL_TENANT, NULL}, - { OUTLOOK_NAME, OUTLOOK_ID, OUTLOOK_SECRET, OUTLOOK_TENANT, NULL}, - { NULL, NULL, NULL, NULL, NULL} + { GMAIL_NAME, GMAIL_ID, GMAIL_SECRET, GMAIL_TENANT, NULL, NULL}, + { OUTLOOK_NAME, OUTLOOK_ID, OUTLOOK_SECRET, OUTLOOK_TENANT, NULL, NULL}, + { NULL, NULL, NULL, NULL, NULL, NULL} }; -typedef enum {Xname = 0, Xid, Xsecret, Xtenant, Xuser, Xend} XTYPES; +typedef enum {Xname = 0, Xid, Xsecret, Xtenant, Xuser, XFlow, Xend} XTYPES; typedef struct xoauh2_info_val_s { char *screen_name; @@ -45,6 +45,7 @@ XOAUTH2_INFO_VAL_S x_default[] = { {"Client-Secret", "/SECRET="}, {"Tenant", "/TENANT="}, {"Username", "/USER="}, + {"Auth Flow", "/Flow="}, {NULL, NULL} }; @@ -53,11 +54,13 @@ XOAUTH2_INFO_VAL_S x_default[] = { #define XSECRET x_default[Xsecret].pinerc_name #define XTENANT x_default[Xtenant].pinerc_name #define XUSER x_default[Xuser].pinerc_name +#define XFLOW x_default[XFlow].pinerc_name #define XOAUTH2_CLIENT_ID x_default[Xid].screen_name #define XOAUTH2_CLIENT_SECRET x_default[Xsecret].screen_name #define XOAUTH2_TENANT x_default[Xtenant].screen_name #define XOAUTH2_USERS x_default[Xuser].screen_name +#define XOAUTH2_FLOW x_default[XFlow].screen_name char *list_to_array(char **); char **array_to_list(char *); @@ -128,7 +131,9 @@ xoauth_config_line(XOAUTH2_INFO_S *x) + strlen(x->client_secret ? XSECRET : "") + strlen(x->client_secret ? x->client_secret : "") + strlen(x->tenant ? XTENANT : "") + strlen(x->tenant ? x->tenant : "") + strlen(XUSER) + strlen(x->users ? x->users : "") - + 2 + 3 + (x->client_secret ? 3 : 0) + (x->tenant ? 3 : 0) + 3 + 1; + + strlen(XFLOW) + strlen(x->flow ? x->flow : "") + + 2 + 3 + (x->client_secret ? 3 : 0) + (x->tenant ? 3 : 0) + + 3 + (x->flow ? 3 : 0) + 1; rv = fs_get(n*sizeof(char)); sprintf(rv, "%s\"%s\" %s\"%s\"", XNAME, x->name, XID, x->client_id); if(x->client_secret) @@ -136,6 +141,8 @@ xoauth_config_line(XOAUTH2_INFO_S *x) if(x->tenant) sprintf(rv + strlen(rv), " %s\"%s\"", XTENANT, x->tenant); sprintf(rv + strlen(rv), " %s\"%s\"", XUSER, x->users ? x->users : ""); + if(x->flow) + sprintf(rv + strlen(rv), " %s\"%s\"", XFLOW, x->flow ? x->flow : ""); return rv; } @@ -419,6 +426,13 @@ write_xoauth_configuration(struct variable *v, struct variable **vlist, EditWhi x->tenant = cpystr(p); continue; } + if (x->flow == NULL && !strcmp(vlist[i]->name, XOAUTH2_FLOW)){ + p = PVAL(vlist[i], ew); + if (p == NULL) p = vlist[i]->current_val.p; + if(p != NULL) + x->flow = cpystr(p); + continue; + } if (x->users == NULL && !strcmp(vlist[i]->name, XOAUTH2_USERS)){ l = LVAL(vlist[i], ew); x->users = list_to_array(l); @@ -492,6 +506,16 @@ xoauth_parse_client_info(char *lvalp) *t = c; } else x->client_secret = NULL; + if((s = strstr(lvalp, XFLOW)) != NULL){ + s += strlen(XFLOW); + if(*s == '"') s++; + for(t = s; *t && *t != '"' && *t != ' '; t++); + c = *t; + *t = '\0'; + if(*s) x->flow = cpystr(s); + *t = c; + } else x->flow = NULL; + if((s = strstr(lvalp, XUSER)) != NULL){ s += strlen(XUSER); if(*s == '"') s++; @@ -674,6 +698,33 @@ write_xoauth_conf_entry(XOAUTH2_INFO_S *x, XOAUTH2_INFO_S *y, CONF_S **cl, CONF_ (*cl)->varnamep = ctmpb; } + /* Set up flow variable */ + if(x->flow){ + varlist[p] = fs_get(sizeof(struct variable)); + memset((void *) varlist[p], 0, sizeof(struct variable)); + varlist[p]->name = cpystr(XOAUTH2_FLOW); + varlist[p]->is_used = 1; + varlist[p]->is_user = 1; + varlist[p]->main_user_val.p = cpystr(x->flow); + varlist[p]->global_val.p = cpystr(x->flow); + varlist[p]->dname = cpystr(tmp2); /* hack, but makes life easier! */ + varlist[p]->descrip = cpystr(x->name); /* hack, but makes life easier! */ + set_current_val(varlist[p], FALSE, FALSE); + + /* Write client-secret variable */ + new_confline(cl)->var = varlist[p]; + utf8_snprintf(tmp, sizeof(tmp), " %-*.100w =", ln, XOAUTH2_FLOW); + tmp[sizeof(tmp)-1] = '\0'; + (*cl)->varname = cpystr(tmp); + (*cl)->varmem = p++; + (*cl)->valoffset = ln + 3 + 3; + (*cl)->value = pretty_value(ps_global, *cl); + (*cl)->keymenu = &config_xoauth2_text_keymenu; + (*cl)->help = h_config_xoauth2_flow; + (*cl)->tool = text_tool; + (*cl)->varnamep = ctmpb; + } + /* Setup users variable */ varlist[p] = fs_get(sizeof(struct variable)); memset((void *) varlist[p], 0, sizeof(struct variable)); @@ -738,6 +789,7 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) char *name_lval, *id_lval, *tenant_lval, *secret_lval, *user_lval, *id_def, *tenant_def, *secret_def; int i, j, k, l, p, q, ln = 0, readonly_warning = 0, pos, count_vars; + XTYPES m; CONF_S *ctmpa = NULL, *ctmpb, *first_line; FEATURE_S *feature; PINERC_S *prc = NULL; @@ -773,13 +825,10 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) mailcap_free(); /* free resources we won't be using for a while */ pos = -1; - ln = strlen(XOAUTH2_CLIENT_ID); - i = strlen(XOAUTH2_CLIENT_SECRET); - if(ln < i) ln = i; - i = strlen(XOAUTH2_TENANT); - if(ln < i) ln = i; - i = strlen(XOAUTH2_USERS); - if(ln < i) ln = i; + for(ln = 0, m = Xid; m < Xend; m++){ + i = strlen(x_default[m].screen_name); + if(ln < i) ln = i; + } alval = ALVAL(&ps->vars[V_XOAUTH2_INFO], ew); lval = *alval = xoauth2_conf_dedup_and_merge(alval); @@ -798,7 +847,7 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) free_xoauth2_info(&y); } if(lval == NULL || lval[k] == NULL){ - count_vars += 2; + count_vars += 3; if(xoauth_default[i].client_secret) count_vars++; if(xoauth_default[i].tenant) count_vars++; } @@ -808,7 +857,7 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) free_xoauth2_info(&y); continue; } - count_vars += 2; + count_vars += 3; if(xoauth_default[i].client_secret != NULL) count_vars++; if(xoauth_default[i].tenant != NULL) count_vars++; free_xoauth2_info(&y); @@ -834,10 +883,22 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) break; free_xoauth2_info(&y); } - if(lval == NULL || lval[k] == NULL) - write_xoauth_conf_entry(&xoauth_default[i], &xoauth_default[i], &ctmpa, &ctmpb, + if(lval == NULL || lval[k] == NULL){ + OAUTH2_S *oa2list; + for(oa2list = alpine_oauth2_list; oa2list; oa2list++){ + if(!strcmp(oa2list->name,xoauth_default[i].name)){ + xoauth_default[i].flow = cpystr(oa2list->server_mthd[0].name ? "Authorize" + : (oa2list->server_mthd[1].name ? "Device" : "Unknown")); + write_xoauth_conf_entry(&xoauth_default[i], &xoauth_default[i], &ctmpa, &ctmpb, &first_line, &varlist, &p, ln, -i-1); + fs_give((void **) &xoauth_default[i].flow); + break; /* just one entry, set the default to the first entry */ + } + } + } for(k = 0, q = 0; lval && lval[k]; k++){ + OAUTH2_S *oa2list, *oa2; + y = xoauth_parse_client_info(lval[k]); if(y && (!y->name || strcmp(y->name, xoauth_default[i].name))){ free_xoauth2_info(&y); @@ -849,6 +910,11 @@ alpine_xoauth2_configuration(struct pine *ps, int edit_exceptions) y->client_secret = cpystr(xoauth_default[i].client_secret); if(y->tenant == NULL && xoauth_default[i].tenant != NULL) y->tenant = cpystr(xoauth_default[i].tenant); + for(oa2 = NULL, oa2list = alpine_oauth2_list; oa2 == NULL && oa2list; oa2list++) + if(!strcmp(oa2list->name, y->name)) oa2 = oa2list; + if(y->flow == NULL) + y->flow = cpystr(oa2->server_mthd[0].name ? "Authorize" + : (oa2->server_mthd[1].name ? "Device" : "Unknown")); write_xoauth_conf_entry(y, &xoauth_default[i], &ctmpa, &ctmpb, &first_line, &varlist, &p, ln, k); free_xoauth2_info(&y); } diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h index 65e02288..32df37ba 100644 --- a/imap/src/c-client/mail.h +++ b/imap/src/c-client/mail.h @@ -1993,6 +1993,7 @@ typedef struct xoauth_default_s { char *client_secret; char *tenant; char *users; + char *flow; } XOAUTH2_INFO_S; /* Supporting external functions for XOAUTH2 and OAUTHBEARER */ diff --git a/imap/src/c-client/oauth2_aux.c b/imap/src/c-client/oauth2_aux.c index e7b14c69..24827e70 100644 --- a/imap/src/c-client/oauth2_aux.c +++ b/imap/src/c-client/oauth2_aux.c @@ -386,6 +386,7 @@ void free_xoauth2_info(XOAUTH2_INFO_S **xp) if((*xp)->client_id) fs_give((void **) &(*xp)->client_id); if((*xp)->client_secret) fs_give((void **) &(*xp)->client_secret); if((*xp)->tenant) fs_give((void **) &(*xp)->tenant); + if((*xp)->flow) fs_give((void **) &(*xp)->flow); if((*xp)->users) fs_give((void **) &(*xp)->users); fs_give((void **) xp); } @@ -400,6 +401,7 @@ XOAUTH2_INFO_S *copy_xoauth2_info(XOAUTH2_INFO_S *x) if(x->client_id) y->client_id = cpystr(x->client_id); if(x->client_secret) y->client_secret = cpystr(x->client_secret); if(x->tenant) y->tenant = cpystr(x->tenant); + if(x->flow) y->flow = cpystr(x->flow); if(x->users) y->users = cpystr(x->users); return y; } diff --git a/pith/pine.hlp b/pith/pine.hlp index b751666b..903873c2 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 486 2020-07-07 14:25:41 +Alpine Commit 488 2020-07-09 00:16:30 ============= h_news ================= <HTML> <HEAD> @@ -16233,6 +16233,40 @@ organization only. <End of help on this topic> </BODY> </HTML> +======= h_config_xoauth2_flow ======= +<HTML> +<HEAD> +<TITLE>Flow Explained</TITLE> +</HEAD> +<BODY> +<H1>Flow Explained</H1> + +The first time you connect to a service to authorize Alpine access to your +email, you will have to do a certain number of steps, which typically involve +to login to your account using a browser, and agreeing to give Alpine certain +rights to access your account. + +<P> +How this process is going to be done depends on the service. Some services +allow you to give access to Alpine and later generate a code that you +input into Alpine, while others give you a code you have to use before you +approve access to Alpine to access your email. + +<P> An example of a service that gives you a code after you authorize +Alpine is Gmail, and this process is called internally as "Authorize". +An example of a service that gives you a code before you authorize Alpine +is Outlook, and this process is called "Device". However, some services, +like Outlook, offer both services, and you can choose which flow you would like +to use. You +can choose between the "Authorize&qupt; and "Device" in these servers. +If you forget to configure +this, Alpine will ask you in these situations which method to use before it starts +to setup the process to get your authrization. + +<P> +<End of help on this topic> +</BODY> +</HTML> ======= h_config_xoauth2_username ======= <HTML> <HEAD> |