summaryrefslogtreecommitdiff
path: root/alpine
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2019-05-04 12:41:11 -0600
committerEduardo Chappa <chappa@washington.edu>2019-05-04 12:41:11 -0600
commitc024a78dbaa9b42db7f18b0fea1894c41e2b0d67 (patch)
tree441e7308e4577ac8766c44edda682704aa432262 /alpine
parent19cde66486e27063a9af8cfd79c6eb7f106b9111 (diff)
downloadalpine-c024a78dbaa9b42db7f18b0fea1894c41e2b0d67.tar.xz
* Initial release of XOAUTH2 authentication support in Alpine for
Gmail.
Diffstat (limited to 'alpine')
-rw-r--r--alpine/alpine.c1
-rw-r--r--alpine/imap.c739
-rw-r--r--alpine/imap.h5
-rw-r--r--alpine/keymenu.c15
-rw-r--r--alpine/keymenu.h1
-rw-r--r--alpine/mailview.c39
-rw-r--r--alpine/mailview.h2
-rw-r--r--alpine/rpdump.c26
-rw-r--r--alpine/rpload.c25
-rw-r--r--alpine/xoauth2.h77
10 files changed, 838 insertions, 92 deletions
diff --git a/alpine/alpine.c b/alpine/alpine.c
index 3b7df35f..973fa2c6 100644
--- a/alpine/alpine.c
+++ b/alpine/alpine.c
@@ -340,6 +340,7 @@ main(int argc, char **argv)
mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_body_sparep);
+ mail_parameters(NULL, SET_OA2CLIENTGETACCESSCODE, (void *) oauth2_get_access_code);
init_pinerc(pine_state, &init_pinerc_debugging);
diff --git a/alpine/imap.c b/alpine/imap.c
index 1fc6c76f..c6da3e61 100644
--- a/alpine/imap.c
+++ b/alpine/imap.c
@@ -39,6 +39,7 @@ static char rcsid[] = "$Id: imap.c 1266 2009-07-14 18:39:12Z hubert@u.washington
#include "arg.h"
#include "busy.h"
#include "titlebar.h"
+#include "xoauth2.h"
#include "../pith/state.h"
#include "../pith/conf.h"
#include "../pith/msgno.h"
@@ -100,12 +101,15 @@ static int storepassprompt = -1;
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 *);
#ifdef LOCAL_PASSWD_CACHE
int read_passfile(char *, MMLOGIN_S **);
void write_passfile(char *, MMLOGIN_S *);
int preserve_prompt(char *);
+int preserve_prompt_auth(char *, char *authtype);
void update_passfile_hostlist(char *, char *, STRLIST_S *, int);
+void update_passfile_hostlist_auth(char *, char *, STRLIST_S *, int, char *);
void free_passfile_cache_work(MMLOGIN_S **);
static MMLOGIN_S *passfile_cache = NULL;
@@ -127,6 +131,477 @@ int init_wincred_funcs(void);
static char *details_cert, *details_host, *details_reason;
+typedef struct auth_code_s {
+ char *code;
+ int answer;
+} AUTH_CODE_S;
+
+char *
+oauth2_get_access_code(char *url, OAUTH2_S *oauth2, int *tryanother)
+{
+ char tmp[MAILTMPLEN];
+ char *code;
+
+ if(ps_global->ttyo){
+ SCROLL_S sargs;
+ STORE_S *in_store, *out_store;
+ gf_io_t pc, gc;
+ HANDLE_S *handles = NULL;
+ AUTH_CODE_S user_input;
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
+ !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
+ goto try_wantto;
+
+ so_puts(in_store, "<HTML><P>");
+ sprintf(tmp, _("<CENTER>Auhtorizing Alpine Access to %s Email Services</CENTER>"), oauth2->name);
+ so_puts(in_store, tmp);
+ sprintf(tmp, _("</P><P>Alpine is attempting to log you into your %s account, using the XOAUTH2 method."), oauth2->name),
+ so_puts(in_store, tmp);
+
+ so_puts(in_store, _(" In order to do that, Alpine needs to open the following URL:"));
+ so_puts(in_store,"</P><P>");
+ sprintf(tmp_20k_buf, _("<A HREF=\"%s\">%s.</A>"), url, url);
+ so_puts(in_store, tmp_20k_buf);
+
+ so_puts(in_store, _("</P><P> Alpine will try to use your URL Viewers setting to find a browser to open this URL."));
+ sprintf(tmp, _(" When you open this link, you will be sent to %s's servers to complete this process."), oauth2->name);
+ so_puts(in_store, tmp);
+ so_puts(in_store, _(" Alternatively, you can copy and paste the previous link and open it with the browser of your choice."));
+
+ so_puts(in_store, _("</P><P> After you open the previous link, you will be asked to authenticate and later to authorize access to Alpine. "));
+ so_puts(in_store, _(" At the end of this process, you will be given an access code or redirected to a web page."));
+ so_puts(in_store, _(" If you see a code, copy it and then press 'C', and enter the code at the prompt."));
+ so_puts(in_store, _(" If you do not see a code, copy the url of the page you were redirected to and again press 'C' and copy and paste it into the prompt. "));
+ so_puts(in_store, _(" Once you have completed this process, Alpine will proceed with authentication."));
+ so_puts(in_store, _(" If you do not wish to proceed, cancel by pressing 'E' to exit"));
+ so_puts(in_store, _("</P></HTML>"));
+
+ so_seek(in_store, 0L, 0);
+ init_handles(&handles);
+ gf_filter_init();
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL,
+ ps_global->ttyo->screen_cols, non_messageview_margin(),
+ &handles, NULL, GFHP_LOCAL_HANDLES));
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+ gf_pipe(gc, pc);
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.handles = handles;
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("help text");
+ sargs.bar.title = _("SETTING UP XOAUTH2 AUTHORIZATION");
+ sargs.proc.tool = oauth2_auth_answer;
+ sargs.proc.data.p = (void *)&user_input;
+ sargs.keys.menu = &oauth2_auth_keymenu;
+ /* don't want to re-enter c-client */
+ sargs.quell_newmail = 1;
+ setbitmap(sargs.keys.bitmap);
+ sargs.help.text = h_oauth2_start;
+ sargs.help.title = _("HELP FOR SETTING UP XOAUTH2");
+
+ do {
+ scrolltool(&sargs);
+ ps_global->mangled_screen = 1;
+ ps_global->painted_body_on_startup = 0;
+ ps_global->painted_footer_on_startup = 0;
+ } while (user_input.answer != 'e');
+
+ if(!struncmp(user_input.code, "https://", 8)){
+ char *s, *t;
+ s = strstr(user_input.code, "code=");
+ if(s != NULL){
+ t = strchr(s, '&');
+ if(t) *t = '\0';
+ code = cpystr(s+5);
+ if(t) *t = '&';
+ }
+ }
+ else code = user_input.code ? cpystr(user_input.code) : NULL;
+ if(user_input.code) fs_give((void **) &user_input.code);
+
+ if(code == NULL) *tryanother = 1;
+
+ so_give(&in_store);
+ so_give(&out_store);
+ free_handles(&handles);
+ }
+ else{
+ int flags, rc, q_line;
+ /* TRANSLATORS: user needs to input an access code from the server */
+ char *accesscodelabel = _("Copy and Paste Access Code");
+ char prompt[MAILTMPLEN], token[MAILTMPLEN];
+ /*
+ * If screen hasn't been initialized yet, use want_to.
+ */
+try_wantto:
+ memset((void *)tmp, 0, sizeof(tmp));
+ strncpy(tmp, _("Alpine would like to get authorization to access your email: "), sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ strncat(tmp, _(": Proceed "), sizeof(tmp)-strlen(tmp)-1);
+
+ if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y'){
+ q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
+ flags = OE_APPEND_CURRENT;
+ sprintf(prompt, "%s: ", accesscodelabel);
+ do {
+ rc = optionally_enter(token, q_line, 0, MAILTMPLEN,
+ prompt, NULL, NO_HELP, &flags);
+ } while (rc != 0 && rc != 1);
+ if(!struncmp(token, "https://", 8)){
+ char *s, *t;
+ s = strstr(token, "code=");
+ if(s != NULL){
+ t = strchr(s, '&');
+ if(t) *t = '\0';
+ code = cpystr(s+5);
+ if(t) *t = '&';
+ }
+ }
+ else code = token[0] ? cpystr(token) : NULL;
+ }
+ }
+
+ return code;
+}
+
+void mm_login_oauth2(NETMBX *, char *, OAUTH2_S *, long int, char *, char *);
+
+/* The purpose of this function is to report to c-client the values of the
+ * different tokens and codes so that c-client can try to log in the user
+ * to the server. This function DOES NOT attempt to get these values for
+ * the user. That is attempted in the c-client side (as best as it can be
+ * done given our circumstances: no http support, no javascript support,
+ * etc.). This is the best we can do:
+ *
+ * 1. In a first call, get an unloaded OAUTH2_S login pointer and load it
+ * as best as we can. Unloaded means that there is no server known to
+ * connect, no access token, etc. Pretty much nothing is known about
+ * how to get access code, access token, etc. We ask the user to fill
+ * it up for us, if they can. If the user fills it up we save those
+ * values.
+ *
+ * 2. In a subsequent call, if the OAUTH2_S login pointer is loaded, we
+ * save the information that c-client got for us.
+ *
+ * 3. When saving this information we use the password caching facilities,
+ * but we must do it in a different format so that old information and
+ * new information are not mixed. In order to accomodate this for new
+ * authentication methods, we save the information in the same fields,
+ * but this time we modify it slightly, so that old functions fail to
+ * understand the new information and so not modify it nor use it. The
+ * modification is simple: Every piece of information that was saved
+ * before is prepended XOAUTH2\001 to indicate the authentication
+ * method for which it works. The character \001 is a separator. New
+ * Alpine will know how to deal with this, but old versions, will not
+ * strip this prefix from the information and fail to get the
+ * information or modify it when needed. Only new versions of Alpine will
+ * know how to process this information.
+ * new_value = authenticator_method separator old_value
+ * authenticator_method = "XOAUTH2_S"
+ * separator = "U+1"
+ * example: if old value is imap.gmail.com, the new value for the XOAUTH2
+ * authenticator is "XOAUTH2\001imap.gmail.com".
+ * In addition, the password field is not used to encode the password
+ * anymore, it is used to save login information needed in a format that
+ * the caller function chooses, but that must be preceeded by the
+ * "authenticator_method separator" code as above.
+ */
+void
+mm_login_oauth2(NETMBX *mb, char *user, OAUTH2_S *login, long int trial,
+ char *usethisprompt, char *altuserforcache)
+{
+ char *token, tmp[MAILTMPLEN];
+ char prompt[4*MAILTMPLEN], value[4*MAILTMPLEN], *last;
+ char defuser[NETMAXUSER];
+ char hostleadin[80], hostname[200], defubuf[200];
+ char logleadin[80], pwleadin[50];
+ char *url_oauth2;
+ char *tool = NULL;
+ char *OldRefreshToken, *OldAccessToken;
+ char *NewRefreshToken, *NewAccessToken;
+ char *SaveRefreshToken, *SaveAccessToken;
+ /* TRANSLATORS: A label for the hostname that the user is logging in on */
+ char *hostlabel = _("HOST");
+ /* TRANSLATORS: user is logging in as a particular user (a particular
+ login name), this is just labelling that user name. */
+ char *userlabel = _("USER");
+ STRLIST_S hostlist[2], hostlist2[OAUTH2_TOT_EQUIV+1];
+ HelpType help ;
+ int len, rc, q_line, flags, i, j;
+ int oespace, avail, need, save_dont_use;
+ int save_in_init;
+ int registered;
+ int ChangeAccessToken, ChangeRefreshToken, ChangeExpirationTime;
+ OAUTH2_S *oa2list;
+ unsigned long OldExpirationTime, NewExpirationTime, SaveExpirationTime;
+#if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE)
+ int preserve_password = -1;
+#endif
+
+ dprint((9, "mm_login_oauth2 trial=%ld user=%s service=%s%s%s%s%s\n",
+ trial, mb->user ? mb->user : "(null)",
+ mb->service ? mb->service : "(null)",
+ mb->port ? " port=" : "",
+ mb->port ? comatose(mb->port) : "",
+ altuserforcache ? " altuserforcache =" : "",
+ altuserforcache ? altuserforcache : ""));
+
+ q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
+
+ save_in_init = ps_global->in_init_seq;
+ ps_global->in_init_seq = 0;
+ ps_global->no_newmail_check_from_optionally_enter = 1;
+
+ /* make sure errors are seen */
+ if(ps_global->ttyo)
+ flush_status_messages(0);
+
+ token = NULL; /* start from scratch */
+
+ hostlist[0].name = mb->host;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = mb->orighost;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+
+ if(hostlist[0].name){
+ dprint((9, "mm_login_oauth2: host=%s\n",
+ hostlist[0].name ? hostlist[0].name : "?"));
+ if(hostlist[0].next && hostlist[1].name){
+ dprint((9, "mm_login_oauth2: orighost=%s\n", hostlist[1].name));
+ }
+ }
+
+ /*
+ * 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;
+ 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){
+ registered++;
+ break;
+ }
+ }
+
+ if(registered){
+ hostlist2[i = 0].name = mb->host;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost))
+ hostlist2[++i].name = mb->orighost;
+
+ for(j = 0; j < OAUTH2_TOT_EQUIV && oa2list->host[j] != NULL; j++){
+ int k;
+ for(k = 0; k <= i && hostlist2[k].name
+ && strcmp(hostlist2[k].name, oa2list->host[j]); k++);
+ if(k == i + 1)
+ hostlist2[++i].name = oa2list->host[j];
+ }
+ hostlist2[i+1].name = NULL;
+ hostlist2[i+1].next = NULL;
+ for(j = i; j >= 0; j--)
+ hostlist2[j].next = &hostlist2[j+1];
+ }
+
+ /*
+ * We check if we have a refresh token saved somewhere, if so
+ * we use it to get a new access token, otherwise we need to
+ * get an access code so we can get (and save) a refresh token
+ * 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,
+ (mb->sslflag||mb->tlsflag), OA2NAME)){
+ dprint((9, "mm_login_oauth2: found a refresh token\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ }
+#ifdef LOCAL_PASSWD_CACHE
+ /* or see if we have saved one in the local password cache and load it */
+ else if(get_passfile_passwd_auth(ps_global->pinerc, &token,
+ user, registered ? hostlist2 : hostlist,
+ (mb->sslflag||mb->tlsflag), OA2NAME)){
+ imap_set_passwd_auth(&mm_login_list, token, user,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0, OA2NAME);
+ update_passfile_hostlist_auth(ps_global->pinerc, user, hostlist,
+ (mb->sslflag||mb->tlsflag), OA2NAME);
+ dprint((9, "mm_login_oauth2: found a refresh token in cache\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ }
+ if(token && *token) preserve_password = 1; /* resave it, no need to ask */
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+ user[NETMAXUSER-1] = '\0';
+
+ /* The Old* variables is what c_client knows */
+ OldRefreshToken = login->param[OA2_RefreshToken].value;
+ OldAccessToken = login->access_token;
+ OldExpirationTime = login->expiration;
+
+ /* The New* variables is what Alpine knows */
+ NewRefreshToken = NewAccessToken = NULL;
+ NewExpirationTime = 0L;
+ ChangeAccessToken = ChangeRefreshToken = ChangeExpirationTime = 0;
+
+ if(token && *token){
+ char *s, *t;
+
+ s = token;
+ t = strchr(s, PWDAUTHSEP);
+ if(t == NULL)
+ NewRefreshToken = cpystr(s);
+ else {
+ *t++ = '\0';
+ NewRefreshToken = cpystr(s);
+ s = t;
+ t = strchr(s, PWDAUTHSEP);
+ if(t == NULL)
+ NewAccessToken = cpystr(s);
+ else {
+ *t++ = '\0';
+ NewAccessToken = cpystr(s);
+ s = t;
+ NewExpirationTime = strtol(s, &s, 10);
+ if(NewExpirationTime <= 0 || NewExpirationTime <= time(0))
+ NewExpirationTime = 0L;
+ }
+ }
+ /* check we got good information, and send good information below */
+ if(NewRefreshToken && !*NewRefreshToken)
+ fs_give((void **) &NewRefreshToken);
+ if(NewAccessToken && (NewExpirationTime == 0L || !*NewAccessToken))
+ fs_give((void **) &NewAccessToken);
+ }
+
+ /* Default to saving what we already had saved */
+
+ SaveRefreshToken = NewRefreshToken;
+ SaveAccessToken = NewAccessToken;
+ SaveExpirationTime = NewExpirationTime;
+
+ /* Translation of the logic below:
+ * if (c-client has a refresh token
+ and (alpine does not have a refresh token or (alpine and c-client have different refresh tokens)){
+ forget the Alpine refresh token;
+ make the Alpine Refresh token = c-client refresh token.;
+ signal that we changed the refresh token;
+ In this situation we do not need to clear up the Alpine access token. This will
+ expire, so we can use it until it expires. We can save the c-client refresh token
+ together with the Alpine Access Token and the expiration date of the Alpine Access
+ Token.
+ } else if(c-client does not have a refresh token and Alpine has one and this is not the first attempt){
+ forget the Alpine refresh token;
+ if Alpine has an access token, forget it;
+ reset the expiration time
+ signal that we changed the refresh token; (because the service expired it)
+ }
+ */
+
+ if(OldRefreshToken != NULL
+ && (NewRefreshToken == NULL || strcmp(OldRefreshToken, NewRefreshToken))){
+ if(NewRefreshToken) fs_give((void **) &NewRefreshToken);
+ NewRefreshToken = cpystr(OldRefreshToken);
+ ChangeRefreshToken++;
+ SaveRefreshToken = OldRefreshToken;
+ SaveAccessToken = NewAccessToken;
+ SaveExpirationTime = NewExpirationTime;
+ } else if (OldRefreshToken == NULL && NewRefreshToken != NULL && trial > 0){
+ fs_give((void **) &NewRefreshToken);
+ if(NewAccessToken) fs_give((void **) &NewAccessToken);
+ NewExpirationTime = 0L;
+ ChangeRefreshToken++;
+ SaveRefreshToken = NULL;
+ SaveAccessToken = NULL;
+ SaveExpirationTime = 0L;
+ }
+
+ if(OldAccessToken != NULL
+ && (NewAccessToken == NULL || strcmp(OldAccessToken, NewAccessToken))){
+ if(NewAccessToken) fs_give((void **) &NewAccessToken);
+ NewAccessToken = cpystr(OldAccessToken);
+ ChangeAccessToken++;
+ NewExpirationTime = OldExpirationTime;
+ SaveRefreshToken = NewRefreshToken;
+ SaveAccessToken = NewAccessToken;
+ SaveExpirationTime = NewExpirationTime;
+ }
+
+ if(!registered){
+ login->param[OA2_RefreshToken].value = SaveRefreshToken;
+ login->access_token = SaveAccessToken;
+ login->expiration = SaveExpirationTime;
+ } else {
+ oa2list->param[OA2_RefreshToken].value = SaveRefreshToken;
+ oa2list->access_token = SaveAccessToken;
+ oa2list->expiration = SaveExpirationTime;
+ *login = *oa2list; /* load login pointer */
+ }
+
+ if(!ChangeAccessToken && !ChangeRefreshToken)
+ return;
+
+ /* get ready to save this information. The format will be
+ * RefreshToken \001 LastAccessToken \001 ExpirationTime
+ * (spaces added for clarity, \001 is PWDAUTHSEP)
+ */
+ if(token) fs_give((void **) &token);
+ sprintf(tmp, "%lu", SaveExpirationTime);
+ tmp[sizeof(tmp) - 1] = '\0';
+ len = strlen(SaveRefreshToken ? SaveRefreshToken : "")
+ + strlen(SaveAccessToken ? SaveAccessToken : "")
+ + strlen(tmp) + 2;
+ token = fs_get(len + 1);
+ sprintf(token, "%s%c%s%c%lu",
+ SaveRefreshToken ? SaveRefreshToken : "", PWDAUTHSEP,
+ SaveAccessToken ? SaveAccessToken : "", PWDAUTHSEP,
+ SaveExpirationTime);
+
+ /* remember the access information for next time */
+ if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global))
+ imap_set_passwd_auth(&mm_login_list, token,
+ altuserforcache ? altuserforcache : user, hostlist,
+ (mb->sslflag||mb->tlsflag), 0, 0, OA2NAME);
+#ifdef LOCAL_PASSWD_CACHE
+ /* if requested, remember it on disk for next session */
+ if(save_password && F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
+ set_passfile_passwd_auth(ps_global->pinerc, token,
+ altuserforcache ? altuserforcache : user, hostlist,
+ (mb->sslflag||mb->tlsflag),
+ (preserve_password == -1 ? 0
+ : (preserve_password == 0 ? 2 :1)), OA2NAME);
+#endif /* LOCAL_PASSWD_CACHE */
+
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+}
/*----------------------------------------------------------------------
recieve notification from IMAP
@@ -328,11 +803,21 @@ mm_log(char *string, long int errflg)
ps_global->last_error[sizeof(ps_global->last_error) - 1] = '\0';
}
+void
+mm_login_method_work(NETMBX *mb, char *user, void *login, long int trial,
+ char *method, char *usethisprompt, char *altuserforcache)
+{
+ if(method == NULL)
+ return;
+ if(strucmp(method, OA2NAME) == 0)
+ mm_login_oauth2(mb, user, (OAUTH2_S *) login, trial, usethisprompt, altuserforcache);
+}
void
-mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
+mm_login_work(NETMBX *mb, char *user, char **pwd, long int trial,
char *usethisprompt, char *altuserforcache)
{
+ char tmp[MAILTMPLEN];
char prompt[1000], *last;
char port[20], non_def_port[20], insecure[20];
char defuser[NETMAXUSER];
@@ -371,6 +856,11 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
altuserforcache ? altuserforcache : ""));
q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
+ if(pwd && *pwd){
+ char *s = *pwd;
+ fs_give((void **) &s);
+ *pwd = NULL;
+ }
save_in_init = ps_global->in_init_seq;
ps_global->in_init_seq = 0;
ps_global->no_newmail_check_from_optionally_enter = 1;
@@ -460,7 +950,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
/* check to see if there's a password left over from last session */
if(get_passfile_passwd(ps_global->pinerc, pwd,
user, hostlist, (mb->sslflag||mb->tlsflag))){
- imap_set_passwd(&mm_login_list, pwd, user,
+ imap_set_passwd(&mm_login_list, *pwd, user,
hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
update_passfile_hostlist(ps_global->pinerc, user, hostlist,
(mb->sslflag||mb->tlsflag));
@@ -503,7 +993,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
/* check to see if there's a password left over from last session */
if(get_passfile_passwd(ps_global->pinerc, pwd,
user, hostlist, (mb->sslflag||mb->tlsflag))){
- imap_set_passwd(&mm_login_list, pwd, user,
+ imap_set_passwd(&mm_login_list, *pwd, user,
hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
update_passfile_hostlist(ps_global->pinerc, user, hostlist,
(mb->sslflag||mb->tlsflag));
@@ -540,7 +1030,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
/* check to see if there's a password left over from last session */
if(get_passfile_passwd(ps_global->pinerc, pwd,
user, hostlist, (mb->sslflag||mb->tlsflag))){
- imap_set_passwd(&mm_login_list, pwd, user,
+ imap_set_passwd(&mm_login_list, *pwd, user,
hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
update_passfile_hostlist(ps_global->pinerc, user, hostlist,
(mb->sslflag||mb->tlsflag));
@@ -785,7 +1275,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
#ifdef LOCAL_PASSWD_CACHE
if(get_passfile_passwd(ps_global->pinerc, pwd,
user, hostlist, (mb->sslflag||mb->tlsflag))){
- imap_set_passwd(&mm_login_list, pwd, user,
+ imap_set_passwd(&mm_login_list, *pwd, user,
hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
ps_global->no_newmail_check_from_optionally_enter = 0;
ps_global->in_init_seq = save_in_init;
@@ -804,7 +1294,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
#ifdef LOCAL_PASSWD_CACHE
if(get_passfile_passwd(ps_global->pinerc, pwd,
altuserforcache, hostlist, (mb->sslflag||mb->tlsflag))){
- imap_set_passwd(&mm_login_list, pwd, altuserforcache,
+ imap_set_passwd(&mm_login_list, *pwd, altuserforcache,
hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
ps_global->no_newmail_check_from_optionally_enter = 0;
ps_global->in_init_seq = save_in_init;
@@ -966,7 +1456,7 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
retry, hostleadin, hostname, port, insecure, logleadin, defubuf, pwleadin);
prompt[sizeof(prompt)-1] = '\0';
- *pwd = '\0';
+ tmp[0] = '\0';
while(1) {
if(ps_global->ttyo)
mm_login_alt_cue(mb);
@@ -975,12 +1465,13 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
ps_global->dont_use_init_cmds = 1;
flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD;
#ifdef _WINDOWS
- rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD, 0, 1,
+ rc = os_login_dialog(mb, user, NETMAXUSER, tmp, NETMAXPASSWD, 0, 1,
&preserve_password);
#else /* !_WINDOWS */
- rc = optionally_enter(pwd, q_line, 0, NETMAXPASSWD,
+ rc = optionally_enter(tmp, q_line, 0, NETMAXPASSWD,
prompt, NULL, help, &flags);
#endif /* !_WINDOWS */
+ *pwd = cpystr(tmp);
ps_global->dont_use_init_cmds = save_dont_use;
if(rc == 3) {
@@ -992,9 +1483,9 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
break;
}
- if(rc == 1 || !pwd[0]) {
+ if(rc == 1 || !tmp[0]) {
ps_global->user_says_cancel = (rc == 1);
- user[0] = pwd[0] = '\0';
+ user[0] = '\0';
ps_global->no_newmail_check_from_optionally_enter = 0;
ps_global->in_init_seq = save_in_init;
return;
@@ -1005,13 +1496,13 @@ mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
#endif
/* remember the password for next time */
if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global))
- imap_set_passwd(&mm_login_list, pwd,
+ imap_set_passwd(&mm_login_list, *pwd,
altuserforcache ? altuserforcache : user, hostlist,
(mb->sslflag||mb->tlsflag), 0, 0);
#ifdef LOCAL_PASSWD_CACHE
/* if requested, remember it on disk for next session */
if(save_password && F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
- set_passfile_passwd(ps_global->pinerc, pwd,
+ set_passfile_passwd(ps_global->pinerc, *pwd,
altuserforcache ? altuserforcache : user, hostlist,
(mb->sslflag||mb->tlsflag),
(preserve_password == -1 ? 0
@@ -1860,6 +2351,47 @@ answer_cert_failure(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
}
+int
+oauth2_auth_answer(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1, rc;
+ AUTH_CODE_S user;
+ int q_line, flags;
+ /* TRANSLATORS: user needs to input an access code from the server */
+ char *accesscodelabel = _("Copy and Paste Access Code");
+ char token[MAILTMPLEN], prompt[MAILTMPLEN];
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ token[0] = '\0';
+ switch(cmd){
+ case MC_YES :
+ q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
+ flags = OE_APPEND_CURRENT;
+ sprintf(prompt, "%s: ", accesscodelabel);
+ do {
+ rc = optionally_enter(token, q_line, 0, MAILTMPLEN,
+ prompt, NULL, NO_HELP, &flags);
+ } while (rc != 0 && rc != 1);
+ user.code = rc == 0 ? cpystr(token) : NULL;
+ user.answer = 'e';
+ rv = rc == 1 ? 0 : 1;
+ break;
+
+ case MC_NO :
+ user.code = NULL;
+ user.answer = 'e';
+ break;
+
+ default:
+ alpine_panic("Unexpected command in oauth2_auth_answer");
+ break;
+ }
+ *(AUTH_CODE_S *) sparms->proc.data.p = user;
+ return(rv);
+}
+
+
/*----------------------------------------------------------------------
This can be used to prevent the flickering of the check_cue char
caused by numerous (5000+) fetches by c-client. Right now, the only
@@ -2117,8 +2649,10 @@ read_passfile(pinerc, l)
if(passwd && host && user){ /* valid field? */
STRLIST_S hostlist[2];
- int flags = sflags ? atoi(sflags) : 0;
+ int flags;
+ tmp = sflags ? strchr(sflags, PWDAUTHSEP) : NULL;
+ flags = sflags ? atoi(tmp ? ++tmp : sflags) : 0;
hostlist[0].name = host;
if(orighost){
hostlist[0].next = &hostlist[1];
@@ -2267,8 +2801,10 @@ read_passfile(pinerc, l)
if(passwd && host && user){ /* valid field? */
STRLIST_S hostlist[2];
- int flags = sflags ? atoi(sflags) : 0;
+ int flags;
+ tmp = sflags ? strchr(sflags, PWDAUTHSEP) : NULL;
+ flags = sflags ? atoi(tmp ? ++tmp : sflags) : 0;
hostlist[0].name = host;
if(orighost){
hostlist[0].next = &hostlist[1];
@@ -2314,9 +2850,10 @@ read_passfile(pinerc, l)
char tmp[MAILTMPLEN], *ui[5];
int i, j, n, rv = 0;
+ size_t len;
#ifdef SMIME
char tmp2[MAILTMPLEN];
- char *text = NULL, *text2 = NULL;
+ char *tmptext, *text = NULL, *text2 = NULL;
int encrypted = 0;
#endif /* SMIME */
FILE *fp;
@@ -2401,6 +2938,7 @@ read_passfile(pinerc, l)
*/
if(encrypted){
text = text2 = decrypt_file((char *)tmp, &i, (PERSONAL_CERT *)ps_global->pwdcert);
+ len = text2 ? strlen(text2) : 0;
switch(i){
case -2: using_passfile = 0;
break;
@@ -2416,8 +2954,14 @@ read_passfile(pinerc, l)
default: break;
}
}
- else
+ else{
+ struct stat sbuf;
+ if(our_stat(tmp, &sbuf) == 0)
+ len = sbuf.st_size;
+ else
+ len = 0;
fp = our_fopen(tmp, "rb"); /* reopen to read data */
+ }
#endif /* SMIME */
if(using_passfile == 0){
@@ -2427,34 +2971,36 @@ read_passfile(pinerc, l)
return using_passfile;
}
+ tmptext = fs_get(len + 1);
#ifdef SMIME
- for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
- : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
+ for(n = 0; len > 0 && (encrypted ? line_get(tmptext, len + 1, &text2)
+ : (fgets(tmptext, len+1, fp) != NULL)); n++){
#else /* SMIME */
- for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
+ for(n = 0; fgets(tmptext, len+1, fp); n++){
#endif /* SMIME */
/*** do any necessary DEcryption here ***/
xlate_key = n;
- for(i = 0; tmp[i]; i++)
- tmp[i] = xlate_out(tmp[i]);
+ for(i = 0; tmptext[i]; i++)
+ tmptext[i] = xlate_out(tmptext[i]);
- if(i && tmp[i-1] == '\n')
- tmp[i-1] = '\0'; /* blast '\n' */
+ if(i && tmptext[i-1] == '\n')
+ tmptext[i-1] = '\0'; /* blast '\n' */
dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
- for(i = 0, j = 0; tmp[i] && j < 5; j++){
- for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
+ for(i = 0, j = 0; tmptext[i] && j < 5; j++){
+ for(ui[j] = &tmptext[i]; tmptext[i] && tmptext[i] != '\t'; i++)
; /* find end of data */
- if(tmp[i])
- tmp[i++] = '\0'; /* tie off data */
+ if(tmptext[i])
+ tmptext[i++] = '\0'; /* tie off data */
}
dprint((10, "read_passfile: calling imap_set_passwd\n"));
if(ui[0] && ui[1] && ui[2]){ /* valid field? */
STRLIST_S hostlist[2];
- int flags = ui[3] ? atoi(ui[3]) : 0;
+ char *s = ui[3] ? strchr(ui[3], PWDAUTHSEP) : NULL;
+ int flags = ui[3] ? atoi(s ? ++s : ui[3]) : 0;
hostlist[0].name = ui[2];
if(ui[4]){
@@ -2470,6 +3016,7 @@ read_passfile(pinerc, l)
}
}
+ if (tmptext) fs_give((void **) &tmptext);
#ifdef SMIME
if (text) fs_give((void **)&text);
#else /* SMIME */
@@ -2486,6 +3033,7 @@ write_passfile(pinerc, l)
char *pinerc;
MMLOGIN_S *l;
{
+ char *authend, *authtype;
#ifdef WINCRED
# if (WINCRED > 0)
char target[MAILTMPLEN];
@@ -2499,11 +3047,21 @@ write_passfile(pinerc, l)
dprint((9, "write_passfile\n"));
for(; l; l = l->next){
- snprintf(target, sizeof(target), "%s%s\t%s\t%d",
+ authtype = l->passwd;
+ authend = strchr(l->passwd, PWDAUTHSEP);
+ if(authend != NULL){
+ *authend = '\0';
+ sprintf(blob, "%s%c%d", authtype, PWDAUTHSEP, l->altflag);
+ *authend = PWDAUTHSEP;
+ }
+ else
+ sprintf(blob, "%d", l->altflag);
+
+ snprintf(target, sizeof(target), "%s%s\t%s\t%s",
TNAME,
(l->hosts && l->hosts->name) ? l->hosts->name : "",
l->user ? l->user : "",
- l->altflag);
+ blob);
ltarget = utf8_to_lptstr((LPSTR) target);
if(ltarget){
@@ -2528,7 +3086,6 @@ write_passfile(pinerc, l)
#endif /* WINCRED > 0 */
#elif APPLEKEYCHAIN
-
int rc;
char target[MAILTMPLEN];
char blob[MAILTMPLEN];
@@ -2540,10 +3097,20 @@ write_passfile(pinerc, l)
dprint((9, "write_passfile\n"));
for(; l; l = l->next){
- snprintf(target, sizeof(target), "%s\t%s\t%d",
+ authtype = l->passwd;
+ authend = strchr(l->passwd, PWDAUTHSEP);
+ if(authend != NULL){
+ *authend = '\0';
+ sprintf(blob, "%s%c%d", authtype, PWDAUTHSEP, l->altflag);
+ *authend = PWDAUTHSEP;
+ }
+ else
+ sprintf(blob, "%d", l->altflag);
+
+ snprintf(target, sizeof(target), "%s\t%s\t%s",
(l->hosts && l->hosts->name) ? l->hosts->name : "",
l->user ? l->user : "",
- l->altflag);
+ blob);
snprintf(blob, sizeof(blob), "%s%s%s",
l->passwd ? l->passwd : "",
@@ -2588,12 +3155,11 @@ write_passfile(pinerc, l)
}
#else /* PASSFILE */
-
- char tmp[MAILTMPLEN];
+ char tmp[4*MAILTMPLEN], blob[4*MAILTMPLEN];
int i, n;
FILE *fp;
#ifdef SMIME
- char *text = NULL, tmp2[MAILTMPLEN];
+ char *text = NULL, tmp2[4*MAILTMPLEN];
int len = 0;
#endif
@@ -2614,9 +3180,19 @@ write_passfile(pinerc, l)
#endif /* SMIME */
for(n = 0; l; l = l->next, n++){
+ authtype = l->passwd;
+ authend = strchr(l->passwd, PWDAUTHSEP);
+ if(authend != NULL){
+ *authend = '\0';
+ sprintf(blob, "%s%c%d", authtype, PWDAUTHSEP, l->altflag);
+ *authend = PWDAUTHSEP;
+ }
+ else
+ sprintf(blob, "%d", l->altflag);
+
/*** do any necessary ENcryption here ***/
- snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
- l->hosts->name, l->altflag,
+ snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%s%s%s\n", l->passwd, l->user,
+ l->hosts->name, blob,
(l->hosts->next && l->hosts->next->name) ? "\t" : "",
(l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
: "");
@@ -2705,22 +3281,31 @@ ask_erase_credentials(void)
#ifdef LOCAL_PASSWD_CACHE
+int
+get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
+ char *pinerc, **passwd, *user;
+ STRLIST_S *hostlist;
+ int altflag;
+{
+ return get_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, NULL);
+}
/*
- * get_passfile_passwd - return the password contained in the special passord
+ * get_passfile_passwd_auth - return the password contained in the special passord
* cache. The file is assumed to be in the same directory
* as the pinerc with the name defined above.
*/
int
-get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
- char *pinerc, *passwd, *user;
+get_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, authtype)
+ char *pinerc, **passwd, *user;
STRLIST_S *hostlist;
int altflag;
+ char *authtype;
{
- dprint((10, "get_passfile_passwd\n"));
+ dprint((10, "get_passfile_passwd_auth\n"));
return((passfile_cache || read_passfile(pinerc, &passfile_cache))
- ? imap_get_passwd(passfile_cache, passwd,
- user, hostlist, altflag)
+ ? imap_get_passwd_auth(passfile_cache, passwd,
+ user, hostlist, altflag, authtype)
: 0);
}
@@ -2769,8 +3354,16 @@ get_passfile_user(pinerc, hostlist)
int
preserve_prompt(char *pinerc)
{
+ return preserve_prompt_auth(pinerc, NULL);
+}
+
+int
+preserve_prompt_auth(char *pinerc, char *authtype)
+{
#ifdef WINCRED
# if (WINCRED > 0)
+#define PROMPT_PWD _("Preserve password for next login")
+#define PROMPT_OA2 _("Preserve Refresh and Access tokens for next login")
/*
* This prompt was going to be able to be turned on and off via a registry
* setting controlled from the config menu. We decided to always use the
@@ -2779,7 +3372,9 @@ preserve_prompt(char *pinerc)
* OS X somewhat uses the behavior just described.
*/
if(mswin_store_pass_prompt()
- && (want_to(_("Preserve password for next login"),
+ && (want_to(authtype
+ ? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
+ : PROMPT_PWD,
'y', 'x', NO_HELP, WT_NORM)
== 'y'))
return(1);
@@ -2790,11 +3385,14 @@ preserve_prompt(char *pinerc)
# endif
#elif APPLEKEYCHAIN
+#define PROMPT_PWD _("Preserve password for next login")
+#define PROMPT_OA2 _("Preserve Refresh and Access tokens for next login")
int rc;
if((rc = macos_store_pass_prompt()) != 0){
- if(want_to(_("Preserve password for next login"),
- 'y', 'x', NO_HELP, WT_NORM)
+ if(want_to(authtype
+ ? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
+ : PROMPT_PWD, 'y', 'x', NO_HELP, WT_NORM)
== 'y'){
if(rc == -1){
macos_set_store_pass_prompt(1);
@@ -2812,6 +3410,9 @@ preserve_prompt(char *pinerc)
}
return(0);
#else /* PASSFILE */
+#define PROMPT_PWD _("Preserve password on DISK for next login")
+#define PROMPT_OA2 _("Preserve Refresh and Access tokens on DISK for next login")
+
char tmp[MAILTMPLEN];
struct stat sbuf;
@@ -2819,8 +3420,9 @@ preserve_prompt(char *pinerc)
return 0;
if(F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
- return(want_to(_("Preserve password on DISK for next login"),
- 'y', 'x', NO_HELP, WT_NORM)
+ return(want_to(authtype
+ ? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
+ : PROMPT_PWD, 'y', 'x', NO_HELP, WT_NORM)
== 'y');
return(0);
#endif /* PASSFILE */
@@ -2947,6 +3549,14 @@ macos_erase_keychain(void)
#ifdef LOCAL_PASSWD_CACHE
+void
+set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
+ char *pinerc, *passwd, *user;
+ STRLIST_S *hostlist;
+ int altflag, already_prompted;
+{
+ set_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, already_prompted, NULL);
+}
/*
* set_passfile_passwd - set the password file entry associated with
* cache. The file is assumed to be in the same directory
@@ -2956,21 +3566,31 @@ macos_erase_keychain(void)
* 2 prompted, answered no
*/
void
-set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
+set_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, already_prompted, authtype)
char *pinerc, *passwd, *user;
STRLIST_S *hostlist;
int altflag, already_prompted;
+ char *authtype;
{
- dprint((10, "set_passfile_passwd\n"));
- if(((already_prompted == 0 && preserve_prompt(pinerc))
+ dprint((10, "set_passfile_passwd_auth\n"));
+ if(((already_prompted == 0 && preserve_prompt_auth(pinerc, authtype))
|| already_prompted == 1)
&& !ps_global->nowrite_password_cache
&& (passfile_cache || read_passfile(pinerc, &passfile_cache))){
- imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
+ imap_set_passwd_auth(&passfile_cache, passwd, user, hostlist, altflag, 0, 0, authtype);
write_passfile(pinerc, passfile_cache);
}
}
+void
+update_passfile_hostlist(pinerc, user, hostlist, altflag)
+ char *pinerc;
+ char *user;
+ STRLIST_S *hostlist;
+ int altflag;
+{
+ update_passfile_hostlist_auth(pinerc, user, hostlist, altflag, NULL);
+}
/*
* Passfile lines are
@@ -2981,21 +3601,24 @@ set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
* This routine attempts to repair that.
*/
void
-update_passfile_hostlist(pinerc, user, hostlist, altflag)
+update_passfile_hostlist_auth(pinerc, user, hostlist, altflag, authtype)
char *pinerc;
char *user;
STRLIST_S *hostlist;
int altflag;
+ char *authtype;
{
#ifdef WINCRED
return;
#else /* !WINCRED */
MMLOGIN_S *l;
+ size_t len = authtype ? strlen(authtype) : 0;
+ size_t offset = authtype ? 1 : 0;
for(l = passfile_cache; l; l = l->next)
- if(imap_same_host(l->hosts, hostlist)
+ if(imap_same_host_auth(l->hosts, hostlist, authtype)
&& *user
- && !strcmp(user, l->user)
+ && !strcmp(user, l->user + len + offset)
&& l->altflag == altflag){
break;
}
@@ -3003,7 +3626,7 @@ update_passfile_hostlist(pinerc, user, hostlist, altflag)
if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
&& hostlist->next->name
&& !ps_global->nowrite_password_cache){
- l->hosts->next = new_strlist(hostlist->next->name);
+ l->hosts->next = new_strlist_auth(hostlist->next->name, authtype, PWDAUTHSEP);
write_passfile(pinerc, passfile_cache);
}
#endif /* !WINCRED */
diff --git a/alpine/imap.h b/alpine/imap.h
index 08f51ce5..cc56e2d1 100644
--- a/alpine/imap.h
+++ b/alpine/imap.h
@@ -31,11 +31,14 @@ char *pine_newsrcquery(MAILSTREAM *, char *, char *);
int url_local_certdetails(char *);
void pine_sslfailure(char *, char *, unsigned long);
void mm_expunged_current(long unsigned int);
+char *oauth2_get_access_code(char *, OAUTH2_S *, int *);
#ifdef LOCAL_PASSWD_CACHE
-int get_passfile_passwd(char *, char *, char *, STRLIST_S *, int);
+int get_passfile_passwd(char *, char **, char *, STRLIST_S *, int);
+int get_passfile_passwd_auth(char *, char **, char *, STRLIST_S *, int, char *);
int is_using_passfile(void);
void set_passfile_passwd(char *, char *, char *, STRLIST_S *, int, int);
+void set_passfile_passwd_auth(char *, char *, char *, STRLIST_S *, int, int, char *);
char *get_passfile_user(char *, STRLIST_S *);
void free_passfile_cache(void);
#endif /* LOCAL_PASSWD_CACHE */
diff --git a/alpine/keymenu.c b/alpine/keymenu.c
index 77969321..016bd375 100644
--- a/alpine/keymenu.c
+++ b/alpine/keymenu.c
@@ -562,6 +562,21 @@ struct key ans_certquery_keys[] =
{"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE}};
INST_KEY_MENU(ans_certquery_keymenu, ans_certquery_keys);
+struct key oauth2_alpine_auth_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"C",N_("Enter Code"),{MC_YES,1,{'c'}},KS_NONE},
+ {"V","[" N_("View URL") "]",{MC_VIEW_HANDLE,3,{'v',ctrl('M'),ctrl('J')}},KS_NONE},
+ {"E",N_("Exit"),{MC_NO,1,{'e'}},KS_NONE},
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE}};
+INST_KEY_MENU(oauth2_auth_keymenu, oauth2_alpine_auth_keys);
+
struct key forge_keys[] =
{HELP_MENU,
diff --git a/alpine/keymenu.h b/alpine/keymenu.h
index 7e3a7f76..47c0eea3 100644
--- a/alpine/keymenu.h
+++ b/alpine/keymenu.h
@@ -582,6 +582,7 @@ extern struct key_menu cancel_keymenu,
rev_msg_keymenu,
ans_certfail_keymenu,
ans_certquery_keymenu,
+ oauth2_auth_keymenu,
forge_keymenu,
listmgr_keymenu,
index_keymenu,
diff --git a/alpine/mailview.c b/alpine/mailview.c
index 0738c376..4e7b79a5 100644
--- a/alpine/mailview.c
+++ b/alpine/mailview.c
@@ -1389,6 +1389,11 @@ dot_on_handle(long int line, int goal)
return(key);
}
+int
+url_launch(HANDLE_S *handle)
+{
+ return do_url_launch(handle->h.url.tool, handle->h.url.path);
+}
/*
* url_launch - Sniff the given url, see if we can do anything with
@@ -1396,19 +1401,17 @@ dot_on_handle(long int line, int goal)
*
*/
int
-url_launch(HANDLE_S *handle)
+do_url_launch(char *toolp, char *url)
{
int rv = 0;
url_tool_t f;
#define URL_MAX_LAUNCH (2 * MAILTMPLEN)
- if(handle->h.url.tool){
- char *toolp, *cmdp, *p, cmd[URL_MAX_LAUNCH + 4];
+ if(toolp){
+ char *cmdp, *p, cmd[URL_MAX_LAUNCH + 4];
int mode, copied = 0;
PIPE_S *syspipe;
- toolp = handle->h.url.tool;
-
/* This code used to quote a URL to prevent arbitrary command execution
* through a URL. The plan was to quote the URL with single quotes,
* and this used to work. BUT some shells do not care about quoting
@@ -1434,8 +1437,7 @@ url_launch(HANDLE_S *handle)
cmdp--;
copied = 1;
- for(p = handle->h.url.path;
- p && *p && cmdp-cmd < URL_MAX_LAUNCH; p++)
+ for(p = url; p && *p && cmdp-cmd < URL_MAX_LAUNCH; p++)
*cmdp++ = *p;
*cmdp = '\0';
@@ -1462,14 +1464,13 @@ url_launch(HANDLE_S *handle)
/* TRANSLATORS: Cannot start command : <command name> */
_("Cannot start command : %s"), cmd);
}
- else if((f = url_local_handler(handle->h.url.path)) != NULL){
- if((*f)(handle->h.url.path) > 1)
+ else if((f = url_local_handler(url)) != NULL){
+ if((*f)(url) > 1)
rv = 1; /* done! */
}
else
q_status_message1(SM_ORDER, 2, 2,
- _("\"URL-Viewer\" not defined: Can't open %s"),
- handle->h.url.path);
+ _("\"URL-Viewer\" not defined: Can't open %s"), url);
return(rv);
}
@@ -1487,6 +1488,12 @@ url_launch_too_long(int return_value)
char *
url_external_handler(HANDLE_S *handle, int specific)
{
+ return get_url_external_handler(handle->h.url.path, specific);
+}
+
+char *
+get_url_external_handler(char *url, int specific)
+{
char **l, *test, *cmd, *p, *q, *ep;
int i, specific_match;
@@ -1524,11 +1531,9 @@ url_external_handler(HANDLE_S *handle, int specific)
else
q = ep;
while(!((i = strlen(p))
- && ((p[i-1] == ':'
- && handle->h.url.path[i - 1] == ':')
- || (p[i-1] != ':'
- && handle->h.url.path[i] == ':'))
- && !struncmp(handle->h.url.path, p, i))
+ && ((p[i-1] == ':' && url[i - 1] == ':')
+ || (p[i-1] != ':' && url[i] == ':'))
+ && !struncmp(url, p, i))
&& *(p = q));
if(*p){
@@ -1571,7 +1576,7 @@ url_external_handler(HANDLE_S *handle, int specific)
cmd = NULL;
if(!specific){
- cmd = url_os_specified_browser(handle->h.url.path);
+ cmd = url_os_specified_browser(url);
/*
* Last chance, anything handling "text/html" in mailcap...
*/
diff --git a/alpine/mailview.h b/alpine/mailview.h
index 2cd3f643..f8962c6d 100644
--- a/alpine/mailview.h
+++ b/alpine/mailview.h
@@ -110,6 +110,8 @@ typedef struct scrolltool_s {
/* exported protoypes */
+char *get_url_external_handler(char *, int);
+int do_url_launch(char *, char *);
void mail_view_screen(struct pine *);
url_tool_t url_local_handler(char *);
int url_local_mailto(char *);
diff --git a/alpine/rpdump.c b/alpine/rpdump.c
index 2c677d93..6dfbfee2 100644
--- a/alpine/rpdump.c
+++ b/alpine/rpdump.c
@@ -536,14 +536,23 @@ void mm_log(string, errflg)
}
}
+void mm_login_method(mb, user, pwd, trial, method)
+ NETMBX *mb;
+ char *user;
+ void *pwd;
+ long trial;
+ char *method;
+{
+ mm_login(mb, user, (char **) pwd, trial);
+}
void mm_login(mb, user, pwd, trial)
NETMBX *mb;
char *user;
- char *pwd;
+ char **pwd;
long trial;
{
- char prompt[100], *last;
+ char prompt[100], *last, tmp[MAILTMPLEN];
int i, j, goal, ugoal, len, rc, flags = 0;
#define NETMAXPASSWD 100
@@ -595,14 +604,14 @@ void mm_login(mb, user, pwd, trial)
if(rc == 1 || !user[0]) {
user[0] = '\0';
- pwd[0] = '\0';
+ *pwd = NULL;
}
}
else
strncpy(user, mb->user, NETMAXUSER);
user[NETMAXUSER-1] = '\0';
- pwd[NETMAXPASSWD-1] = '\0';
+// pwd[NETMAXPASSWD-1] = '\0';
if(!user[0])
return;
@@ -662,16 +671,17 @@ void mm_login(mb, user, pwd, trial)
strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
prompt[sizeof(prompt)-1] = '\0';
- *pwd = '\0';
+ tmp[0] = '\0';
while(1) {
flags = OE_PASSWD;
- rc = opt_enter(pwd, NETMAXPASSWD, prompt, &flags);
+ rc = opt_enter(tmp, NETMAXPASSWD, prompt, &flags);
if(rc != 4)
break;
}
- if(rc == 1 || !pwd[0]) {
- user[0] = pwd[0] = '\0';
+ if(tmp[0]) *pwd = cpystr(tmp);
+ if(rc == 1 || !tmp[0]) {
+ user[0] = '\0';
return;
}
}
diff --git a/alpine/rpload.c b/alpine/rpload.c
index cd74e7fb..a497d3c3 100644
--- a/alpine/rpload.c
+++ b/alpine/rpload.c
@@ -775,14 +775,23 @@ void mm_log(string, errflg)
}
}
+void mm_login_method(mb, user, pwd, trial, method)
+ NETMBX *mb;
+ char *user;
+ void *pwd;
+ long trial;
+ char *method;
+{
+ mm_login(mb, user, (char **) pwd, trial);
+}
void mm_login(mb, user, pwd, trial)
NETMBX *mb;
char *user;
- char *pwd;
+ char **pwd;
long trial;
{
- char prompt[100], *last;
+ char prompt[100], *last, tmp[MAILTMPLEN];
int i, j, goal, ugoal, len, rc, flags = 0;
#define NETMAXPASSWD 100
@@ -834,14 +843,13 @@ void mm_login(mb, user, pwd, trial)
if(rc == 1 || !user[0]) {
user[0] = '\0';
- pwd[0] = '\0';
}
}
else
strncpy(user, mb->user, NETMAXUSER);
user[NETMAXUSER-1] = '\0';
- pwd[NETMAXPASSWD-1] = '\0';
+// pwd[NETMAXPASSWD-1] = '\0';
if(!user[0])
return;
@@ -901,16 +909,17 @@ void mm_login(mb, user, pwd, trial)
strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
prompt[sizeof(prompt)-1] = '\0';
- *pwd = '\0';
+ tmp[0] = '\0';
while(1) {
flags = OE_PASSWD;
- rc = opt_enter(pwd, NETMAXPASSWD, prompt, &flags);
+ rc = opt_enter(tmp, NETMAXPASSWD, prompt, &flags);
if(rc != 4)
break;
}
- if(rc == 1 || !pwd[0]) {
- user[0] = pwd[0] = '\0';
+ if(tmp[0]) *pwd = cpystr(tmp);
+ if(rc == 1 || !tmp[0]) {
+ user[0] = '\0';
return;
}
}
diff --git a/alpine/xoauth2.h b/alpine/xoauth2.h
new file mode 100644
index 00000000..c5a65ddb
--- /dev/null
+++ b/alpine/xoauth2.h
@@ -0,0 +1,77 @@
+/*
+ * ========================================================================
+ * Copyright 2018 Eduardo Chappa
+ * Copyright 2006-2009 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_XOAUTH2_INCLUDED
+#define ALPINE_XOAUTH2_INCLUDED
+
+/*
+ * This is the private information of the client, which is passed to
+ * c-client for processing. Every c-client application must have its
+ * own.
+ */
+OAUTH2_S alpine_oauth2_list[] =
+{
+ {"Gmail",
+ {"imap.gmail.com", "smtp.gmail.com", NULL, NULL},
+ {{"client_id", "624395471329-0qee3goofj7kbl7hsukou3rqq0igntv1.apps.googleusercontent.com"},
+ {"client_secret", "vwnqVJQrJZpR6JilCfAN5nY7"},
+ {"code", NULL},
+ {"refresh_token", NULL},
+ {"scope", "https://mail.google.com/"},
+ {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
+ {"grant_type", "authorization_code"},
+ {"grant_type", "refresh_token"},
+ {"response_type", "code"},
+ {"state", NULL},
+ {"prompt", NULL}
+ },
+ {{"GET", "https://accounts.google.com/o/oauth2/auth",
+ {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End}},
+ {"POST", "https://accounts.google.com/o/oauth2/token",
+ {OA2_Id, OA2_Secret, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Code, OA2_End, OA2_End}},
+ {"POST", "https://accounts.google.com/o/oauth2/token",
+ {OA2_Id, OA2_Secret, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End}}
+ },
+ NULL, 0
+ },
+#if 0
+ {"Outlook",
+ {"outlook.office365.com", "smtp.gmail.com", NULL, NULL},
+// {{"client_id", "2d681b88-9675-4ff0-b033-4de97dcb7a04"},
+// {"client_secret", "FHLY770;@%fmrzxbnEKG44!"},
+ {{"client_id", "c8df0dbf-4750-4bb9-98e9-562b10caa26a"},
+ {"client_secret", "ijrmPVDYP4yxbNL3442;!!_"},
+ {"code", NULL},
+ {"refresh_token", NULL},
+ {"scope", "openid offline_access profile https://outlook.office.com/mail.readwrite https://outlook.office.com/mail.readwrite.shared https://outlook.office.com/mail.send https://outlook.office.com/mail.send.shared https://outlook.office.com/calendars.readwrite https://outlook.office.com/calendars.readwrite.shared https://outlook.office.com/contacts.readwrite https://outlook.office.com/contacts.readwrite.shared https://outlook.office.com/tasks.readwrite https://outlook.office.com/tasks.readwrite.shared https://outlook.office.com/mailboxsettings.readwrite https://outlook.office.com/people.read https://outlook.office.com/user.readbasic.all"},
+ {"redirect_uri", "https://login.microsoftonline.com/common/oauth2/nativeclient"},
+ {"grant_type", "authorization_code"},
+ {"grant_type", "refresh_token"},
+ {"response_type", "code"},
+ {"state", NULL},
+ {"prompt", "login"}
+ },
+ {{"GET", "https://login.microsoftonline.com/common/oauth2/authorize",
+ {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_State, OA2_Prompt, OA2_End}},
+ {"POST", "https://login.microsoftonline.com/common/oauth2/token",
+ {OA2_Id, OA2_Secret, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Code, OA2_Scope, OA2_End}},
+ {"POST", "https://login.microsoftonline.com/common/oauth2/token",
+ {OA2_Id, OA2_Secret, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End}}
+ },
+ NULL, 0
+ },
+#endif
+ { NULL, NULL, NULL, NULL, NULL, 0},
+};
+#endif /* ALPINE_XOAUTH2_INCLUDED */