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
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;
#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)
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))
/* 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)
{
break;
#endif
+
+#ifdef USE_POP
+ case M_POP:
+ break;
+#endif
}
}
#ifdef BUFFY_SIZE
/* 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())
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)
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
#include "buffy.h"
#include "mx.h"
+#ifdef USE_POP
+#include "pop.h"
+#endif
+
#ifdef USE_IMAP
#include "imap.h"
#endif
case OP_MAIN_FETCH_MAIL:
CHECK_ATTACH;
- mutt_fetchPopMail ();
+ pop_fetch_mail ();
menu->redraw = REDRAW_FULL;
break;
#endif /* USE_POP */
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;
<p>
If Mutt was compiled with POP3 support (by running the <em/configure/
-script with the <em/--enable-pop/ flag), it has the ability to fetch
-your mail from a remote server for local browsing. When you invoke the
-<em/fetch-mail/ function (default: G), Mutt attempts to connect to <ref
-id="pop_host" name="pop_host"> and authenticate by logging in
-as <ref id="pop_user" name="pop_user">. 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 <ref id="spoolfile" name="spoolfile">. After this
+script with the <em/--enable-pop/ flag), it has the ability to work
+with mailboxes located on a remote POP3 server and fetch mail for local
+browsing.
+
+You can access the remote POP3 mailbox by selecting the folder
+<tt>pop://popserver/</tt>.
+
+You can select an alternative port by specifying it with the server, ie:
+<tt>pop://popserver:port/</tt>.
+
+You can also specify different username for each folder, ie:
+<tt>pop://username@popserver[:port]/</tt>.
+
+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
+<ref id="pop_checkinterval" name="$pop_checkinterval">
+variable, which defaults to every 60 seconds.
+
+If Mutt was compiled with SSL support (by running the <em/configure/
+script with the <em/--with-ssl/ flag), connections to POP3 servers
+can be encrypted. This naturally requires that the server supports
+SSL encrypted connections. To access a folder with POP3/SSL, you should
+use pops: prefix, ie:
+<tt>pops://[username@]popserver[:port]/</tt>.
+
+Another way to access your POP3 mail is the <em/fetch-mail/ function
+(default: G). It allows to connect to <ref id="pop_host"
+name="pop_host">, fetch all your new mail and place it in the
+local <ref id="spoolfile" name="spoolfile">. After this
point, Mutt runs exactly as if the mail had always been local.
-<bf/Note:/ The POP3 support is there only for convenience,
-and it's rather limited. If you need more functionality you
-should consider using a specialized program, such as <htmlurl
+<bf/Note:/ If you only need to fetch all messages to local mailbox
+you should consider using a specialized program, such as <htmlurl
url="http://www.ccil.org/~esr/fetchmail" name="fetchmail">
<sect1>IMAP Support (OPTIONAL)
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;
WHERE short HistSize;
WHERE short PagerContext;
WHERE short PagerIndexLines;
-WHERE short PopPort;
WHERE short ReadInc;
WHERE short SendmailWait;
WHERE short Timeout;
}
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);
** 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
{ "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 "" },
#ifdef USE_IMAP
int mx_is_imap (const char *);
#endif
+#ifdef USE_POP
+int mx_is_pop (const char *);
+#endif
#endif
OPT_MOVE,
OPT_COPY,
OPT_POPDELETE,
+ OPT_POPRECONNECT,
OPT_POSTPONE,
OPT_QUIT,
OPT_REPLYTO,
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;
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 */
#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);
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)
#include "imap.h"
#endif
+#ifdef USE_POP
+#include "pop.h"
+#endif
+
#ifdef BUFFY_SIZE
#include "buffy.h"
#endif
#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;
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",
break;
#endif /* USE_IMAP */
+#ifdef USE_POP
+ case M_POP:
+ rc = pop_open_mailbox (ctx);
+ break;
+#endif /* USE_POP */
+
default:
rc = -1;
break;
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)
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
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 */
}
}
}
#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);
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
)
{
#ifdef USE_IMAP
, M_IMAP
#endif
+#ifdef USE_POP
+ , M_POP
+#endif
};
WHERE short DefaultMagic INITVAL (M_MBOX);
pgpinvoke.c
pgpkey.c
pop.c
+pop_auth.c
+pop_lib.c
postpone.c
query.c
recvattach.c
/*
- * Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu>
- * Copyright (C) 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
+ * Copyright (C) 2000 Vsevolod Volkov <vvv@mutt.org.ua>
*
* 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
* 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 <string.h>
#include <unistd.h>
-/* 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)
{
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;
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);
}
--- /dev/null
+/*
+ * Copyright (C) 2000 Vsevolod Volkov <vvv@mutt.org.ua>
+ *
+ * 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
--- /dev/null
+/*
+ * Copyright (C) 2000 Vsevolod Volkov <vvv@mutt.org.ua>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+
+#ifdef USE_SASL
+#include <sasl.h>
+#include <saslutil.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2000 Vsevolod Volkov <vvv@mutt.org.ua>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* 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;
+ }
+}
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);
/* 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
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