home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mknews.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  144.0 KB  |  5,338 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 implement NNTP access
  20.  *
  21.  * Designed and originally implemented by Lou Montulli '94
  22.  * Heavily modified by libmsg folks
  23.  */
  24.  
  25. /* Please leave outside of ifdef for windows precompiled headers */
  26. #define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */
  27. #include "rosetta.h"
  28. #include "mkutils.h"
  29.  
  30. #ifdef MOZILLA_CLIENT
  31.  
  32. #include "merrors.h"
  33.  
  34. #include "mime.h"
  35. #include "shist.h"
  36. #include "glhist.h"
  37. #include "xp_reg.h"
  38. #include "mknews.h"
  39. #include "mktcp.h"
  40. #include "mkparse.h"
  41. #include "mkformat.h"
  42. #include "mkstream.h"
  43. #include "mkaccess.h"
  44. #include "mksort.h"
  45. #include "mkcache.h"
  46. #include "mkextcac.h"
  47. #include "mknewsgr.h"
  48. #include "libi18n.h"
  49. #include "gui.h"
  50. #include "cert.h"
  51. #include "ssl.h"
  52. #include "mknews.h"
  53.  
  54. #include "msgcom.h"
  55. #include "msgnet.h"
  56. #include "msg_srch.h"
  57. #include "libmime.h"
  58.  
  59. #include "prtime.h"
  60. #include "prlog.h"
  61.  
  62. #include "xp_error.h"
  63. #include "secnav.h"
  64. #include "prefapi.h"    
  65.  
  66. /*#define CACHE_NEWSGRP_PASSWORD*/
  67.  
  68. /* for XP_GetString() */
  69. #include "xpgetstr.h"
  70. extern int MK_MALFORMED_URL_ERROR;
  71. extern int MK_NEWS_ERROR_FMT;
  72. extern int MK_NNTP_CANCEL_CONFIRM;
  73. extern int MK_NNTP_CANCEL_DISALLOWED;
  74. extern int MK_NNTP_NOT_CANCELLED;
  75. extern int MK_OUT_OF_MEMORY;
  76. extern int XP_CONFIRM_SAVE_NEWSGROUPS;
  77. extern int XP_HTML_ARTICLE_EXPIRED;
  78. extern int XP_HTML_NEWS_ERROR;
  79. extern int XP_PROGRESS_READ_NEWSGROUPINFO;
  80. extern int XP_PROGRESS_RECEIVE_ARTICLE;
  81. extern int XP_PROGRESS_RECEIVE_LISTARTICLES;
  82. extern int XP_PROGRESS_RECEIVE_NEWSGROUP;
  83. extern int XP_PROGRESS_SORT_ARTICLES;
  84. extern int XP_PROGRESS_READ_NEWSGROUP_COUNTS;
  85. extern int XP_THERMO_PERCENT_FORM;
  86. extern int XP_PROMPT_ENTER_USERNAME;
  87. extern int MK_BAD_NNTP_CONNECTION;
  88. extern int MK_NNTP_AUTH_FAILED;
  89. extern int MK_NNTP_ERROR_MESSAGE;
  90. extern int MK_NNTP_NEWSGROUP_SCAN_ERROR;
  91. extern int MK_NNTP_SERVER_ERROR;
  92. extern int MK_NNTP_SERVER_NOT_CONFIGURED;
  93. extern int MK_SECURE_NEWS_PROXY_ERROR;
  94. extern int MK_TCP_READ_ERROR;
  95. extern int MK_TCP_WRITE_ERROR;
  96. extern int MK_NNTP_CANCEL_ERROR;
  97. extern int XP_CONNECT_NEWS_HOST_CONTACTED_WAITING_FOR_REPLY;
  98. extern int XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS;
  99. extern int XP_GARBAGE_COLLECTING;
  100. extern int XP_MESSAGE_SENT_WAITING_NEWS_REPLY;
  101. extern int MK_MSG_DELIV_NEWS;
  102. extern int MK_MSG_COLLABRA_DISABLED;
  103. extern int MK_MSG_EXPIRE_NEWS_ARTICLES;
  104.  
  105. /* Logging stuff */
  106. #ifndef NSPR20
  107. PR_LOG_DEFINE(NNTP);
  108. #define NNTP_LOG_READ(buf) PR_LOG(NNTP, out, ("Receiving: %s", buf))
  109. #define NNTP_LOG_WRITE(buf)    PR_LOG(NNTP, out, ("Sending: %s", buf))
  110. #define NNTP_LOG_NOTE(buf) PR_LOG(NNTP, out, buf)
  111. #else
  112. PRLogModuleInfo* NNTP = NULL;
  113. #define out        PR_LOG_ALWAYS
  114.  
  115. #define NNTP_LOG_READ(buf) \
  116. if (NNTP==NULL) \
  117.     NNTP = PR_NewLogModule("NNTP"); \
  118. PR_LOG(NNTP, out, ("Receiving: %s", buf)) ;
  119.  
  120. #define NNTP_LOG_WRITE(buf) \
  121. if (NNTP==NULL) \
  122.     NNTP = PR_NewLogModule("NNTP"); \
  123. PR_LOG(NNTP, out, ("Sending: %s", buf)) ;
  124.  
  125. #define NNTP_LOG_NOTE(buf) \
  126. if (NNTP==NULL) \
  127.     NNTP = PR_NewLogModule("NNTP"); \
  128. PR_LOG(NNTP, out, buf) ;
  129.  
  130. #endif /* NSPR20 */
  131.  
  132. /* Forward declarations */
  133. static int net_xpat_send (ActiveEntry*);
  134. static int net_list_pretty_names(ActiveEntry*);
  135. PRIVATE int32 net_ProcessNews (ActiveEntry *ce);
  136.  
  137. #ifdef PROFILE
  138. #pragma profile on
  139. #endif
  140.  
  141. #define LIST_WANTED     0
  142. #define ARTICLE_WANTED  1
  143. #define CANCEL_WANTED   2
  144. #define GROUP_WANTED    3
  145. #define NEWS_POST       4
  146. #define READ_NEWS_RC    5
  147. #define NEW_GROUPS      6
  148. #define SEARCH_WANTED   7
  149. #define PRETTY_NAMES_WANTED 8
  150. #define PROFILE_WANTED    9
  151. #define IDS_WANTED        10
  152.  
  153. /* the output_buffer_size must be larger than the largest possible line
  154.  * 2000 seems good for news
  155.  *
  156.  * jwz: I increased this to 4k since it must be big enough to hold the
  157.  * entire button-bar HTML, and with the new "mailto" format, that can
  158.  * contain arbitrarily long header fields like "references".
  159.  *
  160.  * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
  161.  */
  162. #define OUTPUT_BUFFER_SIZE (4096*2)
  163.  
  164. /* the amount of time to subtract from the machine time
  165.  * for the newgroup command sent to the nntp server
  166.  */
  167. #define NEWGROUPS_TIME_OFFSET 60L*60L*12L   /* 12 hours */
  168.  
  169. /* Allow the user to open at most this many connections to one news host*/
  170. #define kMaxConnectionsPerHost 3
  171.  
  172. /* Keep this many connections cached. The cache is global, not per host */
  173. #define kMaxCachedConnections 2
  174.  
  175. /* globals
  176.  */
  177. PRIVATE char * NET_NewsHost = NULL;
  178. PRIVATE XP_List * nntp_connection_list=0;
  179.  
  180. PRIVATE XP_Bool net_news_last_username_probably_valid=FALSE;
  181. PRIVATE int32 net_NewsChunkSize=-1;  /* default */
  182.  
  183. #define PUTSTRING(s)      (*cd->stream->put_block) \
  184.                     (cd->stream, s, XP_STRLEN(s))
  185. #define COMPLETE_STREAM   (*cd->stream->complete)  \
  186.                     (cd->stream)
  187. #define ABORT_STREAM(s)   (*cd->stream->abort) \
  188.                     (cd->stream, s)
  189. #define PUT_STREAM(buf, size)   (*cd->stream->put_block) \
  190.                           (cd->stream, buf, size)
  191.  
  192. /* states of the machine
  193.  */
  194. typedef enum _StatesEnum {
  195. NNTP_RESPONSE,
  196. #ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
  197. NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE,
  198. NNTP_CONNECTIONS_ARE_AVAILABLE,
  199. #endif
  200. NNTP_CONNECT,
  201. NNTP_CONNECT_WAIT,
  202. HG86722
  203. NNTP_LOGIN_RESPONSE,
  204. NNTP_SEND_MODE_READER,
  205. NNTP_SEND_MODE_READER_RESPONSE,
  206. SEND_LIST_EXTENSIONS,
  207. SEND_LIST_EXTENSIONS_RESPONSE,
  208. SEND_LIST_SEARCHES,
  209. SEND_LIST_SEARCHES_RESPONSE,
  210. NNTP_LIST_SEARCH_HEADERS,
  211. NNTP_LIST_SEARCH_HEADERS_RESPONSE,
  212. NNTP_GET_PROPERTIES,
  213. NNTP_GET_PROPERTIES_RESPONSE,
  214. SEND_LIST_SUBSCRIPTIONS,
  215. SEND_LIST_SUBSCRIPTIONS_RESPONSE,
  216. SEND_FIRST_NNTP_COMMAND,
  217. SEND_FIRST_NNTP_COMMAND_RESPONSE,
  218. SETUP_NEWS_STREAM,
  219. NNTP_BEGIN_AUTHORIZE,
  220. NNTP_AUTHORIZE_RESPONSE,
  221. NNTP_PASSWORD_RESPONSE,
  222. NNTP_READ_LIST_BEGIN,
  223. NNTP_READ_LIST,
  224. DISPLAY_NEWSGROUPS,
  225. NNTP_NEWGROUPS_BEGIN,
  226. NNTP_NEWGROUPS,
  227. NNTP_BEGIN_ARTICLE,
  228. NNTP_READ_ARTICLE,
  229. NNTP_XOVER_BEGIN,
  230. NNTP_FIGURE_NEXT_CHUNK,
  231. NNTP_XOVER_SEND,
  232. NNTP_XOVER_RESPONSE,
  233. NNTP_XOVER,
  234. NEWS_PROCESS_XOVER,
  235. NNTP_READ_GROUP,
  236. NNTP_READ_GROUP_RESPONSE,
  237. NNTP_READ_GROUP_BODY,
  238. NNTP_SEND_GROUP_FOR_ARTICLE,
  239. NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE,
  240. NNTP_PROFILE_ADD,
  241. NNTP_PROFILE_ADD_RESPONSE,
  242. NNTP_PROFILE_DELETE,
  243. NNTP_PROFILE_DELETE_RESPONSE,
  244. NNTP_SEND_ARTICLE_NUMBER,
  245. NEWS_PROCESS_BODIES,
  246. NNTP_PRINT_ARTICLE_HEADERS,
  247. NNTP_SEND_POST_DATA,
  248. NNTP_SEND_POST_DATA_RESPONSE,
  249. NNTP_CHECK_FOR_MESSAGE,
  250. NEWS_NEWS_RC_POST,
  251. NEWS_DISPLAY_NEWS_RC,
  252. NEWS_DISPLAY_NEWS_RC_RESPONSE,
  253. NEWS_START_CANCEL,
  254. NEWS_DO_CANCEL,
  255. NNTP_XPAT_SEND,
  256. NNTP_XPAT_RESPONSE,
  257. NNTP_SEARCH,
  258. NNTP_SEARCH_RESPONSE,
  259. NNTP_SEARCH_RESULTS,
  260. NNTP_LIST_PRETTY_NAMES,
  261. NNTP_LIST_PRETTY_NAMES_RESPONSE,
  262. NNTP_LIST_XACTIVE,
  263. NNTP_LIST_XACTIVE_RESPONSE,
  264. NNTP_LIST_GROUP,
  265. NNTP_LIST_GROUP_RESPONSE,
  266. NEWS_DONE,
  267. NEWS_ERROR,
  268. NNTP_ERROR,
  269. NEWS_FREE
  270. } StatesEnum;
  271.  
  272. #ifdef DEBUG
  273. char *stateLabels[] = {
  274. "NNTP_RESPONSE",
  275. #ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
  276. "NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE",
  277. "NNTP_CONNECTIONS_ARE_AVAILABLE",
  278. #endif
  279. "NNTP_CONNECT",
  280. "NNTP_CONNECT_WAIT",
  281. HG87489
  282. "NNTP_LOGIN_RESPONSE",
  283. "NNTP_SEND_MODE_READER",
  284. "NNTP_SEND_MODE_READER_RESPONSE",
  285. "SEND_LIST_EXTENSIONS",
  286. "SEND_LIST_EXTENSIONS_RESPONSE",
  287. "SEND_LIST_SEARCHES",
  288. "SEND_LIST_SEARCHES_RESPONSE",
  289. "NNTP_LIST_SEARCH_HEADERS",
  290. "NNTP_LIST_SEARCH_HEADERS_RESPONSE",
  291. "NNTP_GET_PROPERTIES",
  292. "NNTP_GET_PROPERTIES_RESPONSE",
  293. "SEND_LIST_SUBSCRIPTIONS",
  294. "SEND_LIST_SUBSCRIPTIONS_RESPONSE",
  295. "SEND_FIRST_NNTP_COMMAND",
  296. "SEND_FIRST_NNTP_COMMAND_RESPONSE",
  297. "SETUP_NEWS_STREAM",
  298. "NNTP_BEGIN_AUTHORIZE",
  299. "NNTP_AUTHORIZE_RESPONSE",
  300. "NNTP_PASSWORD_RESPONSE",
  301. "NNTP_READ_LIST_BEGIN",
  302. "NNTP_READ_LIST",
  303. "DISPLAY_NEWSGROUPS",
  304. "NNTP_NEWGROUPS_BEGIN",
  305. "NNTP_NEWGROUPS",
  306. "NNTP_BEGIN_ARTICLE",
  307. "NNTP_READ_ARTICLE",
  308. "NNTP_XOVER_BEGIN",
  309. "NNTP_FIGURE_NEXT_CHUNK",
  310. "NNTP_XOVER_SEND",
  311. "NNTP_XOVER_RESPONSE",
  312. "NNTP_XOVER",
  313. "NEWS_PROCESS_XOVER",
  314. "NNTP_READ_GROUP",
  315. "NNTP_READ_GROUP_RESPONSE",
  316. "NNTP_READ_GROUP_BODY",
  317. "NNTP_SEND_GROUP_FOR_ARTICLE",
  318. "NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE",
  319. "NNTP_PROFILE_ADD",
  320. "NNTP_PROFILE_ADD_RESPONSE",
  321. "NNTP_PROFILE_DELETE",
  322. "NNTP_PROFILE_DELETE_RESPONSE",
  323. "NNTP_SEND_ARTICLE_NUMBER",
  324. "NEWS_PROCESS_BODIES",
  325. "NNTP_PRINT_ARTICLE_HEADERS",
  326. "NNTP_SEND_POST_DATA",
  327. "NNTP_SEND_POST_DATA_RESPONSE",
  328. "NNTP_CHECK_FOR_MESSAGE",
  329. "NEWS_NEWS_RC_POST",
  330. "NEWS_DISPLAY_NEWS_RC",
  331. "NEWS_DISPLAY_NEWS_RC_RESPONSE",
  332. "NEWS_START_CANCEL",
  333. "NEWS_DO_CANCEL",
  334. "NNTP_XPAT_SEND",
  335. "NNTP_XPAT_RESPONSE",
  336. "NNTP_SEARCH",
  337. "NNTP_SEARCH_RESPONSE",
  338. "NNTP_SEARCH_RESULTS",
  339. "NNTP_LIST_PRETTY_NAMES",
  340. "NNTP_LIST_PRETTY_NAMES_RESPONSE",
  341. "NNTP_LIST_XACTIVE_RESPONSE",
  342. "NNTP_LIST_XACTIVE",
  343. "NNTP_LIST_GROUP",
  344. "NNTP_LIST_GROUP_RESPONSE",
  345. "NEWS_DONE",
  346. "NEWS_ERROR",
  347. "NNTP_ERROR",
  348. "NEWS_FREE"
  349. };
  350.  
  351. #endif
  352.  
  353. /* structure to hold data about a tcp connection
  354.  * to a news host
  355.  */
  356. typedef struct _NNTPConnection {
  357.     char   *hostname;       /* hostname string (may contain :port) */
  358.     PRFileDesc *csock;      /* control socket */
  359.     XP_Bool busy;           /* is the connection in use already? */
  360.     XP_Bool prev_cache;     /* did this connection come from the cache? */
  361.     XP_Bool posting_allowed;/* does this server allow posting */
  362.     XP_Bool default_host;
  363.     XP_Bool no_xover;       /* xover command is not supported here */
  364.     XP_Bool secure;         /* is it a secure connection? */
  365.  
  366.     char *current_group;    /* last GROUP command sent on this connection */
  367.  
  368. } NNTPConnection;
  369.  
  370.  
  371. /* structure to hold all the state and data
  372.  * for the state machine
  373.  */
  374. typedef struct _NewsConData {
  375.     MSG_Pane     *pane;
  376.     MSG_NewsHost *host;
  377.  
  378.     StatesEnum  next_state;
  379.     StatesEnum  next_state_after_response;
  380.     int         type_wanted;     /* Article, List, or Group */
  381.     int         response_code;   /* code returned from NNTP server */
  382.     char       *response_txt;    /* text returned from NNTP server */
  383.     Bool     pause_for_read;  /* should we pause for the next read? */
  384.  
  385.     char       *ssl_proxy_server;    /* ssl proxy server hostname */
  386.     XP_Bool     proxy_auth_required; /* is auth required */
  387.     XP_Bool     sent_proxy_auth;     /* have we sent a proxy auth? */
  388.  
  389.     XP_Bool     newsrc_performed;    /* have we done a newsrc update? */
  390.     XP_Bool        mode_reader_performed; /* have we sent any cmds to the server yet? */
  391.  
  392. #ifdef XP_WIN
  393.     XP_Bool     calling_netlib_all_the_time;
  394. #endif
  395.     NET_StreamClass * stream;
  396.  
  397.     NNTPConnection * control_con;  /* all the info about a connection */
  398.  
  399.     char   *data_buf;
  400.     int32   data_buf_size;
  401.  
  402.     /* for group command */
  403.     char   *path; /* message id */
  404.  
  405.     char   *group_name;
  406.     int32   first_art;
  407.     int32   last_art;
  408.     int32   first_possible_art;
  409.     int32   last_possible_art;
  410.  
  411.     int32    num_loaded;            /* How many articles we got XOVER lines for. */
  412.     int32    num_wanted;            /* How many articles we wanted to get XOVER
  413.                                    lines for. */
  414.  
  415.     /* for cancellation: the headers to be used */
  416.     char *cancel_from;
  417.     char *cancel_newsgroups;
  418.     char *cancel_distribution;
  419.     char *cancel_id;
  420.     char *cancel_msg_file;
  421.     int cancel_status;
  422.  
  423.     /* variables for ReadNewsRC */
  424.     int32    newsrc_list_index;
  425.     int32    newsrc_list_count;
  426.  
  427.     XP_Bool  use_fancy_newsgroup_listing;  /* use LIST XACTIVE or LIST */
  428.     XP_Bool  destroy_graph_progress;       /* do we need to destroy 
  429.                                             * graph progress? 
  430.                                             */    
  431.  
  432.     char  *output_buffer;                 /* use it to write out lines */
  433.  
  434.     int32  article_num;   /* current article number */
  435.  
  436.     char  *message_id;    /* for reading groups */
  437.     char  *author;
  438.     char  *subject;
  439.  
  440.     int32  original_content_length; /* the content length at the time of
  441.                                      * calling graph progress
  442.                                      */
  443.  
  444.     /* random pointer for libmsg state */
  445.     void *xover_parse_state;
  446.     void *newsgroup_parse_state;
  447.  
  448.     TCP_ConData * tcp_con_data;
  449.  
  450.     void * write_post_data_data;
  451.  
  452.     XP_Bool some_protocol_succeeded; /* We know we have done some protocol
  453.                                         with this newsserver recently, so don't
  454.                                         respond to an error by closing and
  455.                                         reopening it.  */
  456.     char *command_specific_data;
  457.     char *current_search;
  458.     void *offlineState;                /* offline news state machine for article retrieval */
  459.     int previous_response_code;
  460.  
  461. } NewsConData;
  462.  
  463.  
  464. /* publicly available function to set the news host
  465.  * this will be a useful front end callable routine
  466.  */
  467. PUBLIC void NET_SetNewsHost (const char * value)
  468. {
  469.     /* ### This routine is obsolete and should go away. */
  470. }
  471.  
  472.  
  473.  
  474. /* set the default number of articles retrieved by news at any one time
  475.  */
  476. PUBLIC void 
  477. NET_SetNumberOfNewsArticlesInListing(int32 number)
  478. {
  479.     net_NewsChunkSize = number;
  480. }
  481.  
  482. /* Whether we cache XOVER data.  NYI. */
  483. PUBLIC void
  484. NET_SetCacheXOVER(XP_Bool value)
  485. {
  486. }
  487.  
  488.  
  489.  
  490. PUBLIC void NET_CleanTempXOVERCache(void)
  491. {
  492. }
  493.  
  494.  
  495.  
  496. static void net_nntp_close (PRFileDesc *sock, int status)
  497. {
  498.     if (status != MK_INTERRUPTED)
  499.     {
  500.         const char *command = "QUIT" CRLF;
  501.         NET_BlockingWrite (sock, command, XP_STRLEN(command));
  502.         NNTP_LOG_WRITE (command);
  503.     }
  504.     PR_Close(sock);
  505. }
  506.  
  507.  
  508. /* user has removed a news host from the UI. 
  509.  * be sure it has also been removed from the
  510.  * connection cache so we renegotiate news host
  511.  * capabilities if we try to use it again
  512.  */
  513. PUBLIC void NET_OnNewsHostDeleted (const char *hostName)
  514. {
  515.     NNTPConnection *conn;
  516.     XP_List *list = nntp_connection_list;
  517.  
  518.     while ((conn = (NNTPConnection*) XP_ListNextObject(list)) != NULL)
  519.     {
  520.         if (!(XP_STRCASECMP(conn->hostname, hostName)))
  521.             net_nntp_close (conn->csock, conn->busy ? MK_INTERRUPTED : 0);
  522.  
  523.         list = list->prev ? list->prev : nntp_connection_list;
  524.         XP_ListRemoveObject (nntp_connection_list, conn);
  525.  
  526.         FREEIF(conn->current_group);
  527.         FREEIF(conn->hostname);
  528.         FREE(conn);
  529.     }
  530. }
  531.  
  532.  
  533. /* display HTML error state in the context window
  534.  * returns negative error status
  535.  */
  536. PRIVATE int
  537. net_display_html_error_state (ActiveEntry *ce)
  538. {
  539.     NewsConData * cd = (NewsConData *)ce->con_data;
  540.     XP_Bool inMsgPane = FALSE;
  541.     int major_opcode = cd->response_code/100;
  542.     /* #### maybe make this a dialog box
  543.        to do that, just return an error.
  544.      */
  545.  
  546.     /* ERROR STATE
  547.      *  Set up the HTML stream
  548.      */
  549.     ce->format_out = CLEAR_CACHE_BIT(ce->format_out);
  550.     StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);
  551.  
  552.     /* If we're not in a message pane, don't try to spew HTML
  553.      * This allows error reporting from the folder pane, subscribe UI, etc.
  554.      */
  555.     if (ce->URL_s->msg_pane)
  556.         inMsgPane = (MSG_MESSAGEPANE == MSG_GetPaneType(ce->URL_s->msg_pane));
  557.  
  558.     if (ce->format_out == FO_PRESENT && inMsgPane)
  559.       {
  560.         cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s,
  561.                                       ce->window_id);
  562.         if(!cd->stream)
  563.           {
  564.             ce->URL_s->error_msg =
  565.               NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
  566.             return MK_UNABLE_TO_CONVERT;
  567.           }
  568.  
  569.         PR_snprintf(cd->output_buffer,
  570.                     OUTPUT_BUFFER_SIZE,
  571.                     XP_GetString(XP_HTML_NEWS_ERROR),
  572.                     cd->response_txt);
  573.         if(ce->status > -1)
  574.           ce->status = PUTSTRING(cd->output_buffer);
  575.  
  576.         if(cd->type_wanted == ARTICLE_WANTED ||
  577.            cd->type_wanted == CANCEL_WANTED)
  578.           {
  579.             XP_STRCPY(cd->output_buffer,
  580.                        XP_GetString(XP_HTML_ARTICLE_EXPIRED));
  581.             if(ce->status > -1)
  582.               PUTSTRING(cd->output_buffer);
  583.  
  584.             if (cd->path && *cd->path && ce->status > -1)
  585.               {
  586.                 PR_snprintf(cd->output_buffer, 
  587.                             OUTPUT_BUFFER_SIZE,
  588.                             "<P><%.512s>", cd->path);
  589.                 if (cd->article_num > 0)
  590.                 {
  591.                   PR_snprintf(cd->output_buffer+XP_STRLEN(cd->output_buffer),
  592.                              OUTPUT_BUFFER_SIZE-XP_STRLEN(cd->output_buffer),
  593.                              " (%lu)\n", (unsigned long) cd->article_num);
  594.                   if (ce->URL_s->msg_pane)
  595.                     MSG_MarkMessageKeyRead(ce->URL_s->msg_pane, cd->article_num, "");
  596.                 }
  597.                 PUTSTRING(cd->output_buffer);
  598.                 PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "<P> <A HREF=\"news://%s/%s?list-ids\">%s</A> </P>\n", cd->control_con->hostname, cd->group_name, XP_GetString(MK_MSG_EXPIRE_NEWS_ARTICLES));
  599.                 PUTSTRING(cd->output_buffer);
  600.               }
  601.  
  602.             if(ce->status > -1)
  603.               {
  604.                 COMPLETE_STREAM;
  605.                 cd->stream = 0;
  606.               }
  607.           }
  608.         else
  609.             if (cd->type_wanted == SEARCH_WANTED)
  610.             ce->status = net_xpat_send(ce);
  611.  
  612.         ce->URL_s->expires = 1;
  613.         /* ce->URL_s->error_msg =
  614.            NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR); 
  615.          */
  616.       }
  617.     else
  618.       {
  619.         /* FORMAT_OUT is not FO_PRESENT - so instead of emitting an HTML
  620.            error message, present it in a dialog box. */
  621.  
  622.         PR_snprintf(cd->output_buffer,  /* #### i18n */
  623.                    OUTPUT_BUFFER_SIZE,
  624.                    XP_GetString(MK_NEWS_ERROR_FMT),
  625.                    cd->response_txt);
  626.         ce->URL_s->error_msg = XP_STRDUP (cd->output_buffer);
  627.       }
  628.  
  629.     /* if the server returned a 400 error then it is an expected
  630.      * error.  the NEWS_ERROR state will not sever the connection
  631.      */
  632.     if(major_opcode == 4)
  633.       cd->next_state = NEWS_ERROR;
  634.     else
  635.       cd->next_state = NNTP_ERROR;
  636.  
  637.     /* If we ever get an error, let's re-issue the GROUP command
  638.        before the next ARTICLE command; the overhead isn't very high,
  639.        and weird stuff seems to be happening... */
  640.     FREEIF(cd->control_con->current_group);
  641.  
  642.     /* cd->control_con->prev_cache = FALSE;  to keep if from reconnecting */
  643.     return MK_NNTP_SERVER_ERROR;
  644. }
  645.  
  646.  
  647. /* gets the response code from the nntp server and the
  648.  * response line
  649.  *
  650.  * returns the TCP return code from the read
  651.  */
  652. PRIVATE int
  653. net_news_response (ActiveEntry * ce)
  654. {
  655.     char * line;
  656.     NewsConData * cd = (NewsConData *)ce->con_data;
  657.  
  658.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf, 
  659.                     &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  660.  
  661.     NNTP_LOG_READ(cd->data_buf);
  662.  
  663.     if(ce->status == 0)
  664.       {
  665.         cd->next_state = NNTP_ERROR;
  666.         cd->pause_for_read = FALSE;
  667.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  668.         return(MK_NNTP_SERVER_ERROR);
  669.       }
  670.  
  671.     /* if TCP error of if there is not a full line yet return
  672.      */
  673.     if(ce->status < 0)
  674.       {
  675.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, 
  676.                                                       SOCKET_ERRNO);
  677.  
  678.         /* return TCP error
  679.          */
  680.         return MK_TCP_READ_ERROR;
  681.       }
  682.     else if(!line)
  683.       {
  684.          return ce->status;
  685.       }
  686.  
  687.     cd->pause_for_read = FALSE; /* don't pause if we got a line */
  688.  
  689.     if(ce->bytes_received == 0)
  690.       {
  691.         /* this is where kipp says that I can finally query
  692.          * for the security data.  We can't do it after the
  693.          * connect since the handshake isn't done yet...
  694.          */
  695.         /* clear existing data
  696.          */
  697.         FREEIF(ce->URL_s->key_cipher);
  698.         FREEIF(ce->URL_s->key_issuer);
  699.         FREEIF(ce->URL_s->key_subject);
  700.         CERT_DestroyCertificate(ce->URL_s->certificate);
  701.  
  702.         SSL_SecurityStatus(ce->socket,
  703.                        &ce->URL_s->security_on,
  704.                        &ce->URL_s->key_cipher,
  705.                        &ce->URL_s->key_size,
  706.                        &ce->URL_s->key_secret_size,
  707.                        &ce->URL_s->key_issuer,
  708.                        &ce->URL_s->key_subject);
  709.         ce->URL_s->certificate = SSL_PeerCertificate(ce->socket);
  710.       }
  711.  
  712.     /* almost correct
  713.      */
  714.     if(ce->status > 1)
  715.       {
  716.         ce->bytes_received += ce->status;
  717.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length); 
  718.       }
  719.  
  720.     StrAllocCopy(cd->response_txt, line+4);
  721.  
  722.     cd->previous_response_code = cd->response_code;
  723.  
  724.     sscanf(line, "%d", &cd->response_code);
  725.  
  726.     /* authentication required can come at any time
  727.      */
  728. #ifdef CACHE_NEWSGRP_PASSWORD
  729.     /*
  730.      * This is an effort of trying to cache the username/password
  731.      * per newsgroup. It is extremely hard to make it work along with
  732.      * nntp voluntary password checking mechanism. We are backing this 
  733.      * feature out. Instead of touching various of backend msg files
  734.      * at this late Dogbert 4.0 beta4 game, the infrastructure will
  735.      * remain in the msg library. We only modify codes within this file.
  736.      * Maybe one day we will try to do it again. Zzzzz -- jht
  737.      */
  738.     if(480 == cd->response_code || 450 == cd->response_code || 
  739.        502 == cd->response_code)
  740.       {
  741.         cd->next_state = NNTP_BEGIN_AUTHORIZE;
  742.         if (502 == cd->response_code) {
  743.             if (2 == cd->previous_response_code/100) {
  744.                 if (net_news_last_username_probably_valid) {
  745.                   net_news_last_username_probably_valid = FALSE;
  746.                 }
  747.                 else {
  748.                   MSG_SetNewsgroupUsername(cd->pane, NULL);
  749.                   MSG_SetNewsgroupPassword(cd->pane, NULL);
  750.                 }
  751.             }
  752.             else {
  753.               net_news_last_username_probably_valid = FALSE;
  754.               if (NNTP_PASSWORD_RESPONSE == cd->next_state_after_response) {
  755.                 MSG_SetNewsgroupUsername(cd->pane, NULL);
  756.                 MSG_SetNewsgroupPassword(cd->pane, NULL);
  757.               }
  758.             }
  759.  
  760.         }
  761.       }
  762. #else
  763.     if (480 == cd->response_code || 450 == cd->response_code) 
  764.     {
  765.         cd->next_state = NNTP_BEGIN_AUTHORIZE;
  766.     }
  767.     else if (502 == cd->response_code)
  768.     {
  769.         net_news_last_username_probably_valid = FALSE;
  770.         return net_display_html_error_state(ce);
  771.     }
  772. #endif
  773.     else
  774.       {
  775.         cd->next_state = cd->next_state_after_response;
  776.       }
  777.  
  778.     return(0);  /* everything ok */
  779. }
  780.  
  781. HG40560
  782.  
  783. /* interpret the server response after the connect
  784.  *
  785.  * returns negative if the server responds unexpectedly
  786.  */
  787. PRIVATE int
  788. net_nntp_login_response (ActiveEntry * ce)
  789. {
  790.     NewsConData * cd = (NewsConData *)ce->con_data;
  791.     XP_Bool postingAllowed = cd->response_code == 200;
  792.  
  793.     if((cd->response_code/100)!=2)
  794.       {
  795.         ce->URL_s->error_msg  = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);
  796.  
  797.         cd->next_state = NNTP_ERROR;
  798.         cd->control_con->prev_cache = FALSE; /* to keep if from reconnecting */
  799.         return MK_BAD_NNTP_CONNECTION;
  800.       }
  801.  
  802.     cd->control_con->posting_allowed = postingAllowed; /* ###phil dead code */
  803.     MSG_SetNewsHostPostingAllowed (cd->host, postingAllowed);
  804.     
  805.     cd->next_state = NNTP_SEND_MODE_READER;
  806.     return(0);  /* good */
  807. }
  808.  
  809. PRIVATE int
  810. net_nntp_send_mode_reader (ActiveEntry * ce)
  811. {
  812.     NewsConData * cd = (NewsConData *)ce->con_data;
  813.  
  814.     XP_STRCPY(cd->output_buffer, "MODE READER" CRLF);
  815.  
  816.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  817.     NNTP_LOG_WRITE(cd->output_buffer);
  818.  
  819.     cd->next_state = NNTP_RESPONSE;
  820.     cd->next_state_after_response = NNTP_SEND_MODE_READER_RESPONSE;
  821.     cd->pause_for_read = TRUE;
  822.  
  823.     return(ce->status);
  824.  
  825. }
  826.  
  827. PRIVATE int
  828. net_nntp_send_mode_reader_response (ActiveEntry * ce)
  829. {
  830.     NewsConData * cd = (NewsConData *)ce->con_data;
  831.     cd->mode_reader_performed = TRUE;
  832.  
  833.     /* ignore the response code and continue
  834.      */
  835.  
  836.     if (MSG_GetNewsHostPushAuth(cd->host))
  837.         /* if the news host is set up to require volunteered (pushed) authentication,
  838.          * do that before we do anything else
  839.          */
  840.         cd->next_state = NNTP_BEGIN_AUTHORIZE;
  841.     else
  842.         cd->next_state = SEND_LIST_EXTENSIONS;
  843.  
  844.     return(0);
  845. }
  846.  
  847. PRIVATE int net_nntp_send_list_extensions (ActiveEntry *ce)
  848. {
  849.     NewsConData *cd = (NewsConData*) ce->con_data;
  850.     XP_STRCPY(cd->output_buffer, "LIST EXTENSIONS" CRLF);
  851.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  852.     NNTP_LOG_WRITE(cd->output_buffer);
  853.  
  854.     cd->next_state = NNTP_RESPONSE;
  855.     cd->next_state_after_response = SEND_LIST_EXTENSIONS_RESPONSE;
  856.     cd->pause_for_read = TRUE;
  857.     return ce->status;
  858. }
  859.  
  860. PRIVATE int net_nntp_send_list_extensions_response (ActiveEntry *ce)
  861. {
  862.     NewsConData *cd = (NewsConData*) ce->con_data;
  863.  
  864.     if (cd->response_code > 200 && cd->response_code < 300)
  865.     {
  866.         char *line = NULL;
  867.         MSG_NewsHost *host = cd->host;
  868.  
  869.         ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  870.             &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  871.  
  872.         if(ce->status == 0)
  873.         {
  874.             cd->next_state = NNTP_ERROR;
  875.             cd->pause_for_read = FALSE;
  876.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  877.             return MK_NNTP_SERVER_ERROR;
  878.         }
  879.         if (!line)
  880.             return ce->status;  /* no line yet */
  881.         if (ce->status < 0)
  882.         {
  883.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  884.             /* return TCP error */
  885.             return MK_TCP_READ_ERROR;
  886.         }
  887.  
  888.         if ('.' != line[0])
  889.             MSG_AddNewsExtension (host, line);
  890.         else
  891.         {
  892.             /* tell libmsg that it's ok to ask this news host for extensions */
  893.             MSG_SupportsNewsExtensions (cd->host, TRUE);
  894.  
  895.             /* all extensions received */
  896.             cd->next_state = SEND_LIST_SEARCHES;
  897.             cd->pause_for_read = FALSE;
  898.         }
  899.     }
  900.     else
  901.     {
  902.         /* LIST EXTENSIONS not recognized 
  903.          * tell libmsg not to ask for any more extensions and move on to
  904.          * the real NNTP command we were trying to do.
  905.          */
  906.         MSG_SupportsNewsExtensions (cd->host, FALSE);
  907.         cd->next_state = SEND_FIRST_NNTP_COMMAND;
  908.     }
  909.  
  910.     return ce->status;
  911. }
  912.  
  913. PRIVATE int net_nntp_send_list_searches (ActiveEntry *ce)
  914. {
  915.     NewsConData *cd = (NewsConData*) ce->con_data;
  916.  
  917.     if (MSG_QueryNewsExtension (cd->host, "SEARCH"))
  918.     {
  919.         XP_STRCPY(cd->output_buffer, "LIST SEARCHES" CRLF);
  920.         ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  921.         NNTP_LOG_WRITE(cd->output_buffer);
  922.  
  923.         cd->next_state = NNTP_RESPONSE;
  924.         cd->next_state_after_response = SEND_LIST_SEARCHES_RESPONSE;
  925.         cd->pause_for_read = TRUE;
  926.     }
  927.     else
  928.     {
  929.         /* since SEARCH isn't supported, move on to GET */
  930.         cd->next_state = NNTP_GET_PROPERTIES;
  931.         cd->pause_for_read = FALSE;
  932.     }
  933.  
  934.     return ce->status;
  935. }
  936.  
  937. PRIVATE int net_nntp_send_list_searches_response (ActiveEntry *ce)
  938. {
  939.     NewsConData *cd = (NewsConData*) ce->con_data;
  940.  
  941.     char *line = NULL;
  942.     ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  943.         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  944.  
  945.     if(ce->status == 0)
  946.     {
  947.         cd->next_state = NNTP_ERROR;
  948.         cd->pause_for_read = FALSE;
  949.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  950.         return MK_NNTP_SERVER_ERROR;
  951.     }
  952.     if (!line)
  953.         return ce->status;  /* no line yet */
  954.     if (ce->status < 0)
  955.     {
  956.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  957.         /* return TCP error */
  958.         return MK_TCP_READ_ERROR;
  959.     }
  960.  
  961.     if ('.' != line[0])
  962.     {
  963.         MSG_AddSearchableGroup (cd->host, line);
  964.     }
  965.     else
  966.     {
  967.         /* all searchable groups received */
  968.         /* LIST SRCHFIELDS is legal if the server supports the SEARCH extension, which */
  969.         /* we already know it does */
  970.         cd->next_state = NNTP_LIST_SEARCH_HEADERS;
  971.         cd->pause_for_read = FALSE;
  972.     }
  973.  
  974.     return ce->status;
  975. }
  976.  
  977. PRIVATE int net_send_list_search_headers (ActiveEntry *ce)
  978. {
  979.     NewsConData *cd = (NewsConData*) ce->con_data;
  980.     XP_STRCPY(cd->output_buffer, "LIST SRCHFIELDS" CRLF);
  981.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  982.     NNTP_LOG_WRITE(cd->output_buffer);
  983.  
  984.     cd->next_state = NNTP_RESPONSE;
  985.     cd->next_state_after_response = NNTP_LIST_SEARCH_HEADERS_RESPONSE;
  986.     cd->pause_for_read = TRUE;
  987.  
  988.     return ce->status;
  989. }
  990.  
  991. PRIVATE int net_send_list_search_headers_response (ActiveEntry *ce)
  992. {
  993.     NewsConData *cd = (NewsConData*) ce->con_data;
  994.     MSG_NewsHost *host = cd->host;
  995.  
  996.     char *line = NULL;
  997.     ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  998.         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  999.  
  1000.     if(ce->status == 0)
  1001.     {
  1002.         cd->next_state = NNTP_ERROR;
  1003.         cd->pause_for_read = FALSE;
  1004.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  1005.         return MK_NNTP_SERVER_ERROR;
  1006.     }
  1007.     if (!line)
  1008.         return ce->status;  /* no line yet */
  1009.     if (ce->status < 0)
  1010.     {
  1011.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  1012.         /* return TCP error */
  1013.         return MK_TCP_READ_ERROR;
  1014.     }
  1015.  
  1016.     if ('.' != line[0])
  1017.         MSG_AddSearchableHeader (host, line);
  1018.     else
  1019.     {
  1020.         cd->next_state = NNTP_GET_PROPERTIES;
  1021.         cd->pause_for_read = FALSE;
  1022.     }
  1023.  
  1024.     return ce->status;
  1025. }
  1026.  
  1027. PRIVATE int nntp_get_properties (ActiveEntry *ce)
  1028. {
  1029.     NewsConData *cd = (NewsConData*) ce->con_data;
  1030.  
  1031.     if (MSG_QueryNewsExtension (cd->host, "SETGET"))
  1032.     {
  1033.         XP_STRCPY(cd->output_buffer, "GET" CRLF);
  1034.         ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  1035.         NNTP_LOG_WRITE(cd->output_buffer);
  1036.  
  1037.         cd->next_state = NNTP_RESPONSE;
  1038.         cd->next_state_after_response = NNTP_GET_PROPERTIES_RESPONSE;
  1039.         cd->pause_for_read = TRUE;
  1040.     }
  1041.     else
  1042.     {
  1043.         /* since GET isn't supported, move on LIST SUBSCRIPTIONS */
  1044.         cd->next_state = SEND_LIST_SUBSCRIPTIONS;
  1045.         cd->pause_for_read = FALSE;
  1046.     }
  1047.     return ce->status;
  1048. }
  1049.  
  1050. PRIVATE int nntp_get_properties_response (ActiveEntry *ce)
  1051. {
  1052.     NewsConData *cd = (NewsConData*) ce->con_data;
  1053.  
  1054.     char *line = NULL;
  1055.     ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  1056.         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  1057.  
  1058.     if(ce->status == 0)
  1059.     {
  1060.         cd->next_state = NNTP_ERROR;
  1061.         cd->pause_for_read = FALSE;
  1062.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  1063.         return MK_NNTP_SERVER_ERROR;
  1064.     }
  1065.     if (!line)
  1066.         return ce->status;  /* no line yet */
  1067.     if (ce->status < 0)
  1068.     {
  1069.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  1070.         /* return TCP error */
  1071.         return MK_TCP_READ_ERROR;
  1072.     }
  1073.  
  1074.     if ('.' != line[0])
  1075.     {
  1076.         char *propertyName = XP_STRDUP(line);
  1077.         if (propertyName)
  1078.         {
  1079.             char *space = XP_STRCHR(propertyName, ' ');
  1080.             if (space)
  1081.             {
  1082.                 char *propertyValue = space + 1;
  1083.                 *space = '\0';
  1084.                 MSG_AddPropertyForGet (cd->host, 
  1085.                     propertyName, propertyValue);
  1086.             }
  1087.             XP_FREE(propertyName);
  1088.         }
  1089.     }
  1090.     else
  1091.     {
  1092.         /* all GET properties received, move on to LIST SUBSCRIPTIONS */
  1093.         cd->next_state = SEND_LIST_SUBSCRIPTIONS;
  1094.         cd->pause_for_read = FALSE;
  1095.     }
  1096.  
  1097.     return ce->status;
  1098. }
  1099.  
  1100. PRIVATE int net_send_list_subscriptions (ActiveEntry *ce)
  1101. {
  1102.     NewsConData *cd = (NewsConData*) ce->con_data;
  1103.  
  1104. #ifdef LATER
  1105.     if (MSG_QueryNewsExtension (cd->host, "LISTSUBSCR"))
  1106. #else
  1107.     if (0)
  1108. #endif
  1109.     {
  1110.         XP_STRCPY(cd->output_buffer, "LIST SUBSCRIPTIONS" CRLF);
  1111.         ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  1112.         NNTP_LOG_WRITE(cd->output_buffer);
  1113.  
  1114.         cd->next_state = NNTP_RESPONSE;
  1115.         cd->next_state_after_response = SEND_LIST_SUBSCRIPTIONS_RESPONSE;
  1116.         cd->pause_for_read = TRUE;
  1117.     }
  1118.     else
  1119.     {
  1120.         /* since LIST SUBSCRIPTIONS isn't supported, move on to real work */
  1121.         cd->next_state = SEND_FIRST_NNTP_COMMAND;
  1122.         cd->pause_for_read = FALSE;
  1123.     }
  1124.  
  1125.     return ce->status;
  1126. }
  1127.  
  1128. PRIVATE int net_send_list_subscriptions_response (ActiveEntry *ce)
  1129. {
  1130.     NewsConData *cd = (NewsConData*) ce->con_data;
  1131.  
  1132.     char *line = NULL;
  1133.     ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  1134.         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  1135.  
  1136.     if(ce->status == 0)
  1137.     {
  1138.         cd->next_state = NNTP_ERROR;
  1139.         cd->pause_for_read = FALSE;
  1140.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  1141.         return MK_NNTP_SERVER_ERROR;
  1142.     }
  1143.     if (!line)
  1144.         return ce->status;  /* no line yet */
  1145.     if (ce->status < 0)
  1146.     {
  1147.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  1148.         /* return TCP error */
  1149.         return MK_TCP_READ_ERROR;
  1150.     }
  1151.  
  1152.     if ('.' != line[0])
  1153.     {
  1154. #ifdef LATER
  1155.         char *urlScheme = cd->control_con->secure ? "snews:" : "news:";
  1156.         char *url = PR_smprintf ("%s//%s/%s", urlScheme, cd->control_con->hostname, line);
  1157.         if (url)
  1158.             MSG_AddSubscribedNewsgroup (cd->pane, url);
  1159. #endif
  1160.     }
  1161.     else
  1162.     {
  1163.         /* all default subscriptions received */
  1164.         cd->next_state = SEND_FIRST_NNTP_COMMAND;
  1165.         cd->pause_for_read = FALSE;
  1166.     }
  1167.  
  1168.     return ce->status;
  1169. }
  1170.  
  1171. /* figure out what the first command is and send it
  1172.  *
  1173.  * returns the status from the NETWrite
  1174.  */
  1175. PRIVATE int
  1176. net_send_first_nntp_command (ActiveEntry *ce)
  1177. {
  1178.     char *command=0;
  1179.     NewsConData * cd = (NewsConData *)ce->con_data;
  1180.  
  1181.     if (cd->type_wanted == ARTICLE_WANTED)
  1182.       {
  1183.         const char *group = 0;
  1184.         uint32 number = 0;
  1185.  
  1186.         MSG_NewsGroupAndNumberOfID (cd->pane, cd->path,
  1187.                                     &group, &number);
  1188.         if (group && number)
  1189.           {
  1190.             cd->article_num = number;
  1191.             StrAllocCopy (cd->group_name, group);
  1192.  
  1193.             if (cd->control_con->current_group && !XP_STRCMP (cd->control_con->current_group, group))
  1194.               cd->next_state = NNTP_SEND_ARTICLE_NUMBER;
  1195.             else
  1196.               cd->next_state = NNTP_SEND_GROUP_FOR_ARTICLE;
  1197.  
  1198.             cd->pause_for_read = FALSE;
  1199.             return 0;
  1200.           }
  1201.       }
  1202.  
  1203.     if(cd->type_wanted == NEWS_POST && !ce->URL_s->post_data)
  1204.       {
  1205.         XP_ASSERT(0);
  1206.         return(-1);
  1207.       }
  1208.     else if(cd->type_wanted == NEWS_POST)
  1209.       {  /* posting to the news group */
  1210.         StrAllocCopy(command, "POST");
  1211.       }
  1212.     else if(cd->type_wanted == READ_NEWS_RC)
  1213.       {
  1214.         if(ce->URL_s->method == URL_POST_METHOD ||
  1215.                                 XP_STRCHR(ce->URL_s->address, '?'))
  1216.             cd->next_state = NEWS_NEWS_RC_POST;
  1217.         else
  1218.             cd->next_state = NEWS_DISPLAY_NEWS_RC;
  1219.         return(0);
  1220.       } 
  1221.     else if(cd->type_wanted == NEW_GROUPS)
  1222.       {
  1223.         time_t last_update =
  1224.             MSG_NewsgroupsLastUpdatedTime(cd->host);
  1225.         char small_buf[64];
  1226. #ifndef NSPR20
  1227.         PRTime  expandedTime;
  1228. #else
  1229.         PRExplodedTime  expandedTime;
  1230. #endif /* NSPR20 */
  1231.  
  1232.         if(!last_update)
  1233.            {
  1234.             
  1235.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_NEWSGROUP_SCAN_ERROR);
  1236.             cd->next_state = NEWS_ERROR;
  1237.             return(MK_INTERRUPTED);
  1238.           }
  1239.     
  1240.         /* subtract some hours just to be sure */
  1241.         last_update -= NEWGROUPS_TIME_OFFSET;
  1242.  
  1243.         {
  1244.            int64  secToUSec, timeInSec, timeInUSec;
  1245.            LL_I2L(timeInSec, last_update);
  1246.            LL_I2L(secToUSec, PR_USEC_PER_SEC);
  1247.            LL_MUL(timeInUSec, timeInSec, secToUSec);
  1248. #ifndef NSPR20
  1249.            PR_ExplodeTime( &expandedTime, timeInUSec );
  1250. #else
  1251.            PR_ExplodeTime(timeInUSec, PR_LocalTimeParameters, &expandedTime);
  1252. #endif /* NSPR20 */
  1253.         }
  1254.         PR_FormatTimeUSEnglish(small_buf, sizeof(small_buf), 
  1255.                                "NEWGROUPS %y%m%d %H%M%S", &expandedTime);
  1256.         
  1257.         StrAllocCopy(command, small_buf);
  1258.  
  1259.       }
  1260.     else if(cd->type_wanted == LIST_WANTED)
  1261.     {
  1262.         
  1263.         cd->use_fancy_newsgroup_listing = FALSE;
  1264.         if (MSG_NewsgroupsLastUpdatedTime(cd->host))
  1265.         {
  1266.             cd->next_state = DISPLAY_NEWSGROUPS;
  1267.             return(0);
  1268.         }
  1269.         else
  1270.         {
  1271. #ifdef BUG_21013
  1272.             if(!FE_Confirm(ce->window_id, XP_GetString(XP_CONFIRM_SAVE_NEWSGROUPS)))
  1273.                 {
  1274.                 cd->next_state = NEWS_ERROR;
  1275.                 return(MK_INTERRUPTED);
  1276.                 }
  1277. #endif /* BUG_21013 */
  1278.  
  1279.             if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
  1280.             {
  1281.                 StrAllocCopy(command, "LIST XACTIVE");
  1282.                 cd->use_fancy_newsgroup_listing = TRUE;
  1283.             }
  1284.             else
  1285.             {
  1286.                    StrAllocCopy(command, "LIST");
  1287.             }
  1288.         }
  1289.     } 
  1290.     else if(cd->type_wanted == GROUP_WANTED) 
  1291.     {
  1292.         /* Don't use MKParse because the news: access URL doesn't follow traditional
  1293.          * rules. For instance, if the article reference contains a '#',
  1294.          * the rest of it is lost.
  1295.          */
  1296.         char * slash;
  1297.  
  1298.         StrAllocCopy(command, "GROUP ");
  1299.  
  1300.         slash = XP_STRCHR(cd->group_name, '/');  /* look for a slash */
  1301.         cd->first_art = 0;
  1302.         cd->last_art = 0;
  1303.         if (slash)
  1304.           {
  1305.             *slash = '\0';
  1306. #ifdef __alpha
  1307.             (void) sscanf(slash+1, "%d-%d", &cd->first_art, &cd->last_art);
  1308. #else
  1309.             (void) sscanf(slash+1, "%ld-%ld", &cd->first_art, &cd->last_art);
  1310. #endif
  1311.           }
  1312.  
  1313.         StrAllocCopy (cd->control_con->current_group, cd->group_name);
  1314.         StrAllocCat(command, cd->control_con->current_group);
  1315.       }
  1316.     else if (cd->type_wanted == SEARCH_WANTED)
  1317.     {
  1318.         if (MSG_QueryNewsExtension(cd->host, "SEARCH"))
  1319.         {
  1320.             /* use the SEARCH extension */
  1321.             char *slash = XP_STRCHR (cd->command_specific_data, '/');
  1322.             if (slash)
  1323.             {
  1324.                 char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
  1325.                 if (allocatedCommand)
  1326.                 {
  1327.                     StrAllocCopy (command, allocatedCommand);
  1328.                     XP_FREE(allocatedCommand);
  1329.                 }
  1330.             }
  1331.             cd->next_state = NNTP_RESPONSE;
  1332.             cd->next_state_after_response = NNTP_SEARCH_RESPONSE;
  1333.         }
  1334.         else
  1335.         {
  1336.             /* for XPAT, we have to GROUP into the group before searching */
  1337.             StrAllocCopy (command, "GROUP ");
  1338.             StrAllocCat (command, cd->group_name);
  1339.             cd->next_state = NNTP_RESPONSE;
  1340.             cd->next_state_after_response = NNTP_XPAT_SEND;
  1341.         }
  1342.     }
  1343.     else if (cd->type_wanted == PRETTY_NAMES_WANTED)
  1344.     {
  1345.         if (MSG_QueryNewsExtension(cd->host, "LISTPRETTY"))
  1346.         {
  1347.             cd->next_state = NNTP_LIST_PRETTY_NAMES;
  1348.             return 0;
  1349.         }
  1350.         else
  1351.         {
  1352.             XP_ASSERT(FALSE);
  1353.             cd->next_state = NNTP_ERROR;
  1354.         }
  1355.     }
  1356.     else if (cd->type_wanted == PROFILE_WANTED)
  1357.     {
  1358.         char *slash = XP_STRCHR (cd->command_specific_data, '/');
  1359.         if (slash)
  1360.         {
  1361.             char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
  1362.             if (allocatedCommand)
  1363.             {
  1364.                 StrAllocCopy (command, allocatedCommand);
  1365.                 XP_FREE(allocatedCommand);
  1366.             }
  1367.         }
  1368.         cd->next_state = NNTP_RESPONSE;
  1369.         if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW"))
  1370.             cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
  1371.         else
  1372.             cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
  1373.     }
  1374.     else if (cd->type_wanted == IDS_WANTED)
  1375.     {
  1376.         char *slash = XP_STRCHR(cd->group_name, '/');  /* look for a slash */
  1377.         if (slash)
  1378.             *slash = '\0';
  1379.  
  1380.         cd->next_state = NNTP_LIST_GROUP;
  1381.         return 0;
  1382.     }
  1383.     else  /* article or cancel */
  1384.     {
  1385.         if (cd->type_wanted == CANCEL_WANTED)
  1386.             StrAllocCopy(command, "HEAD ");
  1387.         else
  1388.             StrAllocCopy(command, "ARTICLE ");
  1389.         if (*cd->path != '<')
  1390.             StrAllocCat(command,"<");
  1391.         StrAllocCat(command, cd->path);
  1392.         if (XP_STRCHR(command+8, '>')==0) 
  1393.             StrAllocCat(command,">");
  1394.     }
  1395.  
  1396.     StrAllocCat(command, CRLF);
  1397.     ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
  1398.     NNTP_LOG_WRITE(command);
  1399.     XP_FREE(command);
  1400.  
  1401.     cd->next_state = NNTP_RESPONSE;
  1402.     if (cd->type_wanted != SEARCH_WANTED && cd->type_wanted != PROFILE_WANTED)
  1403.         cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
  1404.     cd->pause_for_read = TRUE;
  1405.     return(ce->status);
  1406.  
  1407. } /* sent first command */
  1408.  
  1409.  
  1410. /* interprets the server response from the first command sent
  1411.  *
  1412.  * returns negative if the server responds unexpectedly
  1413.  */
  1414. PRIVATE int
  1415. net_send_first_nntp_command_response (ActiveEntry *ce)
  1416. {
  1417.     NewsConData * cd = (NewsConData *)ce->con_data;
  1418.     int major_opcode = cd->response_code/100;
  1419.  
  1420.     if((major_opcode == 3 && cd->type_wanted == NEWS_POST)
  1421.          || (major_opcode == 2 && cd->type_wanted != NEWS_POST) )
  1422.       {
  1423.  
  1424.         cd->next_state = SETUP_NEWS_STREAM;
  1425.         cd->some_protocol_succeeded = TRUE;
  1426.  
  1427.         return(0);  /* good */
  1428.       }
  1429.     else
  1430.       {
  1431.         if (cd->response_code == 411 && cd->type_wanted == GROUP_WANTED)
  1432.             MSG_GroupNotFound(cd->pane, cd->host, 
  1433.                                 cd->control_con->current_group, TRUE /* opening group */);
  1434.         return net_display_html_error_state(ce);
  1435.       }
  1436.  
  1437.     /* start the graph progress indicator
  1438.      */
  1439.     FE_GraphProgressInit(ce->window_id, ce->URL_s, ce->URL_s->content_length);
  1440.     cd->destroy_graph_progress = TRUE;  /* we will need to destroy it */
  1441.     cd->original_content_length = ce->URL_s->content_length;
  1442.  
  1443.     return(ce->status);
  1444. }
  1445.  
  1446.  
  1447. PRIVATE int
  1448. net_send_group_for_article (ActiveEntry * ce)
  1449. {
  1450.   NewsConData * cd = (NewsConData *)ce->con_data;
  1451.  
  1452.   StrAllocCopy (cd->control_con->current_group, cd->group_name);
  1453.   PR_snprintf(cd->output_buffer, 
  1454.             OUTPUT_BUFFER_SIZE, 
  1455.             "GROUP %.512s" CRLF, 
  1456.             cd->control_con->current_group);
  1457.  
  1458.    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
  1459.                                       XP_STRLEN(cd->output_buffer));
  1460.     NNTP_LOG_WRITE(cd->output_buffer);
  1461.  
  1462.     cd->next_state = NNTP_RESPONSE;
  1463.     cd->next_state_after_response = NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE;
  1464.     cd->pause_for_read = TRUE;
  1465.  
  1466.     return(ce->status);
  1467. }
  1468.  
  1469. PRIVATE int
  1470. net_send_group_for_article_response (ActiveEntry * ce)
  1471. {
  1472.   NewsConData * cd = (NewsConData *)ce->con_data;
  1473.  
  1474.   /* ignore the response code and continue
  1475.    */
  1476.   cd->next_state = NNTP_SEND_ARTICLE_NUMBER;
  1477.  
  1478.   return(0);
  1479. }
  1480.  
  1481.  
  1482. PRIVATE int
  1483. net_send_article_number (ActiveEntry * ce)
  1484. {
  1485.   NewsConData * cd = (NewsConData *)ce->con_data;
  1486.  
  1487.   PR_snprintf(cd->output_buffer, 
  1488.             OUTPUT_BUFFER_SIZE, 
  1489.             "ARTICLE %lu" CRLF, 
  1490.             cd->article_num);
  1491.  
  1492.   ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
  1493.                                       XP_STRLEN(cd->output_buffer));
  1494.     NNTP_LOG_WRITE(cd->output_buffer);
  1495.  
  1496.     cd->next_state = NNTP_RESPONSE;
  1497.     cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
  1498.     cd->pause_for_read = TRUE;
  1499.  
  1500.     return(ce->status);
  1501. }
  1502.  
  1503.  
  1504.  
  1505.  
  1506. static char *
  1507. news_generate_news_url_fn (const char *dest, void *closure,
  1508.                            MimeHeaders *headers)
  1509. {
  1510.   ActiveEntry *ce = (ActiveEntry *) closure;
  1511.   /* NOTE: you can't use NewsConData here, because ce might refer
  1512.      to a file in the disk cache rather than an NNTP connection. */
  1513.   char *prefix = NET_ParseURL(ce->URL_s->address,
  1514.                               (GET_PROTOCOL_PART | GET_HOST_PART));
  1515.   char *new_dest = NET_Escape (dest, URL_XALPHAS);
  1516.   char *result = (char *) XP_ALLOC (XP_STRLEN (prefix) +
  1517.                                     (new_dest ? XP_STRLEN (new_dest) : 0)
  1518.                                     + 40);
  1519.   if (result && prefix)
  1520.     {
  1521.       XP_STRCPY (result, prefix);
  1522.       if (prefix[XP_STRLEN(prefix) - 1] != ':')
  1523.         /* If there is a host, it must be terminated with a slash. */
  1524.         XP_STRCAT (result, "/");
  1525.       XP_STRCAT (result, new_dest);
  1526.     }
  1527.   FREEIF (prefix);
  1528.   FREEIF (new_dest);
  1529.   return result;
  1530. }
  1531.  
  1532. static char *
  1533. news_generate_reference_url_fn (const char *dest, void *closure,
  1534.                                 MimeHeaders *headers)
  1535. {
  1536.   return news_generate_news_url_fn (dest, closure, headers);
  1537. }
  1538.  
  1539.  
  1540.  
  1541. /* Initiates the stream and sets state for the transfer
  1542.  *
  1543.  * returns negative if unable to setup stream
  1544.  */
  1545. PRIVATE int
  1546. net_setup_news_stream (ActiveEntry * ce)
  1547. {
  1548.     NewsConData * cd = (NewsConData *)ce->con_data;
  1549.     
  1550.     if (cd->type_wanted == NEWS_POST)
  1551.       {
  1552.         NET_ClearReadSelect(ce->window_id, ce->socket);
  1553.         NET_SetConnectSelect(ce->window_id, ce->socket);
  1554. #ifdef XP_WIN
  1555.         cd->calling_netlib_all_the_time = TRUE;
  1556.         NET_SetCallNetlibAllTheTime(ce->window_id, "mknews");
  1557. #endif /* XP_WIN */
  1558.  
  1559.         ce->con_sock = ce->socket;
  1560.         cd->next_state = NNTP_SEND_POST_DATA;
  1561.  
  1562.         NET_Progress(ce->window_id, XP_GetString(MK_MSG_DELIV_NEWS));
  1563.       }
  1564.     else if(cd->type_wanted == LIST_WANTED)
  1565.     {
  1566.         if (cd->use_fancy_newsgroup_listing)
  1567.             cd->next_state = NNTP_LIST_XACTIVE_RESPONSE;
  1568.         else
  1569.             cd->next_state = NNTP_READ_LIST_BEGIN;
  1570.     }
  1571.     else if(cd->type_wanted == GROUP_WANTED)
  1572.         cd->next_state = NNTP_XOVER_BEGIN;
  1573.     else if(cd->type_wanted == NEW_GROUPS)
  1574.         cd->next_state = NNTP_NEWGROUPS_BEGIN;
  1575.     else if(cd->type_wanted == ARTICLE_WANTED ||
  1576.             cd->type_wanted == CANCEL_WANTED)
  1577.         cd->next_state = NNTP_BEGIN_ARTICLE;
  1578.     else if (cd->type_wanted == SEARCH_WANTED)
  1579.         cd->next_state = NNTP_XPAT_SEND;
  1580.     else if (cd->type_wanted == PRETTY_NAMES_WANTED)
  1581.         cd->next_state = NNTP_LIST_PRETTY_NAMES;
  1582.     else if (cd->type_wanted == PROFILE_WANTED)
  1583.     {
  1584.         if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW"))
  1585.             cd->next_state = NNTP_PROFILE_ADD;
  1586.         else
  1587.             cd->next_state = NNTP_PROFILE_DELETE;
  1588.     }
  1589.     else
  1590.       {
  1591.         XP_ASSERT(0);
  1592.         return -1;
  1593.       }
  1594.  
  1595.    return(0); /* good */
  1596. }
  1597.  
  1598. /* This doesn't actually generate HTML - but it is our hook into the 
  1599.    message display code, so that we can get some values out of it
  1600.    after the headers-to-be-displayed have been parsed.
  1601.  */
  1602. static char *
  1603. news_generate_html_header_fn (const char *dest, void *closure,
  1604.                               MimeHeaders *headers)
  1605. {
  1606.   ActiveEntry *ce = (ActiveEntry *) closure;
  1607.   /* NOTE: you can't always use NewsConData here if, because ce might
  1608.      refer to a file in the disk cache rather than an NNTP connection. */
  1609.   NewsConData *cd = 0;
  1610.  
  1611.   XP_ASSERT(ce->protocol == NEWS_TYPE_URL ||
  1612.             ce->protocol == FILE_CACHE_TYPE_URL ||
  1613.             ce->protocol == MEMORY_CACHE_TYPE_URL);
  1614.   if (ce->protocol == NEWS_TYPE_URL)
  1615.     cd = (NewsConData *)ce->con_data;
  1616.  
  1617.   if (cd && cd->type_wanted == CANCEL_WANTED)
  1618.     {
  1619.       /* Save these for later (used in NEWS_DO_CANCEL state.) */
  1620.       cd->cancel_from =
  1621.         MimeHeaders_get(headers, HEADER_FROM, FALSE, FALSE);
  1622.       cd->cancel_newsgroups =
  1623.         MimeHeaders_get(headers, HEADER_NEWSGROUPS, FALSE, TRUE);
  1624.       cd->cancel_distribution =
  1625.         MimeHeaders_get(headers, HEADER_DISTRIBUTION, FALSE, FALSE);
  1626.       cd->cancel_id =
  1627.         MimeHeaders_get(headers, HEADER_MESSAGE_ID, FALSE, FALSE);
  1628.     }
  1629.   else
  1630.     {
  1631.       MSG_Pane *messagepane = ce->URL_s->msg_pane;
  1632.       if (!messagepane)
  1633.       {
  1634.         NNTP_LOG_NOTE(("news_generate_html_header_fn: url->msg_pane NULL for URL: %s", ce->URL_s->address));
  1635.         messagepane = MSG_FindPane(ce->window_id, MSG_MESSAGEPANE);
  1636.       }
  1637.       XP_ASSERT (!cd || cd->type_wanted == ARTICLE_WANTED);
  1638.  
  1639.       if (messagepane)
  1640.         MSG_ActivateReplyOptions (messagepane, headers);
  1641.     }
  1642.   return 0;
  1643. }
  1644.  
  1645.  
  1646.  
  1647. XP_Bool NET_IsNewsMessageURL (const char *url);
  1648.  
  1649. int
  1650. net_InitializeNewsFeData (ActiveEntry * ce)
  1651. {
  1652.   MimeDisplayOptions *opt;
  1653.  
  1654.   if (!NET_IsNewsMessageURL (ce->URL_s->address))
  1655.     {
  1656.       XP_ASSERT(0);
  1657.       return -1;
  1658.     }
  1659.  
  1660.   if (ce->URL_s->fe_data)
  1661.     {
  1662.       XP_ASSERT(0);
  1663.       return -1;
  1664.     }
  1665.  
  1666.   opt = XP_NEW (MimeDisplayOptions);
  1667.   if (!opt) return MK_OUT_OF_MEMORY;
  1668.   XP_MEMSET (opt, 0, sizeof(*opt));
  1669.  
  1670.   opt->generate_reference_url_fn      = news_generate_reference_url_fn;
  1671.   opt->generate_news_url_fn           = news_generate_news_url_fn;
  1672.   opt->generate_header_html_fn          = 0;
  1673.   opt->generate_post_header_html_fn      = news_generate_html_header_fn;
  1674.   opt->html_closure                      = ce;
  1675.  
  1676.   ce->URL_s->fe_data = opt;
  1677.   return 0;
  1678. }
  1679.  
  1680.  
  1681. PRIVATE int
  1682. net_begin_article(ActiveEntry * ce)
  1683. {
  1684.   NewsConData * cd = (NewsConData *)ce->con_data;
  1685.   if (cd->type_wanted != ARTICLE_WANTED &&
  1686.       cd->type_wanted != CANCEL_WANTED)
  1687.     return 0;
  1688.  
  1689.   /*  Set up the HTML stream
  1690.    */ 
  1691.   FREEIF (ce->URL_s->content_type);
  1692.   ce->URL_s->content_type = XP_STRDUP (MESSAGE_RFC822);
  1693.  
  1694. #ifdef NO_ARTICLE_CACHEING
  1695.   ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
  1696. #endif
  1697.  
  1698.   if (cd->type_wanted == CANCEL_WANTED)
  1699.     {
  1700.       XP_ASSERT(ce->format_out == FO_PRESENT);
  1701.       ce->format_out = FO_PRESENT;
  1702.     }
  1703.  
  1704.   /* Only put stuff in the fe_data if this URL is going to get
  1705.      passed to MIME_MessageConverter(), since that's the only
  1706.      thing that knows what to do with this structure. */
  1707.   if (CLEAR_CACHE_BIT(ce->format_out) == FO_PRESENT)
  1708.     {
  1709.       ce->status = net_InitializeNewsFeData (ce);
  1710.       if (ce->status < 0)
  1711.         {
  1712.           /* #### what error message? */
  1713.           return ce->status;
  1714.         }
  1715.     }
  1716.  
  1717.   cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id);
  1718.   XP_ASSERT (cd->stream);
  1719.   if (!cd->stream) return -1;
  1720.  
  1721.   cd->next_state = NNTP_READ_ARTICLE;
  1722.  
  1723.   return 0;
  1724. }
  1725.  
  1726.  
  1727. PRIVATE int
  1728. net_news_begin_authorize(ActiveEntry * ce)
  1729. {
  1730.     char * command = 0;
  1731.     NewsConData * cd = (NewsConData *)ce->con_data;
  1732.     char * username = 0, * munged_username = 0;
  1733.     char * cp;
  1734.     static char * last_username=0;
  1735.     static char * last_username_hostname=0;
  1736.  
  1737. #ifdef CACHE_NEWSGRP_PASSWORD
  1738.     /* reuse cached username from newsgroup folder info*/
  1739.     if (cd->pane && 
  1740.         (!net_news_last_username_probably_valid ||
  1741.          (last_username_hostname && 
  1742.           strcasecomp(last_username_hostname, cd->control_con->hostname)))) {
  1743.       username = SECNAV_UnMungeString(MSG_GetNewsgroupUsername(cd->pane));
  1744.       if (username && last_username &&
  1745.           !XP_STRCMP (username, last_username) &&
  1746.           (cd->previous_response_code == 281 || 
  1747.            cd->previous_response_code == 250 ||
  1748.            cd->previous_response_code == 211)) {
  1749.         FREEIF (username);
  1750.         MSG_SetNewsgroupUsername(cd->pane, NULL);
  1751.         MSG_SetNewsgroupPassword(cd->pane, NULL);
  1752.       }
  1753.     }
  1754. #endif
  1755.     
  1756.     if (cd->pane) 
  1757.     {
  1758.     /* Following a snews://username:password@newhost.domain.com/newsgroup.topic
  1759.      * backend calls MSG_Master::FindNewsHost() to locate the folderInfo and setting 
  1760.      * the username/password to the newsgroup folderInfo
  1761.      */
  1762.       username = SECNAV_UnMungeString(MSG_GetNewsgroupUsername(cd->pane));
  1763.       if (username && *username)
  1764.       {
  1765.         StrAllocCopy(last_username, username);
  1766.         StrAllocCopy(last_username_hostname, cd->control_con->hostname);
  1767.         /* use it for only once */
  1768.         MSG_SetNewsgroupUsername(cd->pane, NULL);
  1769.       }
  1770.       else {
  1771.           /* empty username; free and clear it so it will work with
  1772.            * our logic
  1773.            */
  1774.           FREEIF(username);
  1775.       }
  1776.     }
  1777.  
  1778.     /* If the URL/cd->control_con->hostname contains @ this must be triggered
  1779.      * from the bookmark. Use the embed username if we could.
  1780.      */
  1781.     if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL)
  1782.       {
  1783.         /* in this case the username and possibly
  1784.          * the password are in the URL
  1785.          */
  1786.         char * colon;
  1787.         *cp = '\0';
  1788.  
  1789.         colon = XP_STRCHR(cd->control_con->hostname, ':');
  1790.         if(colon)
  1791.             *colon = '\0';
  1792.  
  1793.         StrAllocCopy(username, cd->control_con->hostname);
  1794.         StrAllocCopy(last_username, cd->control_con->hostname);
  1795.         StrAllocCopy(last_username_hostname, cp+1);
  1796.  
  1797.         *cp = '@';
  1798.  
  1799.         if(colon)
  1800.             *colon = ':';
  1801.       }
  1802.     /* reuse global saved username if we think it is
  1803.      * valid
  1804.      */
  1805.     if (!username && net_news_last_username_probably_valid)
  1806.       {
  1807.         if( last_username_hostname &&
  1808.             !strcasecomp(last_username_hostname, cd->control_con->hostname) )
  1809.             StrAllocCopy(username, last_username);
  1810.         else
  1811.             net_news_last_username_probably_valid = FALSE;
  1812.       }
  1813.  
  1814.  
  1815.     if (!username) {
  1816.       username = FE_Prompt(ce->window_id,
  1817.                            XP_GetString(XP_PROMPT_ENTER_USERNAME),
  1818.                            username ? username : "");
  1819.       
  1820.       /* reset net_news_last_username_probably_valid to false */
  1821.       net_news_last_username_probably_valid = FALSE;
  1822.       if(!username) {
  1823.         ce->URL_s->error_msg = 
  1824.           NET_ExplainErrorDetails( MK_NNTP_AUTH_FAILED, "Aborted by user");
  1825.         return(MK_NNTP_AUTH_FAILED);
  1826.       }
  1827.       else {
  1828.         StrAllocCopy(last_username, username);
  1829.         StrAllocCopy(last_username_hostname, cd->control_con->hostname);
  1830.       }
  1831.     }
  1832.  
  1833. #ifdef CACHE_NEWSGRP_PASSWORD
  1834.     if (cd->pane && !MSG_GetNewsgroupUsername(cd->pane)) {
  1835.       munged_username = SECNAV_MungeString(username);
  1836.       MSG_SetNewsgroupUsername(cd->pane, munged_username);
  1837.       XP_FREEIF(munged_username);
  1838.     }
  1839. #endif
  1840.  
  1841.     StrAllocCopy(command, "AUTHINFO user ");
  1842.     StrAllocCat(command, username);
  1843.     StrAllocCat(command, CRLF);
  1844.  
  1845.     ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
  1846.     NNTP_LOG_WRITE(command);
  1847.  
  1848.     FREE(command);
  1849.     FREE(username);
  1850.  
  1851.     cd->next_state = NNTP_RESPONSE;
  1852.     cd->next_state_after_response = NNTP_AUTHORIZE_RESPONSE;;
  1853.  
  1854.     cd->pause_for_read = TRUE;
  1855.  
  1856.     return ce->status;
  1857. }
  1858.  
  1859. PRIVATE int
  1860. net_news_authorize_response(ActiveEntry * ce)
  1861. {
  1862.     NewsConData * cd = (NewsConData *)ce->con_data;
  1863.     static char * last_password = 0;
  1864.     static char * last_password_hostname = 0;
  1865.  
  1866.     if (281 == cd->response_code || 250 == cd->response_code) 
  1867.       {
  1868.         /* successful login */
  1869.  
  1870.         /* If we're here because the host demanded authentication before we
  1871.          * even sent a single command, then jump back to the beginning of everything
  1872.          */
  1873.         if (!cd->mode_reader_performed)
  1874.             cd->next_state = NNTP_SEND_MODE_READER;
  1875.         /* If we're here because the host needs pushed authentication, then we 
  1876.          * should jump back to SEND_LIST_EXTENSIONS
  1877.          */
  1878.         else if (MSG_GetNewsHostPushAuth(cd->host))
  1879.             cd->next_state = SEND_LIST_EXTENSIONS;
  1880.         else
  1881.             /* Normal authentication */
  1882.             cd->next_state = SEND_FIRST_NNTP_COMMAND;
  1883.  
  1884.         net_news_last_username_probably_valid = TRUE;
  1885.         return(0); 
  1886.       }
  1887.     else if (381 == cd->response_code)
  1888.       {
  1889.         /* password required
  1890.          */    
  1891.         char * command = 0;
  1892.         char * password = 0, * munged_password = 0;
  1893.         char * cp;
  1894.  
  1895.         if (cd->pane)
  1896.         {
  1897.             password = SECNAV_UnMungeString(MSG_GetNewsgroupPassword(cd->pane));
  1898.             /* use it only once */
  1899.             MSG_SetNewsgroupPassword(cd->pane, NULL);
  1900.         }
  1901.  
  1902.         if (net_news_last_username_probably_valid 
  1903.             && last_password
  1904.             && last_password_hostname
  1905.             && !strcasecomp(last_password_hostname, cd->control_con->hostname))
  1906.           {
  1907. #ifdef CACHE_NEWSGRP_PASSWORD
  1908.             if (cd->pane)
  1909.                 password = SECNAV_UnMungeString(MSG_GetNewsgroupPassword(cd->pane));
  1910. #else
  1911.             StrAllocCopy(password, last_password);
  1912. #endif
  1913.           }
  1914.         else if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL)
  1915.           {
  1916.             /* in this case the username and possibly
  1917.              * the password are in the URL
  1918.              */
  1919.             char * colon;
  1920.             *cp = '\0';
  1921.     
  1922.             colon = XP_STRCHR(cd->control_con->hostname, ':');
  1923.             if(colon)
  1924.               {
  1925.                 *colon = '\0';
  1926.     
  1927.                 StrAllocCopy(password, colon+1);
  1928.                 StrAllocCopy(last_password, colon+1);
  1929.                 StrAllocCopy(last_password_hostname, cp+1);
  1930.  
  1931.                 *colon = ':';
  1932.               }
  1933.     
  1934.             *cp = '@';
  1935.     
  1936.           }
  1937.         if (!password) {
  1938.           password = 
  1939.             FE_PromptPassword(ce->window_id, XP_GetString( 
  1940.                        XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS ) );
  1941.           net_news_last_username_probably_valid = FALSE;
  1942.         }
  1943.           
  1944.         if(!password)    {
  1945.           ce->URL_s->error_msg = 
  1946.             NET_ExplainErrorDetails(MK_NNTP_AUTH_FAILED, "Aborted by user");
  1947.           return(MK_NNTP_AUTH_FAILED);
  1948.         }
  1949.         else {
  1950.           StrAllocCopy(last_password, password);
  1951.           StrAllocCopy(last_password_hostname, cd->control_con->hostname);
  1952.         }
  1953.  
  1954. #ifdef CACHE_NEWSGRP_PASSWORD
  1955.         if (cd->pane && !MSG_GetNewsgroupPassword(cd->pane)) {
  1956.           munged_password = SECNAV_MungeString(password);
  1957.           MSG_SetNewsgroupPassword(cd->pane, munged_password);
  1958.           XP_FREEIF(munged_password);
  1959.         }
  1960. #endif
  1961.  
  1962.         StrAllocCopy(command, "AUTHINFO pass ");
  1963.         StrAllocCat(command, password);
  1964.         StrAllocCat(command, CRLF);
  1965.     
  1966.         ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
  1967.         NNTP_LOG_WRITE(command);
  1968.  
  1969.         FREE(command);
  1970.         FREE(password);
  1971.  
  1972.         cd->next_state = NNTP_RESPONSE;
  1973.         cd->next_state_after_response = NNTP_PASSWORD_RESPONSE;
  1974.         cd->pause_for_read = TRUE;
  1975.  
  1976.         return ce->status;
  1977.       }
  1978.     else
  1979.       {
  1980.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  1981.                                     MK_NNTP_AUTH_FAILED,
  1982.                                     cd->response_txt ? cd->response_txt : "");
  1983. #ifdef CACHE_NEWSGRP_PASSWORD
  1984.         if (cd->pane)
  1985.           MSG_SetNewsgroupUsername(cd->pane, NULL);
  1986. #endif
  1987.         net_news_last_username_probably_valid = FALSE;
  1988.  
  1989.         return(MK_NNTP_AUTH_FAILED);
  1990.       }
  1991.         
  1992.     XP_ASSERT(0); /* should never get here */
  1993.     return(-1);
  1994.  
  1995. }
  1996.  
  1997. PRIVATE int
  1998. net_news_password_response(ActiveEntry * ce)
  1999. {
  2000.     NewsConData * cd = (NewsConData *)ce->con_data;
  2001.  
  2002.     if (281 == cd->response_code || 250 == cd->response_code) 
  2003.       {
  2004.         /* successful login */
  2005.          
  2006.         /* If we're here because the host demanded authentication before we
  2007.          * even sent a single command, then jump back to the beginning of everything
  2008.          */
  2009.         if (!cd->mode_reader_performed)
  2010.             cd->next_state = NNTP_SEND_MODE_READER;
  2011.         /* If we're here because the host needs pushed authentication, then we 
  2012.          * should jump back to SEND_LIST_EXTENSIONS
  2013.          */
  2014.         else if (MSG_GetNewsHostPushAuth(cd->host))
  2015.             cd->next_state = SEND_LIST_EXTENSIONS;
  2016.         else
  2017.             /* Normal authentication */
  2018.             cd->next_state = SEND_FIRST_NNTP_COMMAND;
  2019.  
  2020.         net_news_last_username_probably_valid = TRUE;
  2021.         MSG_ResetXOVER( ce->URL_s->msg_pane, &cd->xover_parse_state );
  2022.         return(0);
  2023.       }
  2024.     else
  2025.       {
  2026.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  2027.                                     MK_NNTP_AUTH_FAILED,
  2028.                                     cd->response_txt ? cd->response_txt : "");
  2029. #ifdef CACHE_NEWSGRP_PASSWORD
  2030.         if (cd->pane)
  2031.           MSG_SetNewsgroupPassword(cd->pane, NULL);
  2032. #endif
  2033.         return(MK_NNTP_AUTH_FAILED);
  2034.       }
  2035.         
  2036.     XP_ASSERT(0); /* should never get here */
  2037.     return(-1);
  2038. }
  2039.  
  2040. PRIVATE int
  2041. net_display_newsgroups (ActiveEntry *ce)
  2042. {
  2043.     NewsConData * cd = (NewsConData *)ce->con_data;
  2044.  
  2045.     cd->next_state = NEWS_DONE;
  2046.     cd->pause_for_read = FALSE;
  2047.  
  2048.     NNTP_LOG_NOTE(("about to display newsgroups. path: %s",cd->path));
  2049.  
  2050. #if 0
  2051.     /* #### Now ignoring "news:alt.fan.*"
  2052.        Need to open the root tree of the default news host and keep
  2053.        opening one child at each level until we've exhausted the
  2054.        wildcard...
  2055.      */
  2056.     if(rv < 0)
  2057.        return(rv);  
  2058.     else
  2059. #endif
  2060.        return(MK_DATA_LOADED);  /* all finished */
  2061. }
  2062.  
  2063. PRIVATE int
  2064. net_newgroups_begin (ActiveEntry *ce)
  2065. {
  2066.     NewsConData * cd = (NewsConData *)ce->con_data;
  2067.  
  2068.     cd->next_state = NNTP_NEWGROUPS;
  2069.     NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));
  2070.  
  2071.     ce->bytes_received = 0;
  2072.  
  2073.     return(ce->status);
  2074.  
  2075. }
  2076.  
  2077. PRIVATE int
  2078. net_process_newgroups (ActiveEntry *ce)
  2079. {
  2080.     NewsConData * cd = (NewsConData *)ce->con_data;
  2081.     char *line, *s, *s1, *s2, *flag;
  2082.     int32 oldest, youngest;
  2083.  
  2084.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
  2085.                                         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  2086.  
  2087.     if(ce->status == 0)
  2088.       {
  2089.         cd->next_state = NNTP_ERROR;
  2090.         cd->pause_for_read = FALSE;
  2091.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2092.         return(MK_NNTP_SERVER_ERROR);
  2093.       }
  2094.  
  2095.     if(!line)
  2096.         return(ce->status);  /* no line yet */
  2097.  
  2098.     if(ce->status<0)
  2099.       {
  2100.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  2101.  
  2102.         /* return TCP error
  2103.          */
  2104.         return MK_TCP_READ_ERROR;
  2105.       }
  2106.  
  2107.     /* End of list? 
  2108.      */
  2109.     if (line[0]=='.' && line[1]=='\0')
  2110.       {
  2111.         cd->pause_for_read = FALSE;
  2112.         if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
  2113.         {
  2114.             cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
  2115.             if (cd->group_name)
  2116.             {
  2117.                 cd->next_state = NNTP_LIST_XACTIVE;
  2118. #ifdef DEBUG_bienvenu
  2119.                 XP_Trace("listing xactive for %s\n", cd->group_name);
  2120. #endif
  2121.                 return 0;
  2122.             }
  2123.         }
  2124.         cd->next_state = NEWS_DONE;
  2125.  
  2126.         if(ce->bytes_received == 0)
  2127.           {
  2128.             /* #### no new groups */
  2129.           }
  2130.  
  2131.         NET_SortNewsgroups(cd->control_con->hostname, cd->control_con->secure);
  2132.         NET_SaveNewsgroupsToDisk(cd->control_con->hostname, cd->control_con->secure);
  2133.         if(ce->status > 0)
  2134.             return MK_DATA_LOADED;
  2135.         else
  2136.             return ce->status;
  2137.       }
  2138.     else if (line [0] == '.' && line [1] == '.')
  2139.       /* The NNTP server quotes all lines beginning with "." by doubling it. */
  2140.       line++;
  2141.  
  2142.     /* almost correct
  2143.      */
  2144.     if(ce->status > 1)
  2145.       {
  2146.         ce->bytes_received += ce->status;
  2147.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
  2148.       }
  2149.  
  2150.     /* format is "rec.arts.movies.past-films 7302 7119 y"
  2151.      */
  2152.     s = XP_STRCHR (line, ' ');
  2153.     if (s)
  2154.       {
  2155.         *s = 0;
  2156.         s1 = s+1;
  2157.         s = XP_STRCHR (s1, ' ');
  2158.         if (s)
  2159.           {
  2160.             *s = 0;
  2161.             s2 = s+1;
  2162.             s = XP_STRCHR (s2, ' ');
  2163.             if (s)
  2164.             {
  2165.               *s = 0;
  2166.               flag = s+1;
  2167.             }
  2168.           }
  2169.       }
  2170.     youngest = s2 ? atol(s1) : 0;
  2171.     oldest   = s1 ? atol(s2) : 0;
  2172.  
  2173.     ce->bytes_received++;  /* small numbers of groups never seem
  2174.                            * to trigger this
  2175.                            */
  2176.  
  2177.     MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
  2178.                         line, oldest, youngest, flag, FALSE);
  2179.     if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
  2180.     {
  2181.         MSG_SetGroupNeedsExtraInfo(cd->host, line, TRUE);
  2182.     }
  2183.     return(ce->status);
  2184. }
  2185.  
  2186.         
  2187. /* Ahhh, this like print's out the headers and stuff
  2188.  *
  2189.  * always returns 0
  2190.  */
  2191. PRIVATE int
  2192. net_read_news_list_begin (ActiveEntry *ce)
  2193. {
  2194.     NewsConData * cd = (NewsConData *)ce->con_data;
  2195.  
  2196.     cd->next_state = NNTP_READ_LIST;
  2197.  
  2198.     NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));
  2199.      
  2200.     return(ce->status);
  2201.  
  2202.  
  2203. }
  2204.  
  2205. /* display a list of all or part of the newsgroups list
  2206.  * from the news server
  2207.  */
  2208. PRIVATE int
  2209. net_read_news_list (ActiveEntry *ce)
  2210. {
  2211.     char * line;
  2212.     char * description;
  2213.     int i=0;
  2214.     NewsConData * cd = (NewsConData *)ce->con_data;
  2215.    
  2216.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf, 
  2217.                                            &cd->data_buf_size, (Bool*) &cd->pause_for_read);
  2218.  
  2219.     if(ce->status == 0)
  2220.       {
  2221.         cd->next_state = NNTP_ERROR;
  2222.         cd->pause_for_read = FALSE;
  2223.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2224.         return(MK_NNTP_SERVER_ERROR);
  2225.       }
  2226.  
  2227.     if(!line)
  2228.         return(ce->status);  /* no line yet */
  2229.  
  2230.     if(ce->status<0)
  2231.       {
  2232.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  2233.  
  2234.         /* return TCP error
  2235.          */
  2236.         return MK_TCP_READ_ERROR;
  2237.       }
  2238.  
  2239.             /* End of list? */
  2240.     if (line[0]=='.' && line[1]=='\0')
  2241.       {
  2242.         if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
  2243.         {
  2244.             cd->next_state = NNTP_LIST_PRETTY_NAMES;
  2245.         }
  2246.         else
  2247.         {
  2248.             cd->next_state = DISPLAY_NEWSGROUPS;
  2249.         }
  2250.         cd->pause_for_read = FALSE;
  2251.  
  2252.         NET_SortNewsgroups(cd->control_con->hostname, cd->control_con->secure);
  2253.         NET_SaveNewsgroupsToDisk(cd->control_con->hostname, cd->control_con->secure);
  2254.         return 0;  
  2255.       }
  2256.     else if (line [0] == '.' && line [1] == '.')
  2257.       /* The NNTP server quotes all lines beginning with "." by doubling it. */
  2258.       line++;
  2259.  
  2260.     /* almost correct
  2261.      */
  2262.     if(ce->status > 1)
  2263.       {
  2264.         ce->bytes_received += ce->status;
  2265.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
  2266.       }
  2267.  
  2268.     /* find whitespace seperator if it exits */
  2269.     for(i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++)
  2270.         ;  /* null body */
  2271.  
  2272.     if(line[i] == '\0')
  2273.         description = &line[i];
  2274.     else
  2275.         description = &line[i+1];
  2276.  
  2277.     line[i] = 0; /* terminate group name */
  2278.  
  2279.     /* store all the group names 
  2280.      */
  2281.     MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
  2282.                         line, 0, 0, "", FALSE);
  2283.  
  2284.  
  2285.     return(ce->status);
  2286. }
  2287.  
  2288.  
  2289.  
  2290.  
  2291. /* start the xover command
  2292.  */
  2293. PRIVATE int
  2294. net_read_xover_begin (ActiveEntry *ce)
  2295. {
  2296.     NewsConData * cd = (NewsConData *)ce->con_data;
  2297.     int32 count;     /* Response fields */
  2298.  
  2299.     /* Make sure we never close and automatically reopen the connection at this
  2300.        point; we'll confuse libmsg too much... */
  2301.  
  2302.     cd->some_protocol_succeeded = TRUE;
  2303.  
  2304.     /* We have just issued a GROUP command and read the response.
  2305.        Now parse that response to help decide which articles to request
  2306.        xover data for.
  2307.      */
  2308.     sscanf(cd->response_txt,
  2309. #ifdef __alpha
  2310.            "%d %d %d", 
  2311. #else
  2312.            "%ld %ld %ld", 
  2313. #endif
  2314.            &count, 
  2315.            &cd->first_possible_art, 
  2316.            &cd->last_possible_art);
  2317.  
  2318.     /* We now know there is a summary line there; make sure it has the
  2319.        right numbers in it. */
  2320.     ce->status = MSG_DisplaySubscribedGroup(cd->pane,
  2321.                                             cd->host,
  2322.                                             cd->group_name,
  2323.                                             cd->first_possible_art,
  2324.                                             cd->last_possible_art,
  2325.                                             count, TRUE);
  2326.     if (ce->status < 0) return ce->status;
  2327.  
  2328.     cd->num_loaded = 0;
  2329.     cd->num_wanted = net_NewsChunkSize > 0 ? net_NewsChunkSize : 1L << 30;
  2330.  
  2331.     cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
  2332.     cd->pause_for_read = FALSE;
  2333.     return 0;
  2334. }
  2335.  
  2336.  
  2337.  
  2338. PRIVATE int
  2339. net_figure_next_chunk(ActiveEntry *ce)
  2340. {
  2341.     NewsConData * cd = (NewsConData *)ce->con_data;
  2342.  
  2343.     XP_Bool secure_p = (ce->URL_s->address[0] == 's' ||
  2344.                         ce->URL_s->address[0] == 'S');
  2345.     char *host_and_port = NET_ParseURL (ce->URL_s->address, GET_HOST_PART);
  2346.  
  2347.     if (!host_and_port) return MK_OUT_OF_MEMORY;
  2348.  
  2349.     if (cd->first_art > 0) {
  2350.       ce->status = MSG_AddToKnownArticles(cd->pane, cd->host,
  2351.                                          cd->group_name,
  2352.                                          cd->first_art, cd->last_art);
  2353.       if (ce->status < 0) {
  2354.         FREEIF (host_and_port);
  2355.         return ce->status;
  2356.       }
  2357.     }
  2358.                                          
  2359.  
  2360.     if (cd->num_loaded >= cd->num_wanted) {
  2361.       FREEIF (host_and_port);
  2362.       cd->next_state = NEWS_PROCESS_XOVER;
  2363.       cd->pause_for_read = FALSE;
  2364.       return 0;
  2365.     }
  2366.  
  2367.  
  2368.     ce->status = MSG_GetRangeOfArtsToDownload(cd->pane,
  2369.                                              &cd->xover_parse_state,
  2370.                                              cd->host,
  2371.                                              cd->group_name,
  2372.                                              cd->first_possible_art,
  2373.                                              cd->last_possible_art,
  2374.                                              cd->num_wanted - cd->num_loaded,
  2375.                                              &(cd->first_art),
  2376.                                              &(cd->last_art));
  2377.  
  2378.     if (ce->status < 0) {
  2379.       FREEIF (host_and_port);
  2380.       return ce->status;
  2381.     }
  2382.  
  2383.  
  2384.     if (cd->first_art <= 0 || cd->first_art > cd->last_art) {
  2385.       /* Nothing more to get. */
  2386.       FREEIF (host_and_port);
  2387.       cd->next_state = NEWS_PROCESS_XOVER;
  2388.       cd->pause_for_read = FALSE;
  2389.       return 0;
  2390.     }
  2391.  
  2392.     NNTP_LOG_NOTE(("    Chunk will be (%ld-%ld)", cd->first_art, cd->last_art));
  2393.  
  2394.     cd->article_num = cd->first_art;
  2395.     ce->status = MSG_InitXOVER (cd->pane,
  2396.                                cd->host, cd->group_name,
  2397.                                cd->first_art, cd->last_art,
  2398.                                cd->first_possible_art, cd->last_possible_art,
  2399.                                &cd->xover_parse_state);
  2400.     FREEIF (host_and_port);
  2401.  
  2402.     if (ce->status < 0) {
  2403.       return ce->status;
  2404.     }
  2405.  
  2406.     cd->pause_for_read = FALSE;
  2407.     if (cd->control_con->no_xover) cd->next_state = NNTP_READ_GROUP;
  2408.     else cd->next_state = NNTP_XOVER_SEND;
  2409.  
  2410.     return 0;
  2411. }
  2412.  
  2413. PRIVATE int
  2414. net_xover_send (ActiveEntry *ce)
  2415. {        
  2416.     NewsConData * cd = (NewsConData *)ce->con_data;
  2417.  
  2418.     PR_snprintf(cd->output_buffer, 
  2419.                 OUTPUT_BUFFER_SIZE,
  2420.                 "XOVER %ld-%ld" CRLF, 
  2421.                 cd->first_art, 
  2422.                 cd->last_art);
  2423.  
  2424.     /* printf("XOVER %ld-%ld\n", cd->first_art, cd->last_art); */
  2425.  
  2426.     NNTP_LOG_WRITE(cd->output_buffer);
  2427.  
  2428.     cd->next_state = NNTP_RESPONSE;
  2429.     cd->next_state_after_response = NNTP_XOVER_RESPONSE;
  2430.     cd->pause_for_read = TRUE;
  2431.  
  2432.     NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_LISTARTICLES));
  2433.  
  2434.     return((int) NET_BlockingWrite(ce->socket, 
  2435.                                    cd->output_buffer, 
  2436.                                    XP_STRLEN(cd->output_buffer)));
  2437.     NNTP_LOG_WRITE(cd->output_buffer);
  2438.  
  2439. }
  2440.  
  2441. /* see if the xover response is going to return us data
  2442.  * if the proper code isn't returned then assume xover
  2443.  * isn't supported and use
  2444.  * normal read_group
  2445.  */
  2446. PRIVATE int
  2447. net_read_xover_response (ActiveEntry *ce)
  2448. {
  2449.     NewsConData * cd = (NewsConData *)ce->con_data;
  2450.  
  2451. #ifdef TEST_NO_XOVER_SUPPORT
  2452.     cd->response_code = 500; /* pretend XOVER generated an error */
  2453. #endif
  2454.  
  2455.     if(cd->response_code != 224)
  2456.       {
  2457.         /* If we didn't get back "224 data follows" from the XOVER request,
  2458.            then that must mean that this server doesn't support XOVER.  Or
  2459.            maybe the server's XOVER support is busted or something.  So,
  2460.            in that case, fall back to the very slow HEAD method.
  2461.  
  2462.            But, while debugging here at HQ, getting into this state means
  2463.            something went very wrong, since our servers do XOVER.  Thus
  2464.            the assert.
  2465.          */
  2466.         /*XP_ASSERT (0);*/
  2467.         cd->next_state = NNTP_READ_GROUP;
  2468.         cd->control_con->no_xover = TRUE;
  2469.       }
  2470.     else
  2471.       {
  2472.         cd->next_state = NNTP_XOVER;
  2473.       }
  2474.  
  2475.     return(0);  /* continue */
  2476. }
  2477.  
  2478. /* process the xover list as it comes from the server
  2479.  * and load it into the sort list.  
  2480.  */
  2481. PRIVATE int
  2482. net_read_xover (ActiveEntry *ce)
  2483. {
  2484.     NewsConData * cd = (NewsConData *)ce->con_data;
  2485.     char *line;
  2486.  
  2487.     ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
  2488.                                      &cd->data_buf_size,
  2489.                                      (Bool*)&cd->pause_for_read);
  2490.  
  2491.     if(ce->status == 0)
  2492.       {
  2493.         NNTP_LOG_NOTE(("received unexpected TCP EOF!!!!  aborting!"));
  2494.         cd->next_state = NNTP_ERROR;
  2495.         cd->pause_for_read = FALSE;
  2496.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2497.         return(MK_NNTP_SERVER_ERROR);
  2498.       }
  2499.  
  2500.     if(!line)
  2501.       {
  2502.         return(ce->status);  /* no line yet or TCP error */
  2503.       }
  2504.  
  2505.     if(ce->status<0) 
  2506.       {
  2507.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR,
  2508.                                                       SOCKET_ERRNO);
  2509.  
  2510.         /* return TCP error
  2511.          */
  2512.         return MK_TCP_READ_ERROR;
  2513.       }
  2514.  
  2515.     if(line[0] == '.' && line[1] == '\0')
  2516.       {
  2517.         cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
  2518.         cd->pause_for_read = FALSE;
  2519.         return(0);
  2520.       }
  2521.     else if (line [0] == '.' && line [1] == '.')
  2522.       /* The NNTP server quotes all lines beginning with "." by doubling it. */
  2523.       line++;
  2524.  
  2525.     /* almost correct
  2526.      */
  2527.     if(ce->status > 1)
  2528.       {
  2529.         ce->bytes_received += ce->status;
  2530. /*        FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status,
  2531.                          ce->URL_s->content_length);
  2532. */
  2533.       }
  2534.  
  2535.     ce->status = MSG_ProcessXOVER (cd->pane, line, &cd->xover_parse_state);
  2536.  
  2537.     cd->num_loaded++;
  2538.  
  2539.     return ce->status; /* keep going */
  2540. }
  2541.  
  2542.  
  2543. /* Finished processing all the XOVER data.
  2544.  */
  2545. PRIVATE int
  2546. net_process_xover (ActiveEntry *ce)
  2547. {
  2548.     NewsConData * cd = (NewsConData *)ce->con_data;
  2549.  
  2550.  
  2551.     /*    if (cd->xover_parse_state) { ### dmb - we need a different check */
  2552.       ce->status = MSG_FinishXOVER (cd->pane, &cd->xover_parse_state, 0);
  2553.       XP_ASSERT (!cd->xover_parse_state);
  2554.       if (ce->status < 0) return ce->status;
  2555.  
  2556.     cd->next_state = NEWS_DONE;
  2557.  
  2558.     return(MK_DATA_LOADED);
  2559. }
  2560.  
  2561.  
  2562. PRIVATE int net_profile_add (ActiveEntry *ce)
  2563. {
  2564. #if 0
  2565.     NewsConData *cd = (NewsConData*) ce->con_data;
  2566.     char *slash = XP_STRRCHR(ce->URL_s->address, '/');
  2567.     if (slash)
  2568.     {
  2569.         XP_STRCPY (cd->output_buffer, slash + 1);
  2570.         XP_STRCAT (cd->output_buffer, CRLF);
  2571.         ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  2572.         NNTP_LOG_WRITE(cd->output_buffer);
  2573.  
  2574.         cd->next_state = NNTP_RESPONSE;
  2575.         cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
  2576.         cd->pause_for_read = TRUE;
  2577.     }
  2578.     else
  2579.     {
  2580.         cd->next_state = NNTP_ERROR;
  2581.         ce->status = -1;
  2582.     }
  2583.  
  2584.     return ce->status;
  2585. #else
  2586.     XP_ASSERT(FALSE);
  2587.     return -1;
  2588. #endif
  2589. }
  2590.  
  2591. PRIVATE int net_profile_add_response (ActiveEntry *ce)
  2592. {
  2593.     NewsConData *cd = (NewsConData*) ce->con_data;
  2594.  
  2595.     if (cd->response_code >= 200 && cd->response_code <= 299)
  2596.     {
  2597.         MSG_AddProfileGroup (cd->pane, cd->host, cd->response_txt);
  2598.         cd->next_state = NEWS_DONE;
  2599.     }
  2600.     else
  2601.     {
  2602.         cd->next_state = NNTP_ERROR;
  2603.         cd->pause_for_read = FALSE;
  2604.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2605.         return MK_NNTP_SERVER_ERROR;
  2606.     }
  2607.  
  2608.     return ce->status;
  2609. }
  2610.  
  2611. PRIVATE int net_profile_delete (ActiveEntry *ce)
  2612. {
  2613. #if 0
  2614.     NewsConData *cd = (NewsConData*) ce->con_data;
  2615.     char *slash = XP_STRRCHR(ce->URL_s->address, '/');
  2616.     if (slash)
  2617.     {
  2618.         XP_STRCPY (cd->output_buffer, slash + 1);
  2619.         XP_STRCAT (cd->output_buffer, CRLF);
  2620.         ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  2621.         NNTP_LOG_WRITE(cd->output_buffer);
  2622.  
  2623.         cd->next_state = NNTP_RESPONSE;
  2624.         cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
  2625.         cd->pause_for_read = TRUE;
  2626.     }
  2627.     else
  2628.     {
  2629.         cd->next_state = NNTP_ERROR;
  2630.         ce->status = -1;
  2631.     }
  2632.  
  2633.     return ce->status;
  2634. #else
  2635.     XP_ASSERT(FALSE);
  2636.     return -1;
  2637. #endif
  2638. }
  2639.  
  2640. PRIVATE int net_profile_delete_response (ActiveEntry *ce)
  2641. {
  2642.     NewsConData *cd = (NewsConData*) ce->con_data;
  2643.  
  2644.     if (cd->response_code >= 200 && cd->response_code <= 299)
  2645.         cd->next_state = NEWS_DONE;
  2646.     else
  2647.     {
  2648.         cd->next_state = NNTP_ERROR;
  2649.         cd->pause_for_read = FALSE;
  2650.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2651.         return MK_NNTP_SERVER_ERROR;
  2652.     }
  2653.  
  2654.     return ce->status;
  2655. }
  2656.  
  2657. PRIVATE
  2658. int
  2659. net_read_news_group (ActiveEntry *ce)
  2660. {
  2661.     NewsConData * cd = (NewsConData *)ce->con_data;
  2662.  
  2663.     if(cd->article_num > cd->last_art)
  2664.       {  /* end of groups */
  2665.  
  2666.         cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
  2667.         cd->pause_for_read = FALSE;
  2668.         return(0);
  2669.       }
  2670.     else
  2671.       {
  2672.         PR_snprintf(cd->output_buffer, 
  2673.                    OUTPUT_BUFFER_SIZE,  
  2674.                    "HEAD %ld" CRLF, 
  2675.                    cd->article_num++);
  2676.         cd->next_state = NNTP_RESPONSE;
  2677.         cd->next_state_after_response = NNTP_READ_GROUP_RESPONSE;
  2678.  
  2679.         cd->pause_for_read = TRUE;
  2680.  
  2681.         NNTP_LOG_WRITE(cd->output_buffer);
  2682.         return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, XP_STRLEN(cd->output_buffer)));
  2683.       }
  2684. }
  2685.  
  2686. /* See if the "HEAD" command was successful
  2687.  */
  2688. PRIVATE int
  2689. net_read_news_group_response (ActiveEntry *ce)
  2690. {
  2691.   NewsConData * cd = (NewsConData *)ce->con_data;
  2692.  
  2693.   if (cd->response_code == 221)
  2694.     {     /* Head follows - parse it:*/
  2695.       cd->next_state = NNTP_READ_GROUP_BODY;
  2696.  
  2697.       if(cd->message_id)
  2698.         *cd->message_id = '\0';
  2699.  
  2700.       /* Give the message number to the header parser. */
  2701.       return MSG_ProcessNonXOVER (cd->pane, cd->response_txt,
  2702.                                   &cd->xover_parse_state);
  2703.     }
  2704.   else
  2705.     {
  2706.       NNTP_LOG_NOTE(("Bad group header found!"));
  2707.       cd->next_state = NNTP_READ_GROUP;
  2708.       return(0);
  2709.     }
  2710. }
  2711.  
  2712. /* read the body of the "HEAD" command
  2713.  */
  2714. PRIVATE int
  2715. net_read_news_group_body (ActiveEntry *ce)
  2716. {
  2717.   NewsConData * cd = (NewsConData *)ce->con_data;
  2718.   char *line;
  2719.  
  2720.   ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
  2721.                                    &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  2722.  
  2723.   if(ce->status == 0)
  2724.     {
  2725.       cd->next_state = NNTP_ERROR;
  2726.       cd->pause_for_read = FALSE;
  2727.       ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  2728.       return(MK_NNTP_SERVER_ERROR);
  2729.     }
  2730.  
  2731.   /* if TCP error of if there is not a full line yet return
  2732.    */
  2733.   if(!line)
  2734.     return ce->status;
  2735.  
  2736.   if(ce->status < 0)
  2737.     {
  2738.       ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  2739.  
  2740.       /* return TCP error
  2741.        */
  2742.       return MK_TCP_READ_ERROR;
  2743.     }
  2744.  
  2745.     NNTP_LOG_NOTE(("read_group_body: got line: %s|",line));
  2746.  
  2747.   /* End of body? */
  2748.   if (line[0]=='.' && line[1]=='\0')
  2749.     {
  2750.       cd->next_state = NNTP_READ_GROUP;
  2751.       cd->pause_for_read = FALSE;
  2752.     }
  2753.   else if (line [0] == '.' && line [1] == '.')
  2754.     /* The NNTP server quotes all lines beginning with "." by doubling it. */
  2755.     line++;
  2756.  
  2757.   return MSG_ProcessNonXOVER (cd->pane, line, &cd->xover_parse_state);
  2758. }
  2759.  
  2760.  
  2761. PRIVATE int
  2762. net_send_news_post_data(ActiveEntry * ce)
  2763. {
  2764.     NewsConData * cd = (NewsConData *) ce->con_data;
  2765.  
  2766.     /* returns 0 on done and negative on error
  2767.      * positive if it needs to continue.
  2768.      */
  2769.     ce->status = NET_WritePostData(ce->window_id, ce->URL_s,
  2770.                                   ce->socket,
  2771.                                   &cd->write_post_data_data,
  2772.                                   TRUE);
  2773.  
  2774.     cd->pause_for_read = TRUE;
  2775.  
  2776.     if(ce->status == 0)
  2777.       {
  2778.         /* normal done
  2779.          */
  2780.         XP_STRCPY(cd->output_buffer, CRLF "." CRLF);
  2781.         NNTP_LOG_WRITE(cd->output_buffer);
  2782.         ce->status = (int) NET_BlockingWrite(ce->socket,
  2783.                                             cd->output_buffer,
  2784.                                             XP_STRLEN(cd->output_buffer));
  2785.         NNTP_LOG_WRITE(cd->output_buffer);
  2786.  
  2787.         NET_Progress(ce->window_id,
  2788.                     XP_GetString(XP_MESSAGE_SENT_WAITING_NEWS_REPLY));
  2789.  
  2790.         NET_ClearConnectSelect(ce->window_id, ce->socket);
  2791. #ifdef XP_WIN
  2792.         if(cd->calling_netlib_all_the_time)
  2793.         {
  2794.             cd->calling_netlib_all_the_time = FALSE;
  2795.             NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
  2796.         }
  2797. #endif
  2798.         NET_SetReadSelect(ce->window_id, ce->socket);
  2799.         ce->con_sock = 0;
  2800.  
  2801.         cd->next_state = NNTP_RESPONSE;
  2802.         cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
  2803.         return(0);
  2804.       }
  2805.  
  2806.     return(ce->status);
  2807.  
  2808. }
  2809.  
  2810.  
  2811. /* interpret the response code from the server
  2812.  * after the post is done
  2813.  */   
  2814. PRIVATE int
  2815. net_send_news_post_data_response(ActiveEntry * ce)
  2816. {
  2817.     NewsConData * cd = (NewsConData *) ce->con_data;
  2818.  
  2819.  
  2820.     if (cd->response_code != 240) {
  2821.       ce->URL_s->error_msg =
  2822.         NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, 
  2823.                                 cd->response_txt ? cd->response_txt : "");
  2824.       if (cd->response_code == 441 
  2825.           && MSG_GetPaneType(cd->pane) == MSG_COMPOSITIONPANE
  2826.           && MSG_IsDuplicatePost(cd->pane) &&
  2827.           MSG_GetCompositionMessageID(cd->pane)) {
  2828.         /* The news server won't let us post.  We suspect that we're submitting
  2829.            a duplicate post, and that's why it's failing.  So, let's go see
  2830.            if there really is a message out there with the same message-id.
  2831.            If so, we'll just silently pretend everything went well. */
  2832.         PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "STAT %s" CRLF,
  2833.                     MSG_GetCompositionMessageID(cd->pane));
  2834.         cd->next_state = NNTP_RESPONSE;
  2835.         cd->next_state_after_response = NNTP_CHECK_FOR_MESSAGE;
  2836.         NNTP_LOG_WRITE(cd->output_buffer);
  2837.         return (int) NET_BlockingWrite(ce->socket, cd->output_buffer,
  2838.                                        XP_STRLEN(cd->output_buffer));
  2839.       }
  2840.  
  2841.       MSG_ClearCompositionMessageID(cd->pane); /* So that if the user tries
  2842.                                                       to just post again, we
  2843.                                                       won't immediately decide
  2844.                                                       that this was a duplicate
  2845.                                                       message and ignore the
  2846.                                                       error. */
  2847.       cd->next_state = NEWS_ERROR;
  2848.       return(MK_NNTP_ERROR_MESSAGE);
  2849.     }
  2850.     cd->next_state = NEWS_ERROR; /* even though it worked */
  2851.     cd->pause_for_read = FALSE;
  2852.     return(MK_DATA_LOADED);
  2853. }
  2854.  
  2855.  
  2856. PRIVATE int
  2857. net_check_for_message(ActiveEntry* ce)
  2858. {
  2859.   NewsConData * cd = (NewsConData *) ce->con_data;
  2860.  
  2861.   cd->next_state = NEWS_ERROR;
  2862.   if (cd->response_code >= 220 && cd->response_code <= 223) {
  2863.     /* Yes, this article is already there, we're all done. */
  2864.     return MK_DATA_LOADED;
  2865.   } else {
  2866.     /* The article isn't there, so the failure we had earlier wasn't due to
  2867.        a duplicate message-id.  Return the error from that previous
  2868.        posting attempt (which is already in ce->URL_s->error_msg). */
  2869.     MSG_ClearCompositionMessageID(cd->pane);
  2870.     return MK_NNTP_ERROR_MESSAGE;
  2871.   }
  2872. }
  2873.  
  2874. PRIVATE int
  2875. net_DisplayNewsRC(ActiveEntry * ce)
  2876. {
  2877.     NewsConData * cd = (NewsConData *) ce->con_data;
  2878.  
  2879.     if(!cd->newsrc_performed)
  2880.       {
  2881.         cd->newsrc_performed = TRUE;
  2882.  
  2883.         cd->newsrc_list_count = MSG_GetNewsRCCount(cd->pane, cd->host);
  2884.       }
  2885.     
  2886.  
  2887.     
  2888.     FREEIF(cd->control_con->current_group);
  2889.     cd->control_con->current_group = MSG_GetNewsRCGroup(cd->pane, cd->host);
  2890.     
  2891.  
  2892.     if(cd->control_con->current_group)
  2893.       {
  2894.         /* send group command to server
  2895.          */
  2896.         int32 percent;
  2897.         char *statusText;
  2898.  
  2899.         PR_snprintf(NET_Socket_Buffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF,
  2900.                     cd->control_con->current_group);
  2901.         ce->status = (int) NET_BlockingWrite(ce->socket, NET_Socket_Buffer,
  2902.                                             XP_STRLEN(NET_Socket_Buffer));
  2903.         NNTP_LOG_WRITE(NET_Socket_Buffer);
  2904.  
  2905.         percent = (int32) (100 * (((double) cd->newsrc_list_index) /
  2906.                          ((double) cd->newsrc_list_count)));
  2907.         FE_SetProgressBarPercent (ce->window_id, percent);
  2908.         statusText = PR_smprintf (XP_GetString(XP_PROGRESS_READ_NEWSGROUP_COUNTS), 
  2909.                                 (long) cd->newsrc_list_index, (long) cd->newsrc_list_count);
  2910.         if (statusText)
  2911.         {
  2912.             FE_Progress (ce->window_id, statusText);
  2913.             XP_FREE(statusText);
  2914.         }
  2915.  
  2916.         cd->newsrc_list_index++;
  2917.  
  2918.         cd->pause_for_read = TRUE;
  2919.         cd->next_state = NNTP_RESPONSE;
  2920.         cd->next_state_after_response = NEWS_DISPLAY_NEWS_RC_RESPONSE;
  2921.       }
  2922.     else
  2923.       {
  2924.         if (cd->newsrc_list_count)
  2925.           {
  2926.             FE_SetProgressBarPercent (ce->window_id, -1);
  2927.             cd->newsrc_list_count = 0;
  2928.           }
  2929.         else if (cd->response_code == 215)  
  2930.           {
  2931.             /*
  2932.              * 5-9-96 jefft 
  2933.              * If for some reason the news server returns an empty 
  2934.              * newsgroups list with a nntp response code 215 -- list of
  2935.              * newsgroups follows. We set ce->status to MK_EMPTY_NEWS_LIST
  2936.              * to end the infinite dialog loop.
  2937.              */
  2938.             ce->status = MK_EMPTY_NEWS_LIST;
  2939.           }
  2940.         cd->next_state = NEWS_DONE;
  2941.     
  2942.         if(ce->status > -1)
  2943.           return MK_DATA_LOADED; 
  2944.         else
  2945.           return(ce->status);
  2946.       }
  2947.  
  2948.     return(ce->status); /* keep going */
  2949.  
  2950. }
  2951.  
  2952. /* Parses output of GROUP command */
  2953. PRIVATE int
  2954. net_DisplayNewsRCResponse(ActiveEntry * ce)
  2955. {
  2956.     NewsConData * cd = (NewsConData *) ce->con_data;
  2957.  
  2958.     if(cd->response_code == 211)
  2959.       {
  2960.         char *num_arts = 0, *low = 0, *high = 0, *group = 0;
  2961.         int32 first_art, last_art;
  2962.  
  2963.         /* line looks like:
  2964.          *     211 91 3693 3789 comp.infosystems
  2965.          */
  2966.  
  2967.         num_arts = cd->response_txt;
  2968.         low = XP_STRCHR(num_arts, ' ');
  2969.  
  2970.         if(low)
  2971.           {
  2972.             first_art = atol(low);
  2973.             *low++ = '\0';
  2974.             high= XP_STRCHR(low, ' ');
  2975.           }
  2976.         if(high)
  2977.           {
  2978.             *high++ = '\0';
  2979.             group = XP_STRCHR(high, ' ');
  2980.           }
  2981.         if(group)
  2982.           {
  2983.             *group++ = '\0';
  2984.             /* the group name may be contaminated by "group selected" at
  2985.                the end.  This will be space separated from the group name.
  2986.                If a space is found in the group name terminate at that
  2987.                point. */
  2988.             XP_STRTOK(group, " ");
  2989.             last_art = atol(high);
  2990.           }
  2991.     
  2992.         ce->status = MSG_DisplaySubscribedGroup(cd->pane,
  2993.                                                 cd->host,
  2994.                                                 group,
  2995.                                                 low  ? atol(low)  : 0,
  2996.                                                 high ? atol(high) : 0,
  2997.                                                 atol(num_arts), FALSE);
  2998.         if (ce->status < 0)
  2999.           return ce->status;
  3000.       }
  3001.       else if (cd->response_code == 411)
  3002.       {
  3003.           MSG_GroupNotFound(cd->pane, cd->host, cd->control_con->current_group, FALSE);
  3004.       }
  3005.       /* it turns out subscribe ui depends on getting this displaysubscribedgroup call,
  3006.          even if there was an error.
  3007.       */
  3008.       if(cd->response_code != 211)
  3009.       {
  3010.         /* only on news server error or when zero articles
  3011.          */
  3012.         ce->status = MSG_DisplaySubscribedGroup(cd->pane,
  3013.                                                 cd->host,
  3014.                                                 cd->control_con->current_group,
  3015.                                                 0, 0, 0, FALSE);
  3016.  
  3017.       }
  3018.  
  3019.     cd->next_state = NEWS_DISPLAY_NEWS_RC;
  3020.         
  3021.     return 0;
  3022. }
  3023.  
  3024.  
  3025. PRIVATE int
  3026. net_NewsRCProcessPost(ActiveEntry *ce)
  3027. {
  3028.     return(0);
  3029. }
  3030.  
  3031.  
  3032.  
  3033. static void
  3034. net_cancel_done_cb (MWContext *context, void *data, int status,
  3035.                     const char *file_name)
  3036. {
  3037.   ActiveEntry *ce = (ActiveEntry *) data;
  3038.   NewsConData *cd = (NewsConData *) ce->con_data;
  3039.   cd->cancel_status = status;
  3040.   XP_ASSERT(status < 0 || file_name);
  3041.   cd->cancel_msg_file = (status < 0 ? 0 : XP_STRDUP(file_name));
  3042. }
  3043.  
  3044.  
  3045. static int
  3046. net_start_cancel (ActiveEntry *ce)
  3047. {
  3048.   NewsConData *cd = (NewsConData *) ce->con_data;
  3049.   char *command = "POST" CRLF;
  3050.  
  3051.   ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command));
  3052.     NNTP_LOG_WRITE(command);
  3053.  
  3054.   cd->next_state = NNTP_RESPONSE;
  3055.   cd->next_state_after_response = NEWS_DO_CANCEL;
  3056.   cd->pause_for_read = TRUE;
  3057.   return (ce->status);
  3058. }
  3059.  
  3060.  
  3061. static int
  3062. net_do_cancel (ActiveEntry *ce)
  3063. {
  3064.   int status = 0;
  3065.   NewsConData *cd = (NewsConData *) ce->con_data;
  3066.   char *id, *subject, *newsgroups, *distribution, *other_random_headers, *body;
  3067.   char *from, *old_from, *news_url;
  3068.   int L;
  3069.   MSG_CompositionFields *fields = NULL;
  3070.  
  3071.  
  3072.   /* #### Should we do a more real check than this?  If the POST command
  3073.      didn't respond with "340 Ok", then it's not ready for us to throw a
  3074.      message at it...   But the normal posting code doesn't do this check.
  3075.      Why?
  3076.    */
  3077.   XP_ASSERT (cd->response_code == 340);
  3078.  
  3079.   /* These shouldn't be set yet, since the headers haven't been "flushed" */
  3080.   XP_ASSERT (!cd->cancel_id &&
  3081.              !cd->cancel_from &&
  3082.              !cd->cancel_newsgroups &&
  3083.              !cd->cancel_distribution);
  3084.  
  3085.   /* Write out a blank line.  This will tell mimehtml.c that the headers
  3086.      are done, and it will call news_generate_html_header_fn which will
  3087.      notice the fields we're interested in.
  3088.    */
  3089.   XP_STRCPY (cd->output_buffer, CRLF); /* CRLF used to be LINEBREAK. 
  3090.                                            LINEBREAK is platform dependent
  3091.                                            and is only <CR> on a mac. This
  3092.                                          CRLF is the protocol delimiter 
  3093.                                          and not platform dependent  -km */
  3094.   ce->status = PUTSTRING(cd->output_buffer);
  3095.   if (ce->status < 0) return ce->status;
  3096.  
  3097.   /* Now news_generate_html_header_fn should have been called, and these
  3098.      should have values. */
  3099.   id = cd->cancel_id;
  3100.   old_from = cd->cancel_from;
  3101.   newsgroups = cd->cancel_newsgroups;
  3102.   distribution = cd->cancel_distribution;
  3103.  
  3104.   XP_ASSERT (id && newsgroups);
  3105.   if (!id || !newsgroups) return -1; /* "unknown error"... */
  3106.  
  3107.   cd->cancel_newsgroups = 0;
  3108.   cd->cancel_distribution = 0;
  3109.   cd->cancel_from = 0;
  3110.   cd->cancel_id = 0;
  3111.  
  3112.   L = XP_STRLEN (id);
  3113.  
  3114.   from = MIME_MakeFromField ();
  3115.   subject = (char *) XP_ALLOC (L + 20);
  3116.   other_random_headers = (char *) XP_ALLOC (L + 20);
  3117.   body = (char *) XP_ALLOC (XP_STRLEN (XP_AppCodeName) + 100);
  3118.  
  3119.   /* Make sure that this loser isn't cancelling someone else's posting.
  3120.      Yes, there are occasionally good reasons to do so.  Those people
  3121.      capable of making that decision (news admins) have other tools with
  3122.      which to cancel postings (like telnet.)
  3123.      Don't do this if server tells us it will validate user. DMB 3/19/97
  3124.    */
  3125.   if (!MSG_QueryNewsExtension(cd->host, "CANCELCHK"))
  3126.   {
  3127.     char *us = MSG_ExtractRFC822AddressMailboxes (from);
  3128.     char *them = MSG_ExtractRFC822AddressMailboxes (old_from);
  3129.     XP_Bool ok = (us && them && !strcasecomp (us, them));
  3130.     FREEIF(us);
  3131.     FREEIF(them);
  3132.     if (!ok)
  3133.       {
  3134.         status = MK_NNTP_CANCEL_DISALLOWED;
  3135.         ce->URL_s->error_msg = XP_STRDUP (XP_GetString(status));
  3136.         cd->next_state = NEWS_ERROR; /* even though it worked */
  3137.         cd->pause_for_read = FALSE;
  3138.         goto FAIL;
  3139.       }
  3140.   }
  3141.  
  3142.   /* Last chance to cancel the cancel.
  3143.    */
  3144.   if (!FE_Confirm (ce->window_id, XP_GetString(MK_NNTP_CANCEL_CONFIRM)))
  3145.     {
  3146.       status = MK_NNTP_NOT_CANCELLED;
  3147.       goto FAIL;
  3148.     }
  3149.  
  3150.   news_url = ce->URL_s->address;  /* we can just post here. */
  3151.  
  3152.   if (!from || !subject || !other_random_headers || !body)
  3153.     {
  3154.       status = MK_OUT_OF_MEMORY;
  3155.       goto FAIL;
  3156.     }
  3157.  
  3158.   XP_STRCPY (subject, "cancel ");
  3159.   XP_STRCAT (subject, id);
  3160.  
  3161.   XP_STRCPY (other_random_headers, "Control: cancel ");
  3162.   XP_STRCAT (other_random_headers, id);
  3163.   XP_STRCAT (other_random_headers, CRLF);
  3164.   if (distribution)
  3165.     {
  3166.       XP_STRCAT (other_random_headers, "Distribution: ");
  3167.       XP_STRCAT (other_random_headers, distribution);
  3168.       XP_STRCAT (other_random_headers, CRLF);
  3169.     }
  3170.  
  3171.   XP_STRCPY (body, "This message was cancelled from within ");
  3172.   XP_STRCAT (body, XP_AppCodeName);
  3173.   XP_STRCAT (body, "." CRLF);
  3174.  
  3175.   /* #### Would it be a good idea to S/MIME-sign all "cancel" messages? */
  3176.  
  3177.   fields = MSG_CreateCompositionFields(from, 0, 0, 0, 0, 0, newsgroups,
  3178.                                        0, 0, subject, id, other_random_headers,
  3179.                                        0, 0, news_url,
  3180.                                        FALSE, /* encrypt_p */
  3181.                                        FALSE  /* sign_p */
  3182.                                        );
  3183.   if (!fields)
  3184.   {
  3185.       status = MK_OUT_OF_MEMORY;
  3186.       goto FAIL;
  3187.   }
  3188.   MSG_SetCompFieldsBoolHeader(fields, MSG_ENCRYPTED_BOOL_HEADER_MASK, FALSE);
  3189.   MSG_SetCompFieldsBoolHeader(fields, MSG_SIGNED_BOOL_HEADER_MASK, FALSE);
  3190.  
  3191.   cd->cancel_status = 0;
  3192.   MSG_StartMessageDelivery (cd->pane, (void *) ce,
  3193.                             fields,
  3194.                             FALSE, /* digest_p */
  3195.                             TRUE,  /* dont_deliver_p */
  3196.                             TEXT_PLAIN, body, XP_STRLEN (body),
  3197.                             0, /* other attachments */
  3198.                             NULL, /* multipart/related chunk */
  3199.                             net_cancel_done_cb);
  3200.  
  3201.   /* Since there are no attachments, MSG_StartMessageDelivery will run
  3202.      net_cancel_done_cb right away (it will be called before the return.) */
  3203.  
  3204.   if (!cd->cancel_msg_file)
  3205.     {
  3206.       status = cd->cancel_status;
  3207.       XP_ASSERT (status < 0);
  3208.       if (status >= 0) status = -1;
  3209.       goto FAIL;
  3210.     }
  3211.  
  3212.   /* Now send the data - do it blocking, who cares; the message is known
  3213.      to be very small.  First suck the whole file into memory.  Then delete
  3214.      the file.  Then do a blocking write of the data.
  3215.  
  3216.      (We could use file-posting, maybe, but I couldn't figure out how.)
  3217.    */
  3218.   {
  3219.     char *data;
  3220.     uint32 data_size, data_fp;
  3221.     XP_StatStruct st;
  3222.     int nread = 0;
  3223.     XP_File file = XP_FileOpen (cd->cancel_msg_file,
  3224.                                 xpFileToPost, XP_FILE_READ);
  3225.     if (! file) return -1; /* "unknown error"... */
  3226.     XP_Stat (cd->cancel_msg_file, &st, xpFileToPost);
  3227.  
  3228.     data_fp = 0;
  3229.     data_size = st.st_size + 20;
  3230.     data = (char *) XP_ALLOC (data_size);
  3231.     if (! data)
  3232.       {
  3233.         status = MK_OUT_OF_MEMORY;
  3234.         goto FAIL;
  3235.       }
  3236.  
  3237.     while ((nread = XP_FileRead (data + data_fp, data_size - data_fp, file))
  3238.            > 0)
  3239.       data_fp += nread;
  3240.     data [data_fp] = 0;
  3241.     XP_FileClose (file);
  3242.     XP_FileRemove (cd->cancel_msg_file, xpFileToPost);
  3243.  
  3244.     XP_STRCAT (data, CRLF "." CRLF CRLF);
  3245.     status = NET_BlockingWrite(ce->socket, data, XP_STRLEN(data));
  3246.     NNTP_LOG_WRITE(data);
  3247.     XP_FREE (data);
  3248.     if (status < 0)
  3249.       {
  3250.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR,
  3251.                                                       status);
  3252.         goto FAIL;
  3253.       }
  3254.  
  3255.     cd->pause_for_read = TRUE;
  3256.     cd->next_state = NNTP_RESPONSE;
  3257.     cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
  3258.   }
  3259.  
  3260.  FAIL:
  3261.   FREEIF (id);
  3262.   FREEIF (from);
  3263.   FREEIF (old_from);
  3264.   FREEIF (subject);
  3265.   FREEIF (newsgroups);
  3266.   FREEIF (distribution);
  3267.   FREEIF (other_random_headers);
  3268.   FREEIF (body);
  3269.   FREEIF (cd->cancel_msg_file);
  3270.   if (fields)
  3271.       MSG_DestroyCompositionFields(fields);
  3272.  
  3273.   return status;
  3274. }
  3275.  
  3276.  
  3277. static int net_xpat_send (ActiveEntry *ce)
  3278. {
  3279.     NewsConData *cd = (NewsConData *) ce->con_data;
  3280.     int status = 0;
  3281.     char *thisTerm = NULL;
  3282.  
  3283.     if (cd->current_search &&
  3284.         (thisTerm = XP_STRCHR(cd->current_search, '/')) != NULL)
  3285.     {
  3286.         /* extract the XPAT encoding for one query term */
  3287. /*        char *next_search = NULL; */
  3288.         char *command = NULL;
  3289.         char *unescapedCommand = NULL;
  3290.         char *endOfTerm = NULL;
  3291.         StrAllocCopy (command, ++thisTerm);
  3292.         endOfTerm = XP_STRCHR(command, '/');
  3293.         if (endOfTerm)
  3294.             *endOfTerm = '\0';
  3295.         StrAllocCat (command, CRLF);
  3296.     
  3297.         unescapedCommand = MSG_UnEscapeSearchUrl(command);
  3298.  
  3299.         /* send one term off to the server */
  3300.         NNTP_LOG_WRITE(command);
  3301.         status = NET_BlockingWrite(ce->socket, unescapedCommand, XP_STRLEN(unescapedCommand));
  3302.         NNTP_LOG_WRITE(unescapedCommand);
  3303.  
  3304.         cd->next_state = NNTP_RESPONSE;
  3305.         cd->next_state_after_response = NNTP_XPAT_RESPONSE;
  3306.         cd->pause_for_read = TRUE;
  3307.  
  3308.         XP_FREE(command);
  3309.         XP_FREE(unescapedCommand);
  3310.     }
  3311.     else
  3312.     {
  3313.         cd->next_state = NEWS_DONE;
  3314.         status = MK_DATA_LOADED;
  3315.     }
  3316.     return status;
  3317. }
  3318.  
  3319. static int net_list_pretty_names(ActiveEntry *ce)
  3320. {
  3321.     NewsConData *cd = (NewsConData*) ce->con_data;
  3322.     PR_snprintf(cd->output_buffer, 
  3323.             OUTPUT_BUFFER_SIZE, 
  3324.             "LIST PRETTYNAMES %.512s" CRLF, 
  3325.             cd->group_name ? cd->group_name : "");
  3326.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  3327.     NNTP_LOG_WRITE(cd->output_buffer);
  3328. #ifdef DEBUG_bienvenu
  3329.     XP_Trace(cd->output_buffer);
  3330. #endif
  3331.     cd->next_state = NNTP_RESPONSE;
  3332.     cd->next_state_after_response = NNTP_LIST_PRETTY_NAMES_RESPONSE;
  3333.  
  3334.     return ce->status;
  3335. }
  3336.  
  3337. static int net_list_pretty_names_response(ActiveEntry *ce)
  3338. {
  3339.     char *line;
  3340.     char *prettyName;
  3341.  
  3342.     NewsConData * cd = (NewsConData *)ce->con_data;
  3343.  
  3344.     if (cd->response_code != 215)
  3345.     {
  3346.         cd->next_state = DISPLAY_NEWSGROUPS;
  3347. /*        cd->next_state = NEWS_DONE; */
  3348.         cd->pause_for_read = FALSE;
  3349.         return 0;
  3350.     }
  3351.  
  3352.     ce->status = NET_BufferedReadLine(ce->socket, &line,
  3353.                                      &cd->data_buf, 
  3354.                                      &cd->data_buf_size,
  3355.                                      (Bool*)&cd->pause_for_read);
  3356.     NNTP_LOG_READ(line);
  3357.  
  3358.     if(ce->status == 0)
  3359.     {
  3360.         cd->next_state = NNTP_ERROR;
  3361.         cd->pause_for_read = FALSE;
  3362.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  3363.         return(MK_NNTP_SERVER_ERROR);
  3364.     }
  3365.  
  3366.     if (line)
  3367.     {
  3368.         if (line[0] != '.')
  3369.         {
  3370.             int i;
  3371.             /* find whitespace seperator if it exits */
  3372.             for (i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++)
  3373.                 ;  /* null body */
  3374.  
  3375.             if(line[i] == '\0')
  3376.                 prettyName = &line[i];
  3377.             else
  3378.                 prettyName = &line[i+1];
  3379.  
  3380.             line[i] = 0; /* terminate group name */
  3381.             if (i > 0)
  3382.                 MSG_AddPrettyName(cd->host, line, prettyName);
  3383. #ifdef DEBUG_bienvenu
  3384.             XP_Trace("adding pretty name %s\n", prettyName);
  3385. #endif
  3386.         }
  3387.         else
  3388.         {
  3389.             cd->next_state = DISPLAY_NEWSGROUPS;    /* this assumes we were doing a list */
  3390. /*            cd->next_state = NEWS_DONE;     */ /* ### dmb - don't really know */
  3391.             cd->pause_for_read = FALSE;
  3392.             return 0;
  3393.         }
  3394.     }
  3395.     return 0;
  3396. }
  3397.  
  3398. static int net_list_xactive(ActiveEntry *ce)
  3399. {
  3400.     NewsConData *cd = (NewsConData*) ce->con_data;
  3401.     PR_snprintf(cd->output_buffer, 
  3402.             OUTPUT_BUFFER_SIZE, 
  3403.             "LIST XACTIVE %.512s" CRLF, 
  3404.             cd->group_name);
  3405.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  3406.     NNTP_LOG_WRITE(cd->output_buffer);
  3407.  
  3408.     cd->next_state = NNTP_RESPONSE;
  3409.     cd->next_state_after_response = NNTP_LIST_XACTIVE_RESPONSE;
  3410.  
  3411.     return ce->status;
  3412. }
  3413.  
  3414.  
  3415. static int net_list_xactive_response(ActiveEntry *ce)
  3416. {
  3417.     char *line;
  3418.  
  3419.     NewsConData * cd = (NewsConData *)ce->con_data;
  3420.  
  3421.     XP_ASSERT(cd->response_code == 215);
  3422.     if (cd->response_code != 215)
  3423.     {
  3424.         cd->next_state = DISPLAY_NEWSGROUPS;
  3425. /*        cd->next_state = NEWS_DONE; */
  3426.         cd->pause_for_read = FALSE;
  3427.         return MK_DATA_LOADED;
  3428.     }
  3429.  
  3430.     ce->status = NET_BufferedReadLine(ce->socket, &line,
  3431.                                      &cd->data_buf, 
  3432.                                      &cd->data_buf_size,
  3433.                                      (Bool*)&cd->pause_for_read);
  3434.     NNTP_LOG_READ(line);
  3435.  
  3436.     if(ce->status == 0)
  3437.     {
  3438.         cd->next_state = NNTP_ERROR;
  3439.         cd->pause_for_read = FALSE;
  3440.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  3441.         return(MK_NNTP_SERVER_ERROR);
  3442.     }
  3443.  
  3444.     /* Bug fix (sfraser) -- show download progress here
  3445.     */
  3446.     if(ce->status > 1)
  3447.     {
  3448.         ce->bytes_received += ce->status;
  3449.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
  3450.     }
  3451.  
  3452.     if (line)
  3453.     {
  3454.         if (line[0] != '.')
  3455.         {
  3456.             char *s = line;
  3457.             /* format is "rec.arts.movies.past-films 7302 7119 csp"
  3458.              */
  3459.             while (*s && !XP_IS_SPACE(*s))
  3460.                 s++;
  3461.             if (s)
  3462.             {
  3463.                 char flags[32];    /* ought to be big enough */
  3464.                 *s = 0;
  3465.                 sscanf(s + 1,
  3466.             #ifdef __alpha
  3467.                        "%d %d %31s", 
  3468.             #else
  3469.                        "%ld %ld %31s", 
  3470.             #endif
  3471.                        &cd->first_possible_art, 
  3472.                        &cd->last_possible_art,
  3473.                        flags);
  3474.                 MSG_AddNewNewsGroup(cd->pane, cd->host, line,
  3475.                                     cd->first_possible_art,
  3476.                                     cd->last_possible_art, flags, TRUE /* xactive flags */);
  3477.                 /* we're either going to list prettynames first, or list all prettynames 
  3478.                    every time, so we won't care so much if it gets interrupted. */
  3479. #ifdef DEBUG_bienvenu
  3480.                 XP_Trace("got xactive for %s of %s\n", line, flags);
  3481. #endif
  3482.                 MSG_SetGroupNeedsExtraInfo(cd->host, line, FALSE);
  3483.             }
  3484.         }
  3485.         else
  3486.         {
  3487.             if (cd->type_wanted == NEW_GROUPS && MSG_QueryNewsExtension(cd->host, "XACTIVE"))
  3488.             {
  3489.                 char *old_group_name = cd->group_name;
  3490.                 cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
  3491.                 /* make sure we're not stuck on the same group... */
  3492.                 if (old_group_name && cd->group_name && XP_STRCMP(old_group_name, cd->group_name))
  3493.                 {
  3494.                     XP_FREE(old_group_name);
  3495. #ifdef DEBUG_bienvenu
  3496.                     XP_Trace("listing xactive for %s\n", cd->group_name);
  3497. #endif
  3498.                     cd->next_state = NNTP_LIST_XACTIVE;
  3499.                     cd->pause_for_read = FALSE;
  3500.                     return 0;
  3501.                 }
  3502.                 else
  3503.                 {
  3504.                     FREEIF(old_group_name);
  3505.                     cd->group_name = NULL;
  3506.                 }
  3507.             }
  3508.             if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
  3509.                 cd->next_state = NNTP_LIST_PRETTY_NAMES;
  3510.             else
  3511.                 cd->next_state = DISPLAY_NEWSGROUPS;    /* this assumes we were doing a list - who knows? */
  3512. /*            cd->next_state = NEWS_DONE;     */ /* ### dmb - don't really know */
  3513.             cd->pause_for_read = FALSE;
  3514.             return 0;
  3515.         }
  3516.     }
  3517.     return 0;
  3518. }
  3519.  
  3520. static int net_list_group(ActiveEntry *ce)
  3521. {
  3522.     NewsConData *cd = (NewsConData*) ce->con_data;
  3523.     PR_snprintf(cd->output_buffer, 
  3524.             OUTPUT_BUFFER_SIZE, 
  3525.             "listgroup %.512s" CRLF, 
  3526.             cd->group_name);
  3527.     MSG_InitAddArticleKeyToGroup(cd->pane, cd->host, cd->group_name,
  3528.                                &cd->newsgroup_parse_state);
  3529.     ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer));
  3530.     NNTP_LOG_WRITE(cd->output_buffer);
  3531.  
  3532.     cd->next_state = NNTP_RESPONSE;
  3533.     cd->next_state_after_response = NNTP_LIST_GROUP_RESPONSE;
  3534.  
  3535.     return ce->status;
  3536. }
  3537.  
  3538. static int net_list_group_response(ActiveEntry *ce)
  3539. {
  3540.     char *line;
  3541.  
  3542.     NewsConData * cd = (NewsConData *)ce->con_data;
  3543.  
  3544.     XP_ASSERT(cd->response_code == 211);
  3545.     if (cd->response_code != 211)
  3546.     {
  3547.         cd->next_state = NEWS_DONE; 
  3548.         cd->pause_for_read = FALSE;
  3549.         return MK_DATA_LOADED;
  3550.     }
  3551.  
  3552.     ce->status = NET_BufferedReadLine(ce->socket, &line,
  3553.                                      &cd->data_buf, 
  3554.                                      &cd->data_buf_size,
  3555.                                      (Bool*)&cd->pause_for_read);
  3556.     NNTP_LOG_READ(line);
  3557.  
  3558.     if(ce->status == 0)
  3559.     {
  3560.         cd->next_state = NNTP_ERROR;
  3561.         cd->pause_for_read = FALSE;
  3562.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  3563.         return(MK_NNTP_SERVER_ERROR);
  3564.     }
  3565.  
  3566.     if (line)
  3567.     {
  3568.         if (line[0] != '.')
  3569.         {
  3570.             long found_id = MSG_MESSAGEKEYNONE;
  3571.             sscanf(line, "%ld", &found_id);
  3572.             MSG_AddArticleKeyToGroup(cd->newsgroup_parse_state, (int32) found_id);
  3573.         }
  3574.         else
  3575.         {
  3576.             cd->next_state = NEWS_DONE;     /* ### dmb - don't really know */
  3577.             cd->pause_for_read = FALSE;
  3578.             return 0;
  3579.         }
  3580.     }
  3581.     return 0;
  3582. }
  3583.  
  3584. static int net_xpat_response (ActiveEntry *ce)
  3585. {
  3586.     char *line;
  3587.     NewsConData * cd = (NewsConData *)ce->con_data;
  3588.  
  3589.     if (cd->response_code != 221)
  3590.     {
  3591.         ce->URL_s->error_msg  = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);
  3592.         cd->next_state = NNTP_ERROR;
  3593.         cd->pause_for_read = FALSE;
  3594.         return MK_NNTP_SERVER_ERROR;
  3595.     }
  3596.  
  3597.     ce->status = NET_BufferedReadLine(ce->socket, &line,
  3598.                                      &cd->data_buf, 
  3599.                                      &cd->data_buf_size,
  3600.                                      (Bool*)&cd->pause_for_read);
  3601.     NNTP_LOG_READ(line);
  3602.  
  3603.     if(ce->status == 0)
  3604.     {
  3605.         cd->next_state = NNTP_ERROR;
  3606.         cd->pause_for_read = FALSE;
  3607.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  3608.         return(MK_NNTP_SERVER_ERROR);
  3609.     }
  3610.  
  3611.     if (line)
  3612.     {
  3613.         if (line[0] != '.')
  3614.         {
  3615.             long articleNumber;
  3616.             sscanf(line, "%ld", &articleNumber);
  3617.             MSG_AddNewsXpatHit (ce->window_id, (uint32) articleNumber);
  3618.         }
  3619.         else
  3620.         {
  3621.             /* set up the next term for next time around */
  3622.             char *nextTerm = XP_STRCHR(cd->current_search, '/');
  3623.             if (nextTerm)
  3624.                 cd->current_search = ++nextTerm;
  3625.             else
  3626.                 cd->current_search = NULL;
  3627.  
  3628.             cd->next_state = NNTP_XPAT_SEND;
  3629.             cd->pause_for_read = FALSE;
  3630.             return 0;
  3631.         }
  3632.     }
  3633.     return 0;
  3634. }
  3635.  
  3636.  
  3637. PRIVATE int net_nntp_search(ActiveEntry *ce)
  3638. {
  3639.     XP_ASSERT(FALSE);
  3640.     return 0;
  3641. }
  3642.  
  3643. PRIVATE int net_nntp_search_response(ActiveEntry *ce)
  3644. {
  3645.     NewsConData * cd = (NewsConData *)ce->con_data;
  3646.     
  3647.     if (cd->response_code >= 200 && cd->response_code <= 299)
  3648.         cd->next_state = NNTP_SEARCH_RESULTS;
  3649.     else
  3650.         cd->next_state = NEWS_DONE;
  3651.     cd->pause_for_read = FALSE;
  3652.     return 0;
  3653. }
  3654.  
  3655. PRIVATE int net_nntp_search_results (ActiveEntry *ce)
  3656. {
  3657.     NewsConData *cd = (NewsConData*) ce->con_data;
  3658.  
  3659.     char *line = NULL;
  3660.     ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
  3661.         &cd->data_buf_size, (Bool*)&cd->pause_for_read);
  3662.  
  3663.     if(ce->status == 0)
  3664.     {
  3665.         cd->next_state = NNTP_ERROR;
  3666.         cd->pause_for_read = FALSE;
  3667.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  3668.         return MK_NNTP_SERVER_ERROR;
  3669.     }
  3670.     if (!line)
  3671.         return ce->status;  /* no line yet */
  3672.     if (ce->status < 0)
  3673.     {
  3674.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  3675.         /* return TCP error */
  3676.         return MK_TCP_READ_ERROR;
  3677.     }
  3678.  
  3679.     if ('.' != line[0])
  3680.         MSG_AddNewsSearchHit (ce->window_id, line);
  3681.     else
  3682.     {
  3683.         /* all overview lines received */
  3684.         cd->next_state = NEWS_DONE;
  3685.         cd->pause_for_read = FALSE;
  3686.     }
  3687.  
  3688.     return ce->status;
  3689. }
  3690.  
  3691. /* The main news load init routine, and URL parser.
  3692.    The syntax of news URLs is:
  3693.  
  3694.      To list all hosts:
  3695.        news:
  3696.  
  3697.      To list all groups on a host, or to post a new message:
  3698.        news://HOST
  3699.  
  3700.      To list some articles in a group:
  3701.        news:GROUP
  3702.        news:/GROUP
  3703.        news://HOST/GROUP
  3704.  
  3705.      To list a particular range of articles in a group:
  3706.        news:GROUP/N1-N2
  3707.        news:/GROUP/N1-N2
  3708.        news://HOST/GROUP/N1-N2
  3709.  
  3710.      To retrieve an article:
  3711.        news:MESSAGE-ID                (message-id must contain "@")
  3712.  
  3713.     To cancel an article:
  3714.        news:MESSAGE-ID?cancel
  3715.  
  3716.      (Note that message IDs may contain / before the @:)
  3717.  
  3718.        news:SOME/ID@HOST?headers=all
  3719.        news:/SOME/ID@HOST?headers=all
  3720.        news:/SOME?ID@HOST?headers=all
  3721.         are the same as
  3722.        news://HOST/SOME/ID@HOST?headers=all
  3723.        news://HOST//SOME/ID@HOST?headers=all
  3724.        news://HOST//SOME?ID@HOST?headers=all
  3725.         bug: if the ID is <//xxx@host> we will parse this correctly:
  3726.           news://non-default-host///xxx@host
  3727.         but will mis-parse it if it's on the default host:
  3728.           news://xxx@host
  3729.         whoever had the idea to leave the <> off the IDs should be flogged.
  3730.         So, we'll make sure we quote / in message IDs as %2F.
  3731.  
  3732.  */
  3733. int
  3734. NET_parse_news_url (const char *url,
  3735.                     char **host_and_portP,
  3736.                     XP_Bool *securepP,
  3737.                     char **groupP,
  3738.                     char **message_idP,
  3739.                     char **command_specific_dataP)
  3740. {
  3741.   int status = 0;
  3742.   XP_Bool secure_p = FALSE;
  3743.   char *host_and_port = 0;
  3744.   unsigned long port = 0;
  3745.   char *group = 0;
  3746.   char *message_id = 0;
  3747.   char *command_specific_data = 0;
  3748.   const char *path_part;
  3749.   char *s;
  3750.  
  3751.   if (url[0] == 's' || url[1] == 'S')
  3752.     {
  3753.       secure_p = TRUE;
  3754.       url++;
  3755.     }
  3756.  
  3757.   host_and_port = NET_ParseURL (url, GET_HOST_PART);
  3758.   if (!host_and_port || !*host_and_port)
  3759.     {
  3760.       char* defhost = NULL;
  3761.       int32 defport = 0;
  3762.       FREEIF(host_and_port);
  3763.       PREF_CopyCharPref("network.hosts.nntp_server", &defhost);
  3764.       if (defhost && *defhost == '\0') {
  3765.         XP_FREE(defhost);
  3766.         defhost = NULL;
  3767.       }
  3768.       if (defhost) {
  3769.         PREF_GetIntPref("news.server_port", &defport);
  3770.         if (defport == (secure_p ? SECURE_NEWS_PORT : NEWS_PORT)) {
  3771.           defport = 0;
  3772.         }
  3773.         if (defport) {
  3774.           host_and_port = PR_smprintf("%s:%ld", defhost, (long) defport);
  3775.           XP_FREE(defhost);
  3776.         } else {
  3777.           host_and_port = defhost;
  3778.         }
  3779.         if (!host_and_port) {
  3780.           status = MK_OUT_OF_MEMORY;
  3781.           goto FAIL;
  3782.         }
  3783.       }
  3784.     }
  3785.  
  3786.   if (!host_and_port)
  3787.     {
  3788.       status = MK_NO_NEWS_SERVER;
  3789.       goto FAIL;
  3790.     }
  3791.  
  3792.   /* If a port was specified, but it was the default port, pretend
  3793.      it wasn't specified.
  3794.    */
  3795.   s = XP_STRCHR (host_and_port, ':');
  3796.   if (s && sscanf (s+1, " %lu ", &port) == 1 && port == (secure_p ? SECURE_NEWS_PORT : NEWS_PORT))
  3797.     *s = 0;
  3798.  
  3799.   path_part = XP_STRCHR (url, ':');
  3800.   XP_ASSERT (path_part);
  3801.   if (!path_part)
  3802.     {
  3803.       status = -1;
  3804.       goto FAIL;
  3805.     }
  3806.   path_part++;
  3807.   if (path_part[0] == '/' && path_part[1] == '/')
  3808.     {
  3809.       /* Skip over host name. */
  3810.       path_part = XP_STRCHR (path_part + 2, '/');
  3811.       if (path_part)
  3812.         path_part++;
  3813.     }
  3814.   if (!path_part)
  3815.     path_part = "";
  3816.  
  3817.   group = XP_STRDUP (path_part);
  3818.   if (!group)
  3819.     {
  3820.       status = MK_OUT_OF_MEMORY;
  3821.       goto FAIL;
  3822.     }
  3823.  
  3824.   NET_UnEscape (group);
  3825.  
  3826.   /* "group" now holds the part after the host name:
  3827.      "message@id?search" or "/group/xxx?search" or "/message?id@xx?search"
  3828.  
  3829.      If there is an @, this is a message ID; else it is a group.
  3830.      Either way, there may be search data at the end.
  3831.    */
  3832.  
  3833.   s = XP_STRCHR (group, '@');
  3834.   if (s)
  3835.     {
  3836.       message_id = group;
  3837.       group = 0;
  3838.     }
  3839.   else if (!*group)
  3840.     {
  3841.       XP_FREE (group);
  3842.       group = 0;
  3843.     }
  3844.  
  3845.   /* At this point, the search data is attached to `message_id' (if there
  3846.      is one) or `group' (if there is one) or `host_and_port' (otherwise.)
  3847.      Seperate the search data from what it is clinging to, being careful
  3848.      to interpret the "?" only if it comes after the "@" in an ID, since
  3849.      the syntax of message IDs is tricky.  (There may be a ? in the
  3850.      random-characters part of the ID (before @), but not in the host part
  3851.      (after @).)
  3852.    */
  3853.   if (message_id || group || host_and_port)
  3854.     {
  3855.       char *start;
  3856.       if (message_id)
  3857.         {
  3858.           /* Move past the @. */
  3859.           XP_ASSERT (s && *s == '@');
  3860.           start = s;
  3861.         }
  3862.       else
  3863.         {
  3864.           start = group ? group : host_and_port;
  3865.         }
  3866.  
  3867.       /* Take off the "?" or "#" search data */
  3868.       for (s = start; *s; s++)
  3869.         if (*s == '?' || *s == '#')
  3870.           break;
  3871.       if (*s)
  3872.         {
  3873.           command_specific_data = XP_STRDUP (s);
  3874.           *s = 0;
  3875.           if (!command_specific_data)
  3876.             {
  3877.               status = MK_OUT_OF_MEMORY;
  3878.               goto FAIL;
  3879.             }
  3880.         }
  3881.  
  3882.       /* Discard any now-empty strings. */
  3883.       if (message_id && !*message_id)
  3884.         {
  3885.           XP_FREE (message_id);
  3886.           message_id = 0;
  3887.         }
  3888.       else if (group && !*group)
  3889.         {
  3890.           XP_FREE (group);
  3891.           group = 0;
  3892.         }
  3893.     }
  3894.  
  3895.   FAIL:
  3896.   XP_ASSERT (!message_id || message_id != group);
  3897.   if (status >= 0)
  3898.     {
  3899.       *host_and_portP = host_and_port;
  3900.       *securepP = secure_p;
  3901.       *groupP = group;
  3902.       *message_idP = message_id;
  3903.       *command_specific_dataP = command_specific_data;
  3904.     }
  3905.   else
  3906.     {
  3907.       FREEIF (host_and_port);
  3908.       FREEIF (group);
  3909.       FREEIF (message_id);
  3910.       FREEIF (command_specific_data);
  3911.     }
  3912.   return status;
  3913. }
  3914.  
  3915.  
  3916. #if 0
  3917. static void
  3918. test (const char *url)
  3919. {
  3920.   char *host_and_port = 0;
  3921.   XP_Bool secure_p = FALSE;
  3922.   char *group = 0;
  3923.   char *message_id = 0;
  3924.   char *command_specific_data = 0;
  3925.   int status = NET_parse_news_url (url, &host_and_port, &secure_p,
  3926.                                    &group, &message_id, &command_specific_data);
  3927.   if (status < 0)
  3928.     fprintf (stderr, "%s: status %d\n", url, status);
  3929.   else
  3930.     {
  3931.       fprintf (stderr,
  3932.                "%s:\n"
  3933.                " host \"%s\", %ssecure,\n"
  3934.                " group=\"%s\",\n"
  3935.                " id=\"%s\",\n"
  3936.                " search=\"%s\"\n\n",
  3937.                url,
  3938.                (host_and_port ? host_and_port : "(none)"),
  3939.                (secure_p ? "" : "in"),
  3940.                (group ? group : "(none)"),
  3941.                (message_id ? message_id : "(none)"),
  3942.                (command_specific_data ?  command_specific_data : "(none)"));
  3943.     }
  3944.   FREEIF (host_and_port);
  3945.   FREEIF (group);
  3946.   FREEIF (message_id);
  3947.   FREEIF (command_specific_data);
  3948. }
  3949.  
  3950. static void
  3951. test2 (void)
  3952. {
  3953.   test ("news:");
  3954.   test ("news:?search");
  3955.   test ("news:#anchor");
  3956.   test ("news:?search#anchor");
  3957.   test ("news:#anchor?search");
  3958.   test ("news://HOST");
  3959.   test ("news://HOST?search");
  3960.   test ("news://HOST#anchor");
  3961.   test ("news://HOST?search#anchor");
  3962.   test ("news://HOST#anchor?search");
  3963.   test ("news://HOST/");
  3964.   test ("news://HOST/?search");
  3965.   test ("news://HOST/#anchor");
  3966.   test ("news://HOST/?search#anchor");
  3967.   test ("news://HOST/#anchor?search");
  3968.   test ("news:GROUP");
  3969.   test ("news:GROUP?search");
  3970.   test ("news:GROUP#anchor");
  3971.   test ("news:GROUP?search#anchor");
  3972.   test ("news:GROUP#anchor?search");
  3973.   test ("news:/GROUP");
  3974.   test ("news:/GROUP?search");
  3975.   test ("news:/GROUP#anchor");
  3976.   test ("news:/GROUP?search#anchor");
  3977.   test ("news:/GROUP#anchor?search");
  3978.   test ("news://HOST/GROUP");
  3979.   test ("news://HOST/GROUP?search");
  3980.   test ("news://HOST/GROUP#anchor");
  3981.   test ("news://HOST/GROUP?search#anchor");
  3982.   test ("news://HOST/GROUP#anchor?search");
  3983.  
  3984.   test ("news:GROUP/N1-N2");
  3985.   test ("news:GROUP/N1-N2?search");
  3986.   test ("news:GROUP/N1-N2#anchor");
  3987.   test ("news:GROUP/N1-N2?search#anchor");
  3988.   test ("news:GROUP/N1-N2#anchor?search");
  3989.   test ("news:/GROUP/N1-N2");
  3990.   test ("news:/GROUP/N1-N2?search");
  3991.   test ("news:/GROUP/N1-N2#anchor");
  3992.   test ("news:/GROUP/N1-N2?search#anchor");
  3993.   test ("news:/GROUP/N1-N2#anchor?search");
  3994.  
  3995.   test ("news://HOST/GROUP/N1-N2");
  3996.   test ("news://HOST/GROUP/N1-N2?search");
  3997.   test ("news://HOST/GROUP/N1-N2#anchor");
  3998.   test ("news://HOST/GROUP/N1-N2?search#anchor");
  3999.   test ("news://HOST/GROUP/N1-N2#anchor?search");
  4000.   test ("news:<ID??##??ID@HOST>");
  4001.   test ("news:<ID??##??ID@HOST>?search");
  4002.   test ("news:<ID??##??ID@HOST>#anchor");
  4003.   test ("news:<ID??##??ID@HOST>?search#anchor");
  4004.   test ("news:<ID??##??ID@HOST>#anchor?search");
  4005.  
  4006.   test ("news:<ID/ID/ID??##??ID@HOST>");
  4007.   test ("news:<ID/ID/ID??##??ID@HOST>?search");
  4008.   test ("news:<ID/ID/ID??##??ID@HOST>#anchor");
  4009.   test ("news:<ID/ID/ID??##??ID@HOST>?search#anchor");
  4010.   test ("news:<ID/ID/ID??##??ID@HOST>#anchor?search");
  4011.   test ("news:</ID/ID/ID??##??ID@HOST>");
  4012.   test ("news:</ID/ID/ID??##??ID@HOST>?search");
  4013.   test ("news:</ID/ID/ID??##??ID@HOST>#anchor");
  4014.   test ("news:</ID/ID/ID??##??ID@HOST>?search#anchor");
  4015.   test ("news:</ID/ID/ID??##??ID@HOST>#anchor?search");
  4016.  
  4017.   test ("news://HOST/<ID??##??ID@HOST>");
  4018.   test ("news://HOST/<ID??##??ID@HOST>?search");
  4019.   test ("news://HOST/<ID??##??ID@HOST>#anchor");
  4020.   test ("news://HOST/<ID??##??ID@HOST>?search#anchor");
  4021.   test ("news://HOST/<ID??##??ID@HOST>#anchor?search");
  4022.   test ("news://HOST/<ID/ID/ID??##??ID@HOST>");
  4023.   test ("news://HOST/<ID/ID/ID??##??ID@HOST>?search");
  4024.   test ("news://HOST/<ID/ID/ID??##??ID@HOST>#anchor");
  4025.   test ("news://HOST/<ID/ID/ID??##??ID@HOST>?search#anchor");
  4026.   test ("news://HOST/<ID/ID/ID??##??ID@HOST>#anchor?search");
  4027.  
  4028.   test ("news:ID/ID/ID??##??ID@HOST");
  4029.   test ("news:ID/ID/ID??##??ID@HOST?search");
  4030.   test ("news:ID/ID/ID??##??ID@HOST#anchor");
  4031.   test ("news:ID/ID/ID??##??ID@HOST?search#anchor");
  4032.   test ("news:ID/ID/ID??##??ID@HOST#anchor?search");
  4033.   test ("news:/ID/ID/ID??##??ID@HOST");
  4034.   test ("news:/ID/ID/ID??##??ID@HOST?search");
  4035.   test ("news:/ID/ID/ID??##??ID@HOST#anchor");
  4036.   test ("news:/ID/ID/ID??##??ID@HOST?search#anchor");
  4037.   test ("news:/ID/ID/ID??##??ID@HOST#anchor?search");
  4038.  
  4039.   test ("news://HOST/ID??##??ID@HOST");
  4040.   test ("news://HOST/ID??##??ID@HOST?search");
  4041.   test ("news://HOST/ID??##??ID@HOST#anchor");
  4042.   test ("news://HOST/ID??##??ID@HOST?search#anchor");
  4043.   test ("news://HOST/ID??##??ID@HOST#anchor?search");
  4044.   test ("news://HOST/ID/ID/ID??##??ID@HOST");
  4045.   test ("news://HOST/ID/ID/ID??##??ID@HOST?search");
  4046.   test ("news://HOST/ID/ID/ID??##??ID@HOST#anchor");
  4047.   test ("news://HOST/ID/ID/ID??##??ID@HOST?search#anchor");
  4048.   test ("news://HOST/ID/ID/ID??##??ID@HOST#anchor?search");
  4049. }
  4050. #endif /* 0 */
  4051.  
  4052.  
  4053. /* Returns true if this URL is a reference to a news message,
  4054.    that is, the kind of news URL entity that can be displayed
  4055.    in a regular browser window, and doesn't involve all kinds
  4056.    of other magic state and callbacks like listing a group.
  4057.  */
  4058. XP_Bool
  4059. NET_IsNewsMessageURL (const char *url)
  4060. {
  4061.   char *host_and_port = 0;
  4062.   XP_Bool secure_p = FALSE;
  4063.   char *group = 0;
  4064.   char *message_id = 0;
  4065.   char *command_specific_data = 0;
  4066.   int status = NET_parse_news_url (url, &host_and_port, &secure_p,
  4067.                                    &group, &message_id, &command_specific_data);
  4068.   XP_Bool result = FALSE;
  4069.   if (status >= 0 && message_id && *message_id)
  4070.     result = TRUE;
  4071.   FREEIF (host_and_port);
  4072.   FREEIF (group);
  4073.   FREEIF (message_id);
  4074.   FREEIF (command_specific_data);
  4075.   return result;
  4076. }
  4077.  
  4078. static XP_Bool nntp_are_connections_available (NNTPConnection *conn)
  4079. {
  4080.     int connCount = 0;
  4081.  
  4082.     NNTPConnection *tmpConn;
  4083.     XP_List *tmpList = nntp_connection_list;
  4084.  
  4085.     while ((tmpConn = (NNTPConnection*) XP_ListNextObject(tmpList)) != NULL)
  4086.     {
  4087.         NNTP_LOG_NOTE(("nntp_are_connections_available: comparing %08lX %s", tmpConn, tmpConn->busy ? "busy" : "available"));
  4088.  
  4089.         if (conn != tmpConn && tmpConn->busy && !strcasecomp(tmpConn->hostname, conn->hostname))
  4090.             connCount++;
  4091.     }
  4092.  
  4093.     return connCount < kMaxConnectionsPerHost;
  4094. }
  4095.  
  4096. static XP_Bool isOnline = TRUE;
  4097. static XP_Bool prefInitialized = FALSE;
  4098.  
  4099. /* fix Mac warning of missing prototype */
  4100. MODULE_PRIVATE int PR_CALLBACK 
  4101. NET_OnlinePrefChangedFunc(const char *pref, void *data);
  4102.  
  4103. MODULE_PRIVATE int PR_CALLBACK NET_OnlinePrefChangedFunc(const char *pref, void *data) 
  4104. {
  4105.     int status;
  4106.     int32 port=0;
  4107.     char * socksHost = NULL;
  4108.     char text[MAXHOSTNAMELEN + 8];
  4109.  
  4110.     if (!XP_STRCASECMP(pref,"network.online")) 
  4111.         status = PREF_GetBoolPref("network.online", &isOnline);
  4112.  
  4113.     if ( isOnline ) {
  4114.         CACHE_CloseAllOpenSARCache();
  4115.  
  4116.         /* If the user wants to use a socks server set it up. */
  4117.         if ( (NET_GetProxyStyle() == PROXY_STYLE_MANUAL) ) {
  4118.             PREF_CopyCharPref("network.hosts.socks_server",&socksHost);
  4119.             PREF_GetIntPref("network.hosts.socks_serverport",&port);
  4120.             if (socksHost && *socksHost && port) {
  4121.                 PR_snprintf(text, sizeof(text), "%s:%d", socksHost, port);  
  4122.                 NET_SetSocksHost(text);
  4123.             }
  4124.             else {
  4125.                 NET_SetSocksHost(socksHost); /* NULL is ok */
  4126.             }
  4127.         }
  4128.     }
  4129.     else {
  4130.         CACHE_OpenAllSARCache();
  4131.     }
  4132.  
  4133.     return status;
  4134. }
  4135.  
  4136. /* fix Mac warning of missing prototype */
  4137. MODULE_PRIVATE int PR_CALLBACK
  4138. NET_NewsMaxArticlesChangedFunc(const char *pref, void *data);
  4139.  
  4140. MODULE_PRIVATE int PR_CALLBACK NET_NewsMaxArticlesChangedFunc(const char *pref, void *data) 
  4141. {
  4142.     int status;
  4143.     if (!XP_STRCASECMP(pref,"news.max_articles")) 
  4144.     {
  4145.         int32 maxArticles;
  4146.  
  4147.         status = PREF_GetIntPref("news.max_articles", &maxArticles);
  4148.         NET_SetNumberOfNewsArticlesInListing(maxArticles);
  4149.     }
  4150.     return status;
  4151. }
  4152.  
  4153. MODULE_PRIVATE XP_Bool
  4154. NET_IsOffline()
  4155. {
  4156.     /*  Cache this value, and register a pref callback to
  4157.         find out when it changes. 
  4158.     */
  4159.     if (!prefInitialized)
  4160.     {
  4161.         /*int status =*/ PREF_GetBoolPref("network.online", &isOnline);
  4162.         PREF_RegisterCallback("network.online",NET_OnlinePrefChangedFunc, NULL);
  4163.         /* because this routine gets called so often, we can register this callback here too. */
  4164.         PREF_RegisterCallback("news.max_articles", NET_NewsMaxArticlesChangedFunc, NULL);
  4165.         prefInitialized = TRUE;
  4166.     }
  4167.     return !isOnline;
  4168. }
  4169.  
  4170. PRIVATE int32
  4171. net_NewsLoad (ActiveEntry *ce)
  4172. {
  4173.   int status = 0;
  4174.   NewsConData *cd = XP_NEW(NewsConData);
  4175.   XP_Bool secure_p = FALSE;
  4176.   XP_Bool default_host = FALSE;
  4177.   char *url = ce->URL_s->address;
  4178.   char *host_and_port = 0;
  4179.   int32 port = 0;
  4180.   char *group = 0;
  4181.   char *message_id = 0;
  4182.   char *command_specific_data = 0;
  4183.   XP_Bool cancel_p = FALSE;
  4184.   char* colon;
  4185.   MSG_NewsHost* defhost = NULL;
  4186.   static XP_Bool collabra_enabled = TRUE;
  4187.   static XP_Bool got_enabled_pref = FALSE;
  4188.  
  4189.   if (!got_enabled_pref)
  4190.   {
  4191.       PREF_GetBoolPref("news.enabled",&collabra_enabled);
  4192.       got_enabled_pref = TRUE;
  4193.   }
  4194.  
  4195.   /* fail on protected url types */
  4196.   if(ce->protocol == INTERNAL_NEWS_TYPE_URL
  4197.      && !ce->URL_s->internal_url)
  4198.   {
  4199.     status = MK_MALFORMED_URL_ERROR;
  4200.     goto FAIL;
  4201.   }
  4202.  
  4203.   if (!collabra_enabled)
  4204.   {
  4205.     status = MK_MSG_COLLABRA_DISABLED;
  4206.     goto FAIL;
  4207.   }
  4208.  
  4209.  
  4210. #ifndef NSPR20
  4211.   PR_LogInit (&NNTPLog);
  4212. #endif
  4213.  
  4214.   if(!cd)
  4215.     {
  4216.       status = MK_OUT_OF_MEMORY;
  4217.       goto FAIL;
  4218.     }
  4219.  
  4220.   XP_MEMSET(cd, 0, sizeof(NewsConData));
  4221.   ce->URL_s->content_length = 0;
  4222.  
  4223.   if(!ce->proxy_addr)
  4224.   {
  4225.     /* look for an HTTPS proxy and use it if available,
  4226.      * but the passed in one takes precedence
  4227.      */
  4228.     StrAllocCopy(ce->proxy_addr,
  4229.                  NET_FindProxyHostForUrl(SECURE_HTTP_TYPE_URL, 
  4230.                                          ce->URL_s->address));
  4231.   }
  4232.   else
  4233.   {
  4234.       StrAllocCopy(cd->ssl_proxy_server, ce->proxy_addr);
  4235.   }
  4236.  
  4237.   cd->pane = ce->URL_s->msg_pane;
  4238.   if (!cd->pane)
  4239.   {
  4240.     NNTP_LOG_NOTE(("NET_NewsLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address));
  4241.     cd->pane = MSG_FindPane(ce->window_id, MSG_ANYPANE);
  4242.   }
  4243.   XP_ASSERT(cd->pane && MSG_GetContext(cd->pane) == ce->window_id);
  4244.   if (!cd->pane) {
  4245.     status = -1;                /* ### */
  4246.     goto FAIL;
  4247.   }
  4248.  
  4249.  
  4250.  
  4251.   NNTP_LOG_NOTE (("NET_NewsLoad: %s", ce->URL_s->address));
  4252.  
  4253.   cd->output_buffer = (char *) XP_ALLOC(OUTPUT_BUFFER_SIZE);
  4254.   if(!cd->output_buffer)
  4255.     {
  4256.       status = MK_OUT_OF_MEMORY;
  4257.       goto FAIL;
  4258.     }
  4259.  
  4260.   ce->con_data = cd;
  4261.   ce->socket = NULL;
  4262.   cd->article_num = -1;
  4263.  
  4264.   XP_ASSERT (url);
  4265.   if (!url)
  4266.     {
  4267.       status = -1;
  4268.       goto FAIL;
  4269.     }
  4270.  
  4271.  
  4272.   status = NET_parse_news_url (url, &host_and_port, &secure_p,
  4273.                                &group, &message_id, &command_specific_data);
  4274.   if (status < 0)
  4275.     goto FAIL;
  4276.  
  4277.   colon = XP_STRCHR (host_and_port, ':');
  4278.   if (colon) {
  4279.     unsigned long naturalLong;
  4280.     *colon = '\0';
  4281.     sscanf (colon+1, " %lu ", &naturalLong); /* %l is 64 bits on OSF1 */
  4282.     port = (int32) naturalLong;
  4283.   }
  4284.   cd->host = MSG_CreateNewsHost(MSG_GetMaster(cd->pane), host_and_port,
  4285.                                 secure_p, port);
  4286.   if (colon) *colon = ':';
  4287.  
  4288.   XP_ASSERT(cd->host);
  4289.   if (!cd->host) {
  4290.     status = -1;
  4291.     goto FAIL;
  4292.   }
  4293.  
  4294.   if (message_id && command_specific_data && !XP_STRCMP (command_specific_data, "?cancel"))
  4295.     cancel_p = TRUE;
  4296.  
  4297.   StrAllocCopy(cd->path, message_id);
  4298.   StrAllocCopy(cd->group_name, group);
  4299.  
  4300.   /* make sure the user has a news host configured */
  4301.   
  4302.   
  4303.   defhost = MSG_GetDefaultNewsHost(MSG_GetMaster(cd->pane));
  4304.  
  4305.   if (defhost == NULL)
  4306.     {
  4307.       char* alert = NET_ExplainErrorDetails(MK_NNTP_SERVER_NOT_CONFIGURED);
  4308.       FE_Alert(ce->window_id, alert);
  4309. #ifdef XP_MAC
  4310.       /* AFTER the alert, not before! */
  4311.       FE_EditPreference(PREF_NewsHost);
  4312. #endif
  4313.       status = -1;
  4314.       goto FAIL;
  4315.     }
  4316.  
  4317.   default_host = (cd->host == defhost);
  4318.  
  4319.   if (cancel_p && !ce->URL_s->internal_url)
  4320.     {
  4321.       /* Don't allow manual "news:ID?cancel" URLs, only internal ones. */
  4322.       status = -1;
  4323.       goto FAIL;
  4324.     }
  4325.   else if (ce->URL_s->method == URL_POST_METHOD)
  4326.     {
  4327.       /* news://HOST done with a POST instead of a GET;
  4328.          this means a new message is being posted.
  4329.          Don't allow this unless it's an internal URL.
  4330.        */
  4331.       if (!ce->URL_s->internal_url)
  4332.         {
  4333.           status = -1;
  4334.           goto FAIL;
  4335.         }
  4336.       XP_ASSERT (!group && !message_id && !command_specific_data);
  4337.       cd->type_wanted = NEWS_POST;
  4338.       StrAllocCopy(cd->path, "");
  4339.     }
  4340.   else if (message_id)
  4341.     {
  4342.       /* news:MESSAGE_ID
  4343.          news:/GROUP/MESSAGE_ID (useless)
  4344.          news://HOST/MESSAGE_ID
  4345.          news://HOST/GROUP/MESSAGE_ID (useless)
  4346.        */
  4347.       if (cancel_p)
  4348.         cd->type_wanted = CANCEL_WANTED;
  4349.       else
  4350.         cd->type_wanted = ARTICLE_WANTED;
  4351.     }
  4352.   else if (command_specific_data)
  4353.     {
  4354.       if (XP_STRSTR (command_specific_data, "?newgroups"))
  4355.         /* news://HOST/?newsgroups
  4356.             news:/GROUP/?newsgroups (useless)
  4357.             news:?newsgroups (default host)
  4358.          */
  4359.         cd->type_wanted = NEW_GROUPS;
  4360.       else
  4361.       {
  4362.         if (XP_STRSTR(command_specific_data, "?list-pretty"))
  4363.         {
  4364.             cd->type_wanted = PRETTY_NAMES_WANTED;
  4365.             cd->command_specific_data = XP_STRDUP(command_specific_data);
  4366.         }
  4367.         else if (XP_STRSTR(command_specific_data, "?profile"))
  4368.         {
  4369.             cd->type_wanted = PROFILE_WANTED;
  4370.             cd->command_specific_data = XP_STRDUP(command_specific_data);
  4371.         }
  4372.         else if (XP_STRSTR(command_specific_data, "?list-ids"))
  4373.         {
  4374.             cd->type_wanted = IDS_WANTED;
  4375.             cd->command_specific_data = XP_STRDUP(command_specific_data);
  4376.         }
  4377.         else
  4378.         {
  4379.             cd->type_wanted = SEARCH_WANTED;
  4380.             cd->command_specific_data = XP_STRDUP(command_specific_data);
  4381.             cd->current_search = cd->command_specific_data;
  4382.         }
  4383.       }
  4384.     }
  4385.   else if (group)
  4386.     {
  4387.       /* news:GROUP
  4388.          news:/GROUP
  4389.          news://HOST/GROUP
  4390.        */
  4391.       if (ce->window_id->type != MWContextNews && ce->window_id->type != MWContextNewsMsg 
  4392.           && ce->window_id->type != MWContextMail && ce->window_id->type != MWContextMailNewsProgress)
  4393.         {
  4394.           status = -1;
  4395.           goto FAIL;
  4396.         }
  4397.       if (XP_STRCHR (group, '*'))
  4398.         cd->type_wanted = LIST_WANTED;
  4399.       else
  4400.         cd->type_wanted = GROUP_WANTED;
  4401.     }
  4402.   else
  4403.     {
  4404.       /* news:
  4405.          news://HOST
  4406.        */
  4407.       cd->type_wanted = READ_NEWS_RC;
  4408.     }
  4409.  
  4410.  
  4411.   /* At this point, we're all done parsing the URL, and know exactly
  4412.      what we want to do with it.
  4413.    */
  4414.  
  4415. #ifndef NO_ARTICLE_CACHEING
  4416.   /* Turn off caching on all news entities, except articles. */
  4417.   /* It's very important that this be turned off for CANCEL_WANTED;
  4418.      for the others, I don't know what cacheing would cause, but
  4419.      it could only do harm, not good. */
  4420.   if(cd->type_wanted != ARTICLE_WANTED)
  4421. #endif
  4422.       ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
  4423.  
  4424.   if (cd->type_wanted == ARTICLE_WANTED)
  4425.   {
  4426.         const char *group = 0;
  4427.         uint32 number = 0;
  4428.  
  4429.         if (MSG_IsOfflineArticle (cd->pane, cd->path,
  4430.                                     &group, &number))
  4431.         {
  4432.             ce->local_file = TRUE;
  4433.             ce->socket = NULL;
  4434.             NET_SetCallNetlibAllTheTime(ce->window_id, "mknews");
  4435.             MSG_StartOfflineRetrieval(cd->pane, group, number, &cd->offlineState);
  4436.         }
  4437.   }
  4438.   if (NET_IsOffline() && !ce->local_file)
  4439.   {
  4440.       status = MK_OFFLINE;    
  4441.       goto FAIL;
  4442.   }
  4443.   if (cd->offlineState)
  4444.       goto FAIL;    /* we don't need to do any of this connection stuff */
  4445.  
  4446.   /* check for established connection and use it if available
  4447.    */
  4448.   if(nntp_connection_list)
  4449.     {
  4450.       NNTPConnection * tmp_con;
  4451.       XP_List * list_entry = nntp_connection_list;
  4452.  
  4453.       while((tmp_con = (NNTPConnection *)XP_ListNextObject(list_entry))
  4454.             != NULL)
  4455.         {
  4456.           /* if the hostnames match up exactly and the connection
  4457.            * is not busy at the moment then reuse this connection.
  4458.            */
  4459.           if(!XP_STRCMP(tmp_con->hostname, host_and_port)
  4460.              && secure_p == tmp_con->secure
  4461.              &&!tmp_con->busy)
  4462.             {
  4463.               cd->control_con = tmp_con;
  4464.  
  4465.               NNTP_LOG_NOTE(("Reusing control_con %08lX for %s", tmp_con, url));
  4466.  
  4467.               /* set select on the control socket */
  4468.               ce->socket = cd->control_con->csock;
  4469.               NET_SetReadSelect(ce->window_id, cd->control_con->csock);
  4470.               cd->control_con->prev_cache = TRUE;  /* this was from the cache */
  4471.               break;
  4472.             }
  4473.         }
  4474.     }
  4475.   else
  4476.     {
  4477.       /* initialize the connection list
  4478.        */
  4479.       nntp_connection_list = XP_ListNew();
  4480.     }
  4481.  
  4482.  
  4483.   if(cd->control_con)
  4484.     {
  4485.       cd->next_state = SEND_FIRST_NNTP_COMMAND;
  4486.       /* set the connection busy
  4487.        */
  4488.       cd->control_con->busy = TRUE;
  4489.       NET_TotalNumberOfOpenConnections++;
  4490.     }
  4491.   else
  4492.     {
  4493.       /* build a control connection structure so we
  4494.        * can store the data as we go along
  4495.        */
  4496.       cd->control_con = XP_NEW(NNTPConnection);
  4497.       if(!cd->control_con)
  4498.         {
  4499.           status = MK_OUT_OF_MEMORY;
  4500.           goto FAIL;
  4501.         }
  4502.       NNTP_LOG_NOTE(("Created control_con %08lX for %s", cd->control_con, ce->URL_s->address));
  4503.       XP_MEMSET(cd->control_con, 0, sizeof(NNTPConnection));
  4504.  
  4505.       cd->control_con->default_host = default_host;
  4506.       StrAllocCopy(cd->control_con->hostname, host_and_port);
  4507.       NNTP_LOG_NOTE(("Set host string: %s", cd->control_con->hostname));
  4508.  
  4509.       cd->control_con->secure = secure_p;
  4510.  
  4511.       cd->control_con->prev_cache = FALSE;  /* this wasn't from the cache */
  4512.  
  4513.       cd->control_con->csock = NULL;
  4514.  
  4515.       /* define this to test support for older NNTP servers
  4516.        * that don't support the XOVER command
  4517.        */
  4518. #ifdef TEST_NO_XOVER_SUPPORT
  4519.       cd->control_con->no_xover = TRUE;
  4520. #endif /* TEST_NO_XOVER_SUPPORT */
  4521.  
  4522.       /* add this structure to the connection list even
  4523.        * though it's not really valid yet.
  4524.        * we will fill it in as we go and if
  4525.        * an error occurs will will remove it from the
  4526.        * list.  No one else will be able to use it since
  4527.        * we will mark it busy.
  4528.        */
  4529.       XP_ListAddObject(nntp_connection_list, cd->control_con);
  4530.  
  4531.       /* set the connection busy
  4532.        */
  4533.       cd->control_con->busy = TRUE;
  4534.  
  4535.       /* gate the maximum number of cached connections
  4536.        * but don't delete busy ones.
  4537.        */
  4538.       if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
  4539.         {
  4540.           XP_List * list_ptr = nntp_connection_list->next;
  4541.           NNTPConnection * con;
  4542.  
  4543.           while(list_ptr)
  4544.             {
  4545.               con = (NNTPConnection *) list_ptr->object;
  4546.               list_ptr = list_ptr->next;
  4547.               if(!con->busy)
  4548.                 {
  4549.                   XP_ListRemoveObject(nntp_connection_list, con);
  4550.                   net_nntp_close (con->csock, status);
  4551.                   FREEIF(con->current_group);
  4552.                   FREEIF(con->hostname);
  4553.                   XP_FREE(con);
  4554.                   break;
  4555.                 }
  4556.             }
  4557.         }
  4558.  
  4559.       cd->next_state = NNTP_CONNECT;
  4560.     }
  4561.  
  4562.  FAIL:
  4563.  
  4564.   FREEIF (host_and_port);
  4565.   FREEIF (group);
  4566.   FREEIF (message_id);
  4567.   FREEIF (command_specific_data);
  4568.  
  4569.   if (status < 0)
  4570.   {
  4571.       FREEIF (cd->output_buffer);
  4572.       FREEIF (cd);
  4573.       ce->URL_s->error_msg = NET_ExplainErrorDetails(status);
  4574.       return status;
  4575.   }
  4576.   else 
  4577.   {
  4578.       return (net_ProcessNews (ce));
  4579.   }
  4580. }
  4581. /* process the offline news state machine, such as it is. */
  4582.  
  4583. PRIVATE int
  4584. NET_ProcessOfflineNews(ActiveEntry *ce, NewsConData *cd)
  4585. {
  4586.     int32 read_size = 0;
  4587.     int status;
  4588.  
  4589.     cd->pause_for_read  = TRUE;
  4590.  
  4591.     if (cd->stream)
  4592.         read_size = (*cd->stream->is_write_ready)
  4593.                                 (cd->stream);
  4594.     else
  4595.         ce->status = net_begin_article(ce);
  4596.  
  4597.     if(!read_size)
  4598.         return(0);  /* wait until we are ready to write */
  4599.     else
  4600.         read_size = MIN(read_size, NET_Socket_Buffer_Size);
  4601.  
  4602.     status = MSG_ProcessOfflineNews(cd->offlineState, NET_Socket_Buffer, read_size);
  4603.     if(status > 0)
  4604.     {
  4605.         ce->bytes_received += status;
  4606.         PUT_STREAM(NET_Socket_Buffer, status);    /* blow off error? ### dmb */
  4607.     }
  4608.  
  4609.     if (status == 0)
  4610.     {
  4611.        if (cd->stream)
  4612.         COMPLETE_STREAM;
  4613.            NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
  4614.  
  4615.         if(cd->destroy_graph_progress)
  4616.           FE_GraphProgressDestroy(ce->window_id, 
  4617.                                   ce->URL_s, 
  4618.                                   cd->original_content_length,
  4619.                                   ce->bytes_received);
  4620.         status = -1;
  4621.  
  4622.     }
  4623.     return status;
  4624. }
  4625.  
  4626. /* process the state machine
  4627.  *
  4628.  * returns negative when completely done
  4629.  */
  4630. PRIVATE int32
  4631. net_ProcessNews (ActiveEntry *ce)
  4632. {
  4633.     NewsConData * cd = (NewsConData *)ce->con_data;
  4634.  
  4635.     if (cd->offlineState != NULL)
  4636.     {
  4637.         return NET_ProcessOfflineNews(ce, cd);
  4638.     }
  4639.     cd->pause_for_read = FALSE;
  4640.  
  4641.     while(!cd->pause_for_read)
  4642.       {
  4643.  
  4644. #if DEBUG
  4645.         NNTP_LOG_NOTE(("Next state: %s",stateLabels[cd->next_state])); 
  4646. #endif
  4647.  
  4648.         switch(cd->next_state)
  4649.           {
  4650.             case NNTP_RESPONSE:
  4651.                 ce->status = net_news_response(ce);
  4652.                 break;
  4653.  
  4654. #ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
  4655.             case NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE:
  4656.                 if (nntp_are_connections_available(cd->control_con))
  4657.                 {
  4658.                     /* haven't reached connection ceiling on this host; go to connect */
  4659.                     cd->next_state = NNTP_CONNECTIONS_ARE_AVAILABLE;
  4660.                     cd->pause_for_read = FALSE;
  4661.                 }
  4662.                 else
  4663.                 {
  4664.                     NET_SetReadSelect(ce->window_id, ce->socket);
  4665.                     cd->pause_for_read = TRUE;
  4666.                 }
  4667.                 break;
  4668.             case NNTP_CONNECTIONS_ARE_AVAILABLE:
  4669.                 /* release our hacky little lock and move on to connection */
  4670.                 NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
  4671.                 
  4672.                 cd->next_state = NNTP_CONNECT;
  4673.                 cd->pause_for_read = FALSE;
  4674.                 break;
  4675. #endif
  4676.  
  4677.             case NNTP_CONNECT:
  4678.                 if (nntp_are_connections_available(cd->control_con))
  4679.                 {
  4680.                     ce->status = NET_BeginConnect(
  4681.                         (cd->ssl_proxy_server && cd->control_con->secure ?
  4682.                         cd->ssl_proxy_server :cd->control_con->hostname), 
  4683.                         NULL,
  4684.                         "NNTP", 
  4685.                         (cd->control_con->secure ? SECURE_NEWS_PORT : NEWS_PORT), 
  4686.                         &ce->socket, 
  4687.                         (cd->ssl_proxy_server ? FALSE : cd->control_con->secure), 
  4688.                         &cd->tcp_con_data, 
  4689.                         ce->window_id,
  4690.                         &ce->URL_s->error_msg,
  4691.                         ce->socks_host,
  4692.                         ce->socks_port);
  4693.  
  4694.                     if(ce->socket != NULL)
  4695.                         NET_TotalNumberOfOpenConnections++;
  4696.  
  4697.                     cd->pause_for_read = TRUE;
  4698.                     if(ce->status == MK_CONNECTED)
  4699.                     {
  4700. HG33086
  4701.                         {
  4702.                             cd->next_state = NNTP_RESPONSE;
  4703.                             cd->next_state_after_response = NNTP_LOGIN_RESPONSE;
  4704.                         }
  4705.                         NET_SetReadSelect(ce->window_id, ce->socket);
  4706.                         cd->control_con->csock = ce->socket;
  4707.                     }
  4708.                     else if(ce->status > -1)
  4709.                     {
  4710.                         ce->con_sock = ce->socket;  /* set con_sock so we can select on it */
  4711.                         cd->control_con->csock = ce->socket;
  4712.                         NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4713.                         cd->next_state = NNTP_CONNECT_WAIT;
  4714.                     }
  4715.                 }
  4716.                 else
  4717.                 {
  4718. #ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
  4719.                     /* ###phil this doesn't work yet, but the idea is that we've
  4720.                      * maxed out connections on this host; stay in this state 
  4721.                      */
  4722.                     ce->con_sock = ce->socket;  /* set con_sock so we can select on it */
  4723.                     cd->control_con->csock = ce->socket;
  4724.                     NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4725.                     cd->next_state = NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE;
  4726. #else
  4727.                     ce->status = -1;
  4728.                     cd->next_state = NNTP_ERROR;
  4729.                     ce->socket = cd->control_con->csock = NULL;
  4730. #endif
  4731.                 }
  4732.                 break;
  4733.  
  4734.             case NNTP_CONNECT_WAIT:
  4735.                 ce->status = NET_FinishConnect(
  4736.                               (cd->ssl_proxy_server && cd->control_con->secure ?
  4737.                                 cd->ssl_proxy_server : cd->control_con->hostname),
  4738.                               "NNTP", 
  4739.                               (cd->control_con->secure ? SECURE_NEWS_PORT : NEWS_PORT), 
  4740.                               &ce->socket, 
  4741.                               &cd->tcp_con_data, 
  4742.                               ce->window_id,
  4743.                               &ce->URL_s->error_msg);
  4744.   
  4745.                 cd->pause_for_read = TRUE;
  4746.                 if(ce->status == MK_CONNECTED)
  4747.                   {
  4748.                     cd->control_con->csock = ce->socket;
  4749. HG17928
  4750.                       {
  4751.                         cd->next_state = NNTP_RESPONSE;
  4752.                         cd->next_state_after_response = NNTP_LOGIN_RESPONSE;
  4753.                       }
  4754.  
  4755.                     NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4756.                     ce->con_sock = NULL;  /* reset con_sock so we don't select on it */
  4757.                     NET_SetReadSelect(ce->window_id, ce->socket);
  4758.                   }
  4759.                 else if(ce->status < 0)
  4760.                   {
  4761.                         NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4762.                   }
  4763.                 else
  4764.                   {
  4765.                     /* the not yet connected state */
  4766.  
  4767.                     /* unregister the old ce->socket from the select list
  4768.                       * and register the new value in the case that it changes
  4769.                       */
  4770.                     if(ce->con_sock != ce->socket)
  4771.                         {
  4772.                         NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4773.                         ce->con_sock = ce->socket;
  4774.                         NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4775.                         }
  4776.                   }
  4777.                 break;
  4778. HG68092
  4779.             case NNTP_LOGIN_RESPONSE:
  4780.                 ce->status = net_nntp_login_response(ce);
  4781.                 break;
  4782.  
  4783.             case NNTP_SEND_MODE_READER:
  4784.                 ce->status = net_nntp_send_mode_reader(ce);
  4785.                 break;
  4786.  
  4787.             case NNTP_SEND_MODE_READER_RESPONSE:
  4788.                 ce->status = net_nntp_send_mode_reader_response(ce);
  4789.                 break;
  4790.  
  4791.             case SEND_LIST_EXTENSIONS:
  4792.                 ce->status = net_nntp_send_list_extensions(ce);
  4793.                 break;
  4794.             case SEND_LIST_EXTENSIONS_RESPONSE:
  4795.                 ce->status = net_nntp_send_list_extensions_response(ce);
  4796.                 break;
  4797.             case SEND_LIST_SEARCHES:
  4798.                 ce->status = net_nntp_send_list_searches(ce);
  4799.                 break;
  4800.             case SEND_LIST_SEARCHES_RESPONSE:
  4801.                 ce->status = net_nntp_send_list_searches_response(ce);
  4802.                 break;
  4803.             case NNTP_LIST_SEARCH_HEADERS:
  4804.                 ce->status = net_send_list_search_headers(ce);
  4805.                 break;
  4806.             case NNTP_LIST_SEARCH_HEADERS_RESPONSE:
  4807.                 ce->status = net_send_list_search_headers_response(ce);
  4808.                 break;
  4809.             case NNTP_GET_PROPERTIES:
  4810.                 ce->status = nntp_get_properties(ce);
  4811.                 break;
  4812.             case NNTP_GET_PROPERTIES_RESPONSE:
  4813.                 ce->status = nntp_get_properties_response(ce);
  4814.                 break;
  4815.                 
  4816.             case SEND_LIST_SUBSCRIPTIONS:
  4817.                 ce->status = net_send_list_subscriptions(ce);
  4818.                 break;
  4819.             case SEND_LIST_SUBSCRIPTIONS_RESPONSE:
  4820.                 ce->status = net_send_list_subscriptions_response(ce);
  4821.                 break;
  4822.  
  4823.             case SEND_FIRST_NNTP_COMMAND:
  4824.                 ce->status = net_send_first_nntp_command(ce);
  4825.                 break;
  4826.  
  4827.             case SEND_FIRST_NNTP_COMMAND_RESPONSE:
  4828.                 ce->status = net_send_first_nntp_command_response(ce);
  4829.                 break;
  4830.  
  4831.             case NNTP_SEND_GROUP_FOR_ARTICLE:
  4832.                 ce->status = net_send_group_for_article(ce);
  4833.                 break;
  4834.             case NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE:
  4835.                 ce->status = net_send_group_for_article_response(ce);
  4836.                 break;
  4837.             case NNTP_SEND_ARTICLE_NUMBER:
  4838.                 ce->status = net_send_article_number(ce);
  4839.                 break;
  4840.  
  4841.             case SETUP_NEWS_STREAM:
  4842.                 ce->status = net_setup_news_stream(ce);
  4843.                 break;
  4844.  
  4845.             case NNTP_BEGIN_AUTHORIZE:
  4846.                 ce->status = net_news_begin_authorize(ce);
  4847.                 break;
  4848.  
  4849.             case NNTP_AUTHORIZE_RESPONSE:
  4850.                 ce->status = net_news_authorize_response(ce);
  4851.                 break;
  4852.  
  4853.             case NNTP_PASSWORD_RESPONSE:
  4854.                 ce->status = net_news_password_response(ce);
  4855.                 break;
  4856.     
  4857.             case NNTP_READ_LIST_BEGIN:
  4858.                 ce->status = net_read_news_list_begin(ce);
  4859.                 break;
  4860.  
  4861.             case NNTP_READ_LIST:
  4862.                 ce->status = net_read_news_list(ce);
  4863.                 break;
  4864.  
  4865.             case DISPLAY_NEWSGROUPS:
  4866.                 ce->status = net_display_newsgroups(ce);
  4867.                 break;
  4868.  
  4869.             case NNTP_NEWGROUPS_BEGIN:
  4870.                 ce->status = net_newgroups_begin(ce);
  4871.                 break;
  4872.     
  4873.             case NNTP_NEWGROUPS:
  4874.                 ce->status = net_process_newgroups(ce);
  4875.                 break;
  4876.     
  4877.             case NNTP_BEGIN_ARTICLE:
  4878.                 ce->status = net_begin_article(ce);
  4879.                 break;
  4880.  
  4881.             case NNTP_READ_ARTICLE:
  4882.               {
  4883.                 char *line;
  4884.                 NewsConData * cd =
  4885.                   (NewsConData *)ce->con_data;
  4886.  
  4887.                 ce->status = NET_BufferedReadLine(ce->socket, &line,
  4888.                                                  &cd->data_buf, 
  4889.                                                  &cd->data_buf_size,
  4890.                                                  (Bool*)&cd->pause_for_read);
  4891.  
  4892.                 if(ce->status == 0)
  4893.                   {
  4894.                     cd->next_state = NNTP_ERROR;
  4895.                     cd->pause_for_read = FALSE;
  4896.                     ce->URL_s->error_msg =
  4897.                       NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
  4898.                     return(MK_NNTP_SERVER_ERROR);
  4899.                   }
  4900.  
  4901.                 if(ce->status > 1)
  4902.                   {
  4903.                     ce->bytes_received += ce->status;
  4904.                     /* We shouldn't graph progress if going offline - it messes
  4905.                     up our go offline status msgs. ### dmb
  4906.                     */
  4907.                     FE_GraphProgress(ce->window_id, ce->URL_s,
  4908.                                      ce->bytes_received, ce->status,
  4909.                                      ce->URL_s->content_length);
  4910.  
  4911.                   }
  4912.  
  4913.                 if(!line)
  4914.                   return(ce->status);  /* no line yet or error */
  4915.  
  4916.                 if (cd->type_wanted == CANCEL_WANTED &&
  4917.                     cd->response_code != 221)
  4918.                   {
  4919.                     /* HEAD command failed. */
  4920.                     return MK_NNTP_CANCEL_ERROR;
  4921.                   }
  4922.  
  4923.                 if (line[0] == '.' && line[1] == 0)
  4924.                   {
  4925.                     if (cd->type_wanted == CANCEL_WANTED)
  4926.                       cd->next_state = NEWS_START_CANCEL;
  4927.                     else
  4928.                       cd->next_state = NEWS_DONE;
  4929.  
  4930.                     cd->pause_for_read = FALSE;
  4931.                   }
  4932.                 else
  4933.                   {
  4934.                     if (line[0] == '.')
  4935.                       XP_STRCPY (cd->output_buffer, line + 1);
  4936.                     else
  4937.                       XP_STRCPY (cd->output_buffer, line);
  4938.  
  4939.                     /* When we're sending this line to a converter (ie,
  4940.                        it's a message/rfc822) use the local line termination
  4941.                        convention, not CRLF.  This makes text articles get
  4942.                        saved with the local line terminators.  Since SMTP
  4943.                        and NNTP mandate the use of CRLF, it is expected that
  4944.                        the local system will convert that to the local line
  4945.                        terminator as it is read.
  4946.                      */
  4947.                     XP_STRCAT (cd->output_buffer, LINEBREAK);
  4948.                     /* Don't send content-type to mime parser if we're doing a cancel
  4949.                       because it confuses mime parser into not parsing.
  4950.                       */
  4951.                     if (cd->type_wanted != CANCEL_WANTED || XP_STRNCMP(cd->output_buffer, "Content-Type:", 13))
  4952.                         ce->status = PUTSTRING(cd->output_buffer);
  4953.                   }
  4954.                 break;
  4955.               }
  4956.  
  4957.             case NNTP_XOVER_BEGIN:
  4958.                 NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_READ_NEWSGROUPINFO));
  4959.                 ce->status = net_read_xover_begin(ce);
  4960.                 break;
  4961.  
  4962.           case NNTP_FIGURE_NEXT_CHUNK:
  4963.                 ce->status = net_figure_next_chunk(ce);
  4964.                 break;
  4965.  
  4966.             case NNTP_XOVER_SEND:
  4967.                 ce->status = net_xover_send(ce);
  4968.                 break;
  4969.     
  4970.             case NNTP_XOVER:
  4971.                 ce->status = net_read_xover(ce); 
  4972.                 break;
  4973.  
  4974.             case NNTP_XOVER_RESPONSE:
  4975.                 ce->status = net_read_xover_response(ce); 
  4976.                 break;
  4977.  
  4978.             case NEWS_PROCESS_XOVER:
  4979.             case NEWS_PROCESS_BODIES:
  4980.                 if (cd->group_name && XP_STRSTR (cd->group_name, ".emacs"))
  4981.                   /* This is a joke!  Don't internationalize it... */
  4982.                   NET_Progress(ce->window_id, "Garbage collecting...");
  4983.                 else
  4984.                   NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_SORT_ARTICLES));
  4985.                   ce->status = net_process_xover(ce);
  4986.                 break;
  4987.  
  4988.             case NNTP_PROFILE_ADD:
  4989.                 ce->status = net_profile_add (ce);
  4990.                 break;
  4991.             case NNTP_PROFILE_ADD_RESPONSE:
  4992.                 ce->status = net_profile_add_response(ce);
  4993.                 break;
  4994.             case NNTP_PROFILE_DELETE:
  4995.                 ce->status = net_profile_delete (ce);
  4996.                 break;
  4997.             case NNTP_PROFILE_DELETE_RESPONSE:
  4998.                 ce->status = net_profile_delete_response(ce);
  4999.                 break;
  5000.  
  5001.             case NNTP_READ_GROUP:
  5002.                 ce->status = net_read_news_group(ce);
  5003.                 break;
  5004.     
  5005.             case NNTP_READ_GROUP_RESPONSE:
  5006.                 ce->status = net_read_news_group_response(ce);
  5007.                 break;
  5008.  
  5009.             case NNTP_READ_GROUP_BODY:
  5010.                 ce->status = net_read_news_group_body(ce);
  5011.                 break;
  5012.  
  5013.             case NNTP_SEND_POST_DATA:
  5014.                 ce->status = net_send_news_post_data(ce);
  5015.                 break;
  5016.  
  5017.             case NNTP_SEND_POST_DATA_RESPONSE:
  5018.                 ce->status = net_send_news_post_data_response(ce);
  5019.                 break;
  5020.  
  5021.             case NNTP_CHECK_FOR_MESSAGE:
  5022.                 ce->status = net_check_for_message(ce);
  5023.                 break;
  5024.  
  5025.             case NEWS_NEWS_RC_POST:
  5026.                 ce->status = net_NewsRCProcessPost(ce);
  5027.                 break;
  5028.  
  5029.             case NEWS_DISPLAY_NEWS_RC:
  5030.                 ce->status = net_DisplayNewsRC(ce);
  5031.                 break;
  5032.  
  5033.             case NEWS_DISPLAY_NEWS_RC_RESPONSE:
  5034.                 ce->status = net_DisplayNewsRCResponse(ce);
  5035.                 break;
  5036.  
  5037.             case NEWS_START_CANCEL:
  5038.                 ce->status = net_start_cancel(ce);
  5039.                 break;
  5040.  
  5041.             case NEWS_DO_CANCEL:
  5042.                 ce->status = net_do_cancel(ce);
  5043.                 break;
  5044.  
  5045.             case NNTP_XPAT_SEND:
  5046.                 ce->status = net_xpat_send(ce);
  5047.                 break;
  5048.             case NNTP_XPAT_RESPONSE:
  5049.                 ce->status = net_xpat_response(ce);
  5050.                 break;
  5051.             case NNTP_SEARCH:
  5052.                 ce->status = net_nntp_search(ce);
  5053.                 break;
  5054.             case NNTP_SEARCH_RESPONSE:
  5055.                 ce->status = net_nntp_search_response(ce);
  5056.                 break;
  5057.             case NNTP_SEARCH_RESULTS:
  5058.                 ce->status = net_nntp_search_results(ce);
  5059.                 break;
  5060.             case NNTP_LIST_PRETTY_NAMES:
  5061.                 ce->status = net_list_pretty_names(ce);
  5062.                 break;
  5063.             case NNTP_LIST_PRETTY_NAMES_RESPONSE:
  5064.                 ce->status = net_list_pretty_names_response(ce);
  5065.                 break;
  5066.             case NNTP_LIST_XACTIVE:
  5067.                 ce->status = net_list_xactive(ce);
  5068.                 break;
  5069.             case NNTP_LIST_XACTIVE_RESPONSE:
  5070.                 ce->status = net_list_xactive_response(ce);
  5071.                 break;
  5072.             case NNTP_LIST_GROUP:
  5073.                 ce->status = net_list_group(ce);
  5074.                 break;
  5075.             case NNTP_LIST_GROUP_RESPONSE:
  5076.                 ce->status = net_list_group_response(ce);
  5077.                 break;
  5078.             case NEWS_DONE:
  5079.               /* call into libmsg and see if the article counts
  5080.                * are up to date.  If they are not then we
  5081.                * want to do a "news://host/group" URL so that we
  5082.                * can finish up the article counts.
  5083.                */
  5084.               if (cd->stream)
  5085.                 COMPLETE_STREAM;
  5086.  
  5087.                 cd->next_state = NEWS_FREE;
  5088.                 /* set the connection unbusy
  5089.                   */
  5090.                 cd->control_con->busy = FALSE;
  5091.                 NET_TotalNumberOfOpenConnections--;
  5092.  
  5093.                 NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
  5094.                 NET_RefreshCacheFileExpiration(ce->URL_s);
  5095.                 break;
  5096.  
  5097.             case NEWS_ERROR:
  5098.                 if(cd->stream)
  5099.                      ABORT_STREAM(ce->status);
  5100.                 cd->next_state = NEWS_FREE;
  5101.                 /* set the connection unbusy
  5102.                   */
  5103.                 cd->control_con->busy = FALSE;
  5104.                 NET_TotalNumberOfOpenConnections--;
  5105.  
  5106.                 if(cd->control_con->csock != NULL)
  5107.                   {
  5108.                     NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
  5109.                   }
  5110.                 break;
  5111.  
  5112.             case NNTP_ERROR:
  5113.                 if(cd->stream)
  5114.                   {
  5115.                     ABORT_STREAM(ce->status);
  5116.                     cd->stream=0;
  5117.                   }
  5118.     
  5119.                 if(cd->control_con && cd->control_con->csock != NULL)
  5120.                   {
  5121.                     NNTP_LOG_NOTE(("Clearing read and connect select on socket %d",
  5122.                                                             cd->control_con->csock));
  5123.                     NET_ClearConnectSelect(ce->window_id, cd->control_con->csock);
  5124.                     NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
  5125. #ifdef XP_WIN
  5126.                     if(cd->calling_netlib_all_the_time)
  5127.                     {
  5128.                         cd->calling_netlib_all_the_time = FALSE;
  5129.                         NET_ClearCallNetlibAllTheTime(ce->window_id, "mknews");
  5130.                     }
  5131. #endif /* XP_WIN */
  5132.                     NET_ClearDNSSelect(ce->window_id, cd->control_con->csock);
  5133.                     net_nntp_close (cd->control_con->csock, ce->status);  /* close the socket */
  5134.                     FREEIF(cd->control_con->current_group);
  5135.                     NET_TotalNumberOfOpenConnections--;
  5136.                     ce->socket = NULL;
  5137.                   }
  5138.  
  5139.                 /* check if this connection came from the cache or if it was
  5140.                  * a new connection.  If it was not new lets start it over
  5141.                  * again.  But only if we didn't have any successful protocol
  5142.                  * dialog at all.
  5143.                  */
  5144.                 if(cd->control_con && cd->control_con->prev_cache &&
  5145.                    !cd->some_protocol_succeeded)
  5146.                   {
  5147.                      cd->control_con->prev_cache = FALSE;
  5148.                      cd->next_state = NNTP_CONNECT;
  5149.                      ce->status = 0;  /* keep going */
  5150.                   }
  5151.                 else
  5152.                   {
  5153.                     cd->next_state = NEWS_FREE;
  5154.             
  5155.                     /* remove the connection from the cache list
  5156.                      * and free the data
  5157.                      */
  5158.                     XP_ListRemoveObject(nntp_connection_list, cd->control_con);
  5159.                     if(cd->control_con)
  5160.                       {
  5161.                         FREEIF(cd->control_con->hostname);
  5162.                         FREE(cd->control_con);
  5163.                       }
  5164.                   }
  5165.                 break;
  5166.     
  5167.             case NEWS_FREE:
  5168.  
  5169.                   /* do we need to know if we're parsing xover to call finish xover? */
  5170.                     /* If we've gotten to NEWS_FREE and there is still XOVER
  5171.                        data, there was an error or we were interrupted or
  5172.                        something.  So, tell libmsg there was an abnormal
  5173.                        exit so that it can free its data. */
  5174. /*                if (cd->xover_parse_state != NULL)*/
  5175.                 {
  5176.                     int status;
  5177. /*                    XP_ASSERT (ce->status < 0);*/
  5178.                     status = MSG_FinishXOVER (cd->pane,
  5179.                                               &cd->xover_parse_state,
  5180.                                               ce->status);
  5181.                     XP_ASSERT (!cd->xover_parse_state);
  5182.                     if (ce->status >= 0 && status < 0)
  5183.                       ce->status = status;
  5184.                 }
  5185.                 if (cd->newsgroup_parse_state)
  5186.                     MSG_FinishAddArticleKeyToGroup(cd->pane, &cd->newsgroup_parse_state);
  5187.  
  5188.                 FREEIF(cd->path);
  5189.                 FREEIF(cd->response_txt);
  5190.                 FREEIF(cd->data_buf);
  5191.  
  5192.                 FREEIF(cd->output_buffer);
  5193.                 FREEIF(cd->ssl_proxy_server);
  5194.                 FREEIF(cd->stream);  /* don't forget the stream */
  5195.                 if(cd->tcp_con_data)
  5196.                     NET_FreeTCPConData(cd->tcp_con_data);
  5197.  
  5198.                 FREEIF(cd->group_name);
  5199.  
  5200.                 FREEIF (cd->cancel_id);
  5201.                 FREEIF (cd->cancel_from);
  5202.                 FREEIF (cd->cancel_newsgroups);
  5203.                 FREEIF (cd->cancel_distribution);
  5204.  
  5205.                 if(cd->destroy_graph_progress)
  5206.                     FE_GraphProgressDestroy(ce->window_id, 
  5207.                                             ce->URL_s, 
  5208.                                             cd->original_content_length,
  5209.                                             ce->bytes_received);
  5210.                 XP_FREE(cd);
  5211.  
  5212.                 /* gate the maximum number of cached connections
  5213.                  * but don't delete busy ones.
  5214.                  */
  5215.                 if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
  5216.                   {
  5217.                     XP_List * list_ptr = nntp_connection_list->next;
  5218.                     NNTPConnection * con;
  5219.  
  5220.                     NNTP_LOG_NOTE(("killing cached news connection"));
  5221.  
  5222.                     while(list_ptr)
  5223.                       {
  5224.                         con = (NNTPConnection *) list_ptr->object;
  5225.                         list_ptr = list_ptr->next;
  5226.                         if(!con->busy)
  5227.                           {
  5228.                             XP_ListRemoveObject(nntp_connection_list, con);
  5229.                             net_nntp_close(con->csock, ce->status);
  5230.                             FREEIF(con->current_group);
  5231.                             FREEIF(con->hostname);
  5232.                             XP_FREE(con);
  5233.                             break;
  5234.                           }
  5235.                       }
  5236.                   }
  5237.                 return(-1); /* all done */
  5238.  
  5239.             default:
  5240.                 /* big error */
  5241.                 return(-1);
  5242.           }
  5243.  
  5244.         if(ce->status < 0 && cd->next_state != NEWS_ERROR &&
  5245.                     cd->next_state != NNTP_ERROR && cd->next_state != NEWS_FREE)
  5246.           {
  5247.             cd->next_state = NNTP_ERROR;
  5248.             cd->pause_for_read = FALSE;
  5249.           }
  5250.       } /* end big while */
  5251.  
  5252.       return(0); /* keep going */
  5253. }
  5254.  
  5255. /* abort the connection in progress
  5256.  */
  5257. PRIVATE int32
  5258. net_InterruptNews(ActiveEntry * ce)
  5259. {
  5260.     NewsConData * cd = (NewsConData *)ce->con_data;
  5261.  
  5262.     if (cd->offlineState != NULL)
  5263.         return MSG_InterruptOfflineNews(cd->offlineState);
  5264.  
  5265.     cd->next_state = NNTP_ERROR;
  5266.     ce->status = MK_INTERRUPTED;
  5267.  
  5268.     if (cd->control_con)
  5269.         cd->control_con->prev_cache = FALSE;   /* to keep if from reconnecting */
  5270.  
  5271.     return(net_ProcessNews(ce));
  5272. }
  5273.  
  5274. /* Free any memory used up
  5275.  * close cached connections and
  5276.  * free newsgroup listings
  5277.  */
  5278. PRIVATE void
  5279. net_CleanupNews(void)
  5280. {
  5281.     NNTP_LOG_NOTE(("NET_CleanupNews called"));
  5282.  
  5283.     /* this is a small leak but since I don't have another function to
  5284.      * clear my connections I need to call this one alot and I don't
  5285.      * want to free the newshost
  5286.      *
  5287.      * FREE_AND_CLEAR(NET_NewsHost);
  5288.      */
  5289.  
  5290.     if(nntp_connection_list)
  5291.       {
  5292.         NNTPConnection * tmp_con;
  5293.  
  5294.         while((tmp_con = (NNTPConnection *)XP_ListRemoveTopObject(nntp_connection_list)) != NULL)
  5295.           {
  5296.             if(!tmp_con->busy)
  5297.               {
  5298.                 FREE(tmp_con->hostname);
  5299.                 FREEIF(tmp_con->current_group);
  5300.                 if(tmp_con->csock != NULL)
  5301.                   {
  5302.                     net_nntp_close(tmp_con->csock, 0 /* status? */);
  5303.                   }
  5304.                 FREE(tmp_con);
  5305.               }
  5306.           }
  5307.  
  5308.         if(XP_ListIsEmpty(nntp_connection_list))
  5309.           {
  5310.             XP_ListDestroy(nntp_connection_list);
  5311.             nntp_connection_list = 0;
  5312.           }
  5313.       }
  5314.  
  5315.     return;
  5316. }
  5317.  
  5318. MODULE_PRIVATE void
  5319. NET_InitNewsProtocol(void)
  5320. {
  5321.     static NET_ProtoImpl news_proto_impl;
  5322.  
  5323.     news_proto_impl.init = net_NewsLoad;
  5324.     news_proto_impl.process = net_ProcessNews;
  5325.     news_proto_impl.interrupt = net_InterruptNews;
  5326.     news_proto_impl.cleanup = net_CleanupNews;
  5327.  
  5328.     NET_RegisterProtocolImplementation(&news_proto_impl, NEWS_TYPE_URL);
  5329.     NET_RegisterProtocolImplementation(&news_proto_impl, INTERNAL_NEWS_TYPE_URL);
  5330. }
  5331.  
  5332. #ifdef PROFILE
  5333. #pragma profile off
  5334. #endif
  5335.  
  5336. #endif /* MOZILLA_CLIENT */
  5337.  
  5338.