1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * Pan - A Newsreader for Gtk+
4  * Copyright (C) 2002-2006  Charles Kerr <charles@rebelbase.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifndef __NNTP_h__
21 #define __NNTP_h__
22 
23 #include <stdint.h>
24 #include <deque>
25 #include <pan/general/macros.h> // for UNUSED
26 #include <pan/general/quark.h>
27 #include <pan/general/string-view.h>
28 #include <pan/tasks/health.h>
29 #include <pan/tasks/socket.h>
30 #include <pan/data/data.h>
31 
32 namespace
33 {
34    enum
35    {
36       AUTH_REQUIRED              = 480,
37       AUTH_NEED_MORE             = 381,
38       AUTH_ACCEPTED              = 281,
39       AUTH_REJECTED              = 482,
40 
41       SERVER_READY               = 200,
42       SERVER_READY_NO_POSTING    = 201,
43       SERVER_READY_STREAMING_OK  = 203,
44 
45       GOODBYE                    = 205,
46 
47       GROUP_RESPONSE             = 211,
48       GROUP_NONEXISTENT          = 411,
49 
50       INFORMATION_FOLLOWS        = 215,
51 
52       XOVER_FOLLOWS              = 224,
53       XOVER_NO_ARTICLES          = 420,
54 
55       ARTICLE_FOLLOWS            = 220,
56 
57       NEWGROUPS_FOLLOWS          = 231,
58 
59       ARTICLE_POSTED_OK          = 240,
60 
61       FEATURE_ENABLED            = 290,
62 
63       SEND_ARTICLE_NOW           = 340,
64       NO_POSTING                 = 440,
65       POSTING_FAILED             = 441,
66       DUPE_ARTICLE               = 435,  // sent additionally to 441
67 
68       TOO_MANY_CONNECTIONS       = 400,
69 
70       NO_GROUP_SELECTED          = 412,
71       NO_SUCH_ARTICLE_NUMBER     = 423,
72       NO_SUCH_ARTICLE            = 430,
73 
74       ERROR_CMD_NOT_UNDERSTOOD   = 500,
75       ERROR_CMD_NOT_SUPPORTED    = 501,
76       NO_PERMISSION              = 502,
77       FEATURE_NOT_SUPPORTED      = 503
78    };
79 
80    const char* EOL = ".";
81 }
82 
83 namespace pan
84 {
85   /**
86    * Asynchronously processes NNTP protocol commands.
87    *
88    * @ingroup tasks
89    */
90   class NNTP: private Socket::Listener
91   {
92     public:
93 
94       /**
95        * Base class for objects that listen for NNTP events.
96        *
97        * NNTP responses are lists of lines (like on XOVER and
98        * ARTICLE requests) and one-line status messages.
99        * these correspond to on_nntp_line() and on_nntp_done().
100        * A special case is made for the GROUP command's response
101        * so the client can get back the group's information fields.
102        *
103        * @ingroup tasks
104        * @see NNTP
105        */
106       struct Listener
107       {
ListenerListener108         Listener () {}
109 
~ListenerListener110         virtual ~Listener () {}
111 
112         /**
113          * Invoked for each line of an NNTP server's list responses,
114          * such as a list of headers for an XOVER command or a list of
115          * lines for an ARTICLE command.
116          */
on_nntp_lineListener117         virtual void on_nntp_line  (NNTP               * nntp UNUSED,
118                                     const StringView   & line UNUSED)  {}
119 
120         /**
121          * Called at the end of an NNTP command.  If the command was
122          * one that produced a list, on_nntp_line() may have been
123          * called before this.
124          *
125          * When this is called, the listener can safely clean up
126          * anything associated with processing the command.
127          *
128          * @param health returns OK, ERR_NETWORK, or ERR_SERVER.
129          *               ERR_LOCAL is never used here.
130          */
on_nntp_doneListener131         virtual void on_nntp_done  (NNTP               * nntp     UNUSED,
132                                     Health               health   UNUSED,
133                                     const StringView   & response UNUSED) {}
134 
on_xover_doneListener135         virtual void on_xover_done  (NNTP               * nntp     UNUSED,
136                                     Health               health   UNUSED,
137                                     const StringView   & response UNUSED) {}
138 
139         /**
140          * Called whenever an NNTP object sets the current group.
141          */
on_nntp_groupListener142         virtual void on_nntp_group (NNTP               * nntp          UNUSED,
143                                     const Quark        & group         UNUSED,
144                                     unsigned long        estimated_qty UNUSED,
145                                     uint64_t             low           UNUSED,
146                                     uint64_t             high
147                                            UNUSED) {}
148 
149        };
150 
151       public:
152 
NNTP(const Quark & server,const std::string & username,const std::string & password,ServerInfo & info,Socket * socket)153         NNTP (const Quark        & server,
154               const std::string  & username,
155               const std::string  & password,
156               ServerInfo         & info,
157               Socket             * socket):
158           _server(server),
159           _server_info(info),
160           _socket(socket),
161           _socket_error(false),
162           _listener(0),
163           _username(username),
164           _password(password),
165           _nntp_response_text(false),
166           _compression(false)
167        {}
168 
~NNTP()169        virtual ~NNTP ()
170        {}
171 
172     public:
173 
174       /* Internal only */
175       void enter_group (const Quark& group);
176       void get_headers (const Quark & group, const char * message_id, Listener  * l);
177       void get_headers (const Quark & group, uint64_t article_number, Listener * l);
178       void get_body (const Quark & group, const char * message_id, Listener  * l);
179       void get_body (const Quark & group, uint64_t article_number, Listener * l);
180       /**
181        * Lists all available commands.
182        */
183       void help (Listener * l);
184 
185       /**
186        * Executes a handshake command.
187        *
188        * This is actually an empty string, but the
189        * news server will respond to it.
190        */
191       void handshake        (Listener           * l);
192 
193       /**
194        * Executes an XOVER command: "XOVER low-high"
195        *
196        * If successful, this will invoke Listener::on_nntp_line()
197        * for each article header line we get back.
198        *
199        * Listener::on_nntp_done() will be called whether the
200        * command is successful or not.
201        */
202       void xover            (const Quark        & group,
203                              uint64_t             low,
204                              uint64_t             high,
205                              Listener           * l);
206 
207       void xzver            (const Quark        & group,
208                              uint64_t             low,
209                              uint64_t             high,
210                              Listener           * l);
211 
212       /**
213        * Executes an XOVER command: "XOVER" to count
214        * the xover numbers internally
215        *
216        * If successful, this will invoke Listener::on_nntp_line()
217        * for each article header line we get back.
218        *
219        * Listener::on_nntp_done() will be called whether the
220        * command is successful or not.
221        */
222       void xover_count_only (const Quark        & group,
223                              Listener           * l);
224 
225       /**
226        * Executes a LIST command: "LIST"
227        *
228        * If successful, this will invoke Listener::on_nntp_line()
229        * for each newsgroup we get back.
230        *
231        * Listener::on_nntp_done() will be called whether the
232        * command is successful or not.
233        */
234       void list             (Listener           * l);
235 
236       /**
237        * Executes a LIST command: "LIST GROUPS"
238        *
239        * If successful, this will invoke Listener::on_nntp_line()
240        * for each newsgroup we get back.
241        *
242        * Listener::on_nntp_done() will be called whether the
243        * command is successful or not.
244        */
245       void list_newsgroups  (Listener           * l);
246 
247       /**
248        * Executes an ARTICLE command: "ARTICLE article_number"
249        *
250        * If the NNTP's state isn't currently in the right group,
251        * a group() command will be called first.
252        *
253        * If successful, this will invoke Listener::on_nntp_line()
254        * for each line of the article we get back.
255        *
256        * Listener::on_nntp_done() will be called whether the
257        * command is successful or not.  It will only be called
258        * once, at the end of the article() command, even if we had
259        * to change groups.
260        */
261       void article          (const Quark        & group,
262                              uint64_t             article_number,
263                              Listener           * l);
264 
265       /**
266        * Executes an ARTICLE command: "ARTICLE message-id"
267        *
268        * If the NNTP's state isn't currently in the right group,
269        * a group() command will be called first.
270        *
271        * If successful, this will invoke Listener::on_nntp_line()
272        * for each line of the article we get back.
273        *
274        * Listener::on_nntp_done() will be called whether the
275        * command is successful or not.  It will only be called
276        * once, at the end of the article() command, even if we had
277        * to change groups.
278        */
279       void article          (const Quark        & group,
280                              const char         * message_id,
281                              Listener           * l);
282 
283       /**
284        * Executes an GROUP command: "GROUP groupname"
285        *
286        * If successful, this will invoke Listener::on_nntp_group().
287        * for each line of the article we get back.
288        *
289        * Listener::on_nntp_done() will be called whether the
290        * command is successful or not.
291        */
292       void group            (const Quark        & group,
293                              Listener           * l);
294 
295       /**
296        * Executes a QUIT command: "QUIT"
297        *
298        * Listener::on_nntp_done() will be called whether the
299        * command is successful or not.
300        */
301       void goodbye          (Listener           * l);
302 
303       /**
304        * Executes a short, non-state-changing command ("MODE READER")
305        * to keep the session from timing out.
306        *
307        * Listener::on_nntp_done() will be called whether the
308        * command is successful or not.
309        */
310       void noop             (Listener           * l);
311 
312       void post             (const StringView   & message,
313                              Listener           * l);
314 
315       void cancel           (const Quark        & message_id,
316                              Listener           * l);
317 
318     public:
319 
320       const Quark _server;
321       ServerInfo& _server_info;
322       Quark _group;
323       Socket * _socket;
324       bool _socket_error;
get_username()325       const std::string& get_username()
326       {
327         return _username;
328       }
329 
330     protected:
331 
332       Listener * _listener;
333       /** Kept in case the server gives prompts us for it. */
334       const std::string _username;
335       /** Kept in case the server gives prompts us for it. */
336       const std::string _password;
337       /** Used to remember the article send in via post(). */
338       std::string _post;
339       /** True if the server told us that we're getting a list back. */
340       bool _nntp_response_text;
341 
342       typedef std::deque<std::string> strings_t;
343       strings_t _commands;
344       std::string _previous_command;
345       void write_next_command ();
346 
347       void fire_done_func (Health, const StringView& response);
348 
349     public:
350       /** session flag for compression (gzip-style) (giganews etc.)*/
351       bool _compression;
352 
353     private: // private socket listener funcs, for socket handshake
354 
355       virtual bool on_socket_response (Socket*, const StringView& line);
356       virtual void on_socket_error (Socket*);
357       virtual void on_socket_abort (Socket*);
358 
359     public:
360 
361       /**
362        * Interface class specifying how to dispose of an NNTP.
363        */
364       struct Source {
SourceSource365         Source () {}
~SourceSource366         virtual ~Source () {}
367         virtual void check_in (NNTP*, Health health) = 0;
368       };
369   };
370 }
371 
372 #endif /* __NNTP_H__ */
373