1 |
/* |
/* |
2 |
* $Id$ |
* NNTPConnection.java |
3 |
* Copyright (C) 2002 The Free Software Foundation |
* Copyright (C) 2002 The Free Software Foundation |
4 |
* |
* |
5 |
* This file is part of GNU inetlib, a library. |
* This file is part of GNU inetlib, a library. |
76 |
* server. |
* server. |
77 |
* |
* |
78 |
* @author <a hef='mailto:dog@gnu.org'>Chris Burdess</a> |
* @author <a hef='mailto:dog@gnu.org'>Chris Burdess</a> |
|
* @version $Revision$ $Date$ |
|
79 |
*/ |
*/ |
80 |
public class NNTPConnection |
public class NNTPConnection |
81 |
implements NNTPConstants |
implements NNTPConstants |
138 |
* Creates a new connection object. |
* Creates a new connection object. |
139 |
* @param hostname the hostname or IP address of the news server |
* @param hostname the hostname or IP address of the news server |
140 |
*/ |
*/ |
141 |
public NNTPConnection (String hostname) |
public NNTPConnection(String hostname) |
142 |
throws IOException |
throws IOException |
143 |
{ |
{ |
144 |
this (hostname, DEFAULT_PORT, 0, 0, false); |
this(hostname, DEFAULT_PORT, 0, 0, false); |
145 |
} |
} |
146 |
|
|
147 |
/** |
/** |
149 |
* @param hostname the hostname or IP address of the news server |
* @param hostname the hostname or IP address of the news server |
150 |
* @param port the port to connect to |
* @param port the port to connect to |
151 |
*/ |
*/ |
152 |
public NNTPConnection (String hostname, int port) |
public NNTPConnection(String hostname, int port) |
153 |
throws IOException |
throws IOException |
154 |
{ |
{ |
155 |
this (hostname, port, 0, 0, false); |
this(hostname, port, 0, 0, false); |
156 |
} |
} |
157 |
|
|
158 |
/** |
/** |
163 |
* @param timeout the read timeout on the socket |
* @param timeout the read timeout on the socket |
164 |
* @param debug whether to use debugging |
* @param debug whether to use debugging |
165 |
*/ |
*/ |
166 |
public NNTPConnection (String hostname, int port, |
public NNTPConnection(String hostname, int port, |
167 |
int connectionTimeout, int timeout, |
int connectionTimeout, int timeout, |
168 |
boolean debug) |
boolean debug) |
169 |
throws IOException |
throws IOException |
170 |
{ |
{ |
171 |
if (port < 0) |
if (port < 0) |
178 |
this.debug = debug; |
this.debug = debug; |
179 |
|
|
180 |
// Set up the socket and streams |
// Set up the socket and streams |
181 |
socket = new Socket (); |
socket = new Socket(); |
182 |
InetSocketAddress address = new InetSocketAddress (hostname, port); |
InetSocketAddress address = new InetSocketAddress(hostname, port); |
183 |
if (connectionTimeout > 0) |
if (connectionTimeout > 0) |
184 |
{ |
{ |
185 |
socket.connect (address, connectionTimeout); |
socket.connect(address, connectionTimeout); |
186 |
} |
} |
187 |
else |
else |
188 |
{ |
{ |
189 |
socket.connect (address); |
socket.connect(address); |
190 |
} |
} |
191 |
if (timeout > 0) |
if (timeout > 0) |
192 |
{ |
{ |
193 |
socket.setSoTimeout (timeout); |
socket.setSoTimeout(timeout); |
194 |
} |
} |
195 |
InputStream in = socket.getInputStream (); |
InputStream in = socket.getInputStream(); |
196 |
in = new CRLFInputStream (in); |
in = new CRLFInputStream(in); |
197 |
this.in = new LineInputStream (in); |
this.in = new LineInputStream(in); |
198 |
OutputStream out = socket.getOutputStream (); |
OutputStream out = socket.getOutputStream(); |
199 |
out = new BufferedOutputStream (out); |
out = new BufferedOutputStream(out); |
200 |
this.out = new CRLFOutputStream (out); |
this.out = new CRLFOutputStream(out); |
201 |
|
|
202 |
// Read the welcome message (RFC977:2.4.3) |
// Read the welcome message(RFC977:2.4.3) |
203 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
204 |
switch (response.status) |
switch (response.status) |
205 |
{ |
{ |
206 |
case POSTING_ALLOWED: |
case POSTING_ALLOWED: |
207 |
canPost = true; |
canPost = true; |
208 |
case NO_POSTING_ALLOWED: |
case NO_POSTING_ALLOWED: |
209 |
welcome = response.getMessage (); |
welcome = response.getMessage(); |
210 |
break; |
break; |
211 |
default: |
default: |
212 |
throw new NNTPException (response); |
throw new NNTPException(response); |
213 |
} |
} |
214 |
} |
} |
215 |
|
|
219 |
* This message sometimes contains disclaimers or help information that |
* This message sometimes contains disclaimers or help information that |
220 |
* may be relevant to the user. |
* may be relevant to the user. |
221 |
*/ |
*/ |
222 |
public String getWelcome () |
public String getWelcome() |
223 |
{ |
{ |
224 |
return welcome; |
return welcome; |
225 |
} |
} |
229 |
* This is only required when clients use the NEWGROUPS or NEWNEWS |
* This is only required when clients use the NEWGROUPS or NEWNEWS |
230 |
* methods, therefore rarely: we don't cache any of the variables here. |
* methods, therefore rarely: we don't cache any of the variables here. |
231 |
*/ |
*/ |
232 |
String formatDate (Date date) |
String formatDate(Date date) |
233 |
{ |
{ |
234 |
DateFormat df = new SimpleDateFormat ("yyMMdd HHmmss 'GMT'"); |
DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'"); |
235 |
Calendar cal = new GregorianCalendar (); |
Calendar cal = new GregorianCalendar(); |
236 |
TimeZone gmt = TimeZone.getTimeZone ("GMT"); |
TimeZone gmt = TimeZone.getTimeZone("GMT"); |
237 |
cal.setTimeZone (gmt); |
cal.setTimeZone(gmt); |
238 |
df.setCalendar (cal); |
df.setCalendar(cal); |
239 |
cal.setTime (date); |
cal.setTime(date); |
240 |
return df.format (date); |
return df.format(date); |
241 |
} |
} |
242 |
|
|
243 |
/* |
/* |
244 |
* Parse the specfied NNTP date text. |
* Parse the specfied NNTP date text. |
245 |
*/ |
*/ |
246 |
Date parseDate (String text) |
Date parseDate(String text) |
247 |
throws ParseException |
throws ParseException |
248 |
{ |
{ |
249 |
DateFormat df = new SimpleDateFormat ("yyMMdd HHmmss 'GMT'"); |
DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'"); |
250 |
return df.parse (text); |
return df.parse(text); |
251 |
} |
} |
252 |
|
|
253 |
// RFC977:3.1 The ARTICLE, BODY, HEAD, and STAT commands |
// RFC977:3.1 The ARTICLE, BODY, HEAD, and STAT commands |
259 |
* message-id, and an iterator over the lines of the article header and |
* message-id, and an iterator over the lines of the article header and |
260 |
* body, separated by an empty line |
* body, separated by an empty line |
261 |
*/ |
*/ |
262 |
public ArticleResponse article (int articleNumber) |
public ArticleResponse article(int articleNumber) |
263 |
throws IOException |
throws IOException |
264 |
{ |
{ |
265 |
return articleImpl (ARTICLE, Integer.toString(articleNumber)); |
return articleImpl(ARTICLE, Integer.toString(articleNumber)); |
266 |
} |
} |
267 |
|
|
268 |
/** |
/** |
272 |
* message-id, and an iterator over the lines of the article header and |
* message-id, and an iterator over the lines of the article header and |
273 |
* body, separated by an empty line |
* body, separated by an empty line |
274 |
*/ |
*/ |
275 |
public ArticleResponse article (String messageId) |
public ArticleResponse article(String messageId) |
276 |
throws IOException |
throws IOException |
277 |
{ |
{ |
278 |
return articleImpl (ARTICLE, messageId); |
return articleImpl(ARTICLE, messageId); |
279 |
} |
} |
280 |
|
|
281 |
/** |
/** |
284 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
285 |
* message-id, and an iterator over the lines of the article header |
* message-id, and an iterator over the lines of the article header |
286 |
*/ |
*/ |
287 |
public ArticleResponse head (int articleNumber) |
public ArticleResponse head(int articleNumber) |
288 |
throws IOException |
throws IOException |
289 |
{ |
{ |
290 |
return articleImpl (HEAD, Integer.toString (articleNumber)); |
return articleImpl(HEAD, Integer.toString(articleNumber)); |
291 |
} |
} |
292 |
|
|
293 |
/** |
/** |
296 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
297 |
* message-id, and an iterator over the lines of the article header |
* message-id, and an iterator over the lines of the article header |
298 |
*/ |
*/ |
299 |
public ArticleResponse head (String messageId) |
public ArticleResponse head(String messageId) |
300 |
throws IOException |
throws IOException |
301 |
{ |
{ |
302 |
return articleImpl (HEAD, messageId); |
return articleImpl(HEAD, messageId); |
303 |
} |
} |
304 |
|
|
305 |
/** |
/** |
308 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
309 |
* message-id, and an iterator over the lines of the article body |
* message-id, and an iterator over the lines of the article body |
310 |
*/ |
*/ |
311 |
public ArticleResponse body (int articleNumber) |
public ArticleResponse body(int articleNumber) |
312 |
throws IOException |
throws IOException |
313 |
{ |
{ |
314 |
return articleImpl (BODY, Integer.toString (articleNumber)); |
return articleImpl(BODY, Integer.toString(articleNumber)); |
315 |
} |
} |
316 |
|
|
317 |
/** |
/** |
320 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
321 |
* message-id, and an iterator over the lines of the article body |
* message-id, and an iterator over the lines of the article body |
322 |
*/ |
*/ |
323 |
public ArticleResponse body (String messageId) |
public ArticleResponse body(String messageId) |
324 |
throws IOException |
throws IOException |
325 |
{ |
{ |
326 |
return articleImpl (BODY, messageId); |
return articleImpl(BODY, messageId); |
327 |
} |
} |
328 |
|
|
329 |
/** |
/** |
332 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
333 |
* message-id |
* message-id |
334 |
*/ |
*/ |
335 |
public ArticleResponse stat (int articleNumber) |
public ArticleResponse stat(int articleNumber) |
336 |
throws IOException |
throws IOException |
337 |
{ |
{ |
338 |
return articleImpl (STAT, Integer.toString (articleNumber)); |
return articleImpl(STAT, Integer.toString(articleNumber)); |
339 |
} |
} |
340 |
|
|
341 |
/** |
/** |
344 |
* @return an article response consisting of the article number and |
* @return an article response consisting of the article number and |
345 |
* message-id |
* message-id |
346 |
*/ |
*/ |
347 |
public ArticleResponse stat (String messageId) |
public ArticleResponse stat(String messageId) |
348 |
throws IOException |
throws IOException |
349 |
{ |
{ |
350 |
return articleImpl (STAT, messageId); |
return articleImpl(STAT, messageId); |
351 |
} |
} |
352 |
|
|
353 |
/** |
/** |
355 |
* @param command one of the above commands |
* @param command one of the above commands |
356 |
* @param messageId the article-number or message-id in string form |
* @param messageId the article-number or message-id in string form |
357 |
*/ |
*/ |
358 |
protected ArticleResponse articleImpl (String command, String messageId) |
protected ArticleResponse articleImpl(String command, String messageId) |
359 |
throws IOException |
throws IOException |
360 |
{ |
{ |
361 |
if (messageId != null) |
if (messageId != null) |
362 |
{ |
{ |
363 |
StringBuffer line = new StringBuffer (command); |
StringBuffer line = new StringBuffer(command); |
364 |
line.append (' '); |
line.append(' '); |
365 |
line.append (messageId); |
line.append(messageId); |
366 |
send (line.toString ()); |
send(line.toString()); |
367 |
} |
} |
368 |
else |
else |
369 |
{ |
{ |
370 |
send (command); |
send(command); |
371 |
} |
} |
372 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
373 |
switch (response.status) |
switch (response.status) |
374 |
{ |
{ |
375 |
case ARTICLE_FOLLOWS: |
case ARTICLE_FOLLOWS: |
377 |
case BODY_FOLLOWS: |
case BODY_FOLLOWS: |
378 |
ArticleResponse aresponse = (ArticleResponse) response; |
ArticleResponse aresponse = (ArticleResponse) response; |
379 |
ArticleStream astream = |
ArticleStream astream = |
380 |
new ArticleStream (new MessageInputStream (in)); |
new ArticleStream(new MessageInputStream(in)); |
381 |
pendingData = astream; |
pendingData = astream; |
382 |
aresponse.in = astream; |
aresponse.in = astream; |
383 |
return aresponse; |
return aresponse; |
390 |
// NO_SUCH_ARTICLE |
// NO_SUCH_ARTICLE |
391 |
// NO_PREVIOUS_ARTICLE |
// NO_PREVIOUS_ARTICLE |
392 |
// NO_NEXT_ARTICLE |
// NO_NEXT_ARTICLE |
393 |
throw new NNTPException (response); |
throw new NNTPException(response); |
394 |
} |
} |
395 |
} |
} |
396 |
|
|
401 |
* Returns a group status response. |
* Returns a group status response. |
402 |
* @param name the name of the group to select |
* @param name the name of the group to select |
403 |
*/ |
*/ |
404 |
public GroupResponse group (String name) |
public GroupResponse group(String name) |
405 |
throws IOException |
throws IOException |
406 |
{ |
{ |
407 |
send (GROUP + ' ' + name); |
send(GROUP + ' ' + name); |
408 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
409 |
switch (response.status) |
switch (response.status) |
410 |
{ |
{ |
411 |
case GROUP_SELECTED: |
case GROUP_SELECTED: |
412 |
return (GroupResponse) response; |
return (GroupResponse) response; |
413 |
default: |
default: |
414 |
// NO_SUCH_GROUP |
// NO_SUCH_GROUP |
415 |
throw new NNTPException (response); |
throw new NNTPException(response); |
416 |
} |
} |
417 |
} |
} |
418 |
|
|
422 |
* Requests a help listing. |
* Requests a help listing. |
423 |
* @return an iterator over a collection of help lines. |
* @return an iterator over a collection of help lines. |
424 |
*/ |
*/ |
425 |
public LineIterator help () |
public LineIterator help() |
426 |
throws IOException |
throws IOException |
427 |
{ |
{ |
428 |
send (HELP); |
send(HELP); |
429 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
430 |
switch (response.status) |
switch (response.status) |
431 |
{ |
{ |
432 |
case HELP_TEXT: |
case HELP_TEXT: |
433 |
LineIterator li = new LineIterator (this); |
LineIterator li = new LineIterator(this); |
434 |
pendingData = li; |
pendingData = li; |
435 |
return li; |
return li; |
436 |
default: |
default: |
437 |
throw new NNTPException (response); |
throw new NNTPException(response); |
438 |
} |
} |
439 |
} |
} |
440 |
|
|
447 |
* @return a PostStream if the server wants the specified article, null |
* @return a PostStream if the server wants the specified article, null |
448 |
* otherwise |
* otherwise |
449 |
*/ |
*/ |
450 |
public PostStream ihave (String messageId) |
public PostStream ihave(String messageId) |
451 |
throws IOException |
throws IOException |
452 |
{ |
{ |
453 |
send (IHAVE + ' ' + messageId); |
send(IHAVE + ' ' + messageId); |
454 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
455 |
switch (response.status) |
switch (response.status) |
456 |
{ |
{ |
457 |
case SEND_TRANSFER_ARTICLE: |
case SEND_TRANSFER_ARTICLE: |
458 |
return new PostStream (this, false); |
return new PostStream(this, false); |
459 |
case ARTICLE_NOT_WANTED: |
case ARTICLE_NOT_WANTED: |
460 |
return null; |
return null; |
461 |
default: |
default: |
462 |
throw new NNTPException (response); |
throw new NNTPException(response); |
463 |
} |
} |
464 |
} |
} |
465 |
|
|
470 |
* @return the article number/message-id pair associated with the new |
* @return the article number/message-id pair associated with the new |
471 |
* article |
* article |
472 |
*/ |
*/ |
473 |
public ArticleResponse last () |
public ArticleResponse last() |
474 |
throws IOException |
throws IOException |
475 |
{ |
{ |
476 |
return articleImpl (LAST, null); |
return articleImpl(LAST, null); |
477 |
} |
} |
478 |
|
|
479 |
// RFC977:3.6 The LIST command |
// RFC977:3.6 The LIST command |
483 |
* Returns a GroupIterator. This must be read fully before other commands |
* Returns a GroupIterator. This must be read fully before other commands |
484 |
* are issued. |
* are issued. |
485 |
*/ |
*/ |
486 |
public GroupIterator list () |
public GroupIterator list() |
487 |
throws IOException |
throws IOException |
488 |
{ |
{ |
489 |
return listImpl (LIST); |
return listImpl(LIST); |
490 |
} |
} |
491 |
|
|
492 |
GroupIterator listImpl (String command) |
GroupIterator listImpl(String command) |
493 |
throws IOException |
throws IOException |
494 |
{ |
{ |
495 |
send (command); |
send(command); |
496 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
497 |
switch (response.status) |
switch (response.status) |
498 |
{ |
{ |
499 |
case LIST_FOLLOWS: |
case LIST_FOLLOWS: |
500 |
GroupIterator gi = new GroupIterator (this); |
GroupIterator gi = new GroupIterator(this); |
501 |
pendingData = gi; |
pendingData = gi; |
502 |
return gi; |
return gi; |
503 |
default: |
default: |
504 |
throw new NNTPException (response); |
throw new NNTPException(response); |
505 |
} |
} |
506 |
} |
} |
507 |
|
|
515 |
* @param since the date from which to list new groups |
* @param since the date from which to list new groups |
516 |
* @param distributions if non-null, an array of distributions to match |
* @param distributions if non-null, an array of distributions to match |
517 |
*/ |
*/ |
518 |
public LineIterator newGroups (Date since, String[]distributions) |
public LineIterator newGroups(Date since, String[]distributions) |
519 |
throws IOException |
throws IOException |
520 |
{ |
{ |
521 |
StringBuffer buffer = new StringBuffer (NEWGROUPS); |
StringBuffer buffer = new StringBuffer(NEWGROUPS); |
522 |
buffer.append (' '); |
buffer.append(' '); |
523 |
buffer.append (formatDate (since)); |
buffer.append(formatDate(since)); |
524 |
if (distributions != null) |
if (distributions != null) |
525 |
{ |
{ |
526 |
buffer.append (' '); |
buffer.append(' '); |
527 |
for (int i = 0; i < distributions.length; i++) |
for (int i = 0; i < distributions.length; i++) |
528 |
{ |
{ |
529 |
if (i > 0) |
if (i > 0) |
530 |
{ |
{ |
531 |
buffer.append (','); |
buffer.append(','); |
532 |
} |
} |
533 |
buffer.append (distributions[i]); |
buffer.append(distributions[i]); |
534 |
} |
} |
535 |
} |
} |
536 |
send (buffer.toString ()); |
send(buffer.toString()); |
537 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
538 |
switch (response.status) |
switch (response.status) |
539 |
{ |
{ |
540 |
case NEWGROUPS_LIST_FOLLOWS: |
case NEWGROUPS_LIST_FOLLOWS: |
541 |
LineIterator li = new LineIterator (this); |
LineIterator li = new LineIterator(this); |
542 |
pendingData = li; |
pendingData = li; |
543 |
return li; |
return li; |
544 |
default: |
default: |
545 |
throw new NNTPException (response); |
throw new NNTPException(response); |
546 |
} |
} |
547 |
} |
} |
548 |
|
|
557 |
* @param since the date from which to list new articles |
* @param since the date from which to list new articles |
558 |
* @param distributions if non-null, a list of distributions to match |
* @param distributions if non-null, a list of distributions to match |
559 |
*/ |
*/ |
560 |
public LineIterator newNews (String newsgroup, Date since, |
public LineIterator newNews(String newsgroup, Date since, |
561 |
String[] distributions) |
String[] distributions) |
562 |
throws IOException |
throws IOException |
563 |
{ |
{ |
564 |
StringBuffer buffer = new StringBuffer (NEWNEWS); |
StringBuffer buffer = new StringBuffer(NEWNEWS); |
565 |
buffer.append (' '); |
buffer.append(' '); |
566 |
buffer.append (newsgroup); |
buffer.append(newsgroup); |
567 |
buffer.append (' '); |
buffer.append(' '); |
568 |
buffer.append (formatDate (since)); |
buffer.append(formatDate(since)); |
569 |
if (distributions != null) |
if (distributions != null) |
570 |
{ |
{ |
571 |
buffer.append (' '); |
buffer.append(' '); |
572 |
for (int i = 0; i < distributions.length; i++) |
for (int i = 0; i < distributions.length; i++) |
573 |
{ |
{ |
574 |
if (i > 0) |
if (i > 0) |
575 |
{ |
{ |
576 |
buffer.append (','); |
buffer.append(','); |
577 |
} |
} |
578 |
buffer.append (distributions[i]); |
buffer.append(distributions[i]); |
579 |
} |
} |
580 |
} |
} |
581 |
send (buffer.toString ()); |
send(buffer.toString()); |
582 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
583 |
switch (response.status) |
switch (response.status) |
584 |
{ |
{ |
585 |
case NEWNEWS_LIST_FOLLOWS: |
case NEWNEWS_LIST_FOLLOWS: |
586 |
LineIterator li = new LineIterator (this); |
LineIterator li = new LineIterator(this); |
587 |
pendingData = li; |
pendingData = li; |
588 |
return li; |
return li; |
589 |
default: |
default: |
590 |
throw new NNTPException (response); |
throw new NNTPException(response); |
591 |
} |
} |
592 |
} |
} |
593 |
|
|
598 |
* @return the article number/message-id pair associated with the new |
* @return the article number/message-id pair associated with the new |
599 |
* article |
* article |
600 |
*/ |
*/ |
601 |
public ArticleResponse next () |
public ArticleResponse next() |
602 |
throws IOException |
throws IOException |
603 |
{ |
{ |
604 |
return articleImpl (NEXT, null); |
return articleImpl(NEXT, null); |
605 |
} |
} |
606 |
|
|
607 |
// RFC977:3.10 The POST command |
// RFC977:3.10 The POST command |
615 |
* No other method should be called in between. |
* No other method should be called in between. |
616 |
* @see #postComplete |
* @see #postComplete |
617 |
*/ |
*/ |
618 |
public OutputStream post () |
public OutputStream post() |
619 |
throws IOException |
throws IOException |
620 |
{ |
{ |
621 |
send (POST); |
send(POST); |
622 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
623 |
switch (response.status) |
switch (response.status) |
624 |
{ |
{ |
625 |
case SEND_ARTICLE: |
case SEND_ARTICLE: |
626 |
return new PostStream (this, false); |
return new PostStream(this, false); |
627 |
default: |
default: |
628 |
// POSTING_NOT_ALLOWED |
// POSTING_NOT_ALLOWED |
629 |
throw new NNTPException (response); |
throw new NNTPException(response); |
630 |
} |
} |
631 |
} |
} |
632 |
|
|
636 |
* Called by the PostStream during <code>close()</code>. |
* Called by the PostStream during <code>close()</code>. |
637 |
* @see #post |
* @see #post |
638 |
*/ |
*/ |
639 |
void postComplete () |
void postComplete() |
640 |
throws IOException |
throws IOException |
641 |
{ |
{ |
642 |
send (DOT); |
send(DOT); |
643 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
644 |
switch (response.status) |
switch (response.status) |
645 |
{ |
{ |
646 |
case ARTICLE_POSTED: |
case ARTICLE_POSTED: |
650 |
// POSTING_FAILED |
// POSTING_FAILED |
651 |
// TRANSFER_FAILED |
// TRANSFER_FAILED |
652 |
// ARTICLE_REJECTED |
// ARTICLE_REJECTED |
653 |
throw new NNTPException (response); |
throw new NNTPException(response); |
654 |
} |
} |
655 |
} |
} |
656 |
|
|
660 |
* Close the connection. |
* Close the connection. |
661 |
* After calling this method, no further calls on this object are valid. |
* After calling this method, no further calls on this object are valid. |
662 |
*/ |
*/ |
663 |
public void quit () |
public void quit() |
664 |
throws IOException |
throws IOException |
665 |
{ |
{ |
666 |
send (QUIT); |
send(QUIT); |
667 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
668 |
switch (response.status) |
switch (response.status) |
669 |
{ |
{ |
670 |
case CLOSING_CONNECTION: |
case CLOSING_CONNECTION: |
671 |
socket.close (); |
socket.close(); |
672 |
return; |
return; |
673 |
default: |
default: |
674 |
throw new NNTPException (response); |
throw new NNTPException(response); |
675 |
} |
} |
676 |
} |
} |
677 |
|
|
680 |
/** |
/** |
681 |
* Indicates to the server that this is a slave connection. |
* Indicates to the server that this is a slave connection. |
682 |
*/ |
*/ |
683 |
public void slave () |
public void slave() |
684 |
throws IOException |
throws IOException |
685 |
{ |
{ |
686 |
send (SLAVE); |
send(SLAVE); |
687 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
688 |
switch (response.status) |
switch (response.status) |
689 |
{ |
{ |
690 |
case SLAVE_ACKNOWLEDGED: |
case SLAVE_ACKNOWLEDGED: |
691 |
break; |
break; |
692 |
default: |
default: |
693 |
throw new NNTPException (response); |
throw new NNTPException(response); |
694 |
} |
} |
695 |
} |
} |
696 |
|
|
697 |
// RFC2980:1.1 The CHECK command |
// RFC2980:1.1 The CHECK command |
698 |
|
|
699 |
public boolean check (String messageId) |
public boolean check(String messageId) |
700 |
throws IOException |
throws IOException |
701 |
{ |
{ |
702 |
StringBuffer buffer = new StringBuffer (CHECK); |
StringBuffer buffer = new StringBuffer(CHECK); |
703 |
buffer.append (' '); |
buffer.append(' '); |
704 |
buffer.append (messageId); |
buffer.append(messageId); |
705 |
send (buffer.toString ()); |
send(buffer.toString()); |
706 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
707 |
switch (response.status) |
switch (response.status) |
708 |
{ |
{ |
709 |
case SEND_ARTICLE_VIA_TAKETHIS: |
case SEND_ARTICLE_VIA_TAKETHIS: |
715 |
// TRY_AGAIN_LATER |
// TRY_AGAIN_LATER |
716 |
// TRANSFER_PERMISSION_DENIED |
// TRANSFER_PERMISSION_DENIED |
717 |
// COMMAND_NOT_RECOGNIZED |
// COMMAND_NOT_RECOGNIZED |
718 |
throw new NNTPException (response); |
throw new NNTPException(response); |
719 |
} |
} |
720 |
} |
} |
721 |
|
|
728 |
* |
* |
729 |
* @return true if the server supports streaming mode |
* @return true if the server supports streaming mode |
730 |
*/ |
*/ |
731 |
public boolean modeStream () |
public boolean modeStream() |
732 |
throws IOException |
throws IOException |
733 |
{ |
{ |
734 |
send (MODE_STREAM); |
send(MODE_STREAM); |
735 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
736 |
switch (response.status) |
switch (response.status) |
737 |
{ |
{ |
738 |
case STREAMING_OK: |
case STREAMING_OK: |
752 |
* stream. |
* stream. |
753 |
* @see #takethisComplete |
* @see #takethisComplete |
754 |
*/ |
*/ |
755 |
public OutputStream takethis (String messageId) |
public OutputStream takethis(String messageId) |
756 |
throws IOException |
throws IOException |
757 |
{ |
{ |
758 |
send (TAKETHIS + ' ' + messageId); |
send(TAKETHIS + ' ' + messageId); |
759 |
return new PostStream (this, true); |
return new PostStream(this, true); |
760 |
} |
} |
761 |
|
|
762 |
/** |
/** |
764 |
* Called by PostStream.close(). |
* Called by PostStream.close(). |
765 |
* @see #takethis |
* @see #takethis |
766 |
*/ |
*/ |
767 |
void takethisComplete () |
void takethisComplete() |
768 |
throws IOException |
throws IOException |
769 |
{ |
{ |
770 |
send (DOT); |
send(DOT); |
771 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
772 |
switch (response.status) |
switch (response.status) |
773 |
{ |
{ |
774 |
case ARTICLE_TRANSFERRED_OK: |
case ARTICLE_TRANSFERRED_OK: |
778 |
// ARTICLE_TRANSFER_FAILED |
// ARTICLE_TRANSFER_FAILED |
779 |
// TRANSFER_PERMISSION_DENIED |
// TRANSFER_PERMISSION_DENIED |
780 |
// COMMAND_NOT_RECOGNIZED |
// COMMAND_NOT_RECOGNIZED |
781 |
throw new NNTPException (response); |
throw new NNTPException(response); |
782 |
} |
} |
783 |
} |
} |
784 |
|
|
795 |
* @param wildmat the wildmat pattern. If null, returns all groups. If no |
* @param wildmat the wildmat pattern. If null, returns all groups. If no |
796 |
* groups are matched, returns an empty iterator. |
* groups are matched, returns an empty iterator. |
797 |
*/ |
*/ |
798 |
public GroupIterator listActive (String wildmat) |
public GroupIterator listActive(String wildmat) |
799 |
throws IOException |
throws IOException |
800 |
{ |
{ |
801 |
StringBuffer buffer = new StringBuffer (LIST_ACTIVE); |
StringBuffer buffer = new StringBuffer(LIST_ACTIVE); |
802 |
if (wildmat != null) |
if (wildmat != null) |
803 |
{ |
{ |
804 |
buffer.append (' '); |
buffer.append(' '); |
805 |
buffer.append (wildmat); |
buffer.append(wildmat); |
806 |
} |
} |
807 |
return listImpl (buffer.toString ()); |
return listImpl(buffer.toString()); |
808 |
} |
} |
809 |
|
|
810 |
// RFC2980:2.1.3 The LIST ACTIVE.TIMES command |
// RFC2980:2.1.3 The LIST ACTIVE.TIMES command |
814 |
* Each ActiveTime object returned provides details of who created the |
* Each ActiveTime object returned provides details of who created the |
815 |
* newsgroup and when. |
* newsgroup and when. |
816 |
*/ |
*/ |
817 |
public ActiveTimesIterator listActiveTimes () |
public ActiveTimesIterator listActiveTimes() |
818 |
throws IOException |
throws IOException |
819 |
{ |
{ |
820 |
send (LIST_ACTIVE_TIMES); |
send(LIST_ACTIVE_TIMES); |
821 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
822 |
switch (response.status) |
switch (response.status) |
823 |
{ |
{ |
824 |
case LIST_FOLLOWS: |
case LIST_FOLLOWS: |
825 |
return new ActiveTimesIterator (this); |
return new ActiveTimesIterator(this); |
826 |
default: |
default: |
827 |
throw new NNTPException (response); |
throw new NNTPException(response); |
828 |
} |
} |
829 |
} |
} |
830 |
|
|
845 |
* @return an iterator over group name/description pairs |
* @return an iterator over group name/description pairs |
846 |
* @see #xgtitle |
* @see #xgtitle |
847 |
*/ |
*/ |
848 |
public PairIterator listNewsgroups (String wildmat) |
public PairIterator listNewsgroups(String wildmat) |
849 |
throws IOException |
throws IOException |
850 |
{ |
{ |
851 |
StringBuffer buffer = new StringBuffer (LIST_NEWSGROUPS); |
StringBuffer buffer = new StringBuffer(LIST_NEWSGROUPS); |
852 |
if (wildmat != null) |
if (wildmat != null) |
853 |
{ |
{ |
854 |
buffer.append (' '); |
buffer.append(' '); |
855 |
buffer.append (wildmat); |
buffer.append(wildmat); |
856 |
} |
} |
857 |
send (buffer.toString ()); |
send(buffer.toString()); |
858 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
859 |
switch (response.status) |
switch (response.status) |
860 |
{ |
{ |
861 |
case LIST_FOLLOWS: |
case LIST_FOLLOWS: |
862 |
PairIterator pi = new PairIterator (this); |
PairIterator pi = new PairIterator(this); |
863 |
pendingData = pi; |
pendingData = pi; |
864 |
return pi; |
return pi; |
865 |
default: |
default: |
866 |
throw new NNTPException (response); |
throw new NNTPException(response); |
867 |
} |
} |
868 |
} |
} |
869 |
|
|
875 |
* Each line returned by the iterator contains one header field. |
* Each line returned by the iterator contains one header field. |
876 |
* @see #xover |
* @see #xover |
877 |
*/ |
*/ |
878 |
public LineIterator listOverviewFmt () |
public LineIterator listOverviewFmt() |
879 |
throws IOException |
throws IOException |
880 |
{ |
{ |
881 |
send (LIST_OVERVIEW_FMT); |
send(LIST_OVERVIEW_FMT); |
882 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
883 |
switch (response.status) |
switch (response.status) |
884 |
{ |
{ |
885 |
case LIST_FOLLOWS: |
case LIST_FOLLOWS: |
886 |
LineIterator li = new LineIterator (this); |
LineIterator li = new LineIterator(this); |
887 |
pendingData = li; |
pendingData = li; |
888 |
return li; |
return li; |
889 |
default: |
default: |
890 |
throw new NNTPException (response); |
throw new NNTPException(response); |
891 |
} |
} |
892 |
} |
} |
893 |
|
|
896 |
/** |
/** |
897 |
* Returns a list of newsgroups suitable for new users of the server. |
* Returns a list of newsgroups suitable for new users of the server. |
898 |
*/ |
*/ |
899 |
public GroupIterator listSubscriptions () |
public GroupIterator listSubscriptions() |
900 |
throws IOException |
throws IOException |
901 |
{ |
{ |
902 |
return listImpl (LIST_SUBSCRIPTIONS); |
return listImpl(LIST_SUBSCRIPTIONS); |
903 |
} |
} |
904 |
|
|
905 |
// RFC2980:2.2 The LISTGROUP command |
// RFC2980:2.2 The LISTGROUP command |
910 |
* selected group is assumed. |
* selected group is assumed. |
911 |
* @param group the name of the group to list articles for |
* @param group the name of the group to list articles for |
912 |
*/ |
*/ |
913 |
public ArticleNumberIterator listGroup (String group) |
public ArticleNumberIterator listGroup(String group) |
914 |
throws IOException |
throws IOException |
915 |
{ |
{ |
916 |
StringBuffer buffer = new StringBuffer (LISTGROUP); |
StringBuffer buffer = new StringBuffer(LISTGROUP); |
917 |
if (group != null) |
if (group != null) |
918 |
{ |
{ |
919 |
buffer.append (' '); |
buffer.append(' '); |
920 |
buffer.append (group); |
buffer.append(group); |
921 |
} |
} |
922 |
send (buffer.toString ()); |
send(buffer.toString()); |
923 |
StatusResponse response = parseResponse (read (), true); |
StatusResponse response = parseResponse(read(), true); |
924 |
switch (response.status) |
switch (response.status) |
925 |
{ |
{ |
926 |
case GROUP_SELECTED: |
case GROUP_SELECTED: |
927 |
ArticleNumberIterator ani = new ArticleNumberIterator (this); |
ArticleNumberIterator ani = new ArticleNumberIterator(this); |
928 |
pendingData = ani; |
pendingData = ani; |
929 |
return ani; |
return ani; |
930 |
default: |
default: |
931 |
throw new NNTPException (response); |
throw new NNTPException(response); |
932 |
} |
} |
933 |
} |
} |
934 |
|
|
938 |
* Indicates to the server that this is a user-agent. |
* Indicates to the server that this is a user-agent. |
939 |
* @return true if posting is allowed, false otherwise. |
* @return true if posting is allowed, false otherwise. |
940 |
*/ |
*/ |
941 |
public boolean modeReader () |
public boolean modeReader() |
942 |
throws IOException |
throws IOException |
943 |
{ |
{ |
944 |
send (MODE_READER); |
send(MODE_READER); |
945 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
946 |
switch (response.status) |
switch (response.status) |
947 |
{ |
{ |
948 |
case POSTING_ALLOWED: |
case POSTING_ALLOWED: |
952 |
canPost = false; |
canPost = false; |
953 |
return canPost; |
return canPost; |
954 |
default: |
default: |
955 |
throw new NNTPException (response); |
throw new NNTPException(response); |
956 |
} |
} |
957 |
} |
} |
958 |
|
|
962 |
* Returns an iterator over the list of newsgroup descriptions. |
* Returns an iterator over the list of newsgroup descriptions. |
963 |
* @param wildmat if non-null, the newsgroups to match |
* @param wildmat if non-null, the newsgroups to match |
964 |
*/ |
*/ |
965 |
public PairIterator xgtitle (String wildmat) |
public PairIterator xgtitle(String wildmat) |
966 |
throws IOException |
throws IOException |
967 |
{ |
{ |
968 |
StringBuffer buffer = new StringBuffer (XGTITLE); |
StringBuffer buffer = new StringBuffer(XGTITLE); |
969 |
if (wildmat != null) |
if (wildmat != null) |
970 |
{ |
{ |
971 |
buffer.append (' '); |
buffer.append(' '); |
972 |
buffer.append (wildmat); |
buffer.append(wildmat); |
973 |
} |
} |
974 |
send (buffer.toString ()); |
send(buffer.toString()); |
975 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
976 |
switch (response.status) |
switch (response.status) |
977 |
{ |
{ |
978 |
case XGTITLE_LIST_FOLLOWS: |
case XGTITLE_LIST_FOLLOWS: |
979 |
PairIterator pi = new PairIterator (this); |
PairIterator pi = new PairIterator(this); |
980 |
pendingData = pi; |
pendingData = pi; |
981 |
return pi; |
return pi; |
982 |
default: |
default: |
983 |
throw new NNTPException (response); |
throw new NNTPException(response); |
984 |
} |
} |
985 |
} |
} |
986 |
|
|
987 |
// RFC2980:2.6 The XHDR command |
// RFC2980:2.6 The XHDR command |
988 |
|
|
989 |
public HeaderIterator xhdr (String header, String range) |
public HeaderIterator xhdr(String header, String range) |
990 |
throws IOException |
throws IOException |
991 |
{ |
{ |
992 |
StringBuffer buffer = new StringBuffer (XHDR); |
StringBuffer buffer = new StringBuffer(XHDR); |
993 |
buffer.append (' '); |
buffer.append(' '); |
994 |
buffer.append (header); |
buffer.append(header); |
995 |
if (range != null) |
if (range != null) |
996 |
{ |
{ |
997 |
buffer.append (' '); |
buffer.append(' '); |
998 |
buffer.append (range); |
buffer.append(range); |
999 |
} |
} |
1000 |
send (buffer.toString ()); |
send(buffer.toString()); |
1001 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1002 |
switch (response.status) |
switch (response.status) |
1003 |
{ |
{ |
1004 |
case HEAD_FOLLOWS: |
case HEAD_FOLLOWS: |
1005 |
HeaderIterator hi = new HeaderIterator (this); |
HeaderIterator hi = new HeaderIterator(this); |
1006 |
pendingData = hi; |
pendingData = hi; |
1007 |
return hi; |
return hi; |
1008 |
default: |
default: |
1009 |
// NO_GROUP_SELECTED |
// NO_GROUP_SELECTED |
1010 |
// NO_SUCH_ARTICLE |
// NO_SUCH_ARTICLE |
1011 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1012 |
} |
} |
1013 |
} |
} |
1014 |
|
|
1018 |
|
|
1019 |
// RFC2980:2.8 The XOVER command |
// RFC2980:2.8 The XOVER command |
1020 |
|
|
1021 |
public OverviewIterator xover (Range range) |
public OverviewIterator xover(Range range) |
1022 |
throws IOException |
throws IOException |
1023 |
{ |
{ |
1024 |
StringBuffer buffer = new StringBuffer (XOVER); |
StringBuffer buffer = new StringBuffer(XOVER); |
1025 |
if (range != null) |
if (range != null) |
1026 |
{ |
{ |
1027 |
buffer.append (' '); |
buffer.append(' '); |
1028 |
buffer.append (range.toString()); |
buffer.append(range.toString()); |
1029 |
} |
} |
1030 |
send (buffer.toString ()); |
send(buffer.toString()); |
1031 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1032 |
switch (response.status) |
switch (response.status) |
1033 |
{ |
{ |
1034 |
case OVERVIEW_FOLLOWS: |
case OVERVIEW_FOLLOWS: |
1035 |
OverviewIterator oi = new OverviewIterator (this); |
OverviewIterator oi = new OverviewIterator(this); |
1036 |
pendingData = oi; |
pendingData = oi; |
1037 |
return oi; |
return oi; |
1038 |
default: |
default: |
1039 |
// NO_GROUP_SELECTED |
// NO_GROUP_SELECTED |
1040 |
// PERMISSION_DENIED |
// PERMISSION_DENIED |
1041 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1042 |
} |
} |
1043 |
} |
} |
1044 |
|
|
1063 |
/** |
/** |
1064 |
* Basic authentication strategy. |
* Basic authentication strategy. |
1065 |
* @param username the user to authenticate |
* @param username the user to authenticate |
1066 |
* @param password the (cleartext) password |
* @param password the(cleartext) password |
1067 |
* @return true on success, false on failure |
* @return true on success, false on failure |
1068 |
*/ |
*/ |
1069 |
public boolean authinfo (String username, String password) |
public boolean authinfo(String username, String password) |
1070 |
throws IOException |
throws IOException |
1071 |
{ |
{ |
1072 |
StringBuffer buffer = new StringBuffer (AUTHINFO_USER); |
StringBuffer buffer = new StringBuffer(AUTHINFO_USER); |
1073 |
buffer.append (' '); |
buffer.append(' '); |
1074 |
buffer.append (username); |
buffer.append(username); |
1075 |
send (buffer.toString ()); |
send(buffer.toString()); |
1076 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1077 |
switch (response.status) |
switch (response.status) |
1078 |
{ |
{ |
1079 |
case AUTHINFO_OK: |
case AUTHINFO_OK: |
1080 |
return true; |
return true; |
1081 |
case SEND_AUTHINFOPASS: |
case SEND_AUTHINFOPASS: |
1082 |
buffer.setLength (0); |
buffer.setLength(0); |
1083 |
buffer.append (AUTHINFO_PASS); |
buffer.append(AUTHINFO_PASS); |
1084 |
buffer.append (' '); |
buffer.append(' '); |
1085 |
buffer.append (password); |
buffer.append(password); |
1086 |
send (buffer.toString ()); |
send(buffer.toString()); |
1087 |
response = parseResponse (read ()); |
response = parseResponse(read()); |
1088 |
switch (response.status) |
switch (response.status) |
1089 |
{ |
{ |
1090 |
case AUTHINFO_OK: |
case AUTHINFO_OK: |
1092 |
case PERMISSION_DENIED: |
case PERMISSION_DENIED: |
1093 |
return false; |
return false; |
1094 |
default: |
default: |
1095 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1096 |
} |
} |
1097 |
default: |
default: |
1098 |
// AUTHINFO_REJECTED |
// AUTHINFO_REJECTED |
1099 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1100 |
} |
} |
1101 |
} |
} |
1102 |
|
|
1107 |
* Note that use of this authentication strategy is highly deprecated, |
* Note that use of this authentication strategy is highly deprecated, |
1108 |
* only use on servers that won't accept any other form of authentication. |
* only use on servers that won't accept any other form of authentication. |
1109 |
*/ |
*/ |
1110 |
public boolean authinfoSimple (String username, String password) |
public boolean authinfoSimple(String username, String password) |
1111 |
throws IOException |
throws IOException |
1112 |
{ |
{ |
1113 |
send (AUTHINFO_SIMPLE); |
send(AUTHINFO_SIMPLE); |
1114 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1115 |
switch (response.status) |
switch (response.status) |
1116 |
{ |
{ |
1117 |
case SEND_AUTHINFO_SIMPLE: |
case SEND_AUTHINFO_SIMPLE: |
1118 |
StringBuffer buffer = new StringBuffer (username); |
StringBuffer buffer = new StringBuffer(username); |
1119 |
buffer.append (' '); |
buffer.append(' '); |
1120 |
buffer.append (password); |
buffer.append(password); |
1121 |
send (buffer.toString ()); |
send(buffer.toString()); |
1122 |
response = parseResponse (read ()); |
response = parseResponse(read()); |
1123 |
switch (response.status) |
switch (response.status) |
1124 |
{ |
{ |
1125 |
case AUTHINFO_SIMPLE_OK: |
case AUTHINFO_SIMPLE_OK: |
1126 |
return true; |
return true; |
1127 |
case AUTHINFO_SIMPLE_DENIED: |
case AUTHINFO_SIMPLE_DENIED: |
1128 |
return false; |
return false; |
1129 |
default:throw new NNTPException (response); |
default:throw new NNTPException(response); |
1130 |
} |
} |
1131 |
default: |
default: |
1132 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1133 |
} |
} |
1134 |
} |
} |
1135 |
|
|
1143 |
* @param username the authentication principal |
* @param username the authentication principal |
1144 |
* @param password the authentication credentials |
* @param password the authentication credentials |
1145 |
*/ |
*/ |
1146 |
public boolean authinfoGeneric (String mechanism, |
public boolean authinfoGeneric(String mechanism, |
1147 |
String username, String password) |
String username, String password) |
1148 |
throws IOException |
throws IOException |
1149 |
{ |
{ |
1150 |
String[] m = new String[] { mechanism }; |
String[] m = new String[] { mechanism }; |
1151 |
CallbackHandler ch = new SaslCallbackHandler (username, password); |
CallbackHandler ch = new SaslCallbackHandler(username, password); |
1152 |
// Avoid lengthy callback procedure for GNU Crypto |
// Avoid lengthy callback procedure for GNU Crypto |
1153 |
Properties p = new Properties (); |
Properties p = new Properties(); |
1154 |
p.put ("gnu.crypto.sasl.username", username); |
p.put("gnu.crypto.sasl.username", username); |
1155 |
p.put ("gnu.crypto.sasl.password", password); |
p.put("gnu.crypto.sasl.password", password); |
1156 |
SaslClient sasl = |
SaslClient sasl = |
1157 |
Sasl.createSaslClient (m, null, "smtp", |
Sasl.createSaslClient(m, null, "smtp", |
1158 |
socket.getInetAddress ().getHostName (), |
socket.getInetAddress().getHostName(), |
1159 |
p, ch); |
p, ch); |
1160 |
if (sasl == null) |
if (sasl == null) |
1161 |
{ |
{ |
1162 |
return false; |
return false; |
1163 |
} |
} |
1164 |
|
|
1165 |
StringBuffer cmd = new StringBuffer (AUTHINFO_GENERIC); |
StringBuffer cmd = new StringBuffer(AUTHINFO_GENERIC); |
1166 |
cmd.append (' '); |
cmd.append(' '); |
1167 |
cmd.append (mechanism); |
cmd.append(mechanism); |
1168 |
if (sasl.hasInitialResponse ()) |
if (sasl.hasInitialResponse()) |
1169 |
{ |
{ |
1170 |
cmd.append (' '); |
cmd.append(' '); |
1171 |
byte[] init = sasl.evaluateChallenge (new byte[0]); |
byte[] init = sasl.evaluateChallenge(new byte[0]); |
1172 |
cmd.append (new String (init, "US-ASCII")); |
cmd.append(new String(init, "US-ASCII")); |
1173 |
} |
} |
1174 |
send (cmd.toString ()); |
send(cmd.toString()); |
1175 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1176 |
switch (response.status) |
switch (response.status) |
1177 |
{ |
{ |
1178 |
case AUTHINFO_OK: |
case AUTHINFO_OK: |
1179 |
String qop = (String) sasl.getNegotiatedProperty (Sasl.QOP); |
String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP); |
1180 |
if ("auth-int".equalsIgnoreCase (qop) |
if ("auth-int".equalsIgnoreCase(qop) |
1181 |
|| "auth-conf".equalsIgnoreCase (qop)) |
|| "auth-conf".equalsIgnoreCase(qop)) |
1182 |
{ |
{ |
1183 |
InputStream in = socket.getInputStream (); |
InputStream in = socket.getInputStream(); |
1184 |
in = new BufferedInputStream (in); |
in = new BufferedInputStream(in); |
1185 |
in = new SaslInputStream (sasl, in); |
in = new SaslInputStream(sasl, in); |
1186 |
in = new CRLFInputStream (in); |
in = new CRLFInputStream(in); |
1187 |
this.in = new LineInputStream (in); |
this.in = new LineInputStream(in); |
1188 |
OutputStream out = socket.getOutputStream (); |
OutputStream out = socket.getOutputStream(); |
1189 |
out = new BufferedOutputStream (out); |
out = new BufferedOutputStream(out); |
1190 |
out = new SaslOutputStream (sasl, out); |
out = new SaslOutputStream(sasl, out); |
1191 |
this.out = new CRLFOutputStream (out); |
this.out = new CRLFOutputStream(out); |
1192 |
} |
} |
1193 |
return true; |
return true; |
1194 |
case PERMISSION_DENIED: |
case PERMISSION_DENIED: |
1197 |
case SYNTAX_ERROR: |
case SYNTAX_ERROR: |
1198 |
case INTERNAL_ERROR: |
case INTERNAL_ERROR: |
1199 |
default: |
default: |
1200 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1201 |
// FIXME how does the server send continuations? |
// FIXME how does the server send continuations? |
1202 |
} |
} |
1203 |
} |
} |
1207 |
/** |
/** |
1208 |
* Returns the date on the server. |
* Returns the date on the server. |
1209 |
*/ |
*/ |
1210 |
public Date date () |
public Date date() |
1211 |
throws IOException |
throws IOException |
1212 |
{ |
{ |
1213 |
send (DATE); |
send(DATE); |
1214 |
StatusResponse response = parseResponse (read ()); |
StatusResponse response = parseResponse(read()); |
1215 |
switch (response.status) |
switch (response.status) |
1216 |
{ |
{ |
1217 |
case DATE_OK: |
case DATE_OK: |
1218 |
String message = response.getMessage (); |
String message = response.getMessage(); |
1219 |
try |
try |
1220 |
{ |
{ |
1221 |
DateFormat df = new SimpleDateFormat ("yyyyMMddHHmmss"); |
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); |
1222 |
return df.parse (message); |
return df.parse(message); |
1223 |
} |
} |
1224 |
catch (ParseException e) |
catch (ParseException e) |
1225 |
{ |
{ |
1226 |
throw new IOException ("Invalid date: " + message); |
throw new IOException("Invalid date: " + message); |
1227 |
} |
} |
1228 |
default: |
default: |
1229 |
throw new NNTPException (response); |
throw new NNTPException(response); |
1230 |
} |
} |
1231 |
} |
} |
1232 |
|
|
1235 |
/** |
/** |
1236 |
* Parse a response object from a response line sent by the server. |
* Parse a response object from a response line sent by the server. |
1237 |
*/ |
*/ |
1238 |
protected StatusResponse parseResponse (String line) |
protected StatusResponse parseResponse(String line) |
1239 |
throws ProtocolException |
throws ProtocolException |
1240 |
{ |
{ |
1241 |
return parseResponse (line, false); |
return parseResponse(line, false); |
1242 |
} |
} |
1243 |
|
|
1244 |
/** |
/** |
1245 |
* Parse a response object from a response line sent by the server. |
* Parse a response object from a response line sent by the server. |
1246 |
* @param isListGroup whether we are invoking the LISTGROUP command |
* @param isListGroup whether we are invoking the LISTGROUP command |
1247 |
*/ |
*/ |
1248 |
protected StatusResponse parseResponse (String line, boolean isListGroup) |
protected StatusResponse parseResponse(String line, boolean isListGroup) |
1249 |
throws ProtocolException |
throws ProtocolException |
1250 |
{ |
{ |
1251 |
if (line == null) |
if (line == null) |
1252 |
{ |
{ |
1253 |
throw new ProtocolException (hostname + " closed connection"); |
throw new ProtocolException(hostname + " closed connection"); |
1254 |
} |
} |
1255 |
int start = 0, end; |
int start = 0, end; |
1256 |
short status = -1; |
short status = -1; |
1257 |
String statusText = line; |
String statusText = line; |
1258 |
String message = null; |
String message = null; |
1259 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1260 |
if (end > start) |
if (end > start) |
1261 |
{ |
{ |
1262 |
statusText = line.substring (start, end); |
statusText = line.substring(start, end); |
1263 |
message = line.substring (end + 1); |
message = line.substring(end + 1); |
1264 |
} |
} |
1265 |
try |
try |
1266 |
{ |
{ |
1267 |
status = Short.parseShort (statusText); |
status = Short.parseShort(statusText); |
1268 |
} |
} |
1269 |
catch (NumberFormatException e) |
catch (NumberFormatException e) |
1270 |
{ |
{ |
1271 |
throw new ProtocolException (line); |
throw new ProtocolException(line); |
1272 |
} |
} |
1273 |
StatusResponse response; |
StatusResponse response; |
1274 |
switch (status) |
switch (status) |
1286 |
try |
try |
1287 |
{ |
{ |
1288 |
ArticleResponse aresponse = |
ArticleResponse aresponse = |
1289 |
new ArticleResponse (status, message); |
new ArticleResponse(status, message); |
1290 |
// article number |
// article number |
1291 |
start = end + 1; |
start = end + 1; |
1292 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1293 |
if (end > start) |
if (end > start) |
1294 |
{ |
{ |
1295 |
aresponse.articleNumber = |
aresponse.articleNumber = |
1296 |
Integer.parseInt (line.substring (start, end)); |
Integer.parseInt(line.substring(start, end)); |
1297 |
} |
} |
1298 |
// message-id |
// message-id |
1299 |
start = end + 1; |
start = end + 1; |
1300 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1301 |
if (end > start) |
if (end > start) |
1302 |
{ |
{ |
1303 |
aresponse.messageId = line.substring (start, end); |
aresponse.messageId = line.substring(start, end); |
1304 |
} |
} |
1305 |
else |
else |
1306 |
{ |
{ |
1307 |
aresponse.messageId = line.substring (start); |
aresponse.messageId = line.substring(start); |
1308 |
} |
} |
1309 |
response = aresponse; |
response = aresponse; |
1310 |
} |
} |
1311 |
catch (NumberFormatException e) |
catch (NumberFormatException e) |
1312 |
{ |
{ |
1313 |
// This will happen for XHDR |
// This will happen for XHDR |
1314 |
response = new StatusResponse (status, message); |
response = new StatusResponse(status, message); |
1315 |
} |
} |
1316 |
break; |
break; |
1317 |
} |
} |
1318 |
// This is the normal case for GROUP_SELECTED |
// This is the normal case for GROUP_SELECTED |
1319 |
GroupResponse gresponse = new GroupResponse (status, message); |
GroupResponse gresponse = new GroupResponse(status, message); |
1320 |
try |
try |
1321 |
{ |
{ |
1322 |
// count |
// count |
1323 |
start = end + 1; |
start = end + 1; |
1324 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1325 |
if (end > start) |
if (end > start) |
1326 |
{ |
{ |
1327 |
gresponse.count = |
gresponse.count = |
1328 |
Integer.parseInt (line.substring (start, end)); |
Integer.parseInt(line.substring(start, end)); |
1329 |
} |
} |
1330 |
// first |
// first |
1331 |
start = end + 1; |
start = end + 1; |
1332 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1333 |
if (end > start) |
if (end > start) |
1334 |
{ |
{ |
1335 |
gresponse.first = |
gresponse.first = |
1336 |
Integer.parseInt (line.substring (start, end)); |
Integer.parseInt(line.substring(start, end)); |
1337 |
} |
} |
1338 |
// last |
// last |
1339 |
start = end + 1; |
start = end + 1; |
1340 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1341 |
if (end > start) |
if (end > start) |
1342 |
{ |
{ |
1343 |
gresponse.last = |
gresponse.last = |
1344 |
Integer.parseInt (line.substring (start, end)); |
Integer.parseInt(line.substring(start, end)); |
1345 |
} |
} |
1346 |
// group |
// group |
1347 |
start = end + 1; |
start = end + 1; |
1348 |
end = line.indexOf (' ', start); |
end = line.indexOf(' ', start); |
1349 |
if (end > start) |
if (end > start) |
1350 |
{ |
{ |
1351 |
gresponse.group = line.substring (start, end); |
gresponse.group = line.substring(start, end); |
1352 |
} |
} |
1353 |
else |
else |
1354 |
{ |
{ |
1355 |
gresponse.group = line.substring (start); |
gresponse.group = line.substring(start); |
1356 |
} |
} |
1357 |
} |
} |
1358 |
catch (NumberFormatException e) |
catch (NumberFormatException e) |
1359 |
{ |
{ |
1360 |
throw new ProtocolException (line); |
throw new ProtocolException(line); |
1361 |
} |
} |
1362 |
response = gresponse; |
response = gresponse; |
1363 |
break; |
break; |
1364 |
default: |
default: |
1365 |
response = new StatusResponse (status, message); |
response = new StatusResponse(status, message); |
1366 |
} |
} |
1367 |
return response; |
return response; |
1368 |
} |
} |
1371 |
* Send a single line to the server. |
* Send a single line to the server. |
1372 |
* @param line the line to send |
* @param line the line to send |
1373 |
*/ |
*/ |
1374 |
protected void send (String line) |
protected void send(String line) |
1375 |
throws IOException |
throws IOException |
1376 |
{ |
{ |
1377 |
if (pendingData != null) |
if (pendingData != null) |
1378 |
{ |
{ |
1379 |
// Clear pending data |
// Clear pending data |
1380 |
pendingData.readToEOF (); |
pendingData.readToEOF(); |
1381 |
pendingData = null; |
pendingData = null; |
1382 |
} |
} |
1383 |
if (debug) |
if (debug) |
1384 |
{ |
{ |
1385 |
Logger logger = Logger.getInstance (); |
Logger logger = Logger.getInstance(); |
1386 |
logger.log ("nntp", ">" + line); |
logger.log("nntp", ">" + line); |
1387 |
} |
} |
1388 |
byte[] data = line.getBytes (US_ASCII); |
byte[] data = line.getBytes(US_ASCII); |
1389 |
out.write (data); |
out.write(data); |
1390 |
out.writeln (); |
out.writeln(); |
1391 |
out.flush (); |
out.flush(); |
1392 |
} |
} |
1393 |
|
|
1394 |
/** |
/** |
1395 |
* Read a single line from the server. |
* Read a single line from the server. |
1396 |
* @return a line of text |
* @return a line of text |
1397 |
*/ |
*/ |
1398 |
protected String read () |
protected String read() |
1399 |
throws IOException |
throws IOException |
1400 |
{ |
{ |
1401 |
String line = in.readLine (); |
String line = in.readLine(); |
1402 |
if (debug) |
if (debug) |
1403 |
{ |
{ |
1404 |
Logger logger = Logger.getInstance (); |
Logger logger = Logger.getInstance(); |
1405 |
if (line == null) |
if (line == null) |
1406 |
{ |
{ |
1407 |
logger.log ("nntp", "<EOF"); |
logger.log("nntp", "<EOF"); |
1408 |
} |
} |
1409 |
else |
else |
1410 |
{ |
{ |
1411 |
logger.log ("nntp", "<" + line); |
logger.log("nntp", "<" + line); |
1412 |
} |
} |
1413 |
} |
} |
1414 |
return line; |
return line; |
1415 |
} |
} |
1416 |
|
|
1417 |
} |
} |
1418 |
|
|