home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkpop3.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  84.6 KB  |  3,050 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /*
  19.  * state machine to speak pop3
  20.  *
  21.  * Originally designed and implemented by Lou Montulli '95
  22.  * Extremely heavily modified by Terry Weissman and Jamie Zawinski '95 '96
  23.  */
  24.  
  25. /* Please leave outside of ifdef for windows precompiled headers */
  26. #include "mkutils.h"
  27.  
  28. /* A more guaranteed way of making sure that we never get duplicate messages
  29. is to always get each message's UIDL (if the server supports it)
  30. and use these for storing up deletes which were not committed on the
  31. server.  Based on our experience, it looks like we do NOT need to
  32. do this (it has performance tradeoffs, etc.).  To turn it on, three
  33. things need to happen: #define POP_ALWAYS_USE_UIDL_FOR_DUPLICATES, verify that
  34. the uidl's are correctly getting added when the delete response is received,
  35. and change the POP3_QUIT_RESPONSE state to flush the newly committed deletes. */
  36.  
  37. /* 
  38.  * Cannot have the following line uncommented. It is defined.
  39.  * #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES will always be evaluated
  40.  * as TRUE.
  41.  *
  42. #define POP_ALWAYS_USE_UIDL_FOR_DUPLICATES 0
  43.  *
  44.  */
  45.  
  46. #ifdef MOZILLA_CLIENT
  47.  
  48. #include "mkgeturl.h"
  49. #include "mkpop3.h"
  50. #include "xp_hash.h"
  51. #include "merrors.h"
  52. #include "msgcom.h"
  53. #include "msgnet.h"
  54. #include "secnav.h"
  55. #include "ssl.h"
  56.  
  57. #include "xp_error.h"
  58. #include "xpgetstr.h"
  59.  
  60. #include "prefapi.h"
  61.  
  62. extern int MK_OUT_OF_MEMORY;
  63. extern int MK_POP3_DELE_FAILURE;
  64. extern int MK_POP3_LIST_FAILURE;
  65. extern int MK_POP3_MESSAGE_WRITE_ERROR;
  66. extern int MK_POP3_NO_MESSAGES;
  67. extern int MK_POP3_OUT_OF_DISK_SPACE;
  68. extern int MK_POP3_PASSWORD_FAILURE;
  69. extern int MK_POP3_PASSWORD_UNDEFINED;
  70. extern int MK_POP3_RETR_FAILURE;
  71. extern int MK_POP3_SERVER_ERROR;
  72. extern int MK_POP3_USERNAME_FAILURE;
  73. extern int MK_POP3_USERNAME_UNDEFINED;
  74. extern int MK_TCP_READ_ERROR;
  75. extern int MK_TCP_WRITE_ERROR;
  76. extern int XP_ERRNO_EWOULDBLOCK;
  77. extern int XP_NO_ANSWER;
  78. extern int XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_UIDL_ETC;
  79. extern int XP_RECEIVING_MESSAGE_OF;
  80. extern int XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_THE_TOP_COMMAND;
  81. extern int XP_THE_PREVIOUSLY_ENTERED_PASSWORD_IS_INVALID_ETC;
  82. extern int XP_CONNECT_HOST_CONTACTED_SENDING_LOGIN_INFORMATION;
  83. extern int XP_PASSWORD_FOR_POP3_USER;
  84. extern int MK_MSG_DOWNLOAD_COUNT;
  85.  
  86.  
  87. #define POP3_PORT 110  /* the iana port for pop3 */
  88. #define OUTPUT_BUFFER_SIZE 512  /* max size of command string */
  89.  
  90. /* Globals */
  91. PRIVATE char *net_pop3_username=0;
  92. PRIVATE char *net_pop3_password=0; /* obfuscated pop3 password */
  93. PRIVATE XP_Bool net_pop3_password_pending=FALSE;
  94. PRIVATE XP_Bool net_pop3_block_biff = FALSE;
  95.  
  96. /* We set this if we find that the UIDL command doesn't work.
  97.    It is reset to FALSE if the user name is re-set (a cheap way
  98.    of seeing if the server has changed...)
  99.  */
  100. PRIVATE XP_Bool net_uidl_command_unimplemented = FALSE;
  101. PRIVATE XP_Bool net_xtnd_xlst_command_unimplemented = FALSE;
  102. PRIVATE XP_Bool net_top_command_unimplemented = FALSE;
  103.  
  104. /* definitions of extended POP3 capabilities
  105.  */
  106. typedef enum {
  107.     POP3_CAPABILITY_UNDEFINED = 0x00000000,
  108.     POP3_AUTH_LOGIN_UNDEFINED = 0x00000001,
  109.     POP3_HAS_AUTH_LOGIN          = 0x00000002,
  110.     POP3_XSENDER_UNDEFINED    = 0x00000004,
  111.     POP3_HAS_XSENDER          = 0x00000008,
  112.     POP3_GURL_UNDEFINED          = 0x00000010,
  113.     POP3_HAS_GURL              = 0x00000020
  114. } Pop3CapabilityEnum;
  115.  
  116. PRIVATE uint32 pop3CapabilityFlags = POP3_AUTH_LOGIN_UNDEFINED |
  117.                                      POP3_XSENDER_UNDEFINED |
  118.                                      POP3_GURL_UNDEFINED;
  119.  
  120.  
  121. /* definitions of state for the state machine design
  122.  */
  123. typedef enum {
  124.     POP3_READ_PASSWORD,
  125.     POP3_START_CONNECT,
  126.     POP3_FINISH_CONNECT,
  127.     POP3_WAIT_FOR_RESPONSE,
  128.     POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE,
  129.     POP3_SEND_USERNAME,
  130.     POP3_SEND_PASSWORD,
  131.     POP3_SEND_STAT,
  132.     POP3_GET_STAT,
  133.     POP3_SEND_LIST,
  134.     POP3_GET_LIST,
  135.     POP3_SEND_UIDL_LIST,
  136.     POP3_GET_UIDL_LIST,
  137.     POP3_SEND_XTND_XLST_MSGID,
  138.     POP3_GET_XTND_XLST_MSGID,
  139.     POP3_GET_MSG,
  140.     POP3_SEND_TOP,
  141.     POP3_TOP_RESPONSE,
  142.     POP3_SEND_RETR,
  143.     POP3_RETR_RESPONSE,
  144.     POP3_SEND_DELE,
  145.     POP3_DELE_RESPONSE,
  146.     POP3_SEND_QUIT,
  147.     POP3_DONE,
  148.     POP3_ERROR_DONE,
  149.     POP3_FREE,
  150.     /* The following 3 states support the use of the 'TOP' command instead of UIDL
  151.        for leaving mail on the pop server -km */
  152.     POP3_START_USE_TOP_FOR_FAKE_UIDL, 
  153.     POP3_SEND_FAKE_UIDL_TOP, 
  154.     POP3_GET_FAKE_UIDL_TOP,
  155.     POP3_SEND_AUTH,
  156.     POP3_AUTH_RESPONSE,
  157.     POP3_AUTH_LOGIN,
  158.     POP3_AUTH_LOGIN_RESPONSE,
  159.     POP3_SEND_XSENDER,
  160.     POP3_XSENDER_RESPONSE,
  161.     POP3_SEND_GURL,
  162.     POP3_GURL_RESPONSE,
  163. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  164.     POP3_QUIT_RESPONSE,
  165. #endif
  166.     POP3_INTERRUPTED
  167. } Pop3StatesEnum;
  168.  
  169. /* structure to hold data pertaining to the active state of
  170.  * a transfer in progress.
  171.  *
  172.  */
  173.  
  174.  
  175. #define KEEP        'k'            /* If we want to keep this item on server. */
  176. #define DELETE_CHAR    'd'            /* If we want to delete this item. */
  177.  
  178. typedef struct Pop3AllocedString { /* Need this silliness as a place to
  179.                                       keep the strings that are allocated
  180.                                       for the keys in the hash table. ### */
  181.   char* str;
  182.   struct Pop3AllocedString* next;
  183. } Pop3AllocedString;
  184.  
  185. typedef struct Pop3UidlHost {
  186.   char* host;
  187.   char* user;
  188.   XP_HashTable hash;
  189.   Pop3AllocedString* strings;
  190.   struct Pop3UidlHost* next;
  191. } Pop3UidlHost;
  192.  
  193.  
  194. typedef struct Pop3MsgInfo {
  195.   int32 size;
  196.   char* uidl;
  197. } Pop3MsgInfo;
  198.  
  199. typedef struct _Pop3ConData {
  200.     MSG_Pane* pane;                /* msglib pane object. */
  201.  
  202.     XP_Bool leave_on_server;    /* Whether we're supposed to leave messages
  203.                                    on server. */
  204.     int32 size_limit;            /* Leave messages bigger than this on the
  205.                                    server and only download a partial
  206.                                    message. */
  207.  
  208.     Pop3StatesEnum  next_state;               /* the next state or action to be taken */
  209.     Pop3StatesEnum  next_state_after_response;  
  210.     Bool         pause_for_read;           /* Pause now for next read? */
  211.     
  212.     XP_Bool         command_succeeded;      /* did the last command succeed? */
  213.     char           *command_response;        /* text of last response */
  214.     int32           first_msg;
  215.     TCP_ConData *tcp_con_data;  /* Data pointer for tcp connect state machine */
  216.     char             *data_buffer;
  217.     int32            data_buffer_size;
  218.  
  219.     char *obuffer;        /* line buffer for output to msglib */
  220.     uint32 obuffer_size;
  221.     uint32 obuffer_fp;
  222.  
  223.     int32           number_of_messages;
  224.     Pop3MsgInfo       *msg_info;    /* Message sizes and uidls (used only if we
  225.                                    are playing games that involve leaving
  226.                                    messages on the server). */
  227.     int32           last_accessed_msg;
  228.     int32           cur_msg_size;
  229.     char           *output_buffer;
  230.     XP_Bool         truncating_cur_msg;     /* are we using top and uidl? */
  231.     XP_Bool         msg_del_started;        /* True if MSG_BeginMailDel...
  232.                                              * called
  233.                                              */
  234.     XP_Bool         only_check_for_new_mail;
  235.       MSG_BIFF_STATE    biffstate;    /* If just checking for, what the answer is. */
  236.  
  237.     XP_Bool         password_failed;        /* flag for password querying */
  238.     void            *msg_closure;
  239.     int32            bytes_received_in_message; 
  240.     int32            total_folder_size;
  241.  
  242.       int32            total_download_size; /* Number of bytes we're going to
  243.                                             download.  Might be much less
  244.                                             than the total_folder_size. */
  245.  
  246.     XP_Bool  graph_progress_bytes_p;    /* whether we should display info about
  247.                                            the bytes transferred (usually we
  248.                                            display info about the number of
  249.                                            messages instead.) */
  250.  
  251.     Pop3UidlHost   *uidlinfo;
  252.     XP_HashTable    newuidl;
  253.     char           *only_uidl;    /* If non-NULL, then load only this UIDL. */
  254.     
  255.                                 /* the following three fields support the 
  256.                                    use of the 'TOP' command instead of UIDL
  257.                                       for leaving mail on the pop server -km */
  258.        
  259.                                 /* the current message that we are retrieving 
  260.                                    with the TOP command */
  261.    int32 current_msg_to_top;    
  262.        
  263.                                                    /* we will download this many in 
  264.                                                       POP3_GET_MSG */                               
  265.    int32 number_of_messages_not_seen_before;
  266.                                                 /* reached when we have TOPped 
  267.                                                    all of the new messages */
  268.    XP_Bool found_new_message_boundary; 
  269.    
  270.                                                    /* if this is true then we don't stop 
  271.                                                       at new message boundary, we have to 
  272.                                                       TOP em all to delete some */
  273.    XP_Bool delete_server_message_during_top_traversal;
  274.    XP_Bool get_url;
  275.    XP_Bool seenFromHeader;
  276.    char *sender_info;
  277. } Pop3ConData;
  278.  
  279. /* forward decl */
  280. PRIVATE int32 net_ProcessPop3 (ActiveEntry *ce);
  281.  
  282. PUBLIC void
  283. NET_LeavePopMailOnServer(Bool do_it)
  284. {
  285.     /* XP_ASSERT(0);*/                /* This routine is obsolete. */
  286. }
  287.  
  288. /* Well, someone finally found a legitimate reason to put an @ in the
  289.  * mail server user name. They're trying to use the user name as a UID
  290.  * in the LDAP directory, and the UIDs happen to have the format
  291.  * foo@bar.com. We don't change our default behavior, but we let admins
  292.  * turn it off if they want
  293.  */
  294. PRIVATE XP_Bool net_allow_at_sign_in_mail_user_name = FALSE;
  295.  
  296. MODULE_PRIVATE XP_Bool NET_GetAllowAtSignInMailUserName()
  297. {
  298.     return net_allow_at_sign_in_mail_user_name;
  299. }
  300.  
  301. MODULE_PRIVATE void NET_SetAllowAtSignInMailUserName(XP_Bool allow)
  302. {
  303.     net_allow_at_sign_in_mail_user_name = allow;
  304. }
  305.  
  306. PUBLIC void
  307. NET_SetPopUsername(const char *username)
  308. {
  309.     char *at = NULL;
  310.  
  311.     StrAllocCopy(net_pop3_username, username);
  312.     net_uidl_command_unimplemented = FALSE;
  313.     net_top_command_unimplemented = FALSE;
  314.     net_xtnd_xlst_command_unimplemented = FALSE;
  315.     /*
  316.     ** If we are called with data like "fred@bedrock.com", then we will
  317.     ** help the user by ignoring the stuff after the "@".  People with
  318.     ** @ signs in their user names will be hosed.  They also can't possibly
  319.     ** be current happy internet users.  This will waste a few bytes,
  320.     ** but was the minimal change to make in order to ship cheddar.
  321.     ** (it might even save bytes by avoiding the code which does the
  322.     ** right thing)
  323.     */
  324.     if (!net_allow_at_sign_in_mail_user_name)
  325.     {
  326.         if (net_pop3_username != NULL) at = XP_STRCHR(net_pop3_username, '@');
  327.         if (at != NULL) *at = '\0';
  328.     }
  329. }
  330.  
  331. PUBLIC const char*
  332. NET_GetPopUsername(void)
  333. {
  334.   return net_pop3_username;
  335. }
  336.  
  337. PRIVATE void
  338. net_set_pop3_password(const char *password)
  339. {
  340.     FREEIF(net_pop3_password);
  341.     net_pop3_password = SECNAV_MungeString(password);
  342. }
  343.  
  344. PRIVATE char *
  345. net_get_pop3_password(void)
  346. {
  347.     return SECNAV_UnMungeString(net_pop3_password);
  348. }
  349.  
  350. PUBLIC void
  351. NET_SetPopPassword(const char *password)
  352. {
  353.     if (password && XP_STRLEN(password))
  354.         StrAllocCopy(net_pop3_password, password);
  355.     else
  356.         FREEIF(net_pop3_password);
  357. }
  358.  
  359. PUBLIC const char *
  360. NET_GetPopPassword(void)
  361. {
  362.   return net_get_pop3_password();
  363. }
  364.  
  365. /* fix Mac warning of missing prototype */
  366. PUBLIC void
  367. NET_SetPopPassword2(const char *password);
  368.  
  369. PUBLIC void
  370. NET_SetPopPassword2(const char *password)
  371. {
  372.     net_set_pop3_password(password);
  373. }
  374.  
  375. /* sets the size limit for pop3 messages
  376.  *
  377.  * set the size negative to make it infinite length
  378.  */
  379. PUBLIC void
  380. NET_SetPopMsgSizeLimit(int32 size)
  381. {
  382.     XP_ASSERT(0);                /* This routine is obsolete */
  383. }
  384.  
  385. PUBLIC int32
  386. NET_GetPopMsgSizeLimit(void)
  387. {
  388.     XP_ASSERT(0);                /* This routine is obsolete */
  389.     return -1;
  390. }
  391.  
  392.  
  393. static int
  394. uidl_cmp (const void *obj1, const void *obj2)
  395. {
  396.   XP_ASSERT (obj1 && obj2);
  397.   return XP_STRCMP ((char *) obj1, (char *) obj2);
  398. }
  399.  
  400.  
  401. static void
  402. put_hash(Pop3UidlHost* host, XP_HashTable table, const char* key, char value)
  403. {
  404.   Pop3AllocedString* tmp;
  405.   int v = value;
  406.   tmp = XP_NEW_ZAP(Pop3AllocedString);
  407.   if (tmp) {
  408.     tmp->str = XP_STRDUP(key);
  409.     if (tmp->str) {
  410.       tmp->next = host->strings;
  411.       host->strings = tmp;
  412.       XP_Puthash(table, tmp->str, (void*) v);
  413.     } else {
  414.       XP_FREE(tmp);
  415.     }
  416.   }
  417. }
  418.  
  419. PRIVATE Pop3UidlHost* net_pop3_load_state(const char* searchhost,
  420.                                           const char* searchuser)
  421. {
  422.   XP_File file;
  423.   char* buf;
  424.   char* host;
  425.   char* user;
  426.   char* uidl;
  427.   char* flags;
  428.   Pop3UidlHost* result = NULL;
  429.   Pop3UidlHost* current = NULL;
  430.   Pop3UidlHost* tmp;
  431.   result = XP_NEW_ZAP(Pop3UidlHost);
  432.   if (!result) return NULL;
  433.   result->host = XP_STRDUP(searchhost);
  434.   result->user = XP_STRDUP(searchuser);
  435.   result->hash = XP_HashTableNew(20, XP_StringHash, uidl_cmp);
  436.   if (!result->host || !result->user || !result->hash) {
  437.     FREEIF(result->host);
  438.     FREEIF(result->user);
  439.     if (result->hash) XP_HashTableDestroy(result->hash);
  440.     XP_FREE(result);
  441.     return NULL;
  442.   }
  443.   file = XP_FileOpen("", xpMailPopState, XP_FILE_READ);
  444.   if (!file) return result;
  445.   buf = (char*)XP_ALLOC(512);
  446.   if (buf) {
  447.     while (XP_FileReadLine(buf, 512, file)) {
  448.       if (*buf == '#' || *buf == CR || *buf == LF || *buf == 0)
  449.         continue;
  450.       if (buf[0] == '*') {
  451.         /* It's a host&user line. */
  452.         current = NULL;
  453.         host = XP_STRTOK(buf + 1, " \t" LINEBREAK);
  454.         user = XP_STRTOK(NULL, " \t" LINEBREAK);
  455.         if (host == NULL || user == NULL) continue;
  456.         for (tmp = result ; tmp ; tmp = tmp->next) {
  457.           if (XP_STRCMP(host, tmp->host) == 0 &&
  458.               XP_STRCMP(user, tmp->user) == 0) {
  459.             current = tmp;
  460.             break;
  461.           }
  462.         }
  463.         if (!current) {
  464.           current = XP_NEW_ZAP(Pop3UidlHost);
  465.           if (current) {
  466.             current->host = XP_STRDUP(host);
  467.             current->user = XP_STRDUP(user);
  468.             current->hash = XP_HashTableNew(20, XP_StringHash, uidl_cmp);
  469.             if (!current->host || !current->user || !current->hash) {
  470.               FREEIF(current->host);
  471.               FREEIF(current->user);
  472.               if (current->hash) XP_HashTableDestroy(current->hash);
  473.               XP_FREE(current);
  474.             } else {
  475.               current->next = result->next;
  476.               result->next = current;
  477.             }
  478.           }
  479.         }
  480.       } else {
  481.         /* It's a line with a UIDL on it. */
  482.         if (current) {
  483.           flags = XP_STRTOK(buf, " \t" LINEBREAK);
  484.           uidl = XP_STRTOK(NULL, " \t" LINEBREAK);
  485.           if (flags && uidl) {
  486.             XP_ASSERT(flags[0] == KEEP || flags[0] == DELETE_CHAR);
  487.             if (flags[0] == KEEP || flags[0] == DELETE_CHAR) {
  488.               put_hash(current, current->hash, uidl, flags[0]);
  489.             }
  490.           }
  491.         }
  492.       }
  493.     }
  494.     XP_FREE(buf);
  495.   }
  496.   XP_FileClose(file);
  497.   return result;
  498. }
  499.  
  500.  
  501.  
  502. static XP_Bool
  503. hash_empty_mapper(XP_HashTable hash, const void* key, void* value,
  504.                   void* closure)
  505. {
  506.   *((XP_Bool*) closure) = FALSE;
  507.   return FALSE;
  508. }
  509.  
  510. static XP_Bool
  511. hash_empty(XP_HashTable hash)
  512. {
  513.   XP_Bool result = TRUE;
  514.   XP_Maphash(hash, hash_empty_mapper, &result);
  515.   return result;
  516. }
  517.  
  518.  
  519. PRIVATE XP_Bool
  520. net_pop3_write_mapper(XP_HashTable hash, const void* key, void* value,
  521.                       void* closure)
  522. {
  523.   XP_File file = (XP_File) closure;
  524.   XP_ASSERT(value == ((void *) (int) KEEP) ||
  525.             value == ((void *) (int) DELETE_CHAR));
  526.   XP_FilePrintf(file, "%c %s" LINEBREAK, (char)(long)value, (char*) key);
  527.   return TRUE;
  528. }
  529.  
  530.  
  531. PRIVATE void
  532. net_pop3_write_state(Pop3UidlHost* host)
  533. {
  534.   XP_File file;
  535.   file = XP_FileOpen("", xpMailPopState, XP_FILE_WRITE_BIN);
  536.   if (!file) return;
  537.   XP_FileWrite("# Netscape POP3 State File" LINEBREAK
  538.                "# This is a generated file!  Do not edit." LINEBREAK LINEBREAK,
  539.                -1, file);
  540.   for (; host ; host = host->next) {
  541.       if (!hash_empty(host->hash)) {
  542.       XP_FileWrite("*", 1, file);
  543.       XP_FileWrite(host->host, -1, file);
  544.       XP_FileWrite(" ", 1, file);
  545.       XP_FileWrite(host->user, -1, file);
  546.       XP_FileWrite(LINEBREAK, LINEBREAK_LEN, file);
  547.       XP_Maphash(host->hash, net_pop3_write_mapper, file);
  548.     }
  549.   }
  550.   XP_FileClose(file);
  551. }
  552.  
  553.  
  554. PRIVATE void
  555. net_pop3_free_state(Pop3UidlHost* host) 
  556. {
  557.   Pop3UidlHost* h;
  558.   Pop3AllocedString* tmp;
  559.   Pop3AllocedString* next;
  560.   while (host) {
  561.     h = host->next;
  562.     XP_FREE(host->host);
  563.     XP_FREE(host->user);
  564.     XP_HashTableDestroy(host->hash);
  565.     tmp = host->strings;
  566.     while (tmp) {
  567.       next = tmp->next;
  568.       XP_FREE(tmp->str);
  569.       XP_FREE(tmp);
  570.       tmp = next;
  571.     }
  572.     XP_FREE(host);
  573.     host = h;
  574.   }
  575. }
  576.  
  577.  
  578.  
  579. PRIVATE void
  580. net_pop3_free_msg_info(ActiveEntry* ce)
  581. {
  582.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  583.   int i;
  584.   if (cd->msg_info) {
  585.     for (i=0 ; i<cd->number_of_messages ; i++) {
  586.       if (cd->msg_info[i].uidl) XP_FREE(cd->msg_info[i].uidl);
  587.     }
  588.     XP_FREE(cd->msg_info);
  589.     cd->msg_info = NULL;
  590.   }
  591. }    
  592.  
  593. /* km
  594.     This function will read one line and only go on to the next state
  595.     if the first character of that line is a '+' character.  This will
  596.     accomodate pop mail servers that print a banner when the client 
  597.     initiates a connection.  Bug #7165
  598. */
  599. PRIVATE int
  600. net_pop3_wait_for_start_of_connection_response(ActiveEntry * ce)
  601. {
  602.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  603.     char * line;
  604.  
  605.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  606.                         &cd->data_buffer_size, &cd->pause_for_read);
  607.  
  608.     if(ce->status == 0)
  609.       {
  610.         /* this shouldn't really happen, but...
  611.          */
  612.         cd->next_state = cd->next_state_after_response;
  613.         cd->pause_for_read = FALSE; /* don't pause */
  614.         return(0);
  615.       }
  616.     else if(ce->status < 0)
  617.       {
  618.         int rv = PR_GetError();
  619.  
  620.         if (rv == PR_WOULD_BLOCK_ERROR)
  621.           {
  622.             cd->pause_for_read = TRUE;
  623.             return(0);
  624.           }
  625.  
  626.         TRACEMSG(("TCP Error: %d", rv));
  627.  
  628.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  629.  
  630.         /* return TCP error
  631.          */
  632.         return MK_TCP_READ_ERROR;
  633.       }
  634.     else if(!line)
  635.       {
  636.          return ce->status; /* wait for line */
  637.       }
  638.  
  639.     TRACEMSG(("    Rx: %s", line));
  640.  
  641.     if(*line == '+')
  642.       {
  643.         cd->command_succeeded = TRUE;
  644.         if(XP_STRLEN(line) > 4)
  645.             StrAllocCopy(cd->command_response, line+4);
  646.         else
  647.             StrAllocCopy(cd->command_response, line);
  648.             
  649.         cd->next_state = cd->next_state_after_response;
  650.            cd->pause_for_read = FALSE; /* don't pause */
  651.       }
  652.      
  653.  
  654.     return(1);  /* everything ok */
  655. }
  656.  
  657. PRIVATE int
  658. net_pop3_wait_for_response(ActiveEntry * ce)
  659. {
  660.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  661.     char * line;
  662.  
  663.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  664.                         &cd->data_buffer_size, &cd->pause_for_read);
  665.  
  666.     if(ce->status == 0)
  667.       {
  668.         /* this shouldn't really happen, but...
  669.          */
  670.         cd->next_state = cd->next_state_after_response;
  671.         cd->pause_for_read = FALSE; /* don't pause */
  672.         return(0);
  673.       }
  674.     else if(ce->status < 0)
  675.       {
  676.         int rv = PR_GetError();
  677.  
  678.         if (rv == PR_WOULD_BLOCK_ERROR)
  679.           {
  680.             cd->pause_for_read = TRUE;
  681.             return(0);
  682.           }
  683.  
  684.         TRACEMSG(("TCP Error: %d", rv));
  685.  
  686.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  687.  
  688.         /* return TCP error
  689.          */
  690.         return MK_TCP_READ_ERROR;
  691.       }
  692.     else if(!line)
  693.       {
  694.          return ce->status; /* wait for line */
  695.       }
  696.  
  697.     TRACEMSG(("    Rx: %s", line));
  698.  
  699.     if(*line == '+')
  700.       {
  701.         cd->command_succeeded = TRUE;
  702.         if(XP_STRLEN(line) > 4)
  703.             StrAllocCopy(cd->command_response, line+4);
  704.         else
  705.             StrAllocCopy(cd->command_response, line);
  706.       }
  707.     else
  708.       {
  709.         cd->command_succeeded = FALSE;
  710.         if(XP_STRLEN(line) > 5)
  711.             StrAllocCopy(cd->command_response, line+5);
  712.         else
  713.             StrAllocCopy(cd->command_response, line);
  714.       }
  715.  
  716.     cd->next_state = cd->next_state_after_response;
  717.     cd->pause_for_read = FALSE; /* don't pause */
  718.  
  719.     return(1);  /* everything ok */
  720. }
  721.  
  722. PRIVATE int
  723. net_pop3_error(ActiveEntry *ce, int err_code)
  724. {
  725.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  726.  
  727.     ce->URL_s->error_msg = NET_ExplainErrorDetails(err_code, 
  728.                             cd->command_response ? cd->command_response :
  729.                             XP_GetString( XP_NO_ANSWER ) );
  730.  
  731.     cd->next_state = POP3_ERROR_DONE;
  732.     cd->pause_for_read = FALSE;
  733.  
  734.     return(err_code);
  735. }
  736.  
  737. PRIVATE int
  738. net_pop3_send_command(ActiveEntry *ce, const char * command)
  739. {
  740.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  741.     int status;
  742.  
  743.     status = (int) NET_BlockingWrite(ce->socket,
  744.                                         command,
  745.                                         XP_STRLEN(command));
  746.  
  747.     TRACEMSG(("Pop3 Tx: %s", cd->output_buffer));
  748.  
  749.     if(status < 0)
  750.       {
  751.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, SOCKET_ERRNO);
  752.         cd->next_state = POP3_ERROR_DONE;
  753.         return(MK_TCP_WRITE_ERROR);
  754.       }
  755.  
  756.     cd->pause_for_read = TRUE;
  757.     cd->next_state = POP3_WAIT_FOR_RESPONSE;
  758.  
  759.     return(status);
  760. }
  761.  
  762. /*
  763.  * POP3 AUTH LOGIN extention
  764.  */
  765.  
  766. PRIVATE int
  767. net_pop3_send_auth(ActiveEntry *ce)
  768. {
  769.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  770.  
  771.     /* check login response */
  772.     if(!cd->command_succeeded)
  773.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  774.  
  775.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "AUTH" CRLF);
  776.  
  777.     cd->next_state_after_response = POP3_AUTH_RESPONSE;
  778.  
  779.     return net_pop3_send_command(ce, cd->output_buffer);
  780. }
  781.  
  782. PRIVATE    int
  783. net_pop3_auth_response(ActiveEntry *ce)
  784. {
  785.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  786.     char * line;
  787.  
  788.     if (POP3_AUTH_LOGIN_UNDEFINED & pop3CapabilityFlags)
  789.         pop3CapabilityFlags &= ~POP3_AUTH_LOGIN_UNDEFINED;
  790.  
  791.     if (!cd->command_succeeded) 
  792.     {
  793.         /* AUTH command not implemented 
  794.          * no base64 encoded username/password
  795.          */
  796.         cd->command_succeeded = TRUE;
  797.         pop3CapabilityFlags &= ~POP3_HAS_AUTH_LOGIN;
  798.         cd->next_state = POP3_SEND_USERNAME;
  799.         return 0;
  800.     }
  801.  
  802.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  803.                     &cd->data_buffer_size, &cd->pause_for_read);
  804.  
  805.     if(ce->status == 0) 
  806.     {
  807.         /* this shouldn't really happen, but...
  808.          */
  809.         cd->next_state = cd->next_state_after_response;
  810.         cd->pause_for_read = FALSE; /* don't pause */
  811.         return(0);
  812.     }
  813.     else if (ce->status < 0) 
  814.     {
  815.         int rv = SOCKET_ERRNO;
  816.  
  817.         TRACEMSG(("TCP Error: %d", rv));
  818.  
  819.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  820.  
  821.         /* return TCP error
  822.          */
  823.         return MK_TCP_READ_ERROR;
  824.     } 
  825.     else if (!line) 
  826.          return ce->status; /* wait for line */
  827.  
  828.     TRACEMSG(("    Rx: %s", line));
  829.  
  830.     if (!XP_STRCMP(line, ".")) 
  831.     {
  832.         /* now that we've read all the AUTH responses, decide which 
  833.          * state to go to next 
  834.          */
  835.         if (pop3CapabilityFlags & POP3_HAS_AUTH_LOGIN)
  836.             cd->next_state = POP3_AUTH_LOGIN;
  837.         else
  838.             cd->next_state = POP3_SEND_USERNAME;
  839.         cd->pause_for_read = FALSE; /* don't pause */
  840.     }
  841.     else if (!XP_STRCASECMP (line, "LOGIN")) 
  842.         pop3CapabilityFlags |= POP3_HAS_AUTH_LOGIN;
  843.  
  844.     return 0;
  845. }
  846.  
  847. PRIVATE int
  848. net_pop3_auth_login(ActiveEntry *ce)
  849. {
  850.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  851.  
  852.     /* check login response */
  853.     if(!cd->command_succeeded) {
  854.         pop3CapabilityFlags &= ~POP3_HAS_AUTH_LOGIN;
  855.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  856.     }
  857.  
  858.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "AUTH LOGIN" CRLF);
  859.  
  860.     cd->next_state_after_response = POP3_AUTH_LOGIN_RESPONSE;
  861.  
  862.     return net_pop3_send_command(ce, cd->output_buffer);
  863.  
  864. }
  865.  
  866. PRIVATE int
  867. net_pop3_auth_login_response(ActiveEntry *ce)
  868. {
  869.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  870.  
  871.   if (!cd->command_succeeded) 
  872.     {
  873.       /* sounds like server does not support auth-skey extension
  874.          resume regular logon process */
  875.       /* reset command_succeeded to true */
  876.       cd->command_succeeded = TRUE;
  877.       /* reset auth login state */
  878.         pop3CapabilityFlags &= ~POP3_HAS_AUTH_LOGIN;
  879.     }
  880.   else
  881.     {
  882.       pop3CapabilityFlags |= POP3_HAS_AUTH_LOGIN;
  883.     }
  884.   cd->next_state = POP3_SEND_USERNAME;
  885.   return 0;
  886. }
  887.  
  888.  
  889. PRIVATE int
  890. net_pop3_send_username(ActiveEntry *ce)
  891. {
  892.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  893.  
  894.     /* check login response */
  895.     if(!cd->command_succeeded)
  896.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  897.  
  898.     if(!net_pop3_username || !*net_pop3_username)
  899.         return(net_pop3_error(ce, MK_POP3_USERNAME_UNDEFINED));
  900.  
  901.     if (POP3_HAS_AUTH_LOGIN & pop3CapabilityFlags) {
  902.         char * str = 
  903.           NET_Base64Encode(net_pop3_username, 
  904.                            XP_STRLEN(net_pop3_username));
  905.         if (str)
  906.           {
  907.             PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "%.256s" CRLF,
  908.                         str);
  909.             XP_FREEIF(str);
  910.           }
  911.         else
  912.           {
  913.             return (net_pop3_error(ce, MK_POP3_USERNAME_UNDEFINED));
  914.           }
  915.     }
  916.     else {
  917.         PR_snprintf(cd->output_buffer, 
  918.                     OUTPUT_BUFFER_SIZE, 
  919.                     "USER %.256s" CRLF, net_pop3_username);
  920.     }
  921.  
  922.     cd->next_state_after_response = POP3_SEND_PASSWORD;
  923.  
  924.     return(net_pop3_send_command(ce, cd->output_buffer));
  925. }
  926.  
  927. PRIVATE int
  928. net_pop3_send_password(ActiveEntry *ce)
  929. {
  930.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  931.     char *password;
  932.  
  933.     /* check username response */
  934.     if (!cd->command_succeeded)
  935.         return(net_pop3_error(ce, MK_POP3_USERNAME_FAILURE));
  936.  
  937.     password = net_get_pop3_password();
  938.  
  939.     if (password == NULL)
  940.         return(net_pop3_error(ce, MK_POP3_PASSWORD_UNDEFINED));
  941.  
  942.     if (POP3_HAS_AUTH_LOGIN & pop3CapabilityFlags) {
  943.         char * str = 
  944.           NET_Base64Encode(password, XP_STRLEN(password));
  945.         if (str)
  946.           {
  947.             PR_snprintf(cd->output_buffer, 
  948.                         OUTPUT_BUFFER_SIZE, "%.256s" CRLF, str);
  949.             XP_FREEIF(str);
  950.           }
  951.         else
  952.           {
  953.             return (net_pop3_error(ce, MK_POP3_PASSWORD_UNDEFINED));
  954.           }
  955.     }
  956.     else {
  957.         PR_snprintf(cd->output_buffer, 
  958.                     OUTPUT_BUFFER_SIZE, "PASS %.256s" CRLF, password);
  959.     }
  960.     XP_MEMSET(password, 0, XP_STRLEN(password));
  961.     XP_FREE(password);
  962.  
  963.     if (cd->get_url)
  964.         cd->next_state_after_response = POP3_SEND_GURL;
  965.     else
  966.         cd->next_state_after_response = POP3_SEND_STAT;
  967.  
  968.     return(net_pop3_send_command(ce, cd->output_buffer));
  969. }
  970.  
  971. PRIVATE int
  972. net_pop3_send_stat_or_gurl(ActiveEntry *ce, XP_Bool sendStat)
  973. {
  974.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  975.  
  976.     /* check password response */
  977.     if(!cd->command_succeeded)
  978.       {
  979.         /* The password failed.
  980.  
  981.            Sever the connection and go back to the `read password' state,
  982.            which, upon success, will re-open the connection.  Set a flag
  983.            which causes the prompt to be different that time (to indicate
  984.            that the old password was bogus.)
  985.  
  986.            But if we're just checking for new mail (biff) then don't bother
  987.            prompting the user for a password: just fail silently. */
  988.         if (cd->only_check_for_new_mail)
  989.           return MK_POP3_PASSWORD_UNDEFINED;
  990.  
  991.         cd->password_failed = TRUE;
  992.         cd->next_state = POP3_ERROR_DONE;    /* close */
  993.         cd->pause_for_read = FALSE;           /* try again right away */
  994.         pop3CapabilityFlags = POP3_AUTH_LOGIN_UNDEFINED | POP3_XSENDER_UNDEFINED |
  995.                               POP3_GURL_UNDEFINED;
  996.         if (cd->pane) {
  997.             MSG_SetUserAuthenticated(MSG_GetMaster(cd->pane), FALSE);
  998.             MSG_SetMailAccountURL(MSG_GetMaster(cd->pane), NULL);
  999.         }
  1000.         /* clear the bogus password in case 
  1001.          * we need to sync with auth smtp password 
  1002.          */
  1003.         XP_FREEIF(net_pop3_password);
  1004.         return 0;
  1005.       }
  1006.     else if (net_pop3_password_pending)
  1007.       {
  1008.         /*
  1009.          * First time with this password.  Record it as a keeper.
  1010.          * (The user's preference might say to save it.)
  1011.          */
  1012.         FE_RememberPopPassword(ce->window_id, net_pop3_password);
  1013.         net_pop3_password_pending = FALSE;
  1014.         if (cd->pane)
  1015.             MSG_SetUserAuthenticated(MSG_GetMaster(cd->pane), TRUE);
  1016.       }
  1017.  
  1018.     if (sendStat) {
  1019.         XP_STRCPY(cd->output_buffer, "STAT" CRLF);
  1020.         cd->next_state_after_response = POP3_GET_STAT;
  1021.     }
  1022.     else {
  1023.         XP_STRCPY(cd->output_buffer, "GURL" CRLF);
  1024.         cd->next_state_after_response = POP3_GURL_RESPONSE;
  1025.     }
  1026.  
  1027.     return(net_pop3_send_command(ce, cd->output_buffer));
  1028. }
  1029.  
  1030.  
  1031. PRIVATE int
  1032. net_pop3_send_stat(ActiveEntry *ce)
  1033. {
  1034.     return net_pop3_send_stat_or_gurl(ce, TRUE);
  1035. }
  1036.  
  1037.  
  1038. PRIVATE int
  1039. net_pop3_get_stat(ActiveEntry *ce)
  1040. {
  1041.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1042.  
  1043.     char *num;
  1044.  
  1045.     /* check stat response */
  1046.     if(!cd->command_succeeded)
  1047.         return(net_pop3_error(ce, MK_POP3_PASSWORD_FAILURE));
  1048.  
  1049.     /* stat response looks like:  %d %d
  1050.      * The first number is the number of articles
  1051.      * The second number is the number of bytes
  1052.      *
  1053.      *  grab the first and second arg of stat response
  1054.      */
  1055.     num = XP_STRTOK(cd->command_response, " ");
  1056.  
  1057.     cd->number_of_messages = atol(num);
  1058.  
  1059.     num = XP_STRTOK(NULL, " ");
  1060.  
  1061.     cd->total_folder_size = (int32) atol(num);
  1062.  
  1063.     cd->total_download_size = -1; /* Means we need to calculate it, later. */
  1064.  
  1065.     if(cd->number_of_messages <= 0) {
  1066.       /* We're all done.  We know we have no mail. */
  1067.       cd->next_state = POP3_SEND_QUIT;
  1068.       XP_Clrhash(cd->uidlinfo->hash);
  1069.       return(0);
  1070.     }
  1071.  
  1072.     if (cd->only_check_for_new_mail && !cd->leave_on_server &&
  1073.         cd->size_limit < 0) {
  1074.       /* We're just checking for new mail, and we're not playing any games that
  1075.          involve keeping messages on the server.  Therefore, we now know enough
  1076.          to finish up.  If we had no messages, that would have been handled
  1077.          above; therefore, we know we have some new messages. */
  1078.       cd->biffstate = MSG_BIFF_NewMail;
  1079.       cd->next_state = POP3_SEND_QUIT;
  1080.       return(0);
  1081.     }
  1082.  
  1083.  
  1084.     if (!cd->only_check_for_new_mail) {
  1085.       cd->msg_del_started = MSG_BeginMailDelivery(cd->pane);
  1086.  
  1087.       if(!cd->msg_del_started)
  1088.         {
  1089.           return(net_pop3_error(ce, MK_POP3_MESSAGE_WRITE_ERROR));
  1090.         }
  1091.     }
  1092.  
  1093. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  1094.     if (net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && net_top_command_unimplemented) 
  1095. #else
  1096.     if ((net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && net_top_command_unimplemented) ||
  1097.         (!cd->only_uidl && !cd->leave_on_server &&
  1098.          (cd->size_limit < 0 || net_top_command_unimplemented)))
  1099. #endif
  1100.          /* We don't need message size or uidl info; go directly to getting
  1101.          messages. */
  1102.     {
  1103.       cd->next_state = POP3_GET_MSG;
  1104.     } else {
  1105.       cd->next_state = POP3_SEND_LIST;
  1106.     }
  1107.     return 0;
  1108. }
  1109.  
  1110.  
  1111.  
  1112. PRIVATE int
  1113. net_pop3_send_gurl(ActiveEntry *ce)
  1114. {
  1115.     if (pop3CapabilityFlags == POP3_CAPABILITY_UNDEFINED ||
  1116.         pop3CapabilityFlags & POP3_GURL_UNDEFINED ||
  1117.         pop3CapabilityFlags & POP3_HAS_GURL)
  1118.         return net_pop3_send_stat_or_gurl(ce, FALSE);
  1119.     else 
  1120.         return -1;
  1121. }
  1122.  
  1123.  
  1124. PRIVATE int
  1125. net_pop3_gurl_response(ActiveEntry *ce)
  1126. {
  1127.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1128.     
  1129.     if (POP3_GURL_UNDEFINED & pop3CapabilityFlags)
  1130.         pop3CapabilityFlags &= ~POP3_GURL_UNDEFINED;
  1131.  
  1132.     if (cd->command_succeeded) {
  1133.         pop3CapabilityFlags |= POP3_HAS_GURL;
  1134.         if (cd->pane)
  1135.             MSG_SetMailAccountURL(MSG_GetMaster(cd->pane), cd->command_response);
  1136.     }
  1137.     else {
  1138.         pop3CapabilityFlags &= ~POP3_HAS_GURL;
  1139.     }
  1140.     cd->next_state = POP3_SEND_QUIT;
  1141.     return 0;
  1142. }
  1143.  
  1144. PRIVATE int
  1145. net_pop3_send_list(ActiveEntry *ce)
  1146. {
  1147.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1148.     cd->msg_info = (Pop3MsgInfo *) XP_ALLOC(sizeof(Pop3MsgInfo) *
  1149.                                             cd->number_of_messages);
  1150.     if (!cd->msg_info) return(MK_OUT_OF_MEMORY);
  1151.     XP_MEMSET(cd->msg_info, 0, sizeof(Pop3MsgInfo) * cd->number_of_messages);
  1152.     XP_STRCPY(cd->output_buffer, "LIST" CRLF);
  1153.     cd->next_state_after_response = POP3_GET_LIST;
  1154.     return(net_pop3_send_command(ce, cd->output_buffer));
  1155. }
  1156.  
  1157.  
  1158.  
  1159. PRIVATE int
  1160. net_pop3_get_list(ActiveEntry *ce)
  1161. {
  1162.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1163.     char * line;
  1164.     int32 msg_num;
  1165.  
  1166.     /* check list response 
  1167.      * This will get called multiple times
  1168.      * but it's alright since command_succeeded
  1169.      * will remain constant
  1170.      */
  1171.     if(!cd->command_succeeded)
  1172.         return(net_pop3_error(ce, MK_POP3_LIST_FAILURE));
  1173.  
  1174.  
  1175.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  1176.                         &cd->data_buffer_size, &cd->pause_for_read);
  1177.  
  1178.     if(ce->status == 0)
  1179.       {
  1180.         /* this shouldn't really happen, but...
  1181.          */
  1182.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  1183.       }
  1184.     else if(ce->status < 0)
  1185.       {
  1186.         int rv = PR_GetError();
  1187.  
  1188.         if (rv == PR_WOULD_BLOCK_ERROR)
  1189.           {
  1190.             cd->pause_for_read = TRUE;
  1191.             return(0);
  1192.           }
  1193.  
  1194.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  1195.  
  1196.         /* return TCP error
  1197.          */
  1198.         return MK_TCP_READ_ERROR;
  1199.       }
  1200.     else if(!line)
  1201.       {
  1202.          return ce->status; /* wait for line */
  1203.       }
  1204.  
  1205.     TRACEMSG(("    Rx: %s", line));
  1206.  
  1207.     /* remove CRLF */
  1208.     XP_StripLine(line);
  1209.  
  1210.     /* parse the line returned from the list command 
  1211.      * it looks like
  1212.      * #msg_number #bytes
  1213.      *
  1214.      * list data is terminated by a ".CRLF" line
  1215.      */
  1216.     if(!XP_STRCMP(line, "."))
  1217.       {
  1218.         cd->next_state = POP3_SEND_UIDL_LIST;
  1219.         cd->pause_for_read = FALSE;
  1220.         return(0);
  1221.       }
  1222.  
  1223.     msg_num = atol(XP_STRTOK(line, " "));
  1224.  
  1225.     if(msg_num <= cd->number_of_messages && msg_num > 0)
  1226.         cd->msg_info[msg_num-1].size = atol(XP_STRTOK(NULL, " "));
  1227.  
  1228.     return(0);
  1229. }
  1230. /* km
  1231.  *
  1232.  *
  1233.  *  Process state: POP3_START_USE_TOP_FOR_FAKE_UIDL
  1234.  *
  1235.  *    If we get here then UIDL and XTND are not supported by the mail server.
  1236.  * 
  1237.  *    Now we will walk backwards, from higher message numbers to lower, 
  1238.  *  using the TOP command. Along the way, keep track of messages to down load
  1239.  *  by POP3_GET_MSG.
  1240.  *
  1241.  *  Stop when we reach a msg that we knew about before or we run out of
  1242.  *  messages.
  1243.  *
  1244.  *    There is also conditional code to handle:
  1245.  *        BIFF
  1246.  *        Pop3ConData->only_uidl == true (fetching one message only)
  1247.  *      emptying message hash table if all messages are new
  1248.  *        Deleting messages that are marked deleted.
  1249.  *
  1250.  *
  1251. */
  1252.  
  1253. /* this function gets called for each hash table entry.  If it finds a message
  1254.    marked delete, then we will have to traverse all of the server messages
  1255.    with TOP in order to delete those that are marked delete.
  1256.  
  1257.  
  1258.    If UIDL or XTND XLST are supported then this function will never get called.
  1259.    
  1260.    A pointer to this function is passed to XP_Maphash     -km */
  1261.  
  1262. PRIVATE XP_Bool
  1263. net_pop3_check_for_hash_messages_marked_delete(XP_HashTable table,
  1264.                                                   const void *key, 
  1265.                                                   void *value, 
  1266.                                                   void *closure)
  1267. {
  1268.     char valueChar = (char) (int) value;
  1269.     if (valueChar == DELETE_CHAR)
  1270.     {
  1271.         ((Pop3ConData *) closure)->delete_server_message_during_top_traversal = TRUE;
  1272.         return FALSE;    /* XP_Maphash will stop traversing hash table now */
  1273.     }
  1274.     
  1275.     return TRUE;        /* XP_Maphash will continue traversing the hash */
  1276. }
  1277.  
  1278. PRIVATE int
  1279. send_fake_uidl_top(ActiveEntry *ce)
  1280. {
  1281.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1282.  
  1283.     PR_snprintf(cd->output_buffer, 
  1284.                 OUTPUT_BUFFER_SIZE,  
  1285.                "TOP %ld 1" CRLF,
  1286.                 cd->current_msg_to_top);
  1287.  
  1288.     cd->next_state_after_response = POP3_GET_FAKE_UIDL_TOP;
  1289.     cd->pause_for_read = TRUE;
  1290.     return(net_pop3_send_command(ce, cd->output_buffer));
  1291. }
  1292.  
  1293. PRIVATE int
  1294. start_use_top_for_fake_uidl(ActiveEntry *ce)
  1295. {
  1296.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1297.  
  1298.     cd->current_msg_to_top = cd->number_of_messages;
  1299.     cd->number_of_messages_not_seen_before = 0;
  1300.     cd->found_new_message_boundary = FALSE;
  1301.     cd->delete_server_message_during_top_traversal = FALSE;
  1302.     
  1303.     /* may set delete_server_message_during_top_traversal to true */
  1304.     XP_Maphash(cd->uidlinfo->hash,
  1305.                net_pop3_check_for_hash_messages_marked_delete, cd);
  1306.     
  1307.     return (send_fake_uidl_top(ce));
  1308. }
  1309.  
  1310.  
  1311. PRIVATE int
  1312. get_fake_uidl_top(ActiveEntry *ce)
  1313. {
  1314.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1315.     char * line;
  1316.  
  1317.     /* check list response 
  1318.      * This will get called multiple times
  1319.      * but it's alright since command_succeeded
  1320.      * will remain constant
  1321.      */
  1322.     if(!cd->command_succeeded) {
  1323.  
  1324.       /* UIDL, XTND and TOP are all unsupported for this mail server.
  1325.          Tell the user to join the 20th century.
  1326.  
  1327.          Tell the user this, and refuse to download any messages until they've
  1328.          gone into preferences and turned off the `Keep Mail on Server' and
  1329.          `Maximum Message Size' prefs.  Some people really get their panties
  1330.          in a bunch if we download their mail anyway. (bug 11561)
  1331.        */
  1332.       char *fmt = XP_GetString(XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_UIDL_ETC);
  1333.       char *host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  1334.       ce->URL_s->error_msg = PR_smprintf (fmt, (host ? host : "(null)"));
  1335.       FREEIF(host);
  1336.       cd->next_state = POP3_ERROR_DONE;
  1337.       cd->pause_for_read = FALSE;
  1338.       return -1;
  1339.  
  1340.     }
  1341.  
  1342.  
  1343.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  1344.                         &cd->data_buffer_size, &cd->pause_for_read);
  1345.  
  1346.     if(ce->status == 0)
  1347.       {
  1348.         /* this shouldn't really happen, but...
  1349.          */
  1350.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  1351.       }
  1352.     else if(ce->status < 0)
  1353.       {
  1354.         int rv = PR_GetError();
  1355.  
  1356.         if (rv == PR_WOULD_BLOCK_ERROR)
  1357.           {
  1358.             cd->pause_for_read = TRUE;
  1359.             return(0);
  1360.           }
  1361.  
  1362.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  1363.  
  1364.         /* return TCP error
  1365.          */
  1366.         return MK_TCP_READ_ERROR;
  1367.       }
  1368.     else if(!line)
  1369.       {
  1370.          return ce->status; /* wait for line */
  1371.       }
  1372.  
  1373.        TRACEMSG(("    Rx: %s", line));
  1374.  
  1375.     /* remove CRLF */
  1376.     XP_StripLine(line);
  1377.  
  1378.     if(!XP_STRCMP(line, "."))
  1379.     {
  1380.         cd->current_msg_to_top--;
  1381.         if (!cd->current_msg_to_top || 
  1382.             (cd->found_new_message_boundary &&
  1383.              !cd->delete_server_message_during_top_traversal))
  1384.         {
  1385.             /* we either ran out of messages or reached the edge of new
  1386.                messages and no messages are marked dele */
  1387.             cd->next_state = POP3_GET_MSG;
  1388.             cd->pause_for_read = FALSE;
  1389.             
  1390.             /* if all of the messages are new, toss all hash table entries */
  1391.             if (!cd->current_msg_to_top && !cd->found_new_message_boundary)
  1392.                 XP_Clrhash(cd->uidlinfo->hash);
  1393.         }
  1394.         else
  1395.         {
  1396.             /* this message is done, go to the next */
  1397.             cd->next_state = POP3_SEND_FAKE_UIDL_TOP;
  1398.             cd->pause_for_read = FALSE;
  1399.         }
  1400.     }
  1401.     else
  1402.     {
  1403.         /* we are looking for a string of the form
  1404.            Message-Id: <199602071806.KAA14787@neon.netscape.com> */
  1405.         char *firstToken = XP_STRTOK(line, " ");
  1406.         if (firstToken && !XP_STRCASECMP(firstToken, "MESSAGE-ID:") )
  1407.         {
  1408.             char *message_id_token = XP_STRTOK(NULL, " ");
  1409.             if ( !cd->only_uidl && message_id_token &&
  1410.                  (((int) XP_Gethash(cd->uidlinfo->hash, message_id_token, 0) ) == 0) )
  1411.             {    /* we have not seen this message before */
  1412.                 
  1413.                 /* if we are only doing a biff, stop here */
  1414.                 if (cd->only_check_for_new_mail)
  1415.                 {
  1416.                     cd->biffstate = MSG_BIFF_NewMail;
  1417.                       cd->next_state = POP3_SEND_QUIT;
  1418.                     cd->pause_for_read = FALSE;
  1419.                 }
  1420.                 else    /* we will retrieve it and cache it in GET_MSG */
  1421.                 {
  1422.                 cd->number_of_messages_not_seen_before++;
  1423.                 cd->msg_info[cd->current_msg_to_top-1].uidl = XP_STRDUP(message_id_token);
  1424.                   if (!cd->msg_info[cd->current_msg_to_top-1].uidl)
  1425.                     return MK_OUT_OF_MEMORY;
  1426.                 }
  1427.             }
  1428.             else if (cd->only_uidl && message_id_token &&
  1429.                      !XP_STRCMP(cd->only_uidl, message_id_token))
  1430.             {
  1431.                     cd->last_accessed_msg = cd->current_msg_to_top;
  1432.                     cd->found_new_message_boundary = TRUE;
  1433.             }
  1434.             else if (!cd->only_uidl)
  1435.             {    /* we have seen this message and we care about the edge,
  1436.                    stop looking for new ones */
  1437.                 if (cd->number_of_messages_not_seen_before != 0)
  1438.                 {
  1439.                     cd->last_accessed_msg = cd->current_msg_to_top;
  1440.                     cd->found_new_message_boundary = TRUE;
  1441.                     /* we stay in this state so we can process the rest of the
  1442.                        lines in the top message */
  1443.                 }
  1444.                 else
  1445.                 {
  1446.                       cd->next_state = POP3_SEND_QUIT;
  1447.                     cd->pause_for_read = FALSE;
  1448.                 }
  1449.             }
  1450.         }
  1451.     }
  1452.     return 0;
  1453. }
  1454.  
  1455.  
  1456. /* km
  1457.  *
  1458.  *    net_pop3_send_xtnd_xlst_msgid
  1459.  *
  1460.  *  Process state: POP3_SEND_XTND_XLST_MSGID
  1461.  *
  1462.  *    If we get here then UIDL is not supported by the mail server.
  1463.  *  Some mail servers support a similar command:
  1464.  *
  1465.  *        XTND XLST Message-Id
  1466.  *
  1467.  *     Here is a sample transaction from a QUALCOMM server
  1468.  
  1469.  >>XTND XLST Message-Id
  1470.  <<+OK xlst command accepted; headers coming.
  1471.  <<1 Message-ID: <3117E4DC.2699@netscape.com>
  1472.  <<2 Message-Id: <199602062335.PAA19215@lemon.mcom.com>
  1473.  
  1474.  * This function will send the xtnd command and put us into the
  1475.  * POP3_GET_XTND_XLST_MSGID state
  1476.  *
  1477. */
  1478. PRIVATE int
  1479. net_pop3_send_xtnd_xlst_msgid(ActiveEntry *ce)
  1480. {
  1481.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1482.  
  1483.   if (net_xtnd_xlst_command_unimplemented)
  1484.       return(start_use_top_for_fake_uidl(ce));
  1485.  
  1486.  
  1487.   XP_STRCPY(cd->output_buffer, "XTND XLST Message-Id" CRLF);
  1488.   cd->next_state_after_response = POP3_GET_XTND_XLST_MSGID;
  1489.   cd->pause_for_read = TRUE;
  1490.   return(net_pop3_send_command(ce, cd->output_buffer));
  1491. }
  1492.  
  1493.  
  1494. /* km
  1495.  *
  1496.  *    net_pop3_get_xtnd_xlst_msgid
  1497.  *
  1498.  *  This code was created from the net_pop3_get_uidl_list boiler plate.
  1499.  *    The difference is that the XTND reply strings have one more token per
  1500.  *  string than the UIDL reply strings do.
  1501.  *
  1502.  */
  1503.  
  1504. PRIVATE int
  1505. net_pop3_get_xtnd_xlst_msgid(ActiveEntry *ce)
  1506. {
  1507.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1508.     char * line;
  1509.     int32 msg_num;
  1510.  
  1511.     /* check list response 
  1512.      * This will get called multiple times
  1513.      * but it's alright since command_succeeded
  1514.      * will remain constant
  1515.      */
  1516.     if(!cd->command_succeeded) {
  1517.         net_xtnd_xlst_command_unimplemented = TRUE;
  1518.         cd->next_state = POP3_START_USE_TOP_FOR_FAKE_UIDL;
  1519.         cd->pause_for_read = FALSE;
  1520.         return(0);
  1521.     }
  1522.  
  1523.  
  1524.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  1525.                         &cd->data_buffer_size, &cd->pause_for_read);
  1526.  
  1527.     if(ce->status == 0)
  1528.       {
  1529.         /* this shouldn't really happen, but...
  1530.          */
  1531.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  1532.       }
  1533.     else if(ce->status < 0)
  1534.       {
  1535.         int rv = PR_GetError();
  1536.  
  1537.         if (rv == PR_WOULD_BLOCK_ERROR)
  1538.           {
  1539.             cd->pause_for_read = TRUE;
  1540.             return(0);
  1541.           }
  1542.  
  1543.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  1544.  
  1545.         /* return TCP error
  1546.          */
  1547.         return MK_TCP_READ_ERROR;
  1548.       }
  1549.     else if(!line)
  1550.       {
  1551.          return ce->status; /* wait for line */
  1552.       }
  1553.  
  1554.     TRACEMSG(("    Rx: %s", line));
  1555.  
  1556.     /* remove CRLF */
  1557.     XP_StripLine(line);
  1558.  
  1559.  
  1560.     /* parse the line returned from the list command 
  1561.      * it looks like
  1562.      * 1 Message-ID: <3117E4DC.2699@netscape.com>
  1563.      *
  1564.      * list data is terminated by a ".CRLF" line
  1565.      */
  1566.     if(!XP_STRCMP(line, "."))
  1567.       {
  1568.         cd->next_state = POP3_GET_MSG;
  1569.         cd->pause_for_read = FALSE;
  1570.         return(0);
  1571.       }
  1572.  
  1573.     msg_num = atol(XP_STRTOK(line, " "));
  1574.  
  1575.     if(msg_num <= cd->number_of_messages && msg_num > 0) {
  1576. /*      char *eatMessageIdToken = XP_STRTOK(NULL, " ");    */
  1577.       char *uidl = XP_STRTOK(NULL, " ");    /* not really a uidl but a unique token -km */
  1578.  
  1579.       if (!uidl)
  1580.         /* This is bad.  The server didn't give us a UIDL for this message.
  1581.            I've seen this happen when somehow the mail spool has a message
  1582.            that contains a header that reads "X-UIDL: \n".  But how that got
  1583.            there, I have no idea; must be a server bug.  Or something. */
  1584.         uidl = "";
  1585.  
  1586.       cd->msg_info[msg_num-1].uidl = XP_STRDUP(uidl);
  1587.       if (!cd->msg_info[msg_num-1].uidl) return MK_OUT_OF_MEMORY;
  1588.     }
  1589.  
  1590.     return(0);
  1591. }
  1592.  
  1593.  
  1594. PRIVATE int
  1595. net_pop3_send_uidl_list(ActiveEntry *ce)
  1596. {
  1597.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1598.  
  1599.   if (net_uidl_command_unimplemented)
  1600.       return(net_pop3_send_xtnd_xlst_msgid(ce));
  1601.  
  1602.  
  1603.   XP_STRCPY(cd->output_buffer, "UIDL" CRLF);
  1604.   cd->next_state_after_response = POP3_GET_UIDL_LIST;
  1605.   cd->pause_for_read = TRUE;
  1606.   return(net_pop3_send_command(ce, cd->output_buffer));
  1607. }
  1608.  
  1609.  
  1610. PRIVATE int
  1611. net_pop3_get_uidl_list(ActiveEntry *ce)
  1612. {
  1613.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1614.     char * line;
  1615.     int32 msg_num;
  1616.  
  1617.     /* check list response 
  1618.      * This will get called multiple times
  1619.      * but it's alright since command_succeeded
  1620.      * will remain constant
  1621.      */
  1622.     if(!cd->command_succeeded) {
  1623.         cd->next_state = POP3_SEND_XTND_XLST_MSGID;
  1624.         cd->pause_for_read = FALSE;
  1625.         net_uidl_command_unimplemented = TRUE;
  1626.         return(0);
  1627.  
  1628. #if 0  /* this if block shows what UIDL used to do in this case */
  1629.       /* UIDL doesn't work so we can't retrieve the message later, and we
  1630.        * can't play games notating how to keep it on the server.  Therefore
  1631.        * just go download the whole thing, and warn the user.
  1632.        */
  1633.       char *host, *fmt, *buf;
  1634.  
  1635.       net_uidl_command_unimplemented = TRUE;
  1636.  
  1637.       fmt = XP_GetString( XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_UIDL_ETC );
  1638.  
  1639.       host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  1640.       XP_ASSERT(host);
  1641.       if (!host) host = "(null)";
  1642.       buf = PR_smprintf (fmt, host);
  1643.       if (!buf) return MK_OUT_OF_MEMORY;
  1644.       FE_Alert (ce->window_id, buf);
  1645.       XP_FREE (buf);
  1646.  
  1647.       /* Free up the msg_info structure, as we use its presence later to
  1648.          decide if we can do UIDL-based games. */
  1649.       net_pop3_free_msg_info(ce);
  1650.  
  1651.       cd->next_state = POP3_GET_MSG;
  1652.       return(0);
  1653.  
  1654. #endif /* 0 */
  1655.     }
  1656.  
  1657.  
  1658.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buffer,
  1659.                         &cd->data_buffer_size, &cd->pause_for_read);
  1660.  
  1661.     if(ce->status == 0)
  1662.       {
  1663.         /* this shouldn't really happen, but...
  1664.          */
  1665.         return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  1666.       }
  1667.     else if(ce->status < 0)
  1668.       {
  1669.         int rv = PR_GetError();
  1670.  
  1671.         if (rv == PR_WOULD_BLOCK_ERROR)
  1672.           {
  1673.             cd->pause_for_read = TRUE;
  1674.             return(0);
  1675.           }
  1676.  
  1677.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  1678.  
  1679.         /* return TCP error
  1680.          */
  1681.         return MK_TCP_READ_ERROR;
  1682.       }
  1683.     else if(!line)
  1684.       {
  1685.          return ce->status; /* wait for line */
  1686.       }
  1687.  
  1688.     TRACEMSG(("    Rx: %s", line));
  1689.  
  1690.     /* remove CRLF */
  1691.     XP_StripLine(line);
  1692.  
  1693.     /* parse the line returned from the list command 
  1694.      * it looks like
  1695.      * #msg_number uidl
  1696.      *
  1697.      * list data is terminated by a ".CRLF" line
  1698.      */
  1699.     if(!XP_STRCMP(line, "."))
  1700.       {
  1701.         cd->next_state = POP3_GET_MSG;
  1702.         cd->pause_for_read = FALSE;
  1703.         return(0);
  1704.       }
  1705.  
  1706.     msg_num = atol(XP_STRTOK(line, " "));
  1707.  
  1708.     if(msg_num <= cd->number_of_messages && msg_num > 0) {
  1709.       char *uidl = XP_STRTOK(NULL, " ");
  1710.  
  1711.       if (!uidl)
  1712.         /* This is bad.  The server didn't give us a UIDL for this message.
  1713.            I've seen this happen when somehow the mail spool has a message
  1714.            that contains a header that reads "X-UIDL: \n".  But how that got
  1715.            there, I have no idea; must be a server bug.  Or something. */
  1716.         uidl = "";
  1717.  
  1718.       cd->msg_info[msg_num-1].uidl = XP_STRDUP(uidl);
  1719.       if (!cd->msg_info[msg_num-1].uidl) return MK_OUT_OF_MEMORY;
  1720.     }
  1721.  
  1722.     return(0);
  1723. }
  1724.  
  1725.  
  1726.  
  1727.  
  1728. /* this function decides if we are going to do a
  1729.  * normal RETR or a TOP.  The first time, it also decides the total number
  1730.  * of bytes we're probably going to get.
  1731.  */
  1732. PRIVATE int 
  1733. net_pop3_get_msg(ActiveEntry *ce)
  1734. {
  1735.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1736.     char c;
  1737.     int i;
  1738.     XP_Bool prefBool = FALSE;
  1739.  
  1740.     if(cd->last_accessed_msg >= cd->number_of_messages) {
  1741.       /* Oh, gee, we're all done. */
  1742.       cd->next_state = POP3_SEND_QUIT;
  1743.       return 0;
  1744.     }
  1745.  
  1746.     if (cd->total_download_size < 0) {
  1747.       /* First time.  Figure out how many bytes we're about to get.
  1748.          If we didn't get any message info, then we are going to get
  1749.          everything, and it's easy.  Otherwise, if we only want one
  1750.          uidl, than that's the only one we'll get.  Otherwise, go
  1751.          through each message info, decide if we're going to get that
  1752.          message, and add the number of bytes for it. */
  1753.       if (cd->msg_info) {
  1754.         cd->total_download_size = 0;
  1755.         for (i=0 ; i<cd->number_of_messages ; i++) {
  1756.           c = 0;
  1757.           if (cd->only_uidl) {
  1758.             if (cd->msg_info[i].uidl && XP_STRCMP(cd->msg_info[i].uidl,
  1759.                                                   cd->only_uidl) == 0) {
  1760.               if (cd->msg_info[i].size > cd->size_limit)
  1761.                   cd->total_download_size = cd->size_limit;        /* if more than max, only count max */
  1762.               else
  1763.                   cd->total_download_size = cd->msg_info[i].size;
  1764.               break;
  1765.             }
  1766.             continue;
  1767.           }
  1768.           if (cd->msg_info[i].uidl) {
  1769.             c = (char) (int) XP_Gethash(cd->uidlinfo->hash,
  1770.                                         cd->msg_info[i].uidl, 0);
  1771.           }
  1772.           if (c != KEEP && c != DELETE_CHAR) {
  1773.             if (cd->msg_info[i].size > cd->size_limit)
  1774.                 cd->total_download_size += cd->size_limit;        /* if more than max, only count max */
  1775.             else
  1776.                 cd->total_download_size += cd->msg_info[i].size;
  1777.           }
  1778.         }
  1779.       } else {
  1780.         cd->total_download_size = cd->total_folder_size;
  1781.       }
  1782.       if (cd->only_check_for_new_mail) {
  1783.         if (cd->total_download_size > 0) cd->biffstate = MSG_BIFF_NewMail;
  1784.         cd->next_state = POP3_SEND_QUIT;
  1785.         return(0);
  1786.       }
  1787.       /* get the amount of available space on the drive
  1788.        * and make sure there is enough
  1789.        */    
  1790.  
  1791.       {
  1792.         const char* dir = MSG_GetFolderDirectory(MSG_GetPrefs(cd->pane));
  1793.  
  1794.         /* When checking for disk space available, take in consideration possible database
  1795.         changes, therefore ask for a little more than what the message size is.
  1796.         Also, due to disk sector sizes, allocation blocks, etc. The space "available" may be greater
  1797.         than the actual space usable. */
  1798.         if((cd->total_download_size > 0)
  1799.             && ((uint32)cd->total_download_size + (uint32) 3096) > FE_DiskSpaceAvailable(ce->window_id, dir))
  1800.           {
  1801.             return(net_pop3_error(ce, MK_POP3_OUT_OF_DISK_SPACE));
  1802.           }
  1803.  
  1804.       }
  1805.     }
  1806.  
  1807.  
  1808.     /* Look at this message, and decide whether to ignore it, get it, just get
  1809.        the TOP of it, or delete it. */
  1810.  
  1811.     PREF_GetBoolPref("mail.auth_login", &prefBool);
  1812.     if (prefBool && (pop3CapabilityFlags & POP3_HAS_XSENDER ||
  1813.                      pop3CapabilityFlags & POP3_XSENDER_UNDEFINED))
  1814.         cd->next_state = POP3_SEND_XSENDER;
  1815.     else
  1816.         cd->next_state = POP3_SEND_RETR;
  1817.  
  1818.     cd->pause_for_read = FALSE;
  1819.     if (cd->msg_info) {
  1820.       Pop3MsgInfo* info = cd->msg_info + 
  1821.                             cd->last_accessed_msg;
  1822.       if (cd->only_uidl) {
  1823.         if (info->uidl == NULL || XP_STRCMP(info->uidl, cd->only_uidl) != 0) {
  1824.           cd->next_state = POP3_GET_MSG;
  1825.         }
  1826.       } else {
  1827.         c = 0;
  1828.         if (info->uidl) {
  1829.           c = (char) (int) XP_Gethash(cd->uidlinfo->hash, info->uidl, 0);
  1830.         }
  1831.         if (c == DELETE_CHAR) {
  1832.           cd->next_state = POP3_SEND_DELE;
  1833.         } else if (c == KEEP) {
  1834.           cd->next_state = POP3_GET_MSG;
  1835.         } else if (cd->size_limit >= 0 &&
  1836.                    info->size > cd->size_limit &&
  1837.                    !net_top_command_unimplemented &&
  1838.                    cd->only_uidl == NULL) {
  1839.           cd->next_state = POP3_SEND_TOP;
  1840.         }
  1841.       }
  1842.       if ((cd->leave_on_server && cd->next_state != POP3_SEND_DELE) ||
  1843.           cd->next_state == POP3_GET_MSG ||
  1844.           cd->next_state == POP3_SEND_TOP) {
  1845.  
  1846.         /* This is a message we have decided to keep on the server.  Notate
  1847.            that now for the future.  (Don't change the popstate file at all
  1848.            if only_uidl is set; in that case, there might be brand new messages
  1849.            on the server that we *don't* want to mark KEEP; we just want to
  1850.            leave them around until the user next does a GetNewMail.) */
  1851.  
  1852.         if (info->uidl && cd->only_uidl == NULL) {
  1853.           if (cd->newuidl == NULL) {
  1854.             cd->newuidl = XP_HashTableNew(20, XP_StringHash, uidl_cmp);
  1855.             if (!cd->newuidl) return MK_OUT_OF_MEMORY;
  1856.           }
  1857.           put_hash(cd->uidlinfo, cd->newuidl, info->uidl, KEEP);
  1858.         }
  1859.       }
  1860.       if (cd->next_state == POP3_GET_MSG) {
  1861.         cd->last_accessed_msg++; /* Make sure we check the next message next
  1862.                                     time! */
  1863.       }
  1864.     }
  1865.     return 0;
  1866. }
  1867.  
  1868.  
  1869. /* start retreiving just the first 20 lines
  1870.  */
  1871. PRIVATE int
  1872. net_pop3_send_top(ActiveEntry *ce)
  1873. {
  1874.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1875.  
  1876.     XP_ASSERT(!(net_uidl_command_unimplemented && 
  1877.                 net_xtnd_xlst_command_unimplemented &&
  1878.                 net_top_command_unimplemented) );
  1879.     XP_ASSERT(!net_top_command_unimplemented);
  1880.  
  1881.     PR_snprintf(cd->output_buffer, 
  1882.                OUTPUT_BUFFER_SIZE,  
  1883.                "TOP %ld 20" CRLF,
  1884.                cd->last_accessed_msg+1);
  1885.  
  1886.     cd->truncating_cur_msg = TRUE;
  1887.     cd->next_state_after_response = POP3_TOP_RESPONSE;
  1888.  
  1889.     cd->cur_msg_size = -1;
  1890.  
  1891.     /* zero the bytes received in message in preparation for
  1892.      * the next
  1893.      */
  1894.     cd->bytes_received_in_message = 0;
  1895.  
  1896.     return(net_pop3_send_command(ce, cd->output_buffer));
  1897. }
  1898.  
  1899. /* send the xsender command
  1900.  */
  1901. PRIVATE int
  1902. net_pop3_send_xsender(ActiveEntry *ce)
  1903. {
  1904.   Pop3ConData * cd = (Pop3ConData *) ce->con_data;
  1905.   
  1906.   PR_snprintf(cd->output_buffer,
  1907.               OUTPUT_BUFFER_SIZE,
  1908.               "XSENDER %ld" CRLF,
  1909.               cd->last_accessed_msg+1);
  1910.  
  1911.   cd->next_state_after_response = POP3_XSENDER_RESPONSE;
  1912.   return net_pop3_send_command(ce, cd->output_buffer);
  1913. }
  1914.  
  1915. PRIVATE int
  1916. net_pop3_xsender_response(ActiveEntry *ce)
  1917. {
  1918.   Pop3ConData * cd = (Pop3ConData *) ce->con_data;
  1919.  
  1920.   cd->seenFromHeader = FALSE;
  1921.   FREEIF(cd->sender_info);
  1922.  
  1923.   if (POP3_XSENDER_UNDEFINED & pop3CapabilityFlags)
  1924.       pop3CapabilityFlags &= ~POP3_XSENDER_UNDEFINED;
  1925.  
  1926.   if (cd->command_succeeded) {
  1927.       if (XP_STRLEN (cd->command_response) > 4)
  1928.       {
  1929.           StrAllocCopy(cd->sender_info, cd->command_response);
  1930.       }
  1931.       if (! (POP3_HAS_XSENDER & pop3CapabilityFlags))
  1932.           pop3CapabilityFlags |= POP3_HAS_XSENDER;
  1933.   }
  1934.   else {
  1935.       pop3CapabilityFlags &= ~POP3_HAS_XSENDER;
  1936.   }
  1937.   
  1938.   cd->next_state = POP3_SEND_RETR;
  1939.   return 0;
  1940. }
  1941.  
  1942. /* retreive the whole message
  1943.  */
  1944. PRIVATE int
  1945. net_pop3_send_retr(ActiveEntry *ce)
  1946. {
  1947.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  1948.     char buf[OUTPUT_BUFFER_SIZE];
  1949.  
  1950.     cd->truncating_cur_msg = FALSE;
  1951.     PR_snprintf(cd->output_buffer, 
  1952.                OUTPUT_BUFFER_SIZE,  
  1953.                "RETR %ld" CRLF,
  1954.                cd->last_accessed_msg+1);
  1955.  
  1956.     cd->next_state_after_response = POP3_RETR_RESPONSE;
  1957.  
  1958.     cd->cur_msg_size = -1;
  1959.  
  1960.     /* zero the bytes received in message in preparation for
  1961.      * the next
  1962.      */
  1963.     cd->bytes_received_in_message = 0;
  1964.  
  1965.     if (cd->only_uidl)
  1966.       {
  1967.         /* Display bytes if we're only downloading one message. */
  1968.         XP_ASSERT(!cd->graph_progress_bytes_p);
  1969.         if (!cd->graph_progress_bytes_p)
  1970.           FE_GraphProgressInit(ce->window_id, ce->URL_s,
  1971.                                cd->total_download_size);
  1972.         cd->graph_progress_bytes_p = TRUE;
  1973.       }
  1974.     else
  1975.       {
  1976.         PR_snprintf(buf, OUTPUT_BUFFER_SIZE,
  1977.         XP_GetString( XP_RECEIVING_MESSAGE_OF ),
  1978.                     cd->last_accessed_msg+1,
  1979.                     cd->number_of_messages);
  1980.         NET_Progress(ce->window_id, buf);
  1981.       }
  1982.  
  1983.     return(net_pop3_send_command(ce, cd->output_buffer));
  1984. }
  1985.  
  1986. /* #### should include msgutils.h instead... */
  1987. extern int msg_LineBuffer (const char *net_buffer, int32 net_buffer_size,
  1988.                            char **bufferP, uint32 *buffer_sizeP,
  1989.                            uint32 *buffer_fpP,
  1990.                            XP_Bool convert_newlines_p,
  1991.                            int32 (*per_line_fn) (char *line, uint32
  1992.                                                  line_length, void *closure),
  1993.                            void *closure);
  1994.  
  1995.  
  1996. PRIVATE int32 net_pop3_retr_handle_line(char *line, uint32 line_length,
  1997.                                         void *closure);
  1998.  
  1999. /* digest the message
  2000.  */
  2001. PRIVATE int
  2002. net_pop3_retr_response(ActiveEntry *ce)
  2003. {
  2004.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2005.     char *buffer;
  2006.     int32 buffer_size;
  2007.     int32 old_bytes_received = ce->bytes_received;
  2008.  
  2009.     if(cd->cur_msg_size == -1)
  2010.       {
  2011.         /* this is the beginning of a message
  2012.          * get the response code and byte size
  2013.          */
  2014.         if(!cd->command_succeeded)
  2015.           {
  2016.             return net_pop3_error(ce, MK_POP3_RETR_FAILURE);
  2017.           }
  2018.  
  2019.         /* a successful retr response looks like: #num_bytes Junk
  2020.          */
  2021.         cd->cur_msg_size = atol(XP_STRTOK(cd->command_response, " "));
  2022.  
  2023.         if(cd->cur_msg_size < 0)
  2024.             cd->cur_msg_size = 0;
  2025.  
  2026.         TRACEMSG(("Opening message stream: MSG_IncorporateBegin"));
  2027.         /* open the message stream so we have someplace
  2028.          * to put the data
  2029.          */
  2030.         cd->msg_closure =
  2031.           MSG_IncorporateBegin (cd->pane,
  2032.                                 ce->format_out,
  2033.                                 (cd->truncating_cur_msg ?
  2034.                                  cd->msg_info[cd->last_accessed_msg].uidl :
  2035.                                  NULL),
  2036.                                 ce->URL_s,
  2037.                                 (cd->sender_info ? 
  2038.                                  MSG_FLAG_SENDER_AUTHED : 0x0));
  2039.  
  2040.         TRACEMSG(("Done opening message stream!"));
  2041.                                                     
  2042.         if(!cd->msg_closure)
  2043.           {
  2044.             return(net_pop3_error(ce, MK_POP3_MESSAGE_WRITE_ERROR));
  2045.           }
  2046.       }
  2047.  
  2048.     if (cd->data_buffer_size > 0)
  2049.       {
  2050.         XP_ASSERT(cd->obuffer_fp == 0); /* must be the first time */
  2051.         buffer = cd->data_buffer;
  2052.         buffer_size = cd->data_buffer_size;
  2053.         cd->data_buffer_size = 0;
  2054.       }
  2055.     else
  2056.       {
  2057.         ce->status = PR_Read(ce->socket, 
  2058.                              NET_Socket_Buffer, 
  2059.                              NET_Socket_Buffer_Size);
  2060.         buffer = NET_Socket_Buffer;
  2061.         buffer_size = ce->status;
  2062.  
  2063.         cd->pause_for_read = TRUE;
  2064.  
  2065.         if(ce->status == 0)
  2066.           {
  2067.             /* this shouldn't happen
  2068.              */
  2069.             return(net_pop3_error(ce, MK_POP3_SERVER_ERROR));
  2070.           }
  2071.         else if(ce->status < 0) /* error */
  2072.           {
  2073.             int err = PR_GetError();
  2074.     
  2075.             TRACEMSG(("TCP Error: %d", err));
  2076.     
  2077.             if (err == PR_WOULD_BLOCK_ERROR)
  2078.               {
  2079.                 cd->pause_for_read = TRUE;
  2080.                 return (0);
  2081.               }
  2082.     
  2083.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, err);
  2084.     
  2085.             /* return TCP error
  2086.              */
  2087.             return MK_TCP_READ_ERROR;
  2088.           }
  2089.       }
  2090.  
  2091.     ce->status = msg_LineBuffer(buffer, buffer_size,
  2092.                                 &cd->obuffer, &cd->obuffer_size,
  2093.                                 &cd->obuffer_fp, FALSE,
  2094.                                 net_pop3_retr_handle_line, (void *) ce);
  2095.     if (ce->status < 0)
  2096.       {
  2097.         if (cd->msg_closure) {
  2098.           MSG_IncorporateAbort(cd->pane, cd->msg_closure,
  2099.                                MK_POP3_MESSAGE_WRITE_ERROR);
  2100.           cd->msg_closure = NULL;
  2101.         }
  2102.         MSG_AbortMailDelivery(cd->pane);
  2103.         return(net_pop3_error(ce, MK_POP3_MESSAGE_WRITE_ERROR));
  2104.       }
  2105.  
  2106.     /* normal read. Yay! */
  2107.     if (cd->bytes_received_in_message + buffer_size > cd->cur_msg_size) {
  2108.       buffer_size = cd->cur_msg_size -  cd->bytes_received_in_message;
  2109.     }
  2110.     cd->bytes_received_in_message += buffer_size;
  2111.     ce->bytes_received            += buffer_size;
  2112.  
  2113.     if (!cd->msg_closure) /* meaning _handle_line read ".\r\n" at end-of-msg */
  2114.       {
  2115.         cd->pause_for_read = FALSE;
  2116.         if (cd->truncating_cur_msg ||
  2117.             (cd->leave_on_server && !(net_uidl_command_unimplemented &&
  2118.                                       net_xtnd_xlst_command_unimplemented &&
  2119.                                            net_top_command_unimplemented) )) {
  2120.           /* We've retreived all or part of this message, but we want to
  2121.              keep it on the server.  Go on to the next message. */
  2122.           cd->last_accessed_msg++;
  2123.           cd->next_state = POP3_GET_MSG;
  2124.         } else {
  2125.           cd->next_state = POP3_SEND_DELE;
  2126.         }
  2127.  
  2128.         /* if we didn't get the whole message add the bytes that we didn't get
  2129.            to the bytes received part so that the progress percent stays sane.
  2130.          */
  2131.         if(cd->bytes_received_in_message < cd->cur_msg_size)
  2132.           ce->bytes_received += (cd->cur_msg_size
  2133.                                  - cd->bytes_received_in_message);
  2134.       }
  2135.  
  2136.     if (cd->graph_progress_bytes_p)
  2137.       FE_GraphProgress(ce->window_id, ce->URL_s,
  2138.                        ce->bytes_received,
  2139.                        ce->bytes_received - old_bytes_received,
  2140.                        cd->cur_msg_size);
  2141.  
  2142.     /* set percent done to portion of total bytes of all messages
  2143.        that we're going to download. */
  2144.     FE_SetProgressBarPercent(ce->window_id,
  2145.                              ((ce->bytes_received*100)
  2146.                               / cd->total_download_size));
  2147.  
  2148.     return(0);
  2149. }
  2150.  
  2151.  
  2152. PRIVATE int
  2153. net_pop3_top_response(ActiveEntry *ce)
  2154. {
  2155.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2156.   if(cd->cur_msg_size == -1 &&  /* first line after TOP command sent */
  2157.      !cd->command_succeeded)    /* and TOP command failed */
  2158.     {
  2159.       /* TOP doesn't work so we can't retrieve the first part of this msg.
  2160.          So just go download the whole thing, and warn the user.
  2161.  
  2162.          Note that the progress bar will not be accurate in this case.
  2163.          Oops. #### */
  2164.       char *host, *fmt, *buf;
  2165.       int size;
  2166.       XP_Bool prefBool = FALSE;
  2167.  
  2168.       net_top_command_unimplemented = TRUE;
  2169.       cd->truncating_cur_msg = FALSE;
  2170.  
  2171.       fmt = XP_GetString( XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_THE_TOP_COMMAND );
  2172.  
  2173.       host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  2174.       size = XP_STRLEN(fmt) + XP_STRLEN(host ? host : "(null)") + 100;
  2175.       buf = (char *) XP_ALLOC (size);
  2176.       if (!buf) {
  2177.           FREEIF(host);
  2178.           return MK_OUT_OF_MEMORY;
  2179.       }
  2180.       PR_snprintf (buf, size, fmt, host ? host : "(null)");
  2181.       FE_Alert (ce->window_id, buf);
  2182.       XP_FREE (buf);
  2183.       FREEIF(host);
  2184.  
  2185.       PREF_GetBoolPref ("mail.auth_login", &prefBool);
  2186.       if (prefBool && (POP3_XSENDER_UNDEFINED & pop3CapabilityFlags ||
  2187.                        POP3_HAS_XSENDER & pop3CapabilityFlags))
  2188.           cd->next_state = POP3_SEND_XSENDER;
  2189.       else
  2190.           cd->next_state = POP3_SEND_RETR;
  2191.       return(0);
  2192.     }
  2193.  
  2194.   /* If TOP works, we handle it in the same way as RETR. */
  2195.   return net_pop3_retr_response(ce);
  2196. }
  2197.  
  2198.  
  2199.  
  2200. PRIVATE int32
  2201. net_pop3_retr_handle_line(char *line, uint32 line_length, void *closure)
  2202. {
  2203.   ActiveEntry *ce = (ActiveEntry *) closure;
  2204.   Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2205.   int status;
  2206.  
  2207.   XP_ASSERT(cd->msg_closure);
  2208.   if (!cd->msg_closure) return -1;
  2209.  
  2210.   if (cd->sender_info && !cd->seenFromHeader)
  2211.   {
  2212.       if (line_length > 6 && !XP_MEMCMP("From: ", line, 6))
  2213.       {
  2214.           /* Zzzzz XP_STRSTR only works with NULL terminated string. Since,
  2215.            * the last character of a line is either a carriage return
  2216.            * or a linefeed. Temporary setting the last character of the
  2217.            * line to NULL and later setting it back should be the right 
  2218.            * thing to do. 
  2219.            */
  2220.           char ch = line[line_length-1];
  2221.           line[line_length-1] = 0;
  2222.           cd->seenFromHeader = TRUE;
  2223.           if (XP_STRSTR(line, cd->sender_info) == NULL)
  2224.               MSG_ClearSenderAuthedFlag(cd->pane, cd->msg_closure);
  2225.           line[line_length-1] = ch;
  2226.       }
  2227.   }
  2228.  
  2229.   status = MSG_IncorporateWrite(cd->pane, cd->msg_closure, line, line_length);
  2230.  
  2231.   if (status >= 0 &&
  2232.       line[0] == '.' &&
  2233.       (line[1] == CR || line[1] == LF)) {
  2234.     status = MSG_IncorporateComplete(cd->pane, cd->msg_closure);
  2235.     cd->msg_closure = 0;
  2236.   }
  2237.  
  2238.   return status;
  2239. }
  2240.  
  2241.  
  2242. PRIVATE int
  2243. net_pop3_send_dele(ActiveEntry *ce)
  2244. {
  2245.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2246.  
  2247.     /* increment the last accessed message since we have now read it
  2248.      */
  2249.     cd->last_accessed_msg++;
  2250.  
  2251.     PR_snprintf(cd->output_buffer, 
  2252.                 OUTPUT_BUFFER_SIZE,  
  2253.                 "DELE %ld" CRLF, 
  2254.                 cd->last_accessed_msg);
  2255.     
  2256.     cd->next_state_after_response = POP3_DELE_RESPONSE;
  2257.     
  2258.     return(net_pop3_send_command(ce, cd->output_buffer));
  2259. }
  2260.  
  2261. PRIVATE int
  2262. net_pop3_dele_response(ActiveEntry *ce)
  2263. {
  2264.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2265. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  2266.     Pop3UidlHost *host = cd->uidlinfo;
  2267. #endif
  2268.  
  2269.     /* the return from the delete will come here
  2270.      */
  2271.     if(!cd->command_succeeded)
  2272.         return(net_pop3_error(ce, MK_POP3_DELE_FAILURE));
  2273.  
  2274.  
  2275. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  2276.     /*
  2277.     the delete succeeded.  Write out state so that we
  2278.     keep track of all the deletes which have not yet been
  2279.     committed on the server.  Flush this state upon successful
  2280.     QUIT.
  2281.     
  2282.     We will do this by adding each successfully deleted message id
  2283.     to a list which we will write out to popstate.dat in 
  2284.     net_pop3_write_state().
  2285.     */
  2286.     if (host)
  2287.     {
  2288.         if (cd->msg_info && cd->msg_info[cd->last_accessed_msg-1].uidl)
  2289.             put_hash(host, host->hash, cd->msg_info[cd->last_accessed_msg-1].uidl, DELETE_CHAR);
  2290.     }
  2291. #endif
  2292.  
  2293.     cd->next_state = POP3_GET_MSG;
  2294.  
  2295.     cd->pause_for_read = FALSE;
  2296.  
  2297.     return(0);
  2298. }
  2299.  
  2300.  
  2301. PRIVATE int
  2302. net_pop3_commit_state(ActiveEntry *ce, XP_Bool remove_last_entry)
  2303. {
  2304.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2305.     
  2306.     if (remove_last_entry)
  2307.     {
  2308.         /* now, if we are leaving messages on the server, pull out the last
  2309.            uidl from the hash, because it might have been put in there before
  2310.            we got it into the database. */
  2311.         if (cd->msg_info) {
  2312.             Pop3MsgInfo* info = cd->msg_info + cd->last_accessed_msg;
  2313.             if (info && info->uidl && (cd->only_uidl == NULL) && cd->newuidl) {
  2314.                 XP_Bool val = XP_Remhash (cd->newuidl, info->uidl);
  2315.                 XP_ASSERT(val);
  2316.             }
  2317.         }
  2318.     }
  2319.  
  2320.     if (cd->newuidl) {
  2321.         XP_HashTableDestroy(cd->uidlinfo->hash);
  2322.         cd->uidlinfo->hash = cd->newuidl;
  2323.         cd->newuidl = NULL;
  2324.     }
  2325.     if (!cd->only_check_for_new_mail) {
  2326.         net_pop3_write_state(cd->uidlinfo);
  2327.     }
  2328.     return 0;
  2329. }
  2330.  
  2331.  
  2332. /* start a pop3 load
  2333.  */
  2334. PRIVATE int32
  2335. net_Pop3Load (ActiveEntry * ce)
  2336. {
  2337.     Pop3ConData * cd = XP_NEW(Pop3ConData);
  2338.     char* host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  2339.     char* uidl;
  2340.  
  2341.     if(!cd || !host || !ce->URL_s->internal_url) {
  2342.         FREEIF(cd);
  2343.         FREEIF(host);
  2344.         return(MK_OUT_OF_MEMORY);
  2345.     }
  2346.  
  2347.     XP_MEMSET(cd, 0, sizeof(Pop3ConData));
  2348.  
  2349.     if(!net_pop3_username || !*net_pop3_username)
  2350.       {
  2351.         FREE(cd);
  2352.         FREEIF(host);
  2353.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  2354.                                                 MK_POP3_USERNAME_UNDEFINED);
  2355.         ce->status = MK_POP3_USERNAME_UNDEFINED;
  2356. #ifdef XP_MAC
  2357.         FE_Alert(ce->window_id, ce->URL_s->error_msg); /* BEFORE going to the prefs window */
  2358.         FE_EditPreference(PREF_Pop3ID);
  2359. #endif
  2360.         return(MK_POP3_USERNAME_UNDEFINED);
  2361.       }
  2362.  
  2363.     if(strcasestr(ce->URL_s->address, "?check")) {
  2364.  
  2365.        if (!net_pop3_block_biff)
  2366.          cd->only_check_for_new_mail = TRUE;
  2367.        else
  2368.        {
  2369.          /* don't allow a biff to open a second connection to the 
  2370.           * server because some servers will close the first connection
  2371.           */
  2372.          XP_Trace("blocking biff due to avoid opening a second connection");
  2373.          FREEIF(cd);
  2374.          FREEIF(host);
  2375.          return -1;
  2376.        }
  2377.     }
  2378.  
  2379.     if(strcasestr(ce->URL_s->address, "?gurl")) {
  2380.         cd->get_url = TRUE;
  2381.     }
  2382.  
  2383.     if (!cd->only_check_for_new_mail) {
  2384.       XP_Bool tmp = FALSE;
  2385.       cd->pane = ce->URL_s->msg_pane;
  2386.       if (!cd->pane)
  2387.       {
  2388. #ifdef DEBUG_phil
  2389.         XP_Trace ("NET_Pop3Load: url->msg_pane NULL for URL: %s\n", ce->URL_s->address);
  2390. #endif
  2391.         cd->pane = MSG_FindPane(ce->window_id, MSG_FOLDERPANE); /* ###tw */
  2392.       }
  2393.       XP_ASSERT(cd->pane);
  2394.       if (cd->pane == NULL) return -1; /* ### */
  2395.  
  2396.       PREF_GetBoolPref("mail.leave_on_server", &(cd->leave_on_server));
  2397.       PREF_GetBoolPref("mail.limit_message_size", &tmp);
  2398.       if (tmp) {
  2399.         PREF_GetIntPref("mail.max_size", &(cd->size_limit));
  2400.         cd->size_limit *= 1024;
  2401.       } else {
  2402.         cd->size_limit = -1;
  2403.       }
  2404.     }
  2405.  
  2406.     cd->uidlinfo = net_pop3_load_state(host, net_pop3_username);
  2407.     XP_FREE(host);
  2408.     cd->output_buffer = (char*)XP_ALLOC(OUTPUT_BUFFER_SIZE);
  2409.     if(!cd->uidlinfo || !cd->output_buffer) goto FAIL;
  2410.  
  2411.  
  2412.     ce->con_data = cd;
  2413.  
  2414.     cd->biffstate = MSG_BIFF_NoMail;    /* Return "no mail" unless proven
  2415.                                            otherwise. */
  2416.  
  2417.     uidl = strcasestr(ce->URL_s->address, "?uidl=");
  2418.     if (uidl) {
  2419.       uidl += 6;
  2420.       cd->only_uidl = XP_STRDUP(uidl);
  2421.       if (!cd->only_uidl) goto FAIL;
  2422.     }
  2423.  
  2424.     ce->socket = NULL;
  2425.  
  2426.     cd->next_state = POP3_READ_PASSWORD;
  2427.  
  2428.     /* acquire the semaphore which prevents biff from interrupting connections */
  2429.     net_pop3_block_biff = TRUE;
  2430.  
  2431.     return (net_ProcessPop3(ce));
  2432.  
  2433. FAIL:
  2434.     if (cd->uidlinfo) net_pop3_free_state(cd->uidlinfo);
  2435.     if (cd->output_buffer) XP_FREE(cd->output_buffer);
  2436.     FREE(cd);
  2437.     return(MK_OUT_OF_MEMORY);
  2438. }
  2439.  
  2440. /* NET_process_Pop3  will control the state machine that
  2441.  * loads messages from a pop3 server
  2442.  *
  2443.  * returns negative if the transfer is finished or error'd out
  2444.  *
  2445.  * returns zero or more if the transfer needs to be continued.
  2446.  */
  2447. PRIVATE int32
  2448. net_ProcessPop3 (ActiveEntry *ce)
  2449. {
  2450.  
  2451.    Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  2452.    int oldStatus = 0;
  2453.  
  2454.     TRACEMSG(("Entering NET_ProcessPop3"));
  2455.  
  2456.     cd->pause_for_read = FALSE; /* already paused; reset */
  2457.  
  2458.     if(!net_pop3_username || !*net_pop3_username)
  2459.       return(net_pop3_error(ce, MK_POP3_USERNAME_UNDEFINED));
  2460.  
  2461.     while(!cd->pause_for_read)
  2462.       {
  2463.  
  2464.         TRACEMSG(("POP3: Entering state: %d", cd->next_state));
  2465.  
  2466.         switch(cd->next_state)
  2467.           {
  2468.             case POP3_READ_PASSWORD:
  2469.               /* This is a seperate state so that we're waiting for the
  2470.                  user to type in a password while we don't actually have
  2471.                  a connection to the pop server open; this saves us from
  2472.                  having to worry about the server timing out on us while
  2473.                  we wait for user input. */
  2474.               {
  2475.                 char *fmt1 = 0, *fmt2 = 0;
  2476.  
  2477.                 /* If we're just checking for new mail (biff) then don't
  2478.                    prompt the user for a password; just tell him we don't
  2479.                    know whether he has new mail. */
  2480.                 if (cd->only_check_for_new_mail &&
  2481.                     (!net_pop3_password || !net_pop3_username))
  2482.                   {
  2483.                     ce->status = MK_POP3_PASSWORD_UNDEFINED;
  2484.                     cd->biffstate = MSG_BIFF_Unknown;
  2485.                     MSG_SetBiffStateAndUpdateFE(cd->biffstate);
  2486.                     cd->next_state = POP3_FREE;
  2487.                     cd->pause_for_read = FALSE;
  2488.                     break;
  2489.                   }
  2490.  
  2491.                 XP_ASSERT(net_pop3_username);
  2492.  
  2493.                 if (cd->password_failed)
  2494.                   fmt2 =
  2495.                   XP_GetString( XP_THE_PREVIOUSLY_ENTERED_PASSWORD_IS_INVALID_ETC );
  2496.                 else if (!net_pop3_password)
  2497.                   fmt1 = 
  2498.                    XP_GetString( XP_PASSWORD_FOR_POP3_USER );
  2499.  
  2500.                 if (fmt1 || fmt2)    /* We need to read a password. */
  2501.                   {
  2502.                     char *password;
  2503.                     char *host = NET_ParseURL(ce->URL_s->address,
  2504.                                               GET_HOST_PART);
  2505.                     size_t len = (XP_STRLEN(fmt1 ? fmt1 : fmt2) +
  2506.                                  (host ? XP_STRLEN(host) : 0) + 300) 
  2507.                                  * sizeof(char);
  2508.                     char *prompt = (char *) XP_ALLOC (len);
  2509.                     if (!prompt) {
  2510.                         FREEIF(host);
  2511.                         return MK_OUT_OF_MEMORY;
  2512.                     }
  2513.                     if (fmt1)
  2514.                       PR_snprintf (prompt, len, fmt1, net_pop3_username, host);
  2515.                     else
  2516.                       PR_snprintf (prompt, len, fmt2,
  2517.                                    (cd->command_response
  2518.                                     ? cd->command_response
  2519.                                     : XP_GetString(XP_NO_ANSWER)),
  2520.                                    net_pop3_username, host);
  2521.                     FREEIF (host);
  2522.  
  2523.                     cd->password_failed = FALSE;
  2524.                     password = FE_PromptPassword(ce->window_id, prompt);
  2525.                     XP_FREE(prompt);
  2526.  
  2527.                     if (password == NULL)
  2528.                       return MK_POP3_PASSWORD_UNDEFINED;
  2529.  
  2530.                     net_set_pop3_password(password);
  2531.                     XP_MEMSET(password, 0, XP_STRLEN(password));
  2532.                     XP_FREE(password);
  2533.  
  2534.                     net_pop3_password_pending = TRUE;
  2535.                   }
  2536.  
  2537.                 XP_ASSERT (net_pop3_username && net_pop3_password);
  2538.                 if (!net_pop3_username || !net_pop3_password)
  2539.                   return -1;
  2540.  
  2541.                 cd->next_state = POP3_START_CONNECT;
  2542.                 cd->pause_for_read = FALSE;
  2543.                 break;
  2544.               }
  2545.  
  2546.             case POP3_START_CONNECT:
  2547.  
  2548.                 /* Start the thermometer (in cylon mode.) */
  2549.                 FE_SetProgressBarPercent(ce->window_id, -1);
  2550.  
  2551.                 ce->status = NET_BeginConnect(ce->URL_s->address,
  2552.                                             ce->URL_s->IPAddressString,
  2553.                                              "POP3",
  2554.                                              POP3_PORT,
  2555.                                              &ce->socket,
  2556.                                              FALSE,
  2557.                                              &cd->tcp_con_data,
  2558.                                              ce->window_id,
  2559.                                              &ce->URL_s->error_msg,
  2560.                                              ce->socks_host,
  2561.                                              ce->socks_port);
  2562.     
  2563.                 if(ce->socket != NULL)
  2564.                     NET_TotalNumberOfOpenConnections++;
  2565.    
  2566.                 cd->pause_for_read = TRUE;
  2567.    
  2568.                 if(ce->status == MK_CONNECTED)
  2569.                   {
  2570.                     XP_Bool prefBool = FALSE;
  2571.  
  2572.                     cd->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
  2573.                     
  2574.                     /* cd->next_state_after_response = POP3_SEND_USERNAME; */
  2575.                     PREF_GetBoolPref ("mail.auth_login", &prefBool);
  2576.                     
  2577.                     if (prefBool) {
  2578.                         if (pop3CapabilityFlags & POP3_AUTH_LOGIN_UNDEFINED)
  2579.                             cd->next_state_after_response = POP3_SEND_AUTH;
  2580.                         else if (pop3CapabilityFlags & POP3_HAS_AUTH_LOGIN)
  2581.                             cd->next_state_after_response = POP3_AUTH_LOGIN;
  2582.                         else
  2583.                             cd->next_state_after_response = POP3_SEND_USERNAME;
  2584.                     }
  2585.                     else
  2586.                         cd->next_state_after_response = POP3_SEND_USERNAME;
  2587.  
  2588.                     NET_SetReadSelect(ce->window_id, ce->socket);
  2589. /*DSR112096 - serious thrashing getting mail by modem if this guy isn't reset properly*/
  2590. /*see defect 19736 for more info. I suspect it is a general defect in the backend.    */
  2591. #ifdef XP_OS2_FIX
  2592.                     ce->con_sock = NULL;  /* set con sock so we can select on it */
  2593. #endif
  2594.                   }
  2595.                 else if(ce->status > -1)
  2596.                   {
  2597.                     cd->next_state = POP3_FINISH_CONNECT;
  2598.                     ce->con_sock = ce->socket;  /* set con sock so we can select on it */
  2599.                     NET_SetConnectSelect(ce->window_id, ce->con_sock);
  2600.                   }
  2601.                 else if(ce->status < 0)
  2602.                   {
  2603.                     /* close and clear the socket here
  2604.                      * so that we don't try and send a RSET
  2605.                      */
  2606.                     if(ce->socket != NULL)
  2607.                       {
  2608.                         NET_TotalNumberOfOpenConnections--;
  2609.                         NET_ClearConnectSelect(ce->window_id, ce->socket);
  2610.                         TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket));
  2611.                         PR_Close(ce->socket);
  2612.                         ce->socket = NULL;
  2613.                         cd->next_state = POP3_ERROR_DONE;
  2614.                       }
  2615.                   }
  2616.                 break;
  2617.  
  2618.           case POP3_FINISH_CONNECT:
  2619.                 ce->status = NET_FinishConnect(ce->URL_s->address,
  2620.                                               "POP3",
  2621.                                               POP3_PORT,
  2622.                                               &ce->socket,
  2623.                                               &cd->tcp_con_data,
  2624.                                               ce->window_id,
  2625.                                               &ce->URL_s->error_msg);
  2626.  
  2627.                 cd->pause_for_read = TRUE;
  2628.  
  2629.                 if(ce->status == MK_CONNECTED)
  2630.                   {
  2631.                     XP_Bool prefBool = FALSE;
  2632.  
  2633.                     cd->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
  2634.                     PREF_GetBoolPref ("mail.auth_login", &prefBool);
  2635.                     if (prefBool) {
  2636.                         if (pop3CapabilityFlags & POP3_AUTH_LOGIN_UNDEFINED)
  2637.                             cd->next_state_after_response = POP3_SEND_AUTH;
  2638.                         else if (pop3CapabilityFlags & POP3_HAS_AUTH_LOGIN)
  2639.                             cd->next_state_after_response = POP3_AUTH_LOGIN;
  2640.                         else
  2641.                             cd->next_state_after_response = POP3_SEND_USERNAME;
  2642.                     }
  2643.                     else
  2644.                         cd->next_state_after_response = POP3_SEND_USERNAME;
  2645.                     NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  2646.                     NET_SetReadSelect(ce->window_id, ce->socket);
  2647.                     ce->con_sock = NULL;  /* set con sock so we can select on it */
  2648.                   }
  2649.                 else if(ce->status > -1)
  2650.                   {
  2651.  
  2652.                     /* unregister the old CE_SOCK from the select list
  2653.                       * and register the new value in the case that it changes
  2654.                       */
  2655.                     if(ce->con_sock != ce->socket)
  2656.                         {
  2657.                         NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  2658.                         ce->con_sock = ce->socket;
  2659.                         NET_SetConnectSelect(ce->window_id, ce->con_sock);
  2660.                         }
  2661.                   }
  2662.                 else if(ce->status < 0)
  2663.                   {
  2664.                     /* close and clear the socket here
  2665.                      * so that we don't try and send a RSET
  2666.                      */
  2667.                     NET_TotalNumberOfOpenConnections--;
  2668.                     NET_ClearConnectSelect(ce->window_id, ce->socket);
  2669.                     TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket));
  2670.                     PR_Close(ce->socket);
  2671.                     ce->socket = NULL;
  2672.                     cd->next_state = POP3_ERROR_DONE;
  2673.                   }
  2674.                 break;
  2675.  
  2676.             case POP3_WAIT_FOR_RESPONSE:
  2677.                 ce->status = net_pop3_wait_for_response(ce);
  2678.                 break;
  2679.                 
  2680.             case POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE:
  2681.                 net_pop3_wait_for_start_of_connection_response(ce);
  2682.                 break;
  2683.  
  2684.             case POP3_SEND_AUTH:
  2685.                 ce->status = net_pop3_send_auth(ce);
  2686.                 break;
  2687.  
  2688.             case POP3_AUTH_RESPONSE:
  2689.                 ce->status = net_pop3_auth_response(ce);
  2690.                 break;
  2691.  
  2692.             case POP3_AUTH_LOGIN:
  2693.                 ce->status = net_pop3_auth_login(ce);
  2694.                 break;
  2695.  
  2696.             case POP3_AUTH_LOGIN_RESPONSE:
  2697.                 ce->status = net_pop3_auth_login_response(ce);
  2698.                 break;
  2699.  
  2700.             case POP3_SEND_USERNAME:
  2701.                 NET_Progress(ce->window_id,
  2702.                 XP_GetString(XP_CONNECT_HOST_CONTACTED_SENDING_LOGIN_INFORMATION) );
  2703.                 ce->status = net_pop3_send_username(ce);
  2704.                 break;
  2705.  
  2706.             case POP3_SEND_PASSWORD:
  2707.                 ce->status = net_pop3_send_password(ce);
  2708.                 break;
  2709.  
  2710.             case POP3_SEND_GURL:
  2711.                 ce->status = net_pop3_send_gurl(ce);
  2712.                 break;
  2713.  
  2714.             case POP3_GURL_RESPONSE:
  2715.                 ce->status = net_pop3_gurl_response(ce);
  2716.                 break;
  2717.  
  2718.             case POP3_SEND_STAT:
  2719.                 ce->status = net_pop3_send_stat(ce);
  2720.                 break;
  2721.  
  2722.             case POP3_GET_STAT:
  2723.                 ce->status = net_pop3_get_stat(ce);
  2724.                 break;
  2725.  
  2726.             case POP3_SEND_LIST:
  2727.                 ce->status = net_pop3_send_list(ce);
  2728.                 break;
  2729.  
  2730.             case POP3_GET_LIST:
  2731.                 ce->status = net_pop3_get_list(ce);
  2732.                 break;
  2733.  
  2734.             case POP3_SEND_UIDL_LIST:
  2735.                 ce->status = net_pop3_send_uidl_list(ce);
  2736.                 break;
  2737.  
  2738.             case POP3_GET_UIDL_LIST:
  2739.                 ce->status = net_pop3_get_uidl_list(ce);
  2740.                 break;
  2741.                 
  2742.             case POP3_SEND_XTND_XLST_MSGID:
  2743.                 ce->status = net_pop3_send_xtnd_xlst_msgid(ce);
  2744.                 break;
  2745.  
  2746.             case POP3_GET_XTND_XLST_MSGID:
  2747.                 ce->status = net_pop3_get_xtnd_xlst_msgid(ce);
  2748.                 break;
  2749.             
  2750.             case POP3_START_USE_TOP_FOR_FAKE_UIDL:
  2751.                 ce->status = start_use_top_for_fake_uidl(ce);
  2752.                 break;
  2753.  
  2754.             case POP3_SEND_FAKE_UIDL_TOP:
  2755.                 ce->status = send_fake_uidl_top(ce);
  2756.                 break;
  2757.                 
  2758.             case POP3_GET_FAKE_UIDL_TOP:
  2759.                 ce->status = get_fake_uidl_top(ce);
  2760.                 break;
  2761.     
  2762.             case POP3_GET_MSG:
  2763.                 ce->status = net_pop3_get_msg(ce);
  2764.                 break;
  2765.  
  2766.             case POP3_SEND_TOP:
  2767.                 ce->status = net_pop3_send_top(ce);
  2768.                 break;
  2769.  
  2770.             case POP3_TOP_RESPONSE:
  2771.                 ce->status = net_pop3_top_response(ce);
  2772.                 break;
  2773.  
  2774.             case POP3_SEND_XSENDER:
  2775.                 ce->status = net_pop3_send_xsender(ce);
  2776.                 break;
  2777.  
  2778.             case POP3_XSENDER_RESPONSE:
  2779.                 ce->status = net_pop3_xsender_response(ce);
  2780.                 break;
  2781.  
  2782.             case POP3_SEND_RETR:
  2783.                 ce->status = net_pop3_send_retr(ce);
  2784.                 break;
  2785.  
  2786.             case POP3_RETR_RESPONSE:
  2787.                 ce->status = net_pop3_retr_response(ce);
  2788.                 break;
  2789.  
  2790.             case POP3_SEND_DELE:
  2791.                 ce->status = net_pop3_send_dele(ce);
  2792.                 break;
  2793.  
  2794.             case POP3_DELE_RESPONSE:
  2795.                 ce->status = net_pop3_dele_response(ce);
  2796.                 break;
  2797.  
  2798.             case POP3_SEND_QUIT:
  2799.                 /* attempt to send a server quit command.  Since this means
  2800.                    everything went well, this is a good time to update the
  2801.                    status file and the FE's biff state.
  2802.                  */
  2803.                 if (!cd->only_uidl) {
  2804.                   MSG_SetBiffStateAndUpdateFE(cd->biffstate);
  2805.                   if (!cd->only_check_for_new_mail) {
  2806.                     /* We don't want to pop up a warning message any more (see bug 54116),
  2807.                        so instead we put the "no new messages" or "retrieved x new messages"
  2808.                        in the status line.  Unfortunately, this tends to be running
  2809.                        in a progress pane, so we try to get the real pane and
  2810.                        show the message there. */
  2811.                     MWContext* context = ce->window_id;
  2812.                     if (cd->pane) {
  2813.                       MSG_Pane* parentpane = MSG_GetParentPane(cd->pane);
  2814.                       if (parentpane) {
  2815.                         context = MSG_GetContext(parentpane);
  2816.                       }
  2817.                     }
  2818.                     if (cd->total_download_size <= 0) {
  2819.                         /* There are no new messages.  */
  2820.                         if (context) FE_Progress(context, XP_GetString(MK_POP3_NO_MESSAGES));
  2821.                     }
  2822.                     else {
  2823.                         /* at least 1 message was queued to download */
  2824.                         char *statusTemplate = XP_GetString (MK_MSG_DOWNLOAD_COUNT);
  2825.                         char *statusString = PR_smprintf (statusTemplate,  cd->last_accessed_msg, cd->number_of_messages);
  2826.                         if (context) FE_Progress(context, statusString);
  2827.                         FREEIF(statusString);
  2828.                     }
  2829.                   }
  2830.                 }
  2831.                 XP_STRCPY(cd->output_buffer, "QUIT" CRLF);
  2832.                 oldStatus = ce->status;
  2833.                 ce->status = net_pop3_send_command(ce, cd->output_buffer);
  2834.                 if (oldStatus == MK_INTERRUPTED)
  2835.                     cd->pause_for_read = FALSE;    /* aborting connection, finish clean */
  2836.                 cd->next_state = POP3_WAIT_FOR_RESPONSE;
  2837. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  2838.                 cd->next_state_after_response = POP3_QUIT_RESPONSE;
  2839. #else
  2840.                 cd->next_state_after_response = POP3_DONE;
  2841. #endif
  2842.                 break;
  2843.  
  2844. #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES
  2845.             case POP3_QUIT_RESPONSE:
  2846.                 if(cd->command_succeeded)
  2847.                 {
  2848.                     /*    the QUIT succeeded.  We can now flush the state in popstate.dat which
  2849.                         keeps track of any uncommitted DELE's */
  2850.                     
  2851.                     /* here we need to clear the hash of all our 
  2852.                        uncommitted deletes */
  2853.                     /*
  2854.                     if (cd->uidlinfo && cd->uidlinfo->uncommitted_deletes)
  2855.                         XP_Clrhash (cd->uidlinfo->uncommitted_deletes);*/
  2856.  
  2857.                     cd->next_state = POP3_DONE;
  2858.  
  2859.                 }
  2860.                 else
  2861.                 {
  2862.                     cd->next_state = POP3_ERROR_DONE;
  2863.                 }
  2864.                 break;
  2865. #endif
  2866.  
  2867.             case POP3_DONE:
  2868.                 net_pop3_commit_state(ce, FALSE);
  2869.                 if(ce->socket != NULL)
  2870.                   {
  2871.                     NET_ClearReadSelect(ce->window_id, ce->socket);
  2872.                     TRACEMSG(("Closing and clearing sock ce->socket: %d", 
  2873.                               ce->socket));
  2874.                     PR_Close(ce->socket);
  2875.                     NET_TotalNumberOfOpenConnections--;
  2876.                     ce->socket = NULL;
  2877.                   }
  2878.  
  2879.                 if(cd->msg_del_started)
  2880.                     MSG_EndMailDelivery(cd->pane);
  2881.                 else if (cd->pane)
  2882.                     MSG_GetNextURL(cd->pane);
  2883.  
  2884.                 cd->next_state = POP3_FREE;
  2885.                 break;
  2886.  
  2887.             case POP3_INTERRUPTED:
  2888.                 {
  2889.                     XP_STRCPY(cd->output_buffer, "QUIT" CRLF);
  2890.                     NET_BlockingWrite(ce->socket, cd->output_buffer,
  2891.                                       XP_STRLEN(cd->output_buffer));
  2892.                     PR_Shutdown(ce->socket, PR_SHUTDOWN_SEND); /* make sure QUIT get send
  2893.                                                                * before closing down the socket
  2894.                                                                  */
  2895.                     cd->pause_for_read = FALSE;
  2896.                     cd->next_state = POP3_ERROR_DONE;
  2897.                 }
  2898.                 break;
  2899.  
  2900.             case POP3_ERROR_DONE:
  2901.  
  2902.                 /*  write out the state */
  2903.                 net_pop3_commit_state(ce, TRUE);
  2904.                 
  2905.                 if(ce->socket != NULL)
  2906.                   {
  2907. #if 0
  2908.                     /* attempt to send a server reset command
  2909.                      * so that messages will not be marked as read
  2910.  
  2911.                      #### turns out we never need to do this -- this causes
  2912.                      our DELE commands to not be honored, but that turns out
  2913.                      to never be the right thing, since we never issue a
  2914.                      DELE command until the messages are known to be safely
  2915.                      on disk.
  2916.                      */
  2917.                     XP_STRCPY(cd->output_buffer, "RSET" CRLF);
  2918.                     net_pop3_send_command(ce, cd->output_buffer);
  2919. #endif /* 0 */
  2920.  
  2921.                     NET_ClearReadSelect(ce->window_id, ce->socket);
  2922.                     NET_ClearConnectSelect(ce->window_id, ce->socket);
  2923.                     TRACEMSG(("Closing and clearing socket ce->socket: %d",
  2924.                               ce->socket));
  2925.                     PR_Close(ce->socket);
  2926.                     NET_TotalNumberOfOpenConnections--;
  2927.                     ce->socket = NULL;
  2928.                   }
  2929.  
  2930.                 if(cd->msg_closure)
  2931.                   {
  2932.                     MSG_IncorporateAbort(cd->pane, cd->msg_closure,
  2933.                                          MK_POP3_MESSAGE_WRITE_ERROR);
  2934.                     cd->msg_closure = NULL;
  2935.                     MSG_AbortMailDelivery(cd->pane);
  2936.                    }
  2937.  
  2938.                 if(cd->msg_del_started)
  2939.                   {
  2940.                     char *statusTemplate = XP_GetString (MK_MSG_DOWNLOAD_COUNT);
  2941.                     char *statusString = PR_smprintf (statusTemplate,  cd->last_accessed_msg, cd->number_of_messages);
  2942.                     MWContext* context = ce->window_id;
  2943.                     if (cd->pane) {
  2944.                       MSG_Pane* parentpane = MSG_GetParentPane(cd->pane);
  2945.                       if (parentpane) {
  2946.                         context = MSG_GetContext(parentpane);
  2947.                       }
  2948.                     }
  2949.                     XP_ASSERT (!cd->password_failed);
  2950.                     MSG_AbortMailDelivery(cd->pane);
  2951.                     if (context) FE_Progress(context, statusString);
  2952.                     FREEIF(statusString);
  2953.                   }
  2954.  
  2955.                 if (cd->password_failed)
  2956.                   /* We got here because the password was wrong, so go
  2957.                      read a new one and re-open the connection. */
  2958.                   cd->next_state = POP3_READ_PASSWORD;
  2959.                 else
  2960.                   /* Else we got a "real" error, so finish up. */
  2961.                   cd->next_state = POP3_FREE;
  2962.  
  2963.                 cd->pause_for_read = FALSE;
  2964.                 break;
  2965.  
  2966.             case POP3_FREE:
  2967.                 if (cd->newuidl) XP_HashTableDestroy(cd->newuidl);
  2968.                 net_pop3_free_state(cd->uidlinfo);
  2969.                 if (cd->graph_progress_bytes_p) {
  2970.                   /* Only destroy it if we have initialized it. */
  2971.                   FE_GraphProgressDestroy(ce->window_id, ce->URL_s,
  2972.                                           cd->cur_msg_size,
  2973.                                           ce->bytes_received);
  2974.                 }
  2975.                 net_pop3_free_msg_info(ce);
  2976.                 FREEIF(cd->only_uidl);
  2977.                 FREEIF(cd->output_buffer);
  2978.                 FREEIF(cd->obuffer);
  2979.                 FREEIF(cd->data_buffer);
  2980.                 FREEIF(cd->command_response);
  2981.                 FREEIF(cd->sender_info);
  2982.                 FREE(ce->con_data);
  2983.  
  2984.                 /* release the semaphore which prevents biff from interrupting connections */
  2985.                 net_pop3_block_biff = FALSE;
  2986.  
  2987.                 if (oldStatus == MK_INTERRUPTED)
  2988.                     return MK_INTERRUPTED;    /* Make sure everyone knows we got canceled */
  2989.                 return(-1);
  2990.                 break;
  2991.  
  2992.             default:
  2993.                 XP_ASSERT(0);
  2994.  
  2995.           }  /* end switch */
  2996.  
  2997.        if((ce->status < 0) && (cd->next_state != POP3_FREE))
  2998.           {
  2999.             cd->pause_for_read = FALSE;
  3000.             cd->next_state = POP3_ERROR_DONE;
  3001.           }
  3002.  
  3003.       }  /* end while */
  3004.       if (oldStatus == MK_INTERRUPTED)
  3005.           ce->status = MK_INTERRUPTED;    /* Make sure everyone knows we got canceled */
  3006.  
  3007.     return(ce->status);
  3008.  
  3009. }
  3010.  
  3011. /* abort the connection in progress
  3012.  */
  3013. PRIVATE int32
  3014. net_InterruptPop3(ActiveEntry * ce)
  3015. {
  3016.     Pop3ConData * cd = (Pop3ConData *)ce->con_data;
  3017.  
  3018.     TRACEMSG(("NET_InterruptPop3 called"));
  3019.  
  3020.     cd->next_state = POP3_SEND_QUIT; /* interrupt does not give enough time for the quit
  3021.                                         command to be executed on the server, leaving us
  3022.                                         with a bunch of messages to be re-downloaded.
  3023.                                         Make a graceful quit instead.
  3024.                                         POP3_INTERRUPTED; */
  3025.  
  3026.     ce->status = MK_INTERRUPTED;
  3027.  
  3028.     return(net_ProcessPop3(ce));
  3029. }
  3030.  
  3031. PRIVATE void
  3032. net_CleanupPop3(void)
  3033. {
  3034. }
  3035.  
  3036. MODULE_PRIVATE void
  3037. NET_InitPop3Protocol(void)
  3038. {
  3039.     static NET_ProtoImpl pop3_proto_impl;
  3040.  
  3041.     pop3_proto_impl.init = net_Pop3Load;
  3042.     pop3_proto_impl.process = net_ProcessPop3;
  3043.     pop3_proto_impl.interrupt = net_InterruptPop3;
  3044.     pop3_proto_impl.cleanup = net_CleanupPop3;
  3045.  
  3046.     NET_RegisterProtocolImplementation(&pop3_proto_impl, POP3_TYPE_URL);
  3047. }
  3048.  
  3049. #endif /* MOZILLA_CLIENT */
  3050.