summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alpine/conftype.h4
-rw-r--r--alpine/imap.c227
-rw-r--r--alpine/keymenu.c15
-rw-r--r--alpine/keymenu.h1
-rw-r--r--alpine/xoauth2.h2
-rw-r--r--alpine/xoauth2conf.c98
-rw-r--r--imap/src/c-client/mail.h1
-rw-r--r--imap/src/c-client/oauth2_aux.c2
-rw-r--r--pith/pine.hlp36
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.
&lt;End of help on this topic&gt;
</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 &quot;Authorize&quot;.
+An example of a service that gives you a code before you authorize Alpine
+is Outlook, and this process is called &quot;Device&quot;. However, some services,
+like Outlook, offer both services, and you can choose which flow you would like
+to use. You
+can choose between the &quot;Authorize&qupt; and &quot;Device&quot; 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>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
======= h_config_xoauth2_username =======
<HTML>
<HEAD>