]> granicus.if.org Git - neomutt/commitdiff
Vsevolod Volkov's POP mailbox patch.
authorThomas Roessler <roessler@does-not-exist.org>
Tue, 10 Oct 2000 19:22:44 +0000 (19:22 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Tue, 10 Oct 2000 19:22:44 +0000 (19:22 +0000)
23 files changed:
Makefile.am
browser.c
buffy.c
commands.c
compose.c
configure.in
curs_main.c
doc/manual.sgml.head
globals.h
init.c
init.h
mailbox.h
mutt.h
muttlib.c
mx.c
mx.h
po/POTFILES.in
pop.c
pop.h [new file with mode: 0644]
pop_auth.c [new file with mode: 0644]
pop_lib.c [new file with mode: 0644]
protos.h
recvattach.c

index 652e60f659f0d1d487f76aaed43de31f4852a743..238722dea17ecaaf7d67e9e6e960cbb94c619406 100644 (file)
@@ -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
index 1bdb7cee20c2d6f805588034adb6c1f73ac80276..b4650e521c4800fbed612e215d392b13cfb8da60 100644 (file)
--- 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 5ed50a4a28c61f1976bcd05619ace4e677eb7fef..363f2c9e17cee45a84eb88210ce63f7752f7ea63 100644 (file)
--- 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
index 27c358d6c5e05aeb640717bb7251320c7789601f..915312eae07a3eec29a7c2d3c950333db1698208 100644 (file)
@@ -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())
index 86c1466d387065d91c826f1ffb2278ad31c80fed..a82ddc5acab7ac51afe8c8c89da5e59692f3f89a 100644 (file)
--- 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)
index e5bd60b4d73beec81697c6edd020e800c11df010..758e06ddd1ba1bf686ecb401a6cef1a2f22e2fcb 100644 (file)
@@ -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
index 418a266b8d3b86051d7f741af6cdfef1559573a9..902a663ebf293d6f3d947db331240fff25c8df85 100644 (file)
 #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;
 
index fa419cc565a4cf538f0d0930df0dd5ae09bd649a..75796fdc8ec6c79fc9a089e9787875e50340529f 100644 (file)
@@ -2047,21 +2047,40 @@ message).  Refer to the man page on sendmail for more details on DSN.
 <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&lowbar;host"> and authenticate by logging in
-as <ref id="pop_user" name="pop&lowbar;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&lowbar;checkinterval" name="&dollar;pop&lowbar;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&lowbar;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)
index 543e685f1a88e80ff1da034f0c1eb78cd8702a43..d90e7a3d78b4dbb5f783fddfdcb5291f2f90a49a 100644 (file)
--- 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 9c1cfd325a23798f1e66447748f10d47398294a8..9a1fa6ed0009cd7eac198634ba1a957b4b9c4647 100644 (file)
--- 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 fbb5902ab1e8e934848511731711bd48920c57c5..2e611fd2d8601955c0955f1758d20113fac81a5f 100644 (file)
--- 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 "" },
index 385d280df3b29f299e8022723c6adbbb9ad3d9fa..d442a61feb73cda2a734ce9fc31b3e38ef6e9d9e 100644 (file)
--- 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 44a462a943db0f3b015c0a7f2d07ff9b04da5b93..e5cc5d86a25d8a394be209ca5d6c9c23557ed461 100644 (file)
--- 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 */
 
index 868e22433996ebc73b0aa7701ec783921b764c12..8ab0b4fe9f13a6355d3e09863a4b8dfbe40768cf 100644 (file)
--- 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 74e4d286c795a271727fb7a0179320d6ff6d1b00..fd42a9167fbab7b3791a463b47f7e47580ef296a 100644 (file)
--- a/mx.c
+++ b/mx.c
 #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 23411dfb8c147c2cbe25eee5907d2400caf33947..2b643632b4dc9d062ceecf5d30b37db6ad2686b5 100644 (file)
--- 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);
index 2a49b08f75ce7ce650046c9cea28b10b1696df31..8be803a9b399a35370a249d9c7eea7d56cd2ea65 100644 (file)
@@ -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 0080fd80554082e826bbaa9cbafaf88db35f6e2e..87c2553bf6f4a676b30c4cd906115ea17b608dc1 100644 (file)
--- a/pop.c
+++ b/pop.c
@@ -1,6 +1,5 @@
 /*
- * 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)
   {
@@ -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 (file)
index 0000000..878a312
--- /dev/null
+++ b/pop.h
@@ -0,0 +1,105 @@
+/*
+ * 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
diff --git a/pop_auth.c b/pop_auth.c
new file mode 100644 (file)
index 0000000..2dc3bb1
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * 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;
+}
diff --git a/pop_lib.c b/pop_lib.c
new file mode 100644 (file)
index 0000000..a49d04b
--- /dev/null
+++ b/pop_lib.c
@@ -0,0 +1,472 @@
+/*
+ * 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;
+  }
+}
index a517de354ac94dcd426beed664cf65afa769aded..1b096e926187d430858b2477911a715088f3408c 100644 (file)
--- 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);
index 55d624ff5cc3616742d74e5ea832f7de3f7867d9..8d08702274719cfb9cae37ad201a32842bbd060d 100644 (file)
@@ -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