summaryrefslogtreecommitdiff
path: root/alpine/imap.c
diff options
context:
space:
mode:
Diffstat (limited to 'alpine/imap.c')
-rw-r--r--alpine/imap.c484
1 files changed, 357 insertions, 127 deletions
diff --git a/alpine/imap.c b/alpine/imap.c
index 91f87b16..4a48e342 100644
--- a/alpine/imap.c
+++ b/alpine/imap.c
@@ -1,6 +1,6 @@
/*
* ========================================================================
- * Copyright 2013-2021 Eduardo Chappa
+ * Copyright 2013-2022 Eduardo Chappa
* Copyright 2006-2009 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,8 +55,8 @@
#include <wincred.h>
#define TNAME "UWash_Alpine_"
#define TNAMESTAR "UWash_Alpine_*"
-#define PWDBUFFERSIZE (250)
-#define MAXPWDBUFFERSIZE (2*PWDBUFFERSIZE) /* This number must be less than 512 */
+#define PWDBUFFERSIZE (240)
+#define MAXPWDBUFFERSIZE (2*PWDBUFFERSIZE) /* This number must be less than 512 with some room to TNAME and other extra characters */
/*
* WinCred Function prototypes
@@ -105,17 +105,18 @@ 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);
+void cache_method_message(STORE_S *);
+void cache_method_message_no_screen(void);
#ifdef LOCAL_PASSWD_CACHE
+int cache_method_was_setup(char *);
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;
static int using_passfile = -1;
int save_password = 1;
#endif /* LOCAL_PASSWD_CACHE */
@@ -137,6 +138,69 @@ static char *details_cert, *details_host, *details_reason;
extern XOAUTH2_INFO_S xoauth_default[];
extern OAUTH2_S alpine_oauth2_list[];
+void
+cache_method_message(STORE_S *in_store)
+{
+#ifdef LOCAL_PASSWD_CACHE
+ if(cache_method_was_setup(ps_global->pinerc)){
+ so_puts(in_store, _("</P><P> Once you have authorized Alpine, Alpine will ask you if you want to preserve the Refresh Token and Access Code. If you do "));
+ so_puts(in_store, _("not wish to repeat this process again, answer \'Y\'es. If you did this process quickly, your connection to the server will still be "));
+ so_puts(in_store, _("alive at the end of this process, and your connection will proceed from there."));
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Create a password file in order to save the login information"));
+ so_puts(in_store, _("</P><P> Although your version of Alpine was compiled with password file support, this has not been set up yet. "));
+ so_puts(in_store, _("as a result, the next time you open this folder, you will go through this process once again.\n\n"));
+ }
+#else
+ so_puts(in_store, _("</P><P> Your version of Alpine was not built with password file support, as a result you will not be able to save your "));
+ so_puts(in_store, _("access token, which means that you will have to repeat this process the next time you login to this server. "));
+ so_puts(in_store, _("You can fix this by either recompiling alpine with password file support or by downloading a version of Alpine to your "));
+ so_puts(in_store, _("computer that was compiled with password file support."));
+#endif /* LOCAL_PASSWD_CACHE */
+}
+
+void
+cache_method_message_no_screen (void)
+{
+#ifdef LOCAL_PASSWD_CACHE
+ if(cache_method_was_setup(ps_global->pinerc)){
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _(" Once you have authorized Alpine, you will be asked if you want to preserve the Refresh Token and Access Code. "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
+ "%s", _("If you do not wish to repeat this process again, answer \'Y\'es. If you did this process quickly, your connection "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
+ "%s", _("to the server will still be alive at the end of this process, and your connection will proceed from there.\n\n"));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+ else{
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _(" Although your version of Alpine was compiled with password file support, this has not been set up yet. "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _("as a result, the next time you open this folder, you will go through this process once again.\n\n"));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+#else
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _("Your version of Alpine was not built with password file support, as a result you will not be able to save your "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _("access token, which means that you will have to repeat this process the next time you login to this server. "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _("You can fix this by either recompiling alpine with password file support or by downloading a version of Alpine to your "));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf), "%s",
+ _("computer that was compiled with password file support.\n\n"));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+#endif /* LOCAL_PASSWD_CACHE */
+}
+
int
xoauth2_flow_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
{
@@ -165,9 +229,11 @@ OAUTH2_S *
oauth2_select_flow(char *host)
{
OAUTH2_S *oa2list, *oa2;
- int i, rv;
+ int i = 0, rv;
char *method;
+ dprint((2, "-- oauth2_select_flow()\n"));
+
if(ps_global->ttyo){
CONF_S *ctmp = NULL, *first_line = NULL;
OAUTH2_S *x_sel = NULL;
@@ -299,6 +365,8 @@ oauth2_set_device_info(OAUTH2_S *oa2, char *method)
OAUTH2_DEVICECODE_S *deviceinfo = &oa2->devicecode;
OAUTH2_DEVICEPROC_S aux_value;
+ dprint((2, "-- oauth2_set_device_info\n"));
+ ps_global->in_xoauth2_auth = 1;
if(ps_global->ttyo){
SCROLL_S sargs;
STORE_S *in_store, *out_store;
@@ -338,10 +406,10 @@ oauth2_set_device_info(OAUTH2_S *oa2, char *method)
so_puts(in_store, _("</P><P> After you open the previous link, please enter the code above, and then you will be asked to authenticate and later "));
so_puts(in_store, _("to grant access to Alpine to your data. "));
- so_puts(in_store, _("</P><P> Once you have authorized Alpine, you will be asked if you want to preserve the Refresh Token and Access Code. If you do "));
- so_puts(in_store, _("not wish to repeat this process again, answer \'Y\'es. If you did this process quickly, your connection to the server will still be "));
- so_puts(in_store, _("alive at the end of this process, and your connection will proceed from there."));
- so_puts(in_store, _(" </P><P>If you do not wish to proceed, cancel at any time by pressing 'E' to exit"));
+
+ cache_method_message(in_store);
+
+ so_puts(in_store, _(" </P><P>If you do not wish to proceed, cancel at any time by pressing 'E' to exit."));
so_puts(in_store, _("</P></HTML>"));
so_seek(in_store, 0L, 0);
@@ -369,7 +437,7 @@ oauth2_set_device_info(OAUTH2_S *oa2, char *method)
/* don't want to re-enter c-client */
sargs.quell_newmail = 1;
setbitmap(sargs.keys.bitmap);
- sargs.help.text = h_oauth2_start;
+ sargs.help.text = h_oauth2_start_device;
sargs.help.title = _("HELP FOR SETTING UP XOAUTH2");
sargs.aux_function = oauth2deviceinfo_get_accesscode;
sargs.aux_value = (void *) &aux_value;
@@ -378,7 +446,8 @@ oauth2_set_device_info(OAUTH2_S *oa2, char *method)
sargs.aux_rv_value = (void *) &aux_rv_value;
do {
- scrolltool(&sargs);
+ if(scrolltool(&sargs) == MC_NO)
+ ps_global->user_says_cancel = 1;
ps_global->mangled_screen = 1;
ps_global->painted_body_on_startup = 0;
ps_global->painted_footer_on_startup = 0;
@@ -428,17 +497,7 @@ try_wantto:
"%s", _("to grant access to Alpine to your data.\n\n"));
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
- snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
- "%s", _(" Once you have authorized Alpine, you will be asked if you want to preserve the Refresh Token and Access Code. "));
- tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
-
- snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
- "%s", _("If you do not wish to repeat this process again, answer \'Y\'es. If you did this process quickly, your connection "));
- tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
-
- snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
- "%s", _("to the server will still be alive at the end of this process, and your connection will proceed from there.\n\n"));
- tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ cache_method_message_no_screen();
display_init_err(tmp_20k_buf, 0);
memset((void *)tmp, 0, sizeof(tmp));
@@ -474,8 +533,10 @@ char *
oauth2_get_access_code(unsigned char *url, char *method, OAUTH2_S *oauth2, int *tryanother)
{
char tmp[MAILTMPLEN];
- char *code;
+ char *code = NULL;
+ dprint((2, "-- oauth2_get_access_code\n"));
+ ps_global->in_xoauth2_auth = 1;
if(ps_global->ttyo){
SCROLL_S sargs;
STORE_S *in_store, *out_store;
@@ -493,38 +554,41 @@ oauth2_get_access_code(unsigned char *url, char *method, OAUTH2_S *oauth2, int *
sprintf(tmp, _("<P>Alpine is attempting to log you into your %s account, using the %s method."), oauth2->name, method),
so_puts(in_store, tmp);
- if(strucmp((char *) oauth2->name, (char *) GMAIL_NAME) == 0){
+ if(strucmp((char *) oauth2->name, (char *) GMAIL_NAME) == 0 && strstr(url, (char *) GMAIL_ID) != NULL){
so_puts(in_store, _(" If this is your first time setting up this type of authentication, please follow the steps below. "));
- so_puts(in_store, _("</P><P> First you must register Alpine with Google and create a client-id and client-secret. If you already did that, then you can skip to the authorization step, and continue with the process outlined below."));
+ so_puts(in_store, _("</P><P> First you must register Alpine with Google and create a client-id and client-secret.</P>"));
so_puts(in_store, _("<UL> "));
so_puts(in_store, _("<LI>First, login to <A HREF=\"https://console.developers.google.com\">https://console.developers.google.com</A> "));
so_puts(in_store, _("and create a project. The name of the project is not important."));
- so_puts(in_store, _("<LI> Go to the Consent Screen and make your app INTERNAL, if your account is a G-Suite account, or EXTERNAL if it is a personal gmail.com account."));
+ so_puts(in_store, _("<LI> Go to the OAuth Consent Screen and make your app INTERNAL, if your account is a G-Suite account, or EXTERNAL if it is a personal gmail.com account."));
+ so_puts(in_store, _("<LI> This will take you to several screens where you must input the required information. You can always use your email address for developer and contact information. Do not add scopes when you get to the scopes screen and add your email address to the screen to add Test Users."));
so_puts(in_store, _("<LI> Create OAUTH Credentials."));
so_puts(in_store, _("</UL> "));
so_puts(in_store, _("<P> As a result of this process, you will get a client-id and a client-secret."));
- so_puts(in_store, _(" Exit this screen, and from Alpine's Main Screen press S U to save these values permanently."));
- so_puts(in_store, _(" Then retry login into Gmail's server, skip these steps, and continue with the steps below."));
- so_puts(in_store, _("</P><P> Cancelling this process will lead to an error in authentication that can be ignored."));
- so_puts(in_store, _("</P><P> If you completed these steps successfully, you are ready to move to the second part, where you will authorize Gmail to give access to Alpine to access your email."));
+ so_puts(in_store, _(" Exit this screen, and from Alpine's Main Screen press S U to save these values permanently,"));
+ so_puts(in_store, _(" then retry login into Gmail's server."));
+ so_puts(in_store, _(" More detailed and up to date information on how to configure Alpine for Gmail can be found at the following <A href=\"https://alpine.x10host.com/alpine/alpine-info/misc/RegisteringAlpineinGmail.html\">link</A>."));
}
+ else{
+ so_puts(in_store, _("</P><P>In order to authorize Alpine to access your email, 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>In order to authorize Alpine to access your email, 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> 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, _("</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"));
+ cache_method_message(in_store);
+ }
+
+ so_puts(in_store, _("</P><P> If you do not wish to proceed, cancel by pressing 'E' to exit."));
so_puts(in_store, _("</P></BODY></HTML>"));
so_seek(in_store, 0L, 0);
@@ -556,7 +620,8 @@ oauth2_get_access_code(unsigned char *url, char *method, OAUTH2_S *oauth2, int *
sargs.help.title = _("HELP FOR SETTING UP XOAUTH2");
do {
- scrolltool(&sargs);
+ if(scrolltool(&sargs) == MC_NO)
+ ps_global->user_says_cancel = 1;
ps_global->mangled_screen = 1;
ps_global->painted_body_on_startup = 0;
ps_global->painted_footer_on_startup = 0;
@@ -628,6 +693,8 @@ try_wantto:
"%s", _(" 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. "));
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ cache_method_message_no_screen();
+
snprintf(tmp_20k_buf+strlen(tmp_20k_buf), SIZEOF_20KBUF-strlen(tmp_20k_buf),
"%s", _(" Once you have completed this process, Alpine will proceed with authentication.\n"));
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
@@ -727,7 +794,6 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
int ChangeAccessToken, ChangeRefreshToken, ChangeExpirationTime;
OAUTH2_S *oa2list, *oa2;
XOAUTH2_INFO_S *x;
-
unsigned long OldExpirationTime, NewExpirationTime, SaveExpirationTime;
#if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE)
int preserve_password = -1;
@@ -748,9 +814,13 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
ps_global->no_newmail_check_from_optionally_enter = 1;
/* make sure errors are seen */
- if(ps_global->ttyo && !ps_global->noshow_error)
+ if(ps_global->ttyo && !ps_global->noshow_error
+ && login && (login->flags & OA2_OPENSTREAM))
flush_status_messages(0);
+ if(login && (login->flags & OA2_OPENSTREAM))
+ login->flags |= ~OA2_OPENSTREAM;
+
token = NULL; /* start from scratch */
hostlist[0].name = mb->host;
@@ -801,21 +871,38 @@ 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;
+ if(!x) return; /* user cancelled, let's get out of here */
+ if(x->flow){
+ int authorize = 0, device = 0;
+ for(oa2list = alpine_oauth2_list;
oa2list && oa2list->host != NULL && oa2list->host[0] != NULL;
oa2list++){
- for(i = 0; i < OAUTH2_TOT_EQUIV
+ 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 */
- }
- }
+ if(i < OAUTH2_TOT_EQUIV && oa2list->host[i] != NULL){
+ char *flow = oa2list->server_mthd[0].name ? "Authorize"
+ : (oa2list->server_mthd[1].name ? "Device" : NULL);
+ authorize += oa2list->server_mthd[0].name ? 1 : 0;
+ device += oa2list->server_mthd[1].name ? 1 : 0;
+ if(flow && !strucmp(x->flow, flow)) break; /* found it */
+ }
+ }
+ if(!oa2list || !oa2list->host || !oa2list->host[0]){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("%s does not support or recognize flow type \"%s\". Use %s%s%s"),
+ mb->orighost,
+ x->flow,
+ authorize ? "\"Authorize\"" : "",
+ authorize ? (device ? " or " : "") : "",
+ device ? "\"Device\"" : "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ free_xoauth2_info(&x);
+ return;
+ }
}
- /* else use the one we found earlier, the user has to configure this better */
+ free_xoauth2_info(&x);
}
if(registered){
@@ -842,6 +929,18 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
mail_parameters(NULL, SET_IDPARAMS, (void *) ps_global->id);
}
+ if(registered)
+ oa2list->param[OA2_State].value = login->param[OA2_State].value;
+
+ if(login->cancel_refresh_token){
+ imap_delete_passwd_auth(&mm_login_list, user,
+ registered ? hostlist2 : hostlist,
+ (mb->sslflag||mb->tlsflag), OA2NAME);
+#ifdef LOCAL_PASSWD_CACHE
+ write_passfile(ps_global->pinerc, mm_login_list);
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+
/*
* 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
@@ -885,7 +984,11 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
NewExpirationTime = 0L;
ChangeAccessToken = ChangeRefreshToken = ChangeExpirationTime = 0;
- if(token && *token && !login->cancel_refresh_token){
+ /* We have done all steps that cancellation requires us by this time */
+ if(login->cancel_refresh_token)
+ login->cancel_refresh_token = 0;
+
+ if(token && *token){
char *s, *t;
s = token;
@@ -940,7 +1043,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
/* Default to saving what we already had saved */
- SaveRefreshToken = login->cancel_refresh_token ? NULL : NewRefreshToken;
+ SaveRefreshToken = NewRefreshToken;
SaveAccessToken = NewAccessToken;
SaveExpirationTime = NewExpirationTime;
@@ -984,6 +1087,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
&& (NewAccessToken == NULL || strcmp(OldAccessToken, NewAccessToken))){
if(NewAccessToken) fs_give((void **) &NewAccessToken);
NewAccessToken = cpystr(OldAccessToken);
+ NewAccessToken = OldAccessToken;
ChangeAccessToken++;
NewExpirationTime = OldExpirationTime;
SaveRefreshToken = NewRefreshToken;
@@ -1003,15 +1107,15 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
oa2list->cancel_refresh_token = login->cancel_refresh_token;
*login = *oa2list; /* load login pointer */
}
+ if(token) fs_give((void **) &token);
- if(!ChangeAccessToken && !ChangeRefreshToken && !login->cancel_refresh_token)
+ 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 : "")
@@ -1037,7 +1141,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method,
(preserve_password == -1 ? 0
: (preserve_password == 0 ? 2 :1)), OA2NAME);
#endif /* LOCAL_PASSWD_CACHE */
-
+ if (token) fs_give((void **) &token);
ps_global->no_newmail_check_from_optionally_enter = 0;
}
@@ -1058,6 +1162,65 @@ set_alpine_id(char *pname, char *pversion)
return id;
}
+void
+pine_delete_pwd(NETMBX *mb, char *user)
+{
+ char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN];
+ char port[20], non_def_port[20];
+ STRLIST_S hostlist[2];
+ MMLOGIN_S *l;
+ struct servent *sv;
+ /* do not invalidate password on cancel */
+ if(ps_global->user_says_cancel != 0)
+ return;
+
+ dprint((9, "pine_delete_pwd\n"));
+
+ /* setup hostlist */
+ non_def_port[0] = '\0';
+ if(mb->port && mb->service &&
+ (sv = getservbyname(mb->service, "tcp")) &&
+ (mb->port != ntohs(sv->s_port))){
+ snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port);
+ non_def_port[sizeof(non_def_port)-1] = '\0';
+ dprint((9, "mm_login: using non-default port=%s\n",
+ non_def_port ? non_def_port : "?"));
+ }
+
+ if(*non_def_port){
+ strncpy(hostlist0, mb->host, sizeof(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ hostlist[0].name = hostlist0;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = hostlist1;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+ }
+ else{
+ 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;
+ }
+ imap_delete_passwd(&mm_login_list, user, hostlist, mb->sslflag||mb->tlsflag);
+#ifdef LOCAL_PASSWD_CACHE
+ write_passfile(ps_global->pinerc, mm_login_list);
+#endif /* LOCAL_PASSWD_CACHE */
+}
+
/*----------------------------------------------------------------------
receive notification from IMAP
@@ -1174,9 +1337,9 @@ mm_log(char *string, long int errflg)
now = time((time_t *)0);
tm_now = localtime(&now);
- dprint((((errflg == TCPDEBUG) && ps_global->debug_tcp) ? 1 :
+ dprint((((errflg == TCPDEBUG) && ps_global->debug_tcp) ? debug :
(errflg == TCPDEBUG) ? 10 :
- ((errflg == HTTPDEBUG) && ps_global->debug_http) ? 1 :
+ ((errflg == HTTPDEBUG) && ps_global->debug_http) ? debug :
(errflg == HTTPDEBUG) ? 10 : 2,
"IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
@@ -1203,6 +1366,10 @@ mm_log(char *string, long int errflg)
*/
return;
+ /* if we took too long to authenticate, ignore this error */
+ if(ps_global->in_xoauth2_auth && strstr(string, "[CLOSED]"))
+ return;
+
strncpy(message, string, sizeof(message));
message[sizeof(message) - 1] = '\0';
@@ -1674,7 +1841,7 @@ mm_login_work(NETMBX *mb, char *user, char **pwd, long int trial,
#endif /* LOCAL_PASSWD_CACHE */
0, 0, &preserve_password);
ps_global->dont_use_init_cmds = save_dont_use;
- if(rc == 0 && *user && *pwd)
+ if(rc == 0 && *user && *pwd && **pwd)
goto nopwpmt;
#else /* !_WINDOWS */
rc = optionally_enter(user, q_line, 0, NETMAXUSER,
@@ -3003,6 +3170,18 @@ xlate_out(char c)
#ifdef LOCAL_PASSWD_CACHE
+int cache_method_was_setup (char *pinerc)
+{
+ int rv = 1;
+#ifdef PASSFILE
+ char tmp[MAILTMPLEN];
+ FILE *fp = NULL;
+ if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb")))
+ rv = 0;
+ if(fp != NULL) fclose(fp);
+#endif /* PASSFILE */
+ return rv;
+}
int
line_get(char *tmp, size_t len, char **textp)
@@ -3034,6 +3213,7 @@ typedef struct pwd_s {
char *sflags;
char *passwd;
char *orighost;
+ int uid;
} ALPINE_PWD_S;
#define SAME_VALUE(X, Y) ((((X) == NULL && (Y) == NULL) \
@@ -3070,7 +3250,8 @@ read_passfile(pinerc, l)
DWORD count, k;
PCREDENTIAL *pcred;
char *tmp, *blob, *target = NULL;
- ALPINE_PWD_S **pwd;
+ ALPINE_PWD_S **pwd = NULL;
+ int uid, rewrite_passfile = 0;
char *ui[5];
int i, j;
unsigned long m, n, p, loc;
@@ -3112,11 +3293,25 @@ read_passfile(pinerc, l)
if (*tmp == '.') {
tmp++;
m = strtoul(tmp, &tmp, 10);
+ if(*tmp == '.'){
+ tmp++;
+ uid = m;
+ m = strtoul(tmp, &tmp, 10);
+ }
+ else{
+ uid = 0; /* impossible value, uid >= 1, old format! */
+ rewrite_passfile++;
+ }
if (*tmp == '-') {
tmp++;
n = strtol(tmp, &tmp, 10);
if (*tmp == '_') tmp++;
}
+ else{
+ char *s;
+ for(s = tmp; *s && *s != '_'; s++);
+ if(s && *s) tmp = s;
+ }
}
else {
m = n = 1;
@@ -3137,9 +3332,10 @@ read_passfile(pinerc, l)
* can be done better.
*/
for (loc = 0; pwd[loc]
- && !(SAME_VALUE(ui[0], pwd[loc]->host)
- && SAME_VALUE(ui[1], pwd[loc]->user)
- && SAME_VALUE(ui[2], pwd[loc]->sflags)); loc++);
+ && !(uid == pwd[loc]->uid
+ && SAME_VALUE(ui[0], pwd[loc]->host)
+ && SAME_VALUE(ui[1], pwd[loc]->user)
+ && SAME_VALUE(ui[2], pwd[loc]->sflags)); loc++);
if (pwd[loc] == NULL) {
pwd[loc] = fs_get(sizeof(ALPINE_PWD_S));
@@ -3148,6 +3344,7 @@ read_passfile(pinerc, l)
memset((void *) pwd[loc]->blobarray, 0, (n + 1) * sizeof(char*));
}
+ pwd[loc]->uid = uid;
if (pwd[loc]->host == NULL)
pwd[loc]->host = ui[0] ? cpystr(ui[0]) : NULL;
if (pwd[loc]->user == NULL)
@@ -3170,7 +3367,7 @@ read_passfile(pinerc, l)
strcat(pwd[k]->blob, pwd[k]->blobarray[j]);
fs_give((void **) &pwd[k]->blobarray[j]);
}
- fs_give((void **) pwd[k]->blobarray);
+ fs_give((void **) &pwd[k]->blobarray);
}
else k = count; /* we are done with this step! */
}
@@ -3220,7 +3417,8 @@ read_passfile(pinerc, l)
}
g_CredFree((PVOID)pcred);
}
- fs_give((void **) pwd);
+ fs_give((void **) &pwd);
+ if(rewrite_passfile) write_passfile(pinerc, *l);
}
return(1);
@@ -3397,7 +3595,7 @@ read_passfile(pinerc, l)
#else /* PASSFILE */
char tmp[MAILTMPLEN], *ui[5];
- int i, j, n, rv = 0;
+ int i, j, n, rv = 0, error = 0;
size_t len = 0;
char *tmptext = NULL;
struct stat sbuf;
@@ -3413,11 +3611,28 @@ read_passfile(pinerc, l)
dprint((9, "read_passfile\n"));
- /* if there's no password to read, bag it!! */
+ /* if there's no password to read, create it if we can encrypt it,
+ * or else let the user create it and bail out of here.
+ */
+ tmp[0] = '\0';
if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb"))){
+#ifdef SMIME
+ i = our_creat(tmp, 0600);
+ if(i >= 0){
+ close(i);
+ if(!(fp = our_fopen(tmp, "rb")))
+ error++;
+ }
+ else error++;
+#else
+ error++;
+#endif
+ };
+
+ if(error){
using_passfile = 0;
return(using_passfile);
- };
+ }
#ifndef SMIME
if(our_stat(tmp, &sbuf) == 0)
@@ -3584,10 +3799,11 @@ write_passfile(pinerc, l)
char *authend, *authtype;
#ifdef WINCRED
# if (WINCRED > 0)
- int i, j, k;
- char target[10*MAILTMPLEN];
- char blob[10 * MAILTMPLEN], blob2[10*MAILTMPLEN], *blobp;
- char part[MAILTMPLEN];
+ unsigned long bloblen = 0, len;
+ int i, totalparts, k, uid;
+ char target[MAXPWDBUFFERSIZE];
+ char *blob = NIL, blob2[50], *blobp;
+ char part[MAILTMPLEN];
CREDENTIAL cred;
LPTSTR ltarget = 0;
@@ -3596,29 +3812,42 @@ write_passfile(pinerc, l)
dprint((9, "write_passfile\n"));
- for(; l; l = l->next){
+ erase_windows_credentials(); /* erase all passwords from credentials */
+ /* start writing them back to credentials manager */
+ for(uid = 1; l; l = l->next, uid++){ /* enforce that uid >= 1 */
/* determine how many parts to create first */
- snprintf(blob, sizeof(blob), "%s%s%s",
+ len = (l->passwd ? strlen(l->passwd) : 0)
+ + ((l->hosts && l->hosts->next && l->hosts->next->name) ? 1 : 0)
+ + ((l->hosts && l->hosts->next && l->hosts->next->name) ? strlen(l->hosts->next->name) : 0) + 1;
+
+ if(len > bloblen){
+ bloblen = len;
+ fs_resize((void **) &blob, bloblen);
+ }
+
+ sprintf(blob, "%s%s%s",
l->passwd ? l->passwd : "",
- (l->hosts&& l->hosts->next&& l->hosts->next->name)
+ (l->hosts && l->hosts->next && l->hosts->next->name)
? "\t" : "",
- (l->hosts&& l->hosts->next&& l->hosts->next->name)
+ (l->hosts && l->hosts->next && l->hosts->next->name)
? l->hosts->next->name : "");
- i = strlen(blob);
+ i = len - 1; /* strlen(blob) */
blobp = blob;
- for (j = 1; i > MAXPWDBUFFERSIZE; j++, i -= PWDBUFFERSIZE);
+ for (totalparts = 1; i > MAXPWDBUFFERSIZE; totalparts++, i -= PWDBUFFERSIZE);
authtype = l->passwd;
authend = strchr(l->passwd, PWDAUTHSEP);
+
if (authend != NULL){
*authend = '\0';
sprintf(blob2, "%s%c%d", authtype, PWDAUTHSEP, l->altflag);
- *authend = PWDAUTHSEP;
+ *authend = PWDAUTHSEP;
}
else
sprintf(blob2, "%d", l->altflag);
- for (k = 1, i = strlen(blob), blobp = blob; k <= j; k++) {
- snprintf(target, sizeof(target), "%s.%d-%d_%s\t%s\t%s",
- TNAME, k, j,
+
+ for (k = 1, i = len - 1, blobp = blob; k <= totalparts; k++) {
+ snprintf(target, sizeof(target), "%s.%d.%d-%d_%s\t%s\t%s",
+ TNAME, uid, k, totalparts,
(l->hosts && l->hosts->name) ? l->hosts->name : "",
l->user ? l->user : "",
blob2);
@@ -3644,6 +3873,8 @@ write_passfile(pinerc, l)
}
}
}
+ if(blob) fs_give((void **) &blob);
+
#endif /* WINCRED > 0 */
#elif APPLEKEYCHAIN
@@ -3716,11 +3947,12 @@ write_passfile(pinerc, l)
}
#else /* PASSFILE */
- char tmp[10*MAILTMPLEN], blob[10*MAILTMPLEN];
+ char *tmp = NULL, passfile[MAXPATH + 1], blob[MAILTMPLEN];
int i, n;
+ size_t tmplen = 0, newlen;
FILE *fp;
#ifdef SMIME
- char *text = NULL, tmp2[10*MAILTMPLEN];
+ char *text = NULL, tmp2[MAXPATH + 1];
int len = 0;
#endif
@@ -3730,13 +3962,13 @@ write_passfile(pinerc, l)
dprint((9, "write_passfile\n"));
/* if there's no passfile to read, bag it!! */
- if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
+ if(!passfile_name(pinerc, passfile, sizeof(passfile)) || !(fp = our_fopen(passfile, "wb"))){
using_passfile = 0;
return;
}
#ifdef SMIME
- strncpy(tmp2, tmp, sizeof(tmp2));
+ strncpy(tmp2, passfile, sizeof(tmp2));
tmp2[sizeof(tmp2)-1] = '\0';
#endif /* SMIME */
@@ -3751,8 +3983,18 @@ write_passfile(pinerc, l)
else
sprintf(blob, "%d", l->altflag);
+ newlen = strlen(l->passwd) + strlen(l->user) + strlen(l->hosts->name)
+ + strlen(blob) + strlen((l->hosts->next && l->hosts->next->name) ? "\t" : "")
+ + strlen((l->hosts->next && l->hosts->next->name) ? l->hosts->next->name : "")
+ + 4 + 1;
+
+ if(tmplen < newlen){
+ fs_resize((void **)&tmp, newlen);
+ tmplen = newlen;
+ }
+
/*** do any necessary ENcryption here ***/
- snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%s%s%s\n", l->passwd, l->user,
+ sprintf(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
@@ -3772,9 +4014,11 @@ write_passfile(pinerc, l)
#endif /* SMIME */
}
+ if(tmp) fs_give((void **) &tmp);
fclose(fp);
#ifdef SMIME
if(text != NULL){
+ i = 0; /* to quell gcc */
if(ps_global->pwdcert == NULL){
q_status_message(SM_ORDER, 3, 3, "Attempting to encrypt password file");
i = setup_pwdcert(&ps_global->pwdcert);
@@ -3858,33 +4102,12 @@ get_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, authtype)
char *authtype;
{
dprint((10, "get_passfile_passwd_auth\n"));
- return((passfile_cache || read_passfile(pinerc, &passfile_cache))
- ? imap_get_passwd_auth(passfile_cache, passwd,
+ return((mm_login_list || read_passfile(pinerc, &mm_login_list))
+ ? imap_get_passwd_auth(mm_login_list, passwd,
user, hostlist, altflag, authtype)
: 0);
}
-void
-free_passfile_cache_work(MMLOGIN_S **pwdcache)
-{
- if(pwdcache == NULL || *pwdcache == NULL)
- return;
-
- if((*pwdcache)->user) fs_give((void **)&(*pwdcache)->user);
-// if((*pwdcache)->passwd) fs_give((void **)&(*pwdcache)->passwd);
- if((*pwdcache)->hosts) free_strlist(&(*pwdcache)->hosts);
- free_passfile_cache_work(&(*pwdcache)->next);
- fs_give((void **)pwdcache);
-}
-
-
-void
-free_passfile_cache(void)
-{
- if(passfile_cache)
- free_passfile_cache_work(&passfile_cache);
-}
-
int
is_using_passfile(void)
{
@@ -3900,8 +4123,8 @@ get_passfile_user(pinerc, hostlist)
char *pinerc;
STRLIST_S *hostlist;
{
- return((passfile_cache || read_passfile(pinerc, &passfile_cache))
- ? imap_get_user(passfile_cache, hostlist)
+ return((mm_login_list || read_passfile(pinerc, &mm_login_list))
+ ? imap_get_user(mm_login_list, hostlist)
: NULL);
}
@@ -3915,6 +4138,7 @@ preserve_prompt(char *pinerc)
int
preserve_prompt_auth(char *pinerc, char *authtype)
{
+ ps_global->preserve_password = 0;
#ifdef WINCRED
# if (WINCRED > 0)
#define PROMPT_PWD _("Preserve password for next login")
@@ -3931,8 +4155,10 @@ preserve_prompt_auth(char *pinerc, char *authtype)
? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
: PROMPT_PWD,
'y', 'x', NO_HELP, WT_NORM)
- == 'y'))
+ == 'y')){
+ ps_global->preserve_password = 1;
return(1);
+ }
else
return(0);
# else
@@ -3949,6 +4175,7 @@ preserve_prompt_auth(char *pinerc, char *authtype)
? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
: PROMPT_PWD, 'y', 'x', NO_HELP, WT_NORM)
== 'y'){
+ ps_global->preserve_password = 1;
if(rc == -1){
macos_set_store_pass_prompt(1);
q_status_message(SM_ORDER, 4, 4,
@@ -3975,10 +4202,13 @@ preserve_prompt_auth(char *pinerc, char *authtype)
return 0;
if(F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
- return(want_to(authtype
+ if(want_to(authtype
? (strcmp(authtype, OA2NAME) ? PROMPT_PWD : PROMPT_OA2)
: PROMPT_PWD, 'y', 'x', NO_HELP, WT_NORM)
- == 'y');
+ == 'y'){
+ ps_global->preserve_password = 1;
+ return 1;
+ }
return(0);
#endif /* PASSFILE */
}
@@ -4131,9 +4361,9 @@ set_passfile_passwd_auth(pinerc, passwd, user, hostlist, altflag, already_prompt
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_auth(&passfile_cache, passwd, user, hostlist, altflag, 0, 0, authtype);
- write_passfile(pinerc, passfile_cache);
+ && (mm_login_list || read_passfile(pinerc, &mm_login_list))){
+ imap_set_passwd_auth(&mm_login_list, passwd, user, hostlist, altflag, 0, 0, authtype);
+ write_passfile(pinerc, mm_login_list);
}
}
@@ -4170,7 +4400,7 @@ update_passfile_hostlist_auth(pinerc, user, hostlist, altflag, authtype)
size_t len = authtype ? strlen(authtype) : 0;
size_t offset = authtype ? 1 : 0;
- for(l = passfile_cache; l; l = l->next)
+ for(l = mm_login_list; l; l = l->next)
if(imap_same_host_auth(l->hosts, hostlist, authtype)
&& *user
&& !strcmp(user, l->user + len + offset)
@@ -4182,7 +4412,7 @@ update_passfile_hostlist_auth(pinerc, user, hostlist, altflag, authtype)
&& hostlist->next->name
&& !ps_global->nowrite_password_cache){
l->hosts->next = new_strlist_auth(hostlist->next->name, authtype, PWDAUTHSEP);
- write_passfile(pinerc, passfile_cache);
+ write_passfile(pinerc, mm_login_list);
}
#endif /* !WINCRED */
}