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