3434import string
3535
3636
37- # Exception raised when an error or invalid response is received
38- error_reply = 'nntplib.error_reply' # unexpected [123]xx reply
39- error_temp = 'nntplib.error_temp' # 4xx errors
40- error_perm = 'nntplib.error_perm' # 5xx errors
41- error_proto = 'nntplib.error_proto' # response does not begin with [1-5]
42- error_data = 'nntplib.error_data' # error in response data
37+
38+ # Exceptions raised when an error or invalid response is received
39+ class NNTPError (Exception ):
40+ """Base class for all nntplib exceptions"""
41+ def __init__ (self , * args ):
42+ apply (Exception .__init__ , (self ,)+ args )
43+ try :
44+ self .response = args [0 ]
45+ except IndexError :
46+ self .response = 'No response given'
47+
48+ class NNTPReplyError (NNTPError ):
49+ """Unexpected [123]xx reply"""
50+ pass
51+
52+ class NNTPTemporaryError (NNTPError ):
53+ """4xx errors"""
54+ pass
55+
56+ class NNTPPermanentError (NNTPError ):
57+ """5xx errors"""
58+ pass
59+
60+ class NNTPProtocolError (NNTPError ):
61+ """Response does not begin with [1-5]"""
62+ pass
63+
64+ class NNTPDataError (NNTPError ):
65+ """Error in response data"""
66+ pass
4367
68+ # for backwards compatibility
69+ error_reply = NNTPReplyError
70+ error_temp = NNTPTemporaryError
71+ error_perm = NNTPPermanentError
72+ error_proto = NNTPProtocolError
73+ error_data = NNTPDataError
4474
75+
76+
4577# Standard port used by NNTP servers
4678NNTP_PORT = 119
4779
5486CRLF = '\r \n '
5587
5688
89+
5790# The class itself
58-
5991class NNTP :
60-
61- def __init__ ( self , host , port = NNTP_PORT , user = None , password = None ):
92+ def __init__ ( self , host , port = NNTP_PORT , user = None , password = None ,
93+ readermode = None ):
6294 """Initialize an instance. Arguments:
6395 - host: hostname to connect to
64- - port: port to connect to (default the standard NNTP port)"""
65-
96+ - port: port to connect to (default the standard NNTP port)
97+ - user: username to authenticate with
98+ - password: password to use with username
99+ - readermode: if true, send 'mode reader' command after
100+ connecting.
101+
102+ readermode is sometimes necessary if you are connecting to an
103+ NNTP server on the local machine and intend to call
104+ reader-specific comamnds, such as `group'. If you get
105+ unexpected NNTPPermanentErrors, you might need to set
106+ readermode.
107+ """
66108 self .host = host
67109 self .port = port
68110 self .sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
69111 self .sock .connect (self .host , self .port )
70112 self .file = self .sock .makefile ('rb' )
71113 self .debugging = 0
72114 self .welcome = self .getresp ()
115+ if readermode :
116+ try :
117+ self .welcome = self .shortcmd ('mode reader' )
118+ except NNTPPermanentError :
119+ # error 500, probably 'not implemented'
120+ pass
73121 if user :
74122 resp = self .shortcmd ('authinfo user ' + user )
75123 if resp [:3 ] == '381' :
76124 if not password :
77- raise error_reply , resp
125+ raise NNTPReplyError ( resp )
78126 else :
79127 resp = self .shortcmd (
80128 'authinfo pass ' + password )
81129 if resp [:3 ] != '281' :
82- raise error_perm , resp
130+ raise NNTPPermanentError (resp )
131+
132+ # Get the welcome message from the server
133+ # (this is read and squirreled away by __init__()).
134+ # If the response code is 200, posting is allowed;
135+ # if it 201, posting is not allowed
83136
84137 def getwelcome (self ):
85138 """Get the welcome message from the server
@@ -128,19 +181,19 @@ def getresp(self):
128181 if self .debugging : print '*resp*' , `resp`
129182 c = resp [:1 ]
130183 if c == '4' :
131- raise error_temp , resp
184+ raise NNTPTemporaryError ( resp )
132185 if c == '5' :
133- raise error_perm , resp
186+ raise NNTPPermanentError ( resp )
134187 if c not in '123' :
135- raise error_proto , resp
188+ raise NNTPProtocolError ( resp )
136189 return resp
137190
138191 def getlongresp (self ):
139192 """Internal: get a response plus following text from the server.
140193 Raise various errors if the response indicates an error."""
141194 resp = self .getresp ()
142195 if resp [:3 ] not in LONGRESP :
143- raise error_reply , resp
196+ raise NNTPReplyError ( resp )
144197 list = []
145198 while 1 :
146199 line = self .getline ()
@@ -206,7 +259,7 @@ def group(self, name):
206259
207260 resp = self .shortcmd ('GROUP ' + name )
208261 if resp [:3 ] <> '211' :
209- raise error_reply , resp
262+ raise NNTPReplyError ( resp )
210263 words = string .split (resp )
211264 count = first = last = 0
212265 n = len (words )
@@ -230,7 +283,7 @@ def help(self):
230283 def statparse (self , resp ):
231284 """Internal: parse the response of a STAT, NEXT or LAST command."""
232285 if resp [:2 ] <> '22' :
233- raise error_reply , resp
286+ raise NNTPReplyError ( resp )
234287 words = string .split (resp )
235288 nr = 0
236289 id = ''
@@ -349,7 +402,7 @@ def xover(self,start,end):
349402 elem [6 ],
350403 elem [7 ]))
351404 except IndexError :
352- raise error_data , line
405+ raise NNTPDataError ( line )
353406 return resp ,xover_lines
354407
355408 def xgtitle (self , group ):
@@ -377,11 +430,11 @@ def xpath(self,id):
377430
378431 resp = self .shortcmd ("XPATH " + id )
379432 if resp [:3 ] <> '223' :
380- raise error_reply , resp
433+ raise NNTPReplyError ( resp )
381434 try :
382435 [resp_num , path ] = string .split (resp )
383436 except ValueError :
384- raise error_reply , resp
437+ raise NNTPReplyError ( resp )
385438 else :
386439 return resp , path
387440
@@ -395,14 +448,14 @@ def date (self):
395448
396449 resp = self .shortcmd ("DATE" )
397450 if resp [:3 ] <> '111' :
398- raise error_reply , resp
451+ raise NNTPReplyError ( resp )
399452 elem = string .split (resp )
400453 if len (elem ) != 2 :
401- raise error_data , resp
454+ raise NNTPDataError ( resp )
402455 date = elem [1 ][2 :8 ]
403456 time = elem [1 ][- 6 :]
404457 if len (date ) != 6 or len (time ) != 6 :
405- raise error_data , resp
458+ raise NNTPDataError ( resp )
406459 return resp , date , time
407460
408461
@@ -415,7 +468,7 @@ def post(self, f):
415468 resp = self .shortcmd ('POST' )
416469 # Raises error_??? if posting is not allowed
417470 if resp [0 ] <> '3' :
418- raise error_reply , resp
471+ raise NNTPReplyError ( resp )
419472 while 1 :
420473 line = f .readline ()
421474 if not line :
@@ -439,7 +492,7 @@ def ihave(self, id, f):
439492 resp = self .shortcmd ('IHAVE ' + id )
440493 # Raises error_??? if the server already has it
441494 if resp [0 ] <> '3' :
442- raise error_reply , resp
495+ raise NNTPReplyError ( resp )
443496 while 1 :
444497 line = f .readline ()
445498 if not line :
@@ -465,7 +518,7 @@ def quit(self):
465518
466519def _test ():
467520 """Minimal test function."""
468- s = NNTP ('news' )
521+ s = NNTP ('news' , readermode = 'reader' )
469522 resp , count , first , last , name = s .group ('comp.lang.python' )
470523 print resp
471524 print 'Group' , name , 'has' , count , 'articles, range' , first , 'to' , last
0 commit comments