summaryrefslogtreecommitdiffstats
path: root/mailnews/news/src/nsNNTPProtocol.h
blob: 08db18ee8c90db653aafa7c3b7c2ed7b89205df9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsNNTPProtocol_h___
#define nsNNTPProtocol_h___

#include "nsMsgProtocol.h"

#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsINntpUrl.h"
#include "nsINntpIncomingServer.h"
#include "nsINNTPProtocol.h"

#include "nsINNTPNewsgroupList.h"
#include "nsINNTPArticleList.h"
#include "nsIMsgAsyncPrompter.h"
#include "nsIMsgNewsFolder.h"
#include "nsIMsgWindow.h"

#include "nsMsgLineBuffer.h"
#include "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsICacheEntryOpenCallback.h"

// this is only needed as long as our libmime hack is in place
#include "prio.h"

// State Flags (Note, I use the word state in terms of storing
// state information about the connection (authentication, have we sent
// commands, etc. I do not intend it to refer to protocol state)

#define NNTP_PAUSE_FOR_READ      0x00000001  /* should we pause for the next read */
#define NNTP_PROXY_AUTH_REQUIRED    0x00000002  /* is auth required */
#define NNTP_SENT_PROXY_AUTH        0x00000004  /* have we sent a proxy auth? */
#define NNTP_READER_PERFORMED       0x00000010  /* have we sent any cmds to the server yet? */
#define NNTP_USE_FANCY_NEWSGROUP    0x00000020  /* use LIST XACTIVE or LIST */
#define NNTP_DESTROY_PROGRESS_GRAPH 0x00000040  /* do we need to destroy graph progress */
#define NNTP_SOME_PROTOCOL_SUCCEEDED 0x0000080  /* some protocol has suceeded so don't kill the connection */
#define NNTP_NO_XOVER_SUPPORT       0x00000100  /* xover command is not supported here */

/* states of the machine
 */
typedef enum _StatesEnum {
NNTP_RESPONSE,
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE,
NNTP_CONNECTIONS_ARE_AVAILABLE,
#endif
NNTP_CONNECT,
NNTP_CONNECT_WAIT,
NNTP_LOGIN_RESPONSE,
NNTP_SEND_MODE_READER,
NNTP_SEND_MODE_READER_RESPONSE,
SEND_LIST_EXTENSIONS,
SEND_LIST_EXTENSIONS_RESPONSE,
SEND_LIST_SEARCHES,
SEND_LIST_SEARCHES_RESPONSE,
NNTP_LIST_SEARCH_HEADERS,
NNTP_LIST_SEARCH_HEADERS_RESPONSE,
NNTP_GET_PROPERTIES,
NNTP_GET_PROPERTIES_RESPONSE,
SEND_LIST_SUBSCRIPTIONS,
SEND_LIST_SUBSCRIPTIONS_RESPONSE,
SEND_FIRST_NNTP_COMMAND,
SEND_FIRST_NNTP_COMMAND_RESPONSE,
SETUP_NEWS_STREAM,
NNTP_BEGIN_AUTHORIZE,
NNTP_AUTHORIZE_RESPONSE,
NNTP_PASSWORD_RESPONSE,
NNTP_READ_LIST_BEGIN,
NNTP_READ_LIST,
DISPLAY_NEWSGROUPS,
NNTP_NEWGROUPS_BEGIN,
NNTP_NEWGROUPS,
NNTP_BEGIN_ARTICLE,
NNTP_READ_ARTICLE,
NNTP_XOVER_BEGIN,
NNTP_FIGURE_NEXT_CHUNK,
NNTP_XOVER_SEND,
NNTP_XOVER_RESPONSE,
NNTP_XOVER,
NEWS_PROCESS_XOVER,
NNTP_XHDR_SEND,
NNTP_XHDR_RESPONSE,
NNTP_READ_GROUP,
NNTP_READ_GROUP_RESPONSE,
NNTP_READ_GROUP_BODY,
NNTP_SEND_GROUP_FOR_ARTICLE,
NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE,
NNTP_SEND_ARTICLE_NUMBER,
NEWS_PROCESS_BODIES,
NNTP_PRINT_ARTICLE_HEADERS,
NNTP_SEND_POST_DATA,
NNTP_SEND_POST_DATA_RESPONSE,
NNTP_CHECK_FOR_MESSAGE,
NEWS_START_CANCEL,
NEWS_DO_CANCEL,
NNTP_XPAT_SEND,
NNTP_XPAT_RESPONSE,
NNTP_SEARCH,
NNTP_SEARCH_RESPONSE,
NNTP_SEARCH_RESULTS,
NNTP_LIST_PRETTY_NAMES,
NNTP_LIST_PRETTY_NAMES_RESPONSE,
NNTP_LIST_XACTIVE,
NNTP_LIST_XACTIVE_RESPONSE,
NNTP_LIST_GROUP,
NNTP_LIST_GROUP_RESPONSE,
NEWS_DONE,
NEWS_POST_DONE,
NEWS_ERROR,
NNTP_ERROR,
NEWS_FREE,
NNTP_SUSPENDED
} StatesEnum;

class nsICacheEntry;

class nsNNTPProtocol : public nsMsgProtocol,
                       public nsINNTPProtocol,
                       public nsITimerCallback,
                       public nsICacheEntryOpenCallback,
                       public nsIMsgAsyncPromptListener
{
public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSINNTPPROTOCOL
  NS_DECL_NSICACHEENTRYOPENCALLBACK
  NS_DECL_NSITIMERCALLBACK
  NS_DECL_NSIMSGASYNCPROMPTLISTENER

  // Creating a protocol instance requires the URL
  // need to call Initialize after we do a new of nsNNTPProtocol
  nsNNTPProtocol(nsINntpIncomingServer *aServer, nsIURI *aURL,
                 nsIMsgWindow *aMsgWindow);

  // stop binding is a "notification" informing us that the stream associated with aURL is going away.
  NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports * aCtxt, nsresult aStatus) override;

  char * m_ProxyServer;    /* proxy server hostname */

  NS_IMETHOD Cancel(nsresult status) override;  // handle stop button
  NS_IMETHOD GetContentType(nsACString &aContentType) override;
  NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) override;
  NS_IMETHOD AsyncOpen2(nsIStreamListener *listener) override;
  NS_IMETHOD GetOriginalURI(nsIURI* *aURI) override;
  NS_IMETHOD SetOriginalURI(nsIURI* aURI) override;

  nsresult LoadUrl(nsIURI * aURL, nsISupports * aConsumer) override;

private:
  virtual ~nsNNTPProtocol();
  /**
   * Triggers the protocol state machine.
   * Most of the time, this machine will read as much input as it can before
   * closing.
   *
   * This method additionally handles some states not covered by other methods:
   * NEWS_DONE: Alias for NEWS_FREE
   * NEWS_POST_DONE: Alias for NEWS_FREE that cleans up the URL state
   * NEWS_ERROR: An error which permits further use of the connection
   * NNTP_ERROR: An error which does not permit further use of the connection
   * NEWS_FREE: Cleans up from the current URL and prepares for the next one
   * NNTP_SUSPENDED: A state where the state machine does not read input until
   *                 reenabled by a non-network related callback
   *
   * @note Use of NNTP_SUSPENDED is dangerous: if input comes along the socket,
   * the code will not read the input stream at all. Therefore, it is strongly
   * advised to suspend the request before using this state.
   */
  virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
                                        uint64_t sourceOffset, uint32_t length) override;
  virtual nsresult CloseSocket() override;

  // we have our own implementation of SendData which writes to the nntp log
  // and then calls the base class to transmit the data
  nsresult SendData(const char * dataBuffer, bool aSuppressLogging = false) override;

  nsresult CleanupAfterRunningUrl();
  void Cleanup(); //free char* member variables

  void ParseHeaderForCancel(char *buf);

  virtual const char* GetType() override { return "nntp"; }

  static void CheckIfAuthor(nsIMsgIdentity *aIdentity, const nsCString &aOldFrom, nsCString &aFrom);

  nsCOMPtr <nsINNTPNewsgroupList> m_newsgroupList;
  nsCOMPtr <nsINNTPArticleList> m_articleList;

  nsCOMPtr <nsIMsgNewsFolder> m_newsFolder;
  nsCOMPtr <nsIMsgWindow> m_msgWindow;

  nsCOMPtr<nsIAsyncInputStream> mDisplayInputStream;
  nsCOMPtr<nsIAsyncOutputStream> mDisplayOutputStream;
  nsMsgLineStreamBuffer   * m_lineStreamBuffer; // used to efficiently extract lines from the incoming data stream
  // the nsINntpURL that is currently running
  nsCOMPtr<nsINntpUrl> m_runningURL;
  bool        m_connectionBusy;
  bool        m_fromCache;  // is this connection from the cache?
  PRTime      m_lastActiveTimeStamp;
  nsNewsAction m_newsAction;

  // Generic state information -- What state are we in? What state do we want to go to
  // after the next response? What was the last response code? etc.
  StatesEnum  m_nextState;
  StatesEnum  m_nextStateAfterResponse;
  int32_t     m_typeWanted;     /* Article, List, or Group */
  int32_t     m_responseCode;    /* code returned from NNTP server */
  int32_t   m_previousResponseCode;
  char       *m_responseText;   /* text returned from NNTP server */

  char    *m_dataBuf;
  uint32_t   m_dataBufSize;

  /* for group command */
  nsCString m_currentGroup;     /* current group */

  int32_t   m_firstArticle;
  int32_t   m_lastArticle;
  int32_t   m_firstPossibleArticle;
  int32_t   m_lastPossibleArticle;

  int32_t    m_numArticlesLoaded;  /* How many articles we got XOVER lines for. */
  int32_t    m_numArticlesWanted; /* How many articles we wanted to get XOVER lines for. */
  int32_t   m_maxArticles;        /* max articles to get during an XOVER */

  // Cancelation specific state. In particular, the headers that should be
  // used for the cancelation message.
  // mscott: we can probably replace this stuff with nsString
  char   *m_cancelFromHdr;
  char     *m_cancelNewsgroups;
  char     *m_cancelDistribution;
  char     *m_cancelID;
  int32_t    m_cancelStatus;

  // variable for ReadNewsList
  int32_t   m_readNewsListCount;

  // Per news article state information. (article number, author, subject, id, etc
  nsCString m_messageID;
  int32_t   m_articleNumber;   /* current article number */
  nsCString m_searchData;

  int32_t   m_originalContentLength; /* the content length at the time of calling graph progress */

  nsCOMPtr<nsIStringBundle> m_stringBundle;

  nsCOMPtr<nsINntpIncomingServer> m_nntpServer;

  nsresult GetNewsStringByName(const char *aName, char16_t **aString);
  nsresult GetNewsStringByID(int32_t stringID, char16_t **aString);

  nsresult PostMessageInFile(nsIFile * filePath);

  //////////////////////////////////////////////////////////////////////////////
  // Communication methods --> Reading and writing protocol
  //////////////////////////////////////////////////////////////////////////////

  int32_t ReadLine(nsIInputStream * inputStream, uint32_t length, char ** line);

  //////////////////////////////////////////////////////////////////////////////
  // Protocol Methods --> This protocol is state driven so each protocol method
  //            is designed to re-act to the current "state". I've attempted to
  //            group them together based on functionality.
  //////////////////////////////////////////////////////////////////////////////

  // gets the response code from the nntp server and the response line. Returns the TCP return code
  // from the read.
  nsresult NewsResponse(nsIInputStream *inputStream, uint32_t length);

  // Interpret the server response after the connect.
  // Returns negative if the server responds unexpectedly
  nsresult LoginResponse();
  nsresult SendModeReader();
  nsresult SendModeReaderResponse();

  nsresult SendListExtensions();
  nsresult SendListExtensionsResponse(nsIInputStream *inputStream, uint32_t length);

  nsresult SendListSearches();
  nsresult SendListSearchesResponse(nsIInputStream *inputStream, uint32_t length);

  nsresult SendListSearchHeaders();
  nsresult SendListSearchHeadersResponse(nsIInputStream *inputStream, uint32_t length);

  nsresult GetProperties();
  nsresult GetPropertiesResponse(nsIInputStream *inputStream, uint32_t length);

  nsresult SendListSubscriptions();
  nsresult SendListSubscriptionsResponse(nsIInputStream *inputStream, uint32_t length);

  // Figure out what the first command is and send it.
  // Returns the status from the NETWrite.
  nsresult SendFirstNNTPCommand(nsIURI *url);

  // Interprets the server response from the first command sent.
  // returns negative if the server responds unexpectedly.
  nsresult SendFirstNNTPCommandResponse();

  nsresult SetupForTransfer();

  nsresult SendGroupForArticle();
  nsresult SendGroupForArticleResponse();

  nsresult SendArticleNumber();
  nsresult BeginArticle();
  nsresult ReadArticle(nsIInputStream *inputStream, uint32_t length);
  nsresult DisplayArticle(nsIInputStream *inputStream, uint32_t length);

  //////////////////////////////////////////////////////////////////////////////
  // News authentication code
  //////////////////////////////////////////////////////////////////////////////

  /**
   * Sends the username via AUTHINFO USER, NNTP_BEGIN_AUTHORIZE.
   * This also handles the step of getting authentication credentials; if this
   * requires a prompt to run, the connection will be suspended and we will
   * come back to this point.
   * Followed by: NNTP_AUTHORIZE_RESPONSE if the username was sent
   *              NNTP_SUSPENDED          if we need to wait for the password
   */
  nsresult BeginAuthorization();
  /**
   * Sends the password if necessary, the state NNTP_AUTHORIZE_RESPONSE.
   * This also reads the result of the username.
   * Followed by: NNTP_PASSWORD_RESPONSE  if a password is sent
   *              NNTP_SEND_MODE_READER   if MODE READER needed auth
   *              SEND_FIRST_NNTP_COMMAND if any other command needed auth
   *              NNTP_ERROR              if the username was rejected
   */
  nsresult AuthorizationResponse();
  /**
   * This state, NNTP_PASSWORD_RESPONSE, reads the password.
   * Followed by: NNTP_SEND_MODE_READER   if MODE READER needed auth
   *              SEND_FIRST_NNTP_COMMAND if any other command needed auth
   *              NNTP_ERROR              if the password was rejected
   */
  nsresult PasswordResponse();

  nsresult BeginReadNewsList();
  nsresult ReadNewsList(nsIInputStream *inputStream, uint32_t length);

  // Newsgroup specific protocol handlers
  nsresult DisplayNewsgroups();
  nsresult BeginNewsgroups();
  nsresult ProcessNewsgroups(nsIInputStream *inputStream, uint32_t length);

  // Protocol handlers used for posting data
  nsresult PostData();
  nsresult PostDataResponse();

  nsresult CheckForArticle();

  /////////////////////////////////////////////////////////////////////////////
  // XHDR, XOVER, HEAD filtering process handlers
  // These are ordered by the rough order of usage
  /////////////////////////////////////////////////////////////////////////////
 
  /**
   * The first step in the filtering process, the state NNTP_XOVER_BEGIN.
   * This method sets up m_newsgroupList.
   * Followed by: NNTP_FIGURE_NEXT_CHUNK
   */
  nsresult BeginReadXover();
  /**
   * The loop control for filtering, the state NNTP_FIGURE_NEXT_CHUNK.
   * This method contacts the newsgroupList to figure out which articles to
   * download and then prepares it for XOVER support.
   * Followed by: NEWS_PROCESS_XOVER       if everything is finished
   *              NNTP_READ_GROUP          if XOVER doesn't work
   *              NNTP_XOVER_SEND          if XOVER does work
   */
  nsresult FigureNextChunk();

  // The XOVER process core
  /**
   * The state NNTP_XOVER_SEND, which actually sends the message.
   * Followed by: NNTP_XOVER_RESPONSE
   */
  nsresult XoverSend();
  /**
   * This state, NNTP_XOVER_RESPONSE, actually checks the XOVER capabiliity.
   * Followed by: NNTP_XOVER               if XOVER is supported
   *              NNTP_READ_GROUP          if it isn't
   */
  nsresult ReadXoverResponse();
  /**
   * This state, NNTP_XOVER, processes the results from the XOVER command.
   * It asks nsNNTPNewsgroupList to process the line using ProcessXOVERLINE.
   * Followed by: NNTP_XHDR_SEND
   */
  nsresult ReadXover(nsIInputStream *inputStream, uint32_t length);

  // The XHDR process core
  /**
   * This state, NNTP_XHDR_SEND, sends the XHDR command.
   * The headers are all managed by nsNNTPNewsgroupList, and this picks them up
   * one by one as they are needed.
   * Followed by: NNTP_XHDR_RESPONSE       if there is a header to be sent
   *              NNTP_FIGURE_NEXT_CHUNK   if all headers have been sent
   */
  nsresult XhdrSend();
  /**
   * This state, NNTP_XHDR_RESPONSE, processes the XHDR response.
   * It mostly passes the information off to nsNNTPNewsgroupList, and only does
   * response code checking and a bit of preprocessing. Note that if XHDR
   * doesn't work properly, HEAD fallback is switched on and all subsequent
   * chunks will NOT use XOVER.
   * Followed by: NNTP_READ_GROUP          if XHDR doesn't work properly
   *              NNTP_XHDR_SEND           when finished processing XHR.
   */
  nsresult XhdrResponse(nsIInputStream *inputStream);

  // HEAD processing core
  /**
   * This state, NNTP_READ_GROUP, is the control for the HEAD processor.
   * It sends the HEAD command and increments the article number until it is
   * finished. WARNING: HEAD is REALLY SLOW.
   * Followed by: NNTP_FIGURE_NEXT_CHUNK   when it is finished 
   *              NNTP_READ_GROUP_RESPONSE when it is not
   */
  nsresult ReadHeaders();
  /**
   * This state, NNTP_READ_GROUP_RESPONSE, checks if the article exists.
   * Because it is required by NNTP, if it doesn't work, the only problem would
   * be that the article doesn't exist. Passes off article number data to 
   * nsNNTPNewsgroupList.
   * Followed by: NNTP_READ_GROUP_BODY     if the article exists
   *              NNTP_READ_GROUP          if it doesn't.
   */
  nsresult ReadNewsgroupResponse();
  /**
   * This state, NNTP_READ_GROUP_BODY, reads the body of the HEAD command.
   * Once again, it passes information off to nsNNTPNewsgroupList.
   * Followed by: NNTP_READ_GROUP
   */
  nsresult ReadNewsgroupBody(nsIInputStream *inputStream, uint32_t length);

  /**
   * This state, NNTP_PROCESS_XOVER, is the final step of the filter-processing
   * code. Currently, all it does is cleans up the unread count and calls the
   * filters, both via nsNNTPNewsgroupList.
   * Followed by: NEWS_DONE
   */
  nsresult ProcessXover();



  // Canceling
  nsresult StartCancel();
  nsresult DoCancel();

  // XPAT
  nsresult XPATSend();
  nsresult XPATResponse(nsIInputStream *inputStream, uint32_t length);
  nsresult ListPrettyNames();
  nsresult ListPrettyNamesResponse(nsIInputStream *inputStream, uint32_t length);

  nsresult ListXActive();
  nsresult ListXActiveResponse(nsIInputStream *inputStream, uint32_t length);

  // for "?list-ids"
  nsresult SendListGroup();
  nsresult SendListGroupResponse(nsIInputStream *inputStream, uint32_t length);

  // Searching Protocol....
  nsresult Search();
  nsresult SearchResponse();
  nsresult SearchResults(nsIInputStream *inputStream, uint32_t length);

  //////////////////////////////////////////////////////////////////////////////
  // End of Protocol Methods
  //////////////////////////////////////////////////////////////////////////////

  nsresult ParseURL(nsIURI *aURL, nsCString &aGroup, nsCString &aMessageID);

  void SetProgressBarPercent(uint32_t aProgress, uint32_t aProgressMax);
  nsresult SetProgressStatus(const char16_t *aMessage);
  nsresult InitializeNewsFolderFromUri(const char *uri);
  void TimerCallback();

  void HandleAuthenticationFailure();
  nsCOMPtr <nsIInputStream> mInputStream;
  nsCOMPtr <nsITimer> mUpdateTimer;
  nsresult AlertError(int32_t errorCode, const char *text);
  int32_t mBytesReceived;
  int32_t mBytesReceivedSinceLastStatusUpdate;
  PRTime m_startTime;
  int32_t mNumGroupsListed;
  nsMsgKey m_key;

  nsresult SetCurrentGroup(); /* sets m_currentGroup.  should be called after doing a successful GROUP command */
  nsresult CleanupNewsgroupList(); /* cleans up m_newsgroupList, and set it to null */

  // cache related helper methods
  void FinishMemCacheEntry(bool valid); // either mark it valid, or doom it
  nsresult OpenCacheEntry(); // makes a request to the cache service for a cache entry for a url
  bool ReadFromLocalCache(); // attempts to read the url out of our local (offline) cache....
  nsresult ReadFromNewsConnection(); // creates a new news connection to read the url
  nsresult ReadFromMemCache(nsICacheEntry *entry); // attempts to read the url out of our memory cache
  nsresult SetupPartExtractorListener(nsIStreamListener * aConsumer);
};


#endif  // nsNNTPProtocol_h___