From: Thomas Roessler Date: Tue, 10 Oct 2000 19:22:44 +0000 (+0000) Subject: Vsevolod Volkov's POP mailbox patch. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61f8ee2cd4ccd96af161563469831b1511a47d05;p=neomutt Vsevolod Volkov's POP mailbox patch. --- diff --git a/Makefile.am b/Makefile.am index 652e60f65..238722dea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,18 +72,18 @@ non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1.c \ mutt_ssl.c mutt_ssl.h README.SSL mutt_ssl_nss.c EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \ - pop.c pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1.c gnupgparse.c \ - resize.c dotlock.c remailer.c browser.h mbyte.h remailer.h url.h \ - mutt_ssl_nss.c + pop.c pop_auth.c pop_lib.c pgp.c pgpinvoke.c pgpkey.c pgplib.c \ + sha1.c gnupgparse.c resize.c dotlock.c remailer.c browser.h mbyte.h \ + remailer.h url.h mutt_ssl_nss.c EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP TODO configure acconfig.h account.h \ attach.h buffy.h charset.h copy.h dotlock.h functions.h gen_defs \ globals.h hash.h history.h init.h keymap.h \ mailbox.h mapping.h md5.h mime.h mutt.h mutt_curses.h mutt_menu.h \ mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mx.h pager.h \ - pgp.h protos.h reldate.h rfc1524.h rfc2047.h rfc2231.h rfc822.h \ - sha.h sha_locl.h sort.h mime.types VERSION prepare _regex.h \ - OPS.MIX README.SECURITY remailer.c remailer.h browser.h \ + pgp.h pop.h protos.h reldate.h rfc1524.h rfc2047.h rfc2231.h \ + rfc822.h sha.h sha_locl.h sort.h mime.types VERSION prepare \ + _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \ mbyte.h lib.h extlib.c pgpewrap pgplib.h Muttrc.head Muttrc \ makedoc.c stamp-doc-rc README.SSL README.UPGRADE checktypes.c \ muttbug diff --git a/browser.c b/browser.c index 1bdb7cee2..b4650e521 100644 --- a/browser.c +++ b/browser.c @@ -433,6 +433,13 @@ static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state) add_folder (menu, state, tmp->path, NULL, tmp->new); continue; } +#endif +#ifdef USE_POP + if (mx_is_pop (tmp->path)) + { + add_folder (menu, state, tmp->path, NULL, tmp->new); + continue; + } #endif if (lstat (tmp->path, &s) == -1) continue; diff --git a/buffy.c b/buffy.c index 5ed50a4a2..363f2c9e1 100644 --- a/buffy.c +++ b/buffy.c @@ -250,6 +250,9 @@ int mutt_buffy_check (int force) #ifdef USE_IMAP if (!Context || Context->magic != M_IMAP) +#endif +#ifdef USE_POP + if (!Context || Context->magic != M_POP) #endif /* check device ID and serial number instead of comparing paths */ if (!Context || !Context->path || stat (Context->path, &contex_sb) != 0) @@ -266,6 +269,11 @@ int mutt_buffy_check (int force) if ((tmp->magic == M_IMAP) || mx_is_imap (tmp->path)) tmp->magic = M_IMAP; else +#endif +#ifdef USE_POP + if ((tmp->magic == M_POP) || mx_is_pop (tmp->path)) + tmp->magic = M_POP; + else #endif if (stat (tmp->path, &sb) != 0 || sb.st_size == 0 || (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0)) @@ -282,15 +290,21 @@ int mutt_buffy_check (int force) /* check to see if the folder is the currently selected folder * before polling */ - if (!Context || !Context->path || + if (!Context || !Context->path || +#if defined USE_IMAP || defined USE_POP + (( #ifdef USE_IMAP - (tmp->magic == M_IMAP && mutt_strcmp (tmp->path, Context->path)) || - (tmp->magic != M_IMAP && ( + tmp->magic == M_IMAP #endif - sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino) +#ifdef USE_POP #ifdef USE_IMAP - )) + || +#endif + tmp->magic == M_POP #endif + ) ? mutt_strcmp (tmp->path, Context->path) : +#endif + (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino))) { switch (tmp->magic) { @@ -350,6 +364,11 @@ int mutt_buffy_check (int force) break; #endif + +#ifdef USE_POP + case M_POP: + break; +#endif } } #ifdef BUFFY_SIZE diff --git a/commands.c b/commands.c index 27c358d6c..915312eae 100644 --- a/commands.c +++ b/commands.c @@ -648,10 +648,7 @@ int mutt_save_message (HEADER *h, int delete, /* check to make sure that this file is really the one the user wants */ if (!mutt_save_confirm (buf, &st)) - { - CLEARLINE (LINES-1); - return (-1); - } + return -1; #ifdef HAVE_PGP if(need_passphrase && (decode || decrypt) && !pgp_valid_passphrase()) diff --git a/compose.c b/compose.c index 86c1466d3..a82ddc5ac 100644 --- a/compose.c +++ b/compose.c @@ -739,6 +739,9 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */ mutt_expand_path (fname, sizeof (fname)); #ifdef USE_IMAP if (!mx_is_imap (fname)) +#endif +#ifdef USE_POP + if (!mx_is_pop (fname)) #endif /* check to make sure the file exists and is readable */ if (access (fname, R_OK) == -1) diff --git a/configure.in b/configure.in index e5bd60b4d..758e06ddd 100644 --- a/configure.in +++ b/configure.in @@ -532,7 +532,7 @@ dnl -- socket dependencies -- AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support], [ if test x$enableval = xyes ; then AC_DEFINE(USE_POP) - MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS pop.o" + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS pop.o pop_lib.o pop_auth.o" need_socket="yes" need_md5="yes" fi diff --git a/curs_main.c b/curs_main.c index 418a266b8..902a663eb 100644 --- a/curs_main.c +++ b/curs_main.c @@ -25,6 +25,10 @@ #include "buffy.h" #include "mx.h" +#ifdef USE_POP +#include "pop.h" +#endif + #ifdef USE_IMAP #include "imap.h" #endif @@ -714,7 +718,7 @@ int mutt_index_menu (void) case OP_MAIN_FETCH_MAIL: CHECK_ATTACH; - mutt_fetchPopMail (); + pop_fetch_mail (); menu->redraw = REDRAW_FULL; break; #endif /* USE_POP */ @@ -1603,6 +1607,15 @@ int mutt_index_menu (void) CHECK_READONLY; CHECK_ATTACH; +#ifdef USE_POP + if (Context->magic == M_POP) + { + mutt_flushinp (); + mutt_error _("Can't edit message on POP server."); + break; + } +#endif + mutt_edit_message (Context, tag ? NULL : CURHDR); menu->redraw = REDRAW_FULL; diff --git a/doc/manual.sgml.head b/doc/manual.sgml.head index fa419cc56..75796fdc8 100644 --- a/doc/manual.sgml.head +++ b/doc/manual.sgml.head @@ -2047,21 +2047,40 @@ message). Refer to the man page on sendmail for more details on DSN.

If Mutt was compiled with POP3 support (by running the and authenticate by logging in -as . After the connection -is established, you will be prompted for your password on the remote -system. - -Once you have been authenticated, Mutt will fetch all your new mail and -place it in the local . After this +script with the pop://popserver/. + +You can select an alternative port by specifying it with the server, ie: +pop://popserver:port/. + +You can also specify different username for each folder, ie: +pop://username@popserver[:port]/. + +Polling for new mail is more expensive over POP3 than locally. For this +reason the frequency at which Mutt will check for mail remotely can be +controlled by the + +variable, which defaults to every 60 seconds. + +If Mutt was compiled with SSL support (by running the pops://[username@]popserver[:port]/. + +Another way to access your POP3 mail is the , fetch all your new mail and place it in the +local . After this point, Mutt runs exactly as if the mail had always been local. - IMAP Support (OPTIONAL) diff --git a/globals.h b/globals.h index 543e685f1..d90e7a3d7 100644 --- a/globals.h +++ b/globals.h @@ -76,9 +76,10 @@ WHERE char *Pager; WHERE char *PagerFmt; WHERE char *PipeSep; #ifdef USE_POP +WHERE short PopCheckTimeout; WHERE char *PopHost; -WHERE char *PopPass; -WHERE char *PopUser; +WHERE char *PopPass INITVAL (NULL); +WHERE char *PopUser INITVAL (NULL); #endif WHERE char *PostIndentString; WHERE char *Postponed; @@ -129,7 +130,6 @@ WHERE short ConnectTimeout; WHERE short HistSize; WHERE short PagerContext; WHERE short PagerIndexLines; -WHERE short PopPort; WHERE short ReadInc; WHERE short SendmailWait; WHERE short Timeout; diff --git a/init.c b/init.c index 9c1cfd325..9a1fa6ed0 100644 --- a/init.c +++ b/init.c @@ -1786,11 +1786,6 @@ void mutt_init (int skip_sys_rc, LIST *commands) } Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp"); - -#ifdef USE_POP - PopUser = safe_strdup (Username); -#endif - Editor = safe_strdup ((p = getenv ("EDITOR")) ? p : "vi"); Visual = safe_strdup ((p = getenv ("VISUAL")) ? p : Editor); diff --git a/init.h b/init.h index fbb5902ab..2e611fd2d 100644 --- a/init.h +++ b/init.h @@ -1400,6 +1400,12 @@ struct option_t MuttVars[] = { ** messages to an external Unix command. */ #ifdef USE_POP + { "pop_checkinterval", DT_NUM, R_NONE, UL &PopCheckTimeout, 60 }, + /* + ** .pp + ** This variable configures how often (in seconds) POP should look for + ** new mail. + */ { "pop_delete", DT_QUAD, R_NONE, OPT_POPDELETE, M_ASKNO }, /* ** .pp @@ -1410,34 +1416,39 @@ struct option_t MuttVars[] = { { "pop_host", DT_STR, R_NONE, UL &PopHost, UL "" }, /* ** .pp - ** The name or address of your POP3 server. - */ - { "pop_port", DT_NUM, R_NONE, UL &PopPort, -1 }, - /* + ** The name of your POP server for the fetch-mail function. You + ** can also specify an alternative port, username and password, ie: ** .pp - ** This variable specifies which port your POP server is listening on. - ** If you specify a value less than zero, Mutt will auto-select the port - ** based upon whether your are doing normal pop3 (110) or pop3s (995). - ** If your POP3 server listens on a non-standard port, you will have to - ** set this to the correct value for your environment. + ** [pop[s]://][username[:password]@]popserver[:port] */ { "pop_last", DT_BOOL, R_NONE, OPTPOPLAST, 0 }, /* ** .pp ** If this variable is set, mutt will try to use the "LAST" POP command - ** for retrieving only unread messages from the POP server. + ** for retrieving only unread messages from the POP server when using + ** the fetch-mail function. + */ + { "pop_reconnect", DT_QUAD, R_NONE, OPT_POPRECONNECT, M_ASKYES }, + /* + ** .pp + ** Controls whether or not Mutt will try to reconnect to POP server when + ** connection lost. */ { "pop_user", DT_STR, R_NONE, UL &PopUser, 0 }, /* ** .pp - ** Your login name on the POP3 server. + ** Your login name on the POP server. ** .pp - ** Defaults to your login name on the local system. + ** This variable defaults to your user name on the local machine. */ { "pop_pass", DT_STR, R_NONE, UL &PopPass, UL "" }, /* ** .pp - ** Your password on the POP3 server. + ** Specifies the password for your POP account. If unset, Mutt will + ** prompt you for your password when you open POP mailbox. + ** \fBWarning\fP: you should only use this option when you are on a + ** fairly secure machine, because the superuser can read your muttrc + ** even if you are the only one who can read the file. */ #endif /* USE_POP */ { "post_indent_string",DT_STR, R_NONE, UL &PostIndentString, UL "" }, diff --git a/mailbox.h b/mailbox.h index 385d280df..d442a61fe 100644 --- a/mailbox.h +++ b/mailbox.h @@ -61,6 +61,9 @@ int mx_check_mailbox (CONTEXT *, int *, int); #ifdef USE_IMAP int mx_is_imap (const char *); #endif +#ifdef USE_POP +int mx_is_pop (const char *); +#endif #endif diff --git a/mutt.h b/mutt.h index 44a462a94..e5cc5d86a 100644 --- a/mutt.h +++ b/mutt.h @@ -255,6 +255,7 @@ enum OPT_MOVE, OPT_COPY, OPT_POPDELETE, + OPT_POPRECONNECT, OPT_POSTPONE, OPT_QUIT, OPT_REPLYTO, @@ -627,8 +628,12 @@ typedef struct header LIST *chain; #endif -#ifdef USE_IMAP - void *data; /* driver-specific data (only used by IMAP) */ +#ifdef USE_POP + int refno; /* message number on server */ +#endif + +#if defined USE_POP || defined USE_IMAP + void *data; /* driver-specific data */ #endif } HEADER; @@ -677,7 +682,7 @@ typedef struct int deleted; /* how many deleted messages */ int flagged; /* how many flagged messages */ int msgnotreadyet; /* which msg "new" in pager, -1 if none */ -#ifdef USE_IMAP +#if defined USE_POP || defined USE_IMAP void *data; /* driver specific data */ #endif /* USE_IMAP */ diff --git a/muttlib.c b/muttlib.c index 868e22433..8ab0b4fe9 100644 --- a/muttlib.c +++ b/muttlib.c @@ -261,7 +261,7 @@ void mutt_free_header (HEADER **h) #ifdef MIXMASTER mutt_free_list (&(*h)->chain); #endif -#ifdef USE_IMAP +#if defined USE_POP || defined USE_IMAP safe_free ((void**) &(*h)->data); #endif safe_free ((void **) h); @@ -1105,6 +1105,14 @@ int mutt_save_confirm (const char *s, struct stat *st) magic = mx_get_magic (s); +#ifdef USE_POP + if (magic == M_POP) + { + mutt_error _("Can't save message to POP mailbox."); + return 0; + } +#endif + if (stat (s, st) != -1) { if (magic == -1) diff --git a/mx.c b/mx.c index 74e4d286c..fd42a9167 100644 --- a/mx.c +++ b/mx.c @@ -34,6 +34,10 @@ #include "imap.h" #endif +#ifdef USE_POP +#include "pop.h" +#endif + #ifdef BUFFY_SIZE #include "buffy.h" #endif @@ -330,6 +334,22 @@ int mx_is_imap(const char *p) #endif +#ifdef USE_POP +int mx_is_pop (const char *p) +{ + url_scheme_t scheme; + + if (!p) + return 0; + + scheme = url_check_scheme (p); + if (scheme == U_POP || scheme == U_POPS) + return 1; + + return 0; +} +#endif + int mx_get_magic (const char *path) { struct stat st; @@ -342,6 +362,11 @@ int mx_get_magic (const char *path) return M_IMAP; #endif /* USE_IMAP */ +#ifdef USE_POP + if (mx_is_pop (path)) + return M_POP; +#endif /* USE_POP */ + if (stat (path, &st) == -1) { dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n", @@ -657,6 +682,12 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) break; #endif /* USE_IMAP */ +#ifdef USE_POP + case M_POP: + rc = pop_open_mailbox (ctx); + break; +#endif /* USE_POP */ + default: rc = -1; break; @@ -698,6 +729,10 @@ void mx_fastclose_mailbox (CONTEXT *ctx) if (ctx->magic == M_IMAP) imap_close_mailbox (ctx); #endif /* USE_IMAP */ +#ifdef USE_POP + if (ctx->magic == M_POP) + pop_close_mailbox (ctx); +#endif /* USE_POP */ if (ctx->subj_hash) hash_destroy (&ctx->subj_hash, NULL); if (ctx->id_hash) @@ -746,6 +781,12 @@ static int sync_mailbox (CONTEXT *ctx, int *index_hint) rc = imap_sync_mailbox (ctx, 1, index_hint); break; #endif /* USE_IMAP */ + +#ifdef USE_POP + case M_POP: + rc = pop_sync_mailbox (ctx, index_hint); + break; +#endif /* USE_POP */ } #if 0 @@ -1286,6 +1327,11 @@ int mx_check_mailbox (CONTEXT *ctx, int *index_hint, int lock) case M_IMAP: return (imap_check_mailbox (ctx, index_hint)); #endif /* USE_IMAP */ + +#ifdef USE_POP + case M_POP: + return (pop_check_mailbox (ctx, index_hint)); +#endif /* USE_POP */ } } @@ -1332,6 +1378,15 @@ MESSAGE *mx_open_message (CONTEXT *ctx, int msgno) } #endif /* USE_IMAP */ +#ifdef USE_POP + case M_POP: + { + if (pop_fetch_message (msg, ctx, msgno) != 0) + FREE (&msg); + break; + } +#endif /* USE_POP */ + default: dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic)); FREE (&msg); @@ -1416,6 +1471,9 @@ int mx_close_message (MESSAGE **msg) if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR #ifdef USE_IMAP || (*msg)->magic == M_IMAP +#endif +#ifdef USE_POP + || (*msg)->magic == M_POP #endif ) { diff --git a/mx.h b/mx.h index 23411dfb8..2b643632b 100644 --- a/mx.h +++ b/mx.h @@ -38,6 +38,9 @@ enum #ifdef USE_IMAP , M_IMAP #endif +#ifdef USE_POP + , M_POP +#endif }; WHERE short DefaultMagic INITVAL (M_MBOX); diff --git a/po/POTFILES.in b/po/POTFILES.in index 2a49b08f7..8be803a9b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -47,6 +47,8 @@ pgp.c pgpinvoke.c pgpkey.c pop.c +pop_auth.c +pop_lib.c postpone.c query.c recvattach.c diff --git a/pop.c b/pop.c index 0080fd805..87c2553bf 100644 --- a/pop.c +++ b/pop.c @@ -1,6 +1,5 @@ /* - * Copyright (C) 1996-2000 Michael R. Elkins - * Copyright (C) 2000 Vsevolod Volkov + * Copyright (C) 2000 Vsevolod Volkov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,115 +16,522 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ -/* - * Rather crude POP3 support. - */ - #include "mutt.h" -#include "mailbox.h" #include "mx.h" -#include "mutt_socket.h" +#include "pop.h" + +#ifdef HAVE_PGP +#include "pgp.h" +#endif #include #include -/* pop_authenticate: loop until success or user abort. */ -int pop_authenticate (CONNECTION *conn) +/* write line to file */ +static int fetch_message (char *line, void *file) +{ + FILE *f = (FILE *) file; + + fputs (line, f); + if (fputc ('\n', f) == EOF) + return -1; + + return 0; +} + +/* + * Read header + * returns: + * 0 on success + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error writing to tempfile + */ +static int pop_read_header (POP_DATA *pop_data, HEADER *h) { + FILE *f; + int ret, index; + long length; char buf[LONG_STRING]; - char user[SHORT_STRING]; - char pass[SHORT_STRING]; + char tempfile[_POSIX_PATH_MAX]; - FOREVER + mutt_mktemp (tempfile); + if (!(f = safe_fopen (tempfile, "w+"))) { - if (! (conn->account.flags & M_ACCT_USER)) + mutt_perror (tempfile); + return -3; + } + + snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret == 0) + { + sscanf (buf, "+OK %d %ld", &index, &length); + + snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno); + ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f); + + if (pop_data->cmd_top == 2) { - if (!PopUser) + if (ret == 0) { - strfcpy (user, NONULL (Username), sizeof (user)); - if (mutt_get_field (_("POP Username: "), user, sizeof (user), 0) != 0 || - !user[0]) - return 0; + pop_data->cmd_top = 1; + + dprint (1, (debugfile, "pop_read_header: set TOP capability\n")); + } + + if (ret == -2) + { + pop_data->cmd_top = 0; + + dprint (1, (debugfile, "pop_read_header: unset TOP capability\n")); + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command TOP is not supported by server.")); } - else - strfcpy (user, PopUser, sizeof (user)); } - else - strfcpy (user, conn->account.user, sizeof (user)); + } - if (! (conn->account.flags & M_ACCT_PASS)) + switch (ret) + { + case 0: { - if (!PopPass) + rewind (f); + h->env = mutt_read_rfc822_header (f, h, 0, 0); + h->content->length = length - h->content->offset + 1; + rewind (f); + while (!feof (f)) { - pass[0]=0; - snprintf (buf, sizeof (buf), _("Password for %s@%s: "), user, - conn->account.host); - if (mutt_get_field (buf, pass, sizeof (pass), M_PASS) != 0 || !pass[0]) - return 0; + h->content->length--; + fgets (buf, sizeof (buf), f); } - else - strfcpy (pass, PopPass, sizeof (pass)); + break; + } + case -2: + { + mutt_error ("%s", pop_data->err_msg); + break; + } + case -3: + { + mutt_error _("Can't write header to temporary file!"); + break; + } + } + + fclose (f); + unlink (tempfile); + return ret; +} + +/* parse UIDL */ +static int fetch_uidl (char *line, void *data) +{ + int i, index; + CONTEXT *ctx = (CONTEXT *)data; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + sscanf (line, "%d %s", &index, line); + for (i = 0; i < ctx->msgcount; i++) + if (!mutt_strcmp (line, ctx->hdrs[i]->data)) + break; + + if (i == ctx->msgcount) + { + dprint (1, (debugfile, "pop_fetch_headers: new header %d %s\n", index, line)); + + if (i >= ctx->hdrmax) + mx_alloc_memory(ctx); + + ctx->msgcount++; + ctx->hdrs[i] = mutt_new_header (); + ctx->hdrs[i]->data = safe_strdup (line); + } + else if (ctx->hdrs[i]->index != index - 1) + pop_data->clear_cache = 1; + + ctx->hdrs[i]->refno = index; + ctx->hdrs[i]->index = index - 1; + + return 0; +} + +/* + * Read headers + * returns: + * 0 on success + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error writing to tempfile + */ +static int pop_fetch_headers (CONTEXT *ctx) +{ + int i, ret, old_count, new_count; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + time (&pop_data->check_time); + pop_data->clear_cache = 0; + + for (i = 0; i < ctx->msgcount; i++) + ctx->hdrs[i]->refno = -1; + + old_count = ctx->msgcount; + ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); + new_count = ctx->msgcount; + ctx->msgcount = old_count; + + if (pop_data->cmd_uidl == 2) + { + if (ret == 0) + { + pop_data->cmd_uidl = 1; + + dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n")); + } + + if (ret == -2 && pop_data->cmd_uidl == 2) + { + pop_data->cmd_uidl = 0; + + dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n")); + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command UIDL is not supported by server.")); + } + } + + if (ret == 0) + { + for (i = 0; i < old_count; i++) + if (ctx->hdrs[i]->refno == -1) + ctx->hdrs[i]->deleted = 1; + + for (i = old_count; i < new_count; i++) + { + mutt_message (_("Fetching message headers... [%d/%d]"), + i + 1 - old_count, new_count - old_count); + + ret = pop_read_header (pop_data, ctx->hdrs[i]); + if (ret < 0) + break; + + mx_update_context (ctx); + } + } + + if (ret < 0) + { + for (i = ctx->msgcount; i < new_count; i++) + mutt_free_header (&ctx->hdrs[i]); + return ret; + } + + mutt_clear_error (); + return (new_count - old_count); +} + +/* open POP mailbox - fetch only headers */ +int pop_open_mailbox (CONTEXT *ctx) +{ + int ret; + char buf[LONG_STRING]; + ACCOUNT acct; + POP_DATA *pop_data; + ciss_url_t url; + + if (pop_parse_path (ctx->path, &acct)) + { + mutt_error ("%s is an invalid POP path", ctx->path); + sleep (2); + return -1; + } + + mutt_account_tourl (&acct, &url); + url.path = NULL; + url_ciss_tostring (&url, buf, sizeof (buf)); + + FREE (&ctx->path); + ctx->path = safe_strdup (buf); + + pop_data = safe_calloc (1, sizeof (POP_DATA)); + pop_data->conn = mutt_conn_find (NULL, &acct); + ctx->data = pop_data; + + if (pop_open_connection (pop_data) < 0) + return -1; + + pop_data->conn->data = pop_data; + + FOREVER + { + if (pop_reconnect (ctx) < 0) + return -1; + + ctx->size = pop_data->size; + + mutt_message _("Fetching list of messages..."); + + ret = pop_fetch_headers (ctx); + + if (ret >= 0) + return 0; + + if (ret < -1) + { + sleep (2); + return -1; + } + } +} + +/* delete all cached messages */ +static void pop_clear_cache (POP_DATA *pop_data) +{ + int i; + + if (!pop_data->clear_cache) + return; + + dprint (1, (debugfile, "pop_clear_cache: delete cached messages\n")); + + for (i = 0; i < POP_CACHE_LEN; i++) + { + if (pop_data->cache[i].path) + { + unlink (pop_data->cache[i].path); + safe_free ((void **) &pop_data->cache[i].path); + } + } +} + +/* close POP mailbox */ +void pop_close_mailbox (CONTEXT *ctx) +{ + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + if (!pop_data) + return; + + pop_logout (ctx); + + if (pop_data->status != POP_NONE) + mutt_socket_close (pop_data->conn); + + pop_data->status = POP_NONE; + + pop_data->clear_cache = 1; + pop_clear_cache (pop_data); + + if (!pop_data->conn->data) + mutt_socket_free (pop_data->conn); + + return; +} + +/* fetch message from POP server */ +int pop_fetch_message (MESSAGE* msg, CONTEXT* ctx, int msgno) +{ + int ret; + void *uidl; + char buf[LONG_STRING]; + char path[_POSIX_PATH_MAX]; + char *m = _("Fetching message..."); + POP_DATA *pop_data = (POP_DATA *)ctx->data; + POP_CACHE *cache; + HEADER *h = ctx->hdrs[msgno]; + + /* see if we already have the message in our cache */ + cache = &pop_data->cache[h->index % POP_CACHE_LEN]; + + if (cache->path) + { + if (cache->index == h->index) + { + /* yes, so just return a pointer to the message */ + msg->fp = fopen (cache->path, "r"); + if (msg->fp) + return 0; + + mutt_perror (cache->path); + sleep (2); + return -1; } else - strfcpy (pass, conn->account.pass, sizeof (pass)); + { + /* clear the previous entry */ + unlink (cache->path); + FREE (&cache->path); + } + } - mutt_message _("Logging in..."); + FOREVER + { + if (pop_reconnect (ctx) < 0) + return -1; - snprintf (buf, sizeof (buf), "user %s\r\n", user); - mutt_socket_write (conn, buf); + /* verify that massage index is correct */ + if (h->refno < 0) + { + mutt_error _("The message index is incorrect. Try reopening the mailbox."); + sleep (2); + return -1; + } + + mutt_message (m); - if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + mutt_mktemp (path); + msg->fp = safe_fopen (path, "w+"); + if (!msg->fp) + { + mutt_perror (path); + sleep (2); return -1; + } - if (!mutt_strncmp (buf, "+OK", 3)) + snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno); + + ret = pop_fetch_data (pop_data, buf, m, fetch_message, msg->fp); + if (ret == 0) + break; + + safe_fclose (&msg->fp); + unlink (path); + + if (ret == -2) { - snprintf (buf, sizeof (buf), "pass %s\r\n", pass); - mutt_socket_write_d (conn, buf, 5); + mutt_error ("%s", pop_data->err_msg); + sleep (2); + return -1; + } -#ifdef DEBUG - /* don't print the password unless we're at the ungodly debugging level */ - if (debuglevel < M_SOCK_LOG_FULL) - dprint (M_SOCK_LOG_CMD, (debugfile, "> pass *\r\n")); -#endif + if (ret == -3) + { + mutt_error _("Can't write message to temporary file!"); + sleep (2); + return -1; + } + } + + /* Update the header information. Previously, we only downloaded a + * portion of the headers, those required for the main display. + */ + cache->index = h->index; + cache->path = safe_strdup (path); + rewind (msg->fp); + uidl = h->data; + mutt_free_envelope (&h->env); + h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0); + h->data = uidl; + h->lines = 0; + fgets (buf, sizeof (buf), msg->fp); + while (!feof (msg->fp)) + { + ctx->hdrs[msgno]->lines++; + fgets (buf, sizeof (buf), msg->fp); + } + + h->content->length = ftell (msg->fp) - h->content->offset + ctx->hdrs[msgno]->lines; + + /* This needs to be done in case this is a multipart message */ +#ifdef HAVE_PGP + h->pgp = pgp_query (h->content); +#endif /* HAVE_PGP */ + + mutt_clear_error(); + rewind (msg->fp); + + return 0; +} + +/* update POP mailbox - delete messages from server */ +int pop_sync_mailbox (CONTEXT *ctx, int *index_hint) +{ + int i, ret; + char buf[LONG_STRING]; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + pop_data->check_time = 0; + + FOREVER + { + if (pop_reconnect (ctx) < 0) + return -1; - if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) - return -1; + mutt_message (_("Marking %d messages deleted..."), ctx->deleted); - if (!mutt_strncmp (buf, "+OK", 3)) + for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) + { + if (ctx->hdrs[i]->deleted) { - /* If they have a successful login, we may as well cache the - * user/password. */ - if (!(conn->account.flags & M_ACCT_USER)) - strfcpy (conn->account.user, user, sizeof (conn->account.user)); - if (!(conn->account.flags & M_ACCT_PASS)) - strfcpy (conn->account.pass, pass, sizeof (conn->account.pass)); - conn->account.flags |= (M_ACCT_USER | M_ACCT_PASS); - - return 1; + snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno); + ret = pop_query (pop_data, buf, sizeof (buf)); } } - mutt_remove_trailing_ws (buf); - mutt_error (_("Login failed: %s"), buf); - sleep (1); + if (ret == 0) + { + strfcpy (buf, "QUIT\r\n", sizeof (buf)); + ret = pop_query (pop_data, buf, sizeof (buf)); + } - if (!(conn->account.flags & M_ACCT_USER)) - FREE (&PopUser); - if (!(conn->account.flags & M_ACCT_PASS)) - FREE (&PopPass); - conn->account.flags &= ~M_ACCT_PASS; + if (ret == 0) + { + pop_data->clear_cache = 1; + pop_clear_cache (pop_data); + pop_data->status = POP_DISCONNECTED; + return 0; + } + + if (ret == -2) + { + mutt_error ("%s", pop_data->err_msg); + sleep (2); + return -1; + } } } -void mutt_fetchPopMail (void) +/* Check for new messages and fetch headers */ +int pop_check_mailbox (CONTEXT *ctx, int *index_hint) +{ + int ret; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + if ((pop_data->check_time + PopCheckTimeout) > time (NULL)) + return 0; + + pop_logout (ctx); + + mutt_socket_close (pop_data->conn); + + if (pop_open_connection (pop_data) < 0) + return -1; + + ctx->size = pop_data->size; + + mutt_message _("Checking for new messages..."); + + ret = pop_fetch_headers (ctx); + pop_clear_cache (pop_data); + + if (ret < 0) + return -1; + + if (ret > 0) + return M_NEW_MAIL; + + return 0; +} + +/* Fetch messages and save them in $spoolfile */ +void pop_fetch_mail (void) { char buffer[LONG_STRING]; - char msgbuf[SHORT_STRING], *p; - int i, delanswer, last = 0, msgs, bytes, err = 0; + char msgbuf[SHORT_STRING]; + char *url, *p; + int i, delanswer, last = 0, msgs, bytes, rset = 0, ret; CONTEXT ctx; MESSAGE *msg = NULL; - ACCOUNT account; - CONNECTION *conn; + ACCOUNT acct; + POP_DATA *pop_data; if (!PopHost) { @@ -133,73 +539,55 @@ void mutt_fetchPopMail (void) return; } - /* if the host is specified as `my.com/ssl', use ssl to connect to the - * given port. this is kinda silly (looks like nobody was using this - * as of 9-29-2000), but it's easier to test pop+ssl than imap+ssl for - * the new NSS support. -me - */ - account.flags = 0; - strfcpy (account.host, PopHost, sizeof (account.host)); - p = strchr(account.host, '/'); - if (p) { - if (!strcmp (p+1, "ssl")) { - *p = 0; - account.flags |= M_ACCT_SSL; - } - } - account.type = M_ACCT_TYPE_POP; - if (PopPort < 0) { - /* auto-select the port based upon what we are attempting to do */ - if (account.flags & M_ACCT_SSL) - account.port = 995; /* pop3s (pop3 + ssl) */ - else - account.port = 110; /* pop3 */ - } - else - account.port = PopPort; - conn = mutt_conn_find (NULL, &account); - if (mutt_socket_open (conn) < 0) - return; - - if (mutt_socket_readln (buffer, sizeof (buffer), conn) < 0) - goto fail; - - if (mutt_strncmp (buffer, "+OK", 3)) + url = p = safe_calloc (strlen (PopHost) + 6, sizeof (char)); + if (url_check_scheme (PopHost) == U_UNKNOWN) { - mutt_remove_trailing_ws (buffer); - mutt_error ("%s", buffer); - goto finish; + strcpy (url, "pop://"); + p = strchr (url, '\0'); } + strcpy (p, PopHost); - switch (pop_authenticate (conn)) + if (pop_parse_path (url, &acct)) { - case -1: - goto fail; - case 0: - { - mutt_clear_error (); - goto finish; - } + mutt_error ("%s is an invalid POP path", PopHost); + return; } + pop_data = safe_calloc (1, sizeof (POP_DATA)); + pop_data->conn = mutt_conn_find (NULL, &acct); + + if (pop_open_connection (pop_data) < 0) + return; + + pop_data->conn->data = pop_data; + mutt_message _("Checking for new messages..."); /* find out how many messages are in the mailbox. */ - mutt_socket_write (conn, "stat\r\n"); - - if (mutt_socket_readln (buffer, sizeof (buffer), conn) < 0) + strfcpy (buffer, "STAT\r\n", sizeof (buffer)); + ret = pop_query (pop_data, buffer, sizeof (buffer)); + if (ret == -1) goto fail; - - if (mutt_strncmp (buffer, "+OK", 3) != 0) + if (ret == -2) { - mutt_remove_trailing_ws (buffer); - mutt_error ("%s", buffer); + mutt_error ("%s", pop_data->err_msg); goto finish; } sscanf (buffer, "+OK %d %d", &msgs, &bytes); - if (msgs == 0) + /* only get unread messages */ + if (msgs > 0 && option (OPTPOPLAST)) + { + strfcpy (buffer, "LAST\r\n", sizeof (buffer)); + ret = pop_query (pop_data, buffer, sizeof (buffer)); + if (ret == -1) + goto fail; + if (ret == 0) + sscanf (buffer, "+OK %d", &last); + } + + if (msgs <= last) { mutt_message _("No new mail in POP mailbox."); goto finish; @@ -208,147 +596,78 @@ void mutt_fetchPopMail (void) if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL) goto finish; - /* only get unread messages */ - if (option (OPTPOPLAST)) - { - mutt_socket_write (conn, "last\r\n"); - if (mutt_socket_readln (buffer, sizeof (buffer), conn) == -1) - goto fail; - - if (mutt_strncmp (buffer, "+OK", 3) == 0) - sscanf (buffer, "+OK %d", &last); - else - /* ignore an error here and assume all messages are new */ - last = 0; - } + delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?")); - if (msgs - last) - delanswer = query_quadoption (OPT_POPDELETE, _("Delete messages from server?")); - - snprintf (msgbuf, sizeof (msgbuf), - msgs > 1 ? _("Reading new messages (%d bytes)...") : - _("Reading new message (%d bytes)..."), bytes); + snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), bytes); mutt_message ("%s", msgbuf); for (i = last + 1 ; i <= msgs ; i++) { - snprintf (buffer, sizeof (buffer), "retr %d\r\n", i); - mutt_socket_write (conn, buffer); - - if (mutt_socket_readln (buffer, sizeof (buffer), conn) < 0) - { - mx_fastclose_mailbox (&ctx); - goto fail; - } - - if (mutt_strncmp (buffer, "+OK", 3) != 0) - { - mutt_remove_trailing_ws (buffer); - mutt_error ("%s", buffer); - break; - } - if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL) + ret = -3; + else { - err = 1; - break; - } - - /* Now read the actual message. */ - FOREVER - { - char *p; - int chunk, tail = 0; + snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i); + ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp); + if (ret == -3) + rset = 1; - if ((chunk = mutt_socket_readln_d (buffer, sizeof (buffer), conn, 3)) < 0) + if (ret == 0 && mx_commit_message (msg, &ctx) != 0) { - mutt_error _("Error reading message!"); - err = 1; - break; + rset = 1; + ret = -3; } - p = buffer; - if (!tail && buffer[0] == '.') - { - if (buffer[1] == '.') - p = buffer + 1; - else - break; /* end of message */ - } - - fputs (p, msg->fp); - if (chunk >= sizeof (buffer)) - { - tail = 1; - } - else - { - fputc ('\n', msg->fp); - tail = 0; - } + mx_close_message (&msg); } - if (mx_commit_message (msg, &ctx) != 0) + if (ret == 0 && delanswer == M_YES) { - mutt_error _("Error while writing mailbox!"); - err = 1; + /* delete the message on the server */ + snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i); + ret = pop_query (pop_data, buffer, sizeof (buffer)); } - mx_close_message (&msg); - - if (err) + if (ret == -1) + { + mx_close_mailbox (&ctx, NULL); + goto fail; + } + if (ret == -2) + { + mutt_error ("%s", pop_data->err_msg); break; - - if (delanswer == M_YES) + } + if (ret == -3) { - /* delete the message on the server */ - snprintf (buffer, sizeof (buffer), "dele %d\r\n", i); - mutt_socket_write (conn, buffer); - - /* eat the server response */ - mutt_socket_readln (buffer, sizeof (buffer), conn); - if (mutt_strncmp (buffer, "+OK", 3) != 0) - { - err = 1; - mutt_remove_trailing_ws (buffer); - mutt_error ("%s", buffer); - break; - } + mutt_error _("Error while writing mailbox!"); + break; } - if ( msgs > 1) - mutt_message (_("%s [%d of %d messages read]"), msgbuf, i, msgs); - else - mutt_message (_("%s [%d message read]"), msgbuf, msgs); + mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, msgs - last); } - if (msg) - { - if (mx_commit_message (msg, &ctx) != 0) - err = 1; - mx_close_message (&msg); - } mx_close_mailbox (&ctx, NULL); - if (err) + if (rset) { /* make sure no messages get deleted */ - mutt_socket_write (conn, "rset\r\n"); - mutt_socket_readln (buffer, sizeof (buffer), conn); /* snarf the response */ + strfcpy (buffer, "RSET\r\n", sizeof (buffer)); + if (pop_query (pop_data, buffer, sizeof (buffer)) == -1) + goto fail; } finish: - /* exit gracefully */ - mutt_socket_write (conn, "quit\r\n"); - mutt_socket_readln (buffer, sizeof (buffer), conn); /* snarf the response */ - mutt_socket_close (conn); + strfcpy (buffer, "QUIT\r\n", sizeof (buffer)); + if (pop_query (pop_data, buffer, sizeof (buffer)) == -1) + goto fail; + mutt_socket_close (pop_data->conn); + FREE (&pop_data); return; - /* not reached */ - fail: - mutt_error _("Server closed connection!"); - mutt_socket_close (conn); + mutt_socket_close (pop_data->conn); + FREE (&pop_data); } diff --git a/pop.h b/pop.h new file mode 100644 index 000000000..878a3120b --- /dev/null +++ b/pop.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2000 Vsevolod Volkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#ifndef _POP_H +#define _POP_H 1 + +#include "mailbox.h" +#include "mutt_socket.h" + +#define POP_PORT 110 +#define POP_SSL_PORT 995 + +/* number of entries in the hash table */ +#define POP_CACHE_LEN 10 + +/* maximal length of the server response (RFC1939) */ +#define POP_CMD_RESPONSE 512 + +enum +{ + /* Status */ + POP_NONE = 0, + POP_CONNECTED, + POP_DISCONNECTED, + POP_BYE +}; + +typedef enum +{ + POP_A_SUCCESS = 0, + POP_A_SOCKET, + POP_A_FAILURE, + POP_A_SKIP, + POP_A_UNAVAIL +} pop_auth_res_t; + +typedef struct +{ + unsigned int index; + char *path; +} POP_CACHE; + +typedef struct +{ + CONNECTION *conn; + unsigned int status : 2; + unsigned int capabilities : 1; + unsigned int cmd_capa : 1; /* optional command CAPA */ + unsigned int cmd_user : 2; /* optional command USER */ + unsigned int cmd_uidl : 2; /* optional command UIDL */ + unsigned int cmd_top : 2; /* optional command TOP */ + unsigned int resp_codes : 1; /* server supports extended response codes */ + unsigned int expire : 1; /* expire is greater than 0 */ + unsigned int clear_cache : 1; + unsigned int authenticator; + size_t size; + time_t check_time; + time_t login_delay; /* minimal login delay capability */ + char *auth_list; /* list of auth mechanisms */ + char *timestamp; + char err_msg[POP_CMD_RESPONSE]; + POP_CACHE cache[POP_CACHE_LEN]; +} POP_DATA; + +typedef pop_auth_res_t (*pop_auth_t)(POP_DATA *); + +/* pop_auth.c */ +int pop_authenticate (POP_DATA *); +void pop_apop_timestamp (POP_DATA *, char *); + +/* pop_lib.c */ +#define pop_query(A,B,C) pop_query_d(A,B,C,NULL) +int pop_parse_path (const char *, ACCOUNT *); +int pop_connect (POP_DATA *); +int pop_open_connection (POP_DATA *); +int pop_query_d (POP_DATA *, char *, size_t, char *); +int pop_fetch_data (POP_DATA *, char *, char *, int (*funct) (char *, void *), void *); +int pop_reconnect (CONTEXT *); +void pop_logout (CONTEXT *); +void pop_error (POP_DATA *, char *); + +/* pop.c */ +int pop_check_mailbox (CONTEXT *, int *); +int pop_open_mailbox (CONTEXT *); +int pop_sync_mailbox (CONTEXT *, int *); +int pop_fetch_message (MESSAGE *, CONTEXT *, int); +void pop_close_mailbox (CONTEXT *); +void pop_fetch_mail (void); + +#endif diff --git a/pop_auth.c b/pop_auth.c new file mode 100644 index 000000000..2dc3bb1ad --- /dev/null +++ b/pop_auth.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2000 Vsevolod Volkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "mutt.h" +#include "mx.h" +#include "md5.h" +#include "pop.h" + +#include +#include + +#ifdef USE_SASL +#include +#include + +#include "mutt_sasl.h" +#endif + +#ifdef USE_SASL +/* SASL authenticator */ +static pop_auth_res_t pop_auth_sasl (POP_DATA *pop_data) +{ + sasl_conn_t *saslconn; + sasl_interact_t *interaction = NULL; + int rc; + char buf[LONG_STRING]; + char inbuf[LONG_STRING]; + const char* mech; + char* pc; + unsigned int len, olen; + unsigned char client_start; + + if (!pop_data->auth_list) + return POP_A_UNAVAIL; + + if (mutt_sasl_start () != SASL_OK || + sasl_client_new ("pop-3", pop_data->conn->account.host, + mutt_sasl_get_callbacks (&pop_data->conn->account), + SASL_SECURITY_LAYER, &saslconn) != SASL_OK) + { + dprint (1, (debugfile, "pop_auth_sasl: Error allocating SASL connection.\n")); + return POP_A_FAILURE; + } + + FOREVER + { + rc = sasl_client_start (saslconn, pop_data->auth_list, NULL, + &interaction, &pc, &olen, &mech); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact (interaction); + } + + if (rc != SASL_OK && rc != SASL_CONTINUE) + { + dprint (1, (debugfile, "pop_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n")); + + /* SASL doesn't support suggested mechanisms, so fall back */ + return POP_A_UNAVAIL; + } + + client_start = (olen > 0); + + mutt_message _("Authenticating (SASL)..."); + + snprintf (buf, sizeof (buf), "AUTH %s", mech); + olen = strlen (buf); + + /* looping protocol */ + FOREVER + { + strfcpy (buf + olen, "\r\n", sizeof (buf) - olen); + mutt_socket_write (pop_data->conn, buf); + if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) + { + sasl_dispose (&saslconn); + pop_data->status = POP_DISCONNECTED; + return POP_A_SOCKET; + } + + if (rc != SASL_CONTINUE) + break; + + if (mutt_strncmp (inbuf, "+ ", 2)) + goto bail; + + if (sasl_decode64 (inbuf, strlen (inbuf), buf, &len) != SASL_OK) + { + dprint (1, (debugfile, "pop_auth_sasl: error base64-decoding server response.\n")); + goto bail; + } + + if (!client_start) + FOREVER + { + rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact (interaction); + } + else + client_start = 0; + + if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) + break; + + /* send out response, or line break if none needed */ + if (pc) + { + if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) + { + dprint (1, (debugfile, "pop_auth_sasl: error base64-encoding client response.\n")); + goto bail; + } + + /* sasl_client_st(art|ep) allocate pc with malloc, expect me to + * free it */ + free (pc); + } + } + + if (rc != SASL_OK) + goto bail; + + if (!mutt_strncmp (inbuf, "+OK", 3)) + { + mutt_sasl_setup_conn (pop_data->conn, saslconn); + return POP_A_SUCCESS; + } + +bail: + sasl_dispose (&saslconn); + + /* terminate SASL sessoin if the last responce is not +OK nor -ERR */ + if (!mutt_strncmp (inbuf, "+ ", 2)) + { + snprintf (buf, sizeof (buf), "*\r\n"); + if (pop_query (pop_data, buf, sizeof (buf)) == -1) + return POP_A_SOCKET; + } + + mutt_error _("SASL authentication failed."); + sleep (2); + + if (rc == SASL_OK) + return POP_A_FAILURE; + + return POP_A_SKIP; +} +#endif + +/* Get the server timestamp for APOP authentication */ +void pop_apop_timestamp (POP_DATA *pop_data, char *buf) +{ + char *p1, *p2; + + FREE (&pop_data->timestamp); + + if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) + { + p2[1] = '\0'; + pop_data->timestamp = safe_strdup (p1); + } +} + +/* APOP authenticator */ +static pop_auth_res_t pop_auth_apop (POP_DATA *pop_data) +{ + MD5_CTX mdContext; + unsigned char digest[16]; + char hash[33]; + char buf[LONG_STRING]; + int i; + + if (!pop_data->timestamp) + return POP_A_UNAVAIL; + + mutt_message _("Authenticating (APOP)..."); + + /* Compute the authentication hash to send to the server */ + MD5Init (&mdContext); + MD5Update (&mdContext, (unsigned char *)pop_data->timestamp, + strlen (pop_data->timestamp)); + MD5Update (&mdContext, (unsigned char *)pop_data->conn->account.pass, + strlen (pop_data->conn->account.pass)); + MD5Final (digest, &mdContext); + + for (i = 0; i < sizeof (digest); i++) + sprintf (hash + 2 * i, "%02x", digest[i]); + + /* Send APOP command to server */ + snprintf (buf, sizeof (buf), "APOP %s %s\r\n", pop_data->conn->account.user, hash); + + switch (pop_query (pop_data, buf, sizeof (buf))) + { + case 0: + return POP_A_SUCCESS; + case -1: + return POP_A_SOCKET; + } + + mutt_error _("APOP authentication failed."); + sleep (2); + + return POP_A_SKIP; +} + +/* USER authenticator */ +static pop_auth_res_t pop_auth_user (POP_DATA *pop_data) +{ + char buf[LONG_STRING]; + int ret; + + if (!pop_data->cmd_user) + return POP_A_UNAVAIL; + + mutt_message _("Logging in..."); + + snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user); + ret = pop_query (pop_data, buf, sizeof (buf)); + + if (pop_data->cmd_user == 2) + { + if (ret == 0) + { + pop_data->cmd_user = 1; + + dprint (1, (debugfile, "pop_auth_user: set USER capability\n")); + } + + if (ret == -2) + { + pop_data->cmd_user = 0; + + dprint (1, (debugfile, "pop_auth_user: unset USER capability\n")); + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command USER is not supported by server.")); + } + } + + if (ret == 0) + { + snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass); + ret = pop_query_d (pop_data, buf, sizeof (buf), +#ifdef DEBUG + /* don't print the password unless we're at the ungodly debugging level */ + debuglevel < M_SOCK_LOG_FULL ? "PASS *\r\n" : +#endif + NULL); + } + + switch (ret) + { + case 0: + return POP_A_SUCCESS; + case -1: + return POP_A_SOCKET; + } + + mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); + sleep (2); + + return POP_A_FAILURE; +} + +static pop_auth_t pop_authenticators[] = { +#ifdef USE_SASL + pop_auth_sasl, +#endif + pop_auth_apop, + pop_auth_user, + NULL +}; + +/* + * Authentication + * 0 - successful, + * -1 - conection lost, + * -2 - login failed, + * -3 - authentication canceled. +*/ +int pop_authenticate (POP_DATA* pop_data) +{ + ACCOUNT *acct = &pop_data->conn->account; + pop_auth_t* authenticator; + int attempt = 0; + int auth = 0; + int ret = POP_A_UNAVAIL; + + if (mutt_account_getuser (acct) || !acct->user[0] || + mutt_account_getpass (acct) || !acct->pass[0]) + return -3; + + if (pop_data->authenticator) + { + authenticator = &pop_authenticators[pop_data->authenticator - 1]; + ret = (*authenticator)(pop_data); + attempt = 1; + } + else + { + authenticator = pop_authenticators; + + while (authenticator) + { + auth++; + + ret = (*authenticator)(pop_data); + if (ret == POP_A_SOCKET) + switch (pop_connect (pop_data)) + { + case 0: + { + ret = (*authenticator)(pop_data); + break; + } + case -2: + ret = POP_A_FAILURE; + } + + if (ret == POP_A_SKIP) + attempt = 1; + else if (ret != POP_A_UNAVAIL) + break; + + authenticator++; + } + + if (ret == POP_A_SUCCESS) + { + pop_data->authenticator = auth; + dprint (1, (debugfile, "pop_authenticate: Authenticator #%d.\n", auth)); + } + } + + switch (ret) + { + case POP_A_SUCCESS: + return 0; + case POP_A_SOCKET: + return -1; + case POP_A_UNAVAIL: + if (!attempt) + mutt_error _("Authentication method is unknown."); + } + + return -2; +} diff --git a/pop_lib.c b/pop_lib.c new file mode 100644 index 000000000..a49d04b70 --- /dev/null +++ b/pop_lib.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2000 Vsevolod Volkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "mutt.h" +#include "mx.h" +#include "url.h" +#include "pop.h" + +#include +#include +#include + +/* given an POP mailbox name, return host, port, username and password */ +int pop_parse_path (const char* path, ACCOUNT* acct) +{ + ciss_url_t url; + char *c; + int ret = -1; + + /* Defaults */ + acct->flags = 0; + acct->port = POP_PORT; + acct->type = M_ACCT_TYPE_POP; + + c = safe_strdup (path); + url_parse_ciss (&url, c); + + if (url.scheme == U_POP || url.scheme == U_POPS) + { + if (url.scheme == U_POPS) + { + acct->flags |= M_ACCT_SSL; + acct->port = POP_SSL_PORT; + } + + if ((!url.path || !*url.path) && mutt_account_fromurl (acct, &url) == 0) + ret = 0; + } + + FREE (&c); + return ret; +} + +/* Copy error message to err_msg buffer */ +void pop_error (POP_DATA *pop_data, char *msg) +{ + char *c, *c2; + + c = msg; + + if (!mutt_strncmp (msg, "-ERR ", 5)) + { + c2 = msg + 5; + SKIPWS (c2); + + if (*c2) + c = c2; + } + + strfcpy (pop_data->err_msg, c, sizeof (pop_data->err_msg)); + mutt_remove_trailing_ws (pop_data->err_msg); +} + +/* Parse CAPA output */ +static int fetch_capa (char *line, void *data) +{ + POP_DATA *pop_data = (POP_DATA *)data; + char *c; + + if (!mutt_strncmp (line, "SASL", 4)) + { + c = line + 4; + SKIPWS (c); + pop_data->auth_list = safe_strdup (c); + } + + else if (!mutt_strncmp (line, "USER", 4)) + pop_data->cmd_user = 1; + + else if (!mutt_strncmp (line, "UIDL", 4)) + pop_data->cmd_uidl = 1; + + else if (!mutt_strncmp (line, "TOP", 3)) + pop_data->cmd_top = 1; + + return 0; +} + +/* Fetch list of the authentication mechanisms */ +static int fetch_auth (char *line, void *data) +{ + POP_DATA *pop_data = (POP_DATA *)data; + + if (!pop_data->auth_list) + { + pop_data->auth_list = safe_malloc (strlen (line) + 1); + *pop_data->auth_list = '\0'; + } + else + { + safe_realloc ((void **) &pop_data->auth_list, + strlen (pop_data->auth_list) + strlen (line) + 1); + strcat (pop_data->auth_list, " "); + } + strcat (pop_data->auth_list, line); + + return 0; +} + +/* + * Get capabilities + * 0 - successful, + * -1 - conection lost, + * -2 - execution error. +*/ +static int pop_capabilities (POP_DATA *pop_data) +{ + char buf[LONG_STRING]; + char *msg = NULL; + + if (pop_data->capabilities) + return 0; + + pop_data->cmd_capa = 0; + pop_data->cmd_user = 0; + pop_data->cmd_uidl = 0; + pop_data->cmd_top = 0; + pop_data->resp_codes = 0; + pop_data->expire = 1; + pop_data->login_delay = 0; + FREE (&pop_data->auth_list); + + strfcpy (buf, "CAPA\r\n", sizeof (buf)); + switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) + { + case 0: + { + pop_data->cmd_capa = 1; + + if (!pop_data->expire) + msg = _("Unable to leave messages on server."); + if (!pop_data->cmd_top) + msg = _("Command TOP is not supported by server."); + if (!pop_data->cmd_uidl) + msg = _("Command UIDL is not supported by server."); + break; + } + case -1: + return -1; + } + + if (!pop_data->cmd_capa) + { + pop_data->cmd_user = 2; + pop_data->cmd_uidl = 2; + pop_data->cmd_top = 2; + + strfcpy (buf, "AUTH\r\n", sizeof (buf)); + if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == -1) + return -1; + } + + if (msg) + { + mutt_error (msg); + return -2; + } + + pop_data->capabilities = 1; + + return 0; +} + +/* + * Open connection + * 0 - successful, + * -1 - conection lost, + * -2 - invalid response. +*/ +int pop_connect (POP_DATA *pop_data) +{ + char buf[LONG_STRING]; + + pop_data->status = POP_NONE; + if (mutt_socket_open (pop_data->conn) < 0 || + mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) + { + mutt_error (_("Error connecting to server: %s"), pop_data->conn->account.host); + return -1; + } + + pop_data->status = POP_CONNECTED; + + if (mutt_strncmp (buf, "+OK", 3)) + { + pop_error (pop_data, buf); + mutt_error ("%s", pop_data->err_msg); + return -2; + } + + pop_apop_timestamp (pop_data, buf); + + return 0; +} + +/* + * Open connection and authenticate + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - authentication canceled. +*/ +int pop_open_connection (POP_DATA *pop_data) +{ + int ret; + char buf[LONG_STRING]; + + ret = pop_connect (pop_data); + if (ret < 0) + { + sleep (2); + return ret; + } + + ret = pop_capabilities (pop_data); + if (ret == -1) + goto err_conn; + if (ret == -2) + { + sleep (2); + return -2; + } + + ret = pop_authenticate (pop_data); + if (ret == -1) + goto err_conn; + if (ret != 0) + return ret; + + /* get total size of mailbox */ + strfcpy (buf, "STAT\r\n", sizeof (buf)); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret == -1) + goto err_conn; + if (ret == -2) + { + mutt_error ("%s", pop_data->err_msg); + sleep (2); + return ret; + } + + sscanf (buf, "+OK %d %d", &ret, &pop_data->size); + return 0; + +err_conn: + pop_data->status = POP_DISCONNECTED; + mutt_error _("Server closed connection!"); + sleep (2); + return -1; +} + +/* logout from POP server */ +void pop_logout (CONTEXT *ctx) +{ + int ret = 0; + char buf[LONG_STRING]; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + if (pop_data->status == POP_CONNECTED) + { + mutt_message _("Closing connection to POP server..."); + + if (ctx->readonly) + { + strfcpy (buf, "RSET\r\n", sizeof (buf)); + ret = pop_query (pop_data, buf, sizeof (buf)); + } + + if (ret != -1) + { + strfcpy (buf, "QUIT\r\n", sizeof (buf)); + pop_query (pop_data, buf, sizeof (buf)); + } + + mutt_clear_error (); + } + + pop_data->status = POP_DISCONNECTED; + return; +} + +/* + * Send data from buffer and receive answer to the same buffer + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error. +*/ +int pop_query_d (POP_DATA *pop_data, char *buf, size_t buflen, char *msg) +{ + int dbg = M_SOCK_LOG_CMD; + + if (pop_data->status != POP_CONNECTED) + return -1; + +#ifdef DEBUG + /* print msg instaed of real command */ + if (msg) + { + dbg = M_SOCK_LOG_FULL; + dprint (M_SOCK_LOG_CMD, (debugfile, "> %s", msg)); + } +#endif + + mutt_socket_write_d (pop_data->conn, buf, dbg); + if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) + { + pop_data->status = POP_DISCONNECTED; + return -1; + } + if (!mutt_strncmp (buf, "+OK", 3)) + return 0; + + pop_error (pop_data, buf); + return -2; +} + +/* + * This function calls funct(*line, *data) for each received line, + * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. + * Returned codes: + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error in funct(*line, *data) + */ +int pop_fetch_data (POP_DATA *pop_data, char *query, char *msg, + int (*funct) (char *, void *), void *data) +{ + char buf[LONG_STRING]; + char *inbuf; + char *p; + int ret, chunk, line = 0; + size_t lenbuf = 0; + + strfcpy (buf, query, sizeof (buf)); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret < 0) + return ret; + + inbuf = safe_malloc (sizeof (buf)); + + FOREVER + { + chunk = mutt_socket_readln_d (buf, sizeof (buf), pop_data->conn, M_SOCK_LOG_HDR); + if (chunk < 0) + { + pop_data->status = POP_DISCONNECTED; + ret = -1; + break; + } + + p = buf; + if (!lenbuf && buf[0] == '.') + { + if (buf[1] != '.') + break; + p++; + } + + strfcpy (inbuf + lenbuf, p, sizeof (buf)); + + if (chunk >= sizeof (buf)) + { + lenbuf += strlen (p); + } + else + { + line++; + if (msg && ReadInc && (line % ReadInc == 0)) + mutt_message ("%s %d", msg, line); + if (ret == 0 && funct (inbuf, data) < 0) + ret = -3; + lenbuf = 0; + } + + safe_realloc ((void **) &inbuf, lenbuf + sizeof (buf)); + } + + FREE (&inbuf); + return ret; +} + +/* find message with this UIDL and set refno */ +static int check_uidl (char *line, void *data) +{ + int i, index; + CONTEXT *ctx = (CONTEXT *)data; + + sscanf (line, "%d %s", &index, line); + for (i = 0; i < ctx->msgcount; i++) + { + if (!mutt_strcmp (ctx->hdrs[i]->data, line)) + { + ctx->hdrs[i]->refno = index; + break; + } + } + + return 0; +} + +/* reconnect and verify idnexes if connection was lost */ +int pop_reconnect (CONTEXT *ctx) +{ + int ret; + POP_DATA *pop_data = (POP_DATA *)ctx->data; + + if (pop_data->status == POP_CONNECTED) + return 0; + if (pop_data->status == POP_BYE) + return -1; + + FOREVER + { + mutt_socket_close (pop_data->conn); + + ret = pop_open_connection (pop_data); + if (ret == 0) + { + char *msg = _("Verifying message indexes..."); + int i; + + for (i = 0; i < ctx->msgcount; i++) + ctx->hdrs[i]->refno = -1; + + mutt_message (msg); + + ret = pop_fetch_data (pop_data, "UIDL\r\n", msg, check_uidl, ctx); + if (ret == -2) + { + mutt_error ("%s", pop_data->err_msg); + sleep (2); + } + } + if (ret == 0) + return 0; + + pop_logout (ctx); + + if (ret < -1) + return -1; + + if (query_quadoption (OPT_POPRECONNECT, + _("Connection lost. Reconnect to POP server?")) != M_YES) + return -1; + } +} diff --git a/protos.h b/protos.h index a517de354..1b096e926 100644 --- a/protos.h +++ b/protos.h @@ -160,7 +160,6 @@ void mutt_expand_aliases_env (ENVELOPE *); void mutt_expand_file_fmt (char *, size_t, const char *, const char *); void mutt_expand_fmt (char *, size_t, const char *, const char *); void mutt_expand_link (char *, const char *, const char *); -void mutt_fetchPopMail (void); void mutt_fix_reply_recipients (ENVELOPE *env); void mutt_folder_hook (char *); void mutt_format_string (char *, size_t, int, int, int, char, const char *, size_t); diff --git a/recvattach.c b/recvattach.c index 55d624ff5..8d0870227 100644 --- a/recvattach.c +++ b/recvattach.c @@ -396,10 +396,7 @@ static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr) /* check to make sure that this file is really the one the user wants */ if (!mutt_save_confirm (buf, &st)) - { - CLEARLINE (LINES-1); return -1; - } strfcpy(tfile, buf, sizeof(tfile)); } else @@ -878,6 +875,15 @@ void mutt_view_attachments (HEADER *hdr) case OP_DELETE: CHECK_READONLY; +#ifdef USE_POP + if (Context->magic == M_POP) + { + mutt_flushinp (); + mutt_error _("Can't delete attachment from POP server."); + break; + } +#endif + #ifdef HAVE_PGP