--- nntplib.py.orig 2009-02-05 17:33:03.000000000 -0600 +++ nntplib.py 2009-02-10 15:55:35.667955064 -0600 @@ -1,4 +1,4 @@ -"""An NNTP client class based on RFC 977: Network News Transfer Protocol. +"""An NNTP client class based on RFC 3977: Network News Transfer Protocol. Example: @@ -26,7 +26,7 @@ # RFC 977 by Brian Kantor and Phil Lapsley. # xover, xgtitle, xpath, date methods by Kevan Heydon - +# RFC 3977 by Travis H. for Giganews, Inc. # Imports import re @@ -81,7 +81,7 @@ # Response numbers that are followed by additional text (e.g. article) -LONGRESP = ['100', '215', '220', '221', '222', '224', '230', '231', '282'] +LONGRESP = ['100', '101', '215', '220', '221', '222', '224', '230', '231', '282'] # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) @@ -92,7 +92,7 @@ # The class itself class NNTP: def __init__(self, host, port=NNTP_PORT, user=None, password=None, - readermode=None, usenetrc=True): + readermode=None, usenetrc=True, bind=None): """Initialize an instance. Arguments: - host: hostname to connect to - port: port to connect to (default the standard NNTP port) @@ -100,6 +100,8 @@ - password: password to use with username - readermode: if true, send 'mode reader' command after connecting. + - bind: If set, bind to a particular IP before connecting. + May be tuple (IP, port) or simply an IP. readermode is sometimes necessary if you are connecting to an NNTP server on the local machine and intend to call @@ -110,6 +112,11 @@ self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if bind: + if type(bind) == type("string"): + self.sock.bind((bind, 0)) + else: + self.sock.bind(bind) self.sock.connect((self.host, self.port)) self.file = self.sock.makefile('rb') self.debugging = 0 @@ -154,7 +161,10 @@ else: resp = self.shortcmd( 'authinfo pass '+password) - if resp[:3] != '281': + # Code 281 is specified by RFC 4643 but some clients don't + # like that response so some servers give 200. Just check + # the first digit. + if resp[:1] != '2': raise NNTPPermanentError(resp) if readermode_afterauth: try: @@ -164,14 +174,13 @@ pass - # Get the welcome message from the server - # (this is read and squirreled away by __init__()). - # If the response code is 200, posting is allowed; - # if it 201, posting is not allowed - def getwelcome(self): """Get the welcome message from the server (this is read and squirreled away by __init__()). + Whether posting is allowed depends on whether it + is included in results of CAPABILITIES; see + RFC 3977 section 5.1.2. + Deprecated: If the response code is 200, posting is allowed; if it 201, posting is not allowed.""" @@ -286,6 +295,7 @@ cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time return self.longcmd(cmd, file) + # TODO: LIST variants def list(self, file=None): """Process a LIST command. Return: - resp: server response if successful @@ -593,6 +603,28 @@ self.putline('.') return self.getresp() + def capabilities(self, file=None): + """Process a CAPABILITIES command. + Return: + - resp: server response if successful + - list: list of capabilities""" + + return self.longcmd('CAPABILITIES', file) + + # TODO: AUTHINFO (for testing NNTP servers) + + # TODO: HDR + + # This can be run in __init__, but there may be cases where + # you want to issue this at a later time, perhaps for testing + # an NNTP server implementation. + def mode_reader(self): + """Process a MODE READER command. Returns: + - resp: server response if successful""" + return self.shortcmd('mode reader') + + # TODO: OVER + def quit(self): """Process a QUIT command and close the socket. Returns: - resp: server response if successful"""