1:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58:
59: import ;
60: import ;
61: import ;
62: import ;
63:
64: import ;
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72:
73:
80: public class NNTPConnection
81: implements NNTPConstants
82: {
83:
84:
87: public static final int DEFAULT_PORT = 119;
88:
89:
92: protected String hostname;
93:
94:
97: protected int port;
98:
99:
102: protected Socket socket;
103:
104:
107: protected LineInputStream in;
108:
109:
112: protected CRLFOutputStream out;
113:
114:
117: protected boolean canPost;
118:
119:
122: protected String welcome;
123:
124:
127: protected PendingData pendingData;
128:
129:
132: protected boolean debug;
133:
134: private static final String DOT = ".";
135: private static final String US_ASCII = "US-ASCII";
136:
137:
141: public NNTPConnection(String hostname)
142: throws IOException
143: {
144: this(hostname, DEFAULT_PORT, 0, 0, false);
145: }
146:
147:
152: public NNTPConnection(String hostname, int port)
153: throws IOException
154: {
155: this(hostname, port, 0, 0, false);
156: }
157:
158:
166: public NNTPConnection(String hostname, int port,
167: int connectionTimeout, int timeout,
168: boolean debug)
169: throws IOException
170: {
171: if (port < 0)
172: {
173: port = DEFAULT_PORT;
174: }
175:
176: this.hostname = hostname;
177: this.port = port;
178: this.debug = debug;
179:
180:
181: socket = new Socket();
182: InetSocketAddress address = new InetSocketAddress(hostname, port);
183: if (connectionTimeout > 0)
184: {
185: socket.connect(address, connectionTimeout);
186: }
187: else
188: {
189: socket.connect(address);
190: }
191: if (timeout > 0)
192: {
193: socket.setSoTimeout(timeout);
194: }
195: InputStream in = socket.getInputStream();
196: in = new CRLFInputStream(in);
197: this.in = new LineInputStream(in);
198: OutputStream out = socket.getOutputStream();
199: out = new BufferedOutputStream(out);
200: this.out = new CRLFOutputStream(out);
201:
202:
203: StatusResponse response = parseResponse(read());
204: switch (response.status)
205: {
206: case POSTING_ALLOWED:
207: canPost = true;
208: case NO_POSTING_ALLOWED:
209: welcome = response.getMessage();
210: break;
211: default:
212: throw new NNTPException(response);
213: }
214: }
215:
216:
222: public String getWelcome()
223: {
224: return welcome;
225: }
226:
227:
232: String formatDate(Date date)
233: {
234: DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'");
235: Calendar cal = new GregorianCalendar();
236: TimeZone gmt = TimeZone.getTimeZone("GMT");
237: cal.setTimeZone(gmt);
238: df.setCalendar(cal);
239: cal.setTime(date);
240: return df.format(date);
241: }
242:
243:
246: Date parseDate(String text)
247: throws ParseException
248: {
249: DateFormat df = new SimpleDateFormat("yyMMdd HHmmss 'GMT'");
250: return df.parse(text);
251: }
252:
253:
254:
255:
262: public ArticleResponse article(int articleNumber)
263: throws IOException
264: {
265: return articleImpl(ARTICLE, Integer.toString(articleNumber));
266: }
267:
268:
275: public ArticleResponse article(String messageId)
276: throws IOException
277: {
278: return articleImpl(ARTICLE, messageId);
279: }
280:
281:
287: public ArticleResponse head(int articleNumber)
288: throws IOException
289: {
290: return articleImpl(HEAD, Integer.toString(articleNumber));
291: }
292:
293:
299: public ArticleResponse head(String messageId)
300: throws IOException
301: {
302: return articleImpl(HEAD, messageId);
303: }
304:
305:
311: public ArticleResponse body(int articleNumber)
312: throws IOException
313: {
314: return articleImpl(BODY, Integer.toString(articleNumber));
315: }
316:
317:
323: public ArticleResponse body(String messageId)
324: throws IOException
325: {
326: return articleImpl(BODY, messageId);
327: }
328:
329:
335: public ArticleResponse stat(int articleNumber)
336: throws IOException
337: {
338: return articleImpl(STAT, Integer.toString(articleNumber));
339: }
340:
341:
347: public ArticleResponse stat(String messageId)
348: throws IOException
349: {
350: return articleImpl(STAT, messageId);
351: }
352:
353:
358: protected ArticleResponse articleImpl(String command, String messageId)
359: throws IOException
360: {
361: if (messageId != null)
362: {
363: StringBuffer line = new StringBuffer(command);
364: line.append(' ');
365: line.append(messageId);
366: send(line.toString());
367: }
368: else
369: {
370: send(command);
371: }
372: StatusResponse response = parseResponse(read());
373: switch (response.status)
374: {
375: case ARTICLE_FOLLOWS:
376: case HEAD_FOLLOWS:
377: case BODY_FOLLOWS:
378: ArticleResponse aresponse = (ArticleResponse) response;
379: ArticleStream astream =
380: new ArticleStream(new MessageInputStream(in));
381: pendingData = astream;
382: aresponse.in = astream;
383: return aresponse;
384: case ARTICLE_RETRIEVED:
385: return (ArticleResponse) response;
386: default:
387:
388:
389:
390:
391:
392:
393: throw new NNTPException(response);
394: }
395: }
396:
397:
398:
399:
404: public GroupResponse group(String name)
405: throws IOException
406: {
407: send(GROUP + ' ' + name);
408: StatusResponse response = parseResponse(read());
409: switch (response.status)
410: {
411: case GROUP_SELECTED:
412: return (GroupResponse) response;
413: default:
414:
415: throw new NNTPException(response);
416: }
417: }
418:
419:
420:
421:
425: public LineIterator help()
426: throws IOException
427: {
428: send(HELP);
429: StatusResponse response = parseResponse(read());
430: switch (response.status)
431: {
432: case HELP_TEXT:
433: LineIterator li = new LineIterator(this);
434: pendingData = li;
435: return li;
436: default:
437: throw new NNTPException(response);
438: }
439: }
440:
441:
442:
443:
450: public PostStream ihave(String messageId)
451: throws IOException
452: {
453: send(IHAVE + ' ' + messageId);
454: StatusResponse response = parseResponse(read());
455: switch (response.status)
456: {
457: case SEND_TRANSFER_ARTICLE:
458: return new PostStream(this, false);
459: case ARTICLE_NOT_WANTED:
460: return null;
461: default:
462: throw new NNTPException(response);
463: }
464: }
465:
466:
467:
468:
473: public ArticleResponse last()
474: throws IOException
475: {
476: return articleImpl(LAST, null);
477: }
478:
479:
480:
481:
486: public GroupIterator list()
487: throws IOException
488: {
489: return listImpl(LIST);
490: }
491:
492: GroupIterator listImpl(String command)
493: throws IOException
494: {
495: send(command);
496: StatusResponse response = parseResponse(read());
497: switch (response.status)
498: {
499: case LIST_FOLLOWS:
500: GroupIterator gi = new GroupIterator(this);
501: pendingData = gi;
502: return gi;
503: default:
504: throw new NNTPException(response);
505: }
506: }
507:
508:
509:
510:
518: public LineIterator newGroups(Date since, String[]distributions)
519: throws IOException
520: {
521: StringBuffer buffer = new StringBuffer(NEWGROUPS);
522: buffer.append(' ');
523: buffer.append(formatDate(since));
524: if (distributions != null)
525: {
526: buffer.append(' ');
527: for (int i = 0; i < distributions.length; i++)
528: {
529: if (i > 0)
530: {
531: buffer.append(',');
532: }
533: buffer.append(distributions[i]);
534: }
535: }
536: send(buffer.toString());
537: StatusResponse response = parseResponse(read());
538: switch (response.status)
539: {
540: case NEWGROUPS_LIST_FOLLOWS:
541: LineIterator li = new LineIterator(this);
542: pendingData = li;
543: return li;
544: default:
545: throw new NNTPException(response);
546: }
547: }
548:
549:
550:
551:
560: public LineIterator newNews(String newsgroup, Date since,
561: String[] distributions)
562: throws IOException
563: {
564: StringBuffer buffer = new StringBuffer(NEWNEWS);
565: buffer.append(' ');
566: buffer.append(newsgroup);
567: buffer.append(' ');
568: buffer.append(formatDate(since));
569: if (distributions != null)
570: {
571: buffer.append(' ');
572: for (int i = 0; i < distributions.length; i++)
573: {
574: if (i > 0)
575: {
576: buffer.append(',');
577: }
578: buffer.append(distributions[i]);
579: }
580: }
581: send(buffer.toString());
582: StatusResponse response = parseResponse(read());
583: switch (response.status)
584: {
585: case NEWNEWS_LIST_FOLLOWS:
586: LineIterator li = new LineIterator(this);
587: pendingData = li;
588: return li;
589: default:
590: throw new NNTPException(response);
591: }
592: }
593:
594:
595:
596:
601: public ArticleResponse next()
602: throws IOException
603: {
604: return articleImpl(NEXT, null);
605: }
606:
607:
608:
609:
618: public OutputStream post()
619: throws IOException
620: {
621: send(POST);
622: StatusResponse response = parseResponse(read());
623: switch (response.status)
624: {
625: case SEND_ARTICLE:
626: return new PostStream(this, false);
627: default:
628:
629: throw new NNTPException(response);
630: }
631: }
632:
633:
639: void postComplete()
640: throws IOException
641: {
642: send(DOT);
643: StatusResponse response = parseResponse(read());
644: switch (response.status)
645: {
646: case ARTICLE_POSTED:
647: case ARTICLE_TRANSFERRED:
648: return;
649: default:
650:
651:
652:
653: throw new NNTPException(response);
654: }
655: }
656:
657:
658:
659:
663: public void quit()
664: throws IOException
665: {
666: send(QUIT);
667: StatusResponse response = parseResponse(read());
668: switch (response.status)
669: {
670: case CLOSING_CONNECTION:
671: socket.close();
672: return;
673: default:
674: throw new NNTPException(response);
675: }
676: }
677:
678:
679:
680:
683: public void slave()
684: throws IOException
685: {
686: send(SLAVE);
687: StatusResponse response = parseResponse(read());
688: switch (response.status)
689: {
690: case SLAVE_ACKNOWLEDGED:
691: break;
692: default:
693: throw new NNTPException(response);
694: }
695: }
696:
697:
698:
699: public boolean check(String messageId)
700: throws IOException
701: {
702: StringBuffer buffer = new StringBuffer(CHECK);
703: buffer.append(' ');
704: buffer.append(messageId);
705: send(buffer.toString());
706: StatusResponse response = parseResponse(read());
707: switch (response.status)
708: {
709: case SEND_ARTICLE_VIA_TAKETHIS:
710: return true;
711: case ARTICLE_NOT_WANTED_VIA_TAKETHIS:
712: return false;
713: default:
714:
715:
716:
717:
718: throw new NNTPException(response);
719: }
720: }
721:
722:
723:
724:
731: public boolean modeStream()
732: throws IOException
733: {
734: send(MODE_STREAM);
735: StatusResponse response = parseResponse(read());
736: switch (response.status)
737: {
738: case STREAMING_OK:
739: return true;
740: default:
741:
742: return false;
743: }
744: }
745:
746:
747:
748:
755: public OutputStream takethis(String messageId)
756: throws IOException
757: {
758: send(TAKETHIS + ' ' + messageId);
759: return new PostStream(this, true);
760: }
761:
762:
767: void takethisComplete()
768: throws IOException
769: {
770: send(DOT);
771: StatusResponse response = parseResponse(read());
772: switch (response.status)
773: {
774: case ARTICLE_TRANSFERRED_OK:
775: return;
776: default:
777:
778:
779:
780:
781: throw new NNTPException(response);
782: }
783: }
784:
785:
786:
787:
788:
789:
790:
791:
798: public GroupIterator listActive(String wildmat)
799: throws IOException
800: {
801: StringBuffer buffer = new StringBuffer(LIST_ACTIVE);
802: if (wildmat != null)
803: {
804: buffer.append(' ');
805: buffer.append(wildmat);
806: }
807: return listImpl(buffer.toString());
808: }
809:
810:
811:
812:
817: public ActiveTimesIterator listActiveTimes()
818: throws IOException
819: {
820: send(LIST_ACTIVE_TIMES);
821: StatusResponse response = parseResponse(read());
822: switch (response.status)
823: {
824: case LIST_FOLLOWS:
825: return new ActiveTimesIterator(this);
826: default:
827: throw new NNTPException(response);
828: }
829: }
830:
831:
832:
833:
834:
835:
836:
837:
838:
839:
840:
841:
848: public PairIterator listNewsgroups(String wildmat)
849: throws IOException
850: {
851: StringBuffer buffer = new StringBuffer(LIST_NEWSGROUPS);
852: if (wildmat != null)
853: {
854: buffer.append(' ');
855: buffer.append(wildmat);
856: }
857: send(buffer.toString());
858: StatusResponse response = parseResponse(read());
859: switch (response.status)
860: {
861: case LIST_FOLLOWS:
862: PairIterator pi = new PairIterator(this);
863: pendingData = pi;
864: return pi;
865: default:
866: throw new NNTPException(response);
867: }
868: }
869:
870:
871:
872:
878: public LineIterator listOverviewFmt()
879: throws IOException
880: {
881: send(LIST_OVERVIEW_FMT);
882: StatusResponse response = parseResponse(read());
883: switch (response.status)
884: {
885: case LIST_FOLLOWS:
886: LineIterator li = new LineIterator(this);
887: pendingData = li;
888: return li;
889: default:
890: throw new NNTPException(response);
891: }
892: }
893:
894:
895:
896:
899: public GroupIterator listSubscriptions()
900: throws IOException
901: {
902: return listImpl(LIST_SUBSCRIPTIONS);
903: }
904:
905:
906:
907:
913: public ArticleNumberIterator listGroup(String group)
914: throws IOException
915: {
916: StringBuffer buffer = new StringBuffer(LISTGROUP);
917: if (group != null)
918: {
919: buffer.append(' ');
920: buffer.append(group);
921: }
922: send(buffer.toString());
923: StatusResponse response = parseResponse(read(), true);
924: switch (response.status)
925: {
926: case GROUP_SELECTED:
927: ArticleNumberIterator ani = new ArticleNumberIterator(this);
928: pendingData = ani;
929: return ani;
930: default:
931: throw new NNTPException(response);
932: }
933: }
934:
935:
936:
937:
941: public boolean modeReader()
942: throws IOException
943: {
944: send(MODE_READER);
945: StatusResponse response = parseResponse(read());
946: switch (response.status)
947: {
948: case POSTING_ALLOWED:
949: canPost = true;
950: return canPost;
951: case POSTING_NOT_ALLOWED:
952: canPost = false;
953: return canPost;
954: default:
955: throw new NNTPException(response);
956: }
957: }
958:
959:
960:
961:
965: public PairIterator xgtitle(String wildmat)
966: throws IOException
967: {
968: StringBuffer buffer = new StringBuffer(XGTITLE);
969: if (wildmat != null)
970: {
971: buffer.append(' ');
972: buffer.append(wildmat);
973: }
974: send(buffer.toString());
975: StatusResponse response = parseResponse(read());
976: switch (response.status)
977: {
978: case XGTITLE_LIST_FOLLOWS:
979: PairIterator pi = new PairIterator(this);
980: pendingData = pi;
981: return pi;
982: default:
983: throw new NNTPException(response);
984: }
985: }
986:
987:
988:
989: public HeaderIterator xhdr(String header, String range)
990: throws IOException
991: {
992: StringBuffer buffer = new StringBuffer(XHDR);
993: buffer.append(' ');
994: buffer.append(header);
995: if (range != null)
996: {
997: buffer.append(' ');
998: buffer.append(range);
999: }
1000: send(buffer.toString());
1001: StatusResponse response = parseResponse(read());
1002: switch (response.status)
1003: {
1004: case HEAD_FOLLOWS:
1005: HeaderIterator hi = new HeaderIterator(this);
1006: pendingData = hi;
1007: return hi;
1008: default:
1009:
1010:
1011: throw new NNTPException(response);
1012: }
1013: }
1014:
1015:
1016:
1017:
1018:
1019:
1020:
1021: public OverviewIterator xover(Range range)
1022: throws IOException
1023: {
1024: StringBuffer buffer = new StringBuffer(XOVER);
1025: if (range != null)
1026: {
1027: buffer.append(' ');
1028: buffer.append(range.toString());
1029: }
1030: send(buffer.toString());
1031: StatusResponse response = parseResponse(read());
1032: switch (response.status)
1033: {
1034: case OVERVIEW_FOLLOWS:
1035: OverviewIterator oi = new OverviewIterator(this);
1036: pendingData = oi;
1037: return oi;
1038: default:
1039:
1040:
1041: throw new NNTPException(response);
1042: }
1043: }
1044:
1045:
1046:
1047:
1048:
1049:
1050:
1051:
1052:
1053:
1054:
1055:
1056:
1057:
1058:
1059:
1060:
1061:
1062:
1063:
1069: public boolean authinfo(String username, String password)
1070: throws IOException
1071: {
1072: StringBuffer buffer = new StringBuffer(AUTHINFO_USER);
1073: buffer.append(' ');
1074: buffer.append(username);
1075: send(buffer.toString());
1076: StatusResponse response = parseResponse(read());
1077: switch (response.status)
1078: {
1079: case AUTHINFO_OK:
1080: return true;
1081: case SEND_AUTHINFOPASS:
1082: buffer.setLength(0);
1083: buffer.append(AUTHINFO_PASS);
1084: buffer.append(' ');
1085: buffer.append(password);
1086: send(buffer.toString());
1087: response = parseResponse(read());
1088: switch (response.status)
1089: {
1090: case AUTHINFO_OK:
1091: return true;
1092: case PERMISSION_DENIED:
1093: return false;
1094: default:
1095: throw new NNTPException(response);
1096: }
1097: default:
1098:
1099: throw new NNTPException(response);
1100: }
1101: }
1102:
1103:
1104:
1105:
1110: public boolean authinfoSimple(String username, String password)
1111: throws IOException
1112: {
1113: send(AUTHINFO_SIMPLE);
1114: StatusResponse response = parseResponse(read());
1115: switch (response.status)
1116: {
1117: case SEND_AUTHINFO_SIMPLE:
1118: StringBuffer buffer = new StringBuffer(username);
1119: buffer.append(' ');
1120: buffer.append(password);
1121: send(buffer.toString());
1122: response = parseResponse(read());
1123: switch (response.status)
1124: {
1125: case AUTHINFO_SIMPLE_OK:
1126: return true;
1127: case AUTHINFO_SIMPLE_DENIED:
1128: return false;
1129: default:throw new NNTPException(response);
1130: }
1131: default:
1132: throw new NNTPException(response);
1133: }
1134: }
1135:
1136:
1137:
1138:
1146: public boolean authinfoGeneric(String mechanism,
1147: String username, String password)
1148: throws IOException
1149: {
1150: String[] m = new String[] { mechanism };
1151: CallbackHandler ch = new SaslCallbackHandler(username, password);
1152:
1153: Properties p = new Properties();
1154: p.put("gnu.crypto.sasl.username", username);
1155: p.put("gnu.crypto.sasl.password", password);
1156: SaslClient sasl =
1157: Sasl.createSaslClient(m, null, "smtp",
1158: socket.getInetAddress().getHostName(),
1159: p, ch);
1160: if (sasl == null)
1161: {
1162: return false;
1163: }
1164:
1165: StringBuffer cmd = new StringBuffer(AUTHINFO_GENERIC);
1166: cmd.append(' ');
1167: cmd.append(mechanism);
1168: if (sasl.hasInitialResponse())
1169: {
1170: cmd.append(' ');
1171: byte[] init = sasl.evaluateChallenge(new byte[0]);
1172: cmd.append(new String(init, "US-ASCII"));
1173: }
1174: send(cmd.toString());
1175: StatusResponse response = parseResponse(read());
1176: switch (response.status)
1177: {
1178: case AUTHINFO_OK:
1179: String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP);
1180: if ("auth-int".equalsIgnoreCase(qop)
1181: || "auth-conf".equalsIgnoreCase(qop))
1182: {
1183: InputStream in = socket.getInputStream();
1184: in = new BufferedInputStream(in);
1185: in = new SaslInputStream(sasl, in);
1186: in = new CRLFInputStream(in);
1187: this.in = new LineInputStream(in);
1188: OutputStream out = socket.getOutputStream();
1189: out = new BufferedOutputStream(out);
1190: out = new SaslOutputStream(sasl, out);
1191: this.out = new CRLFOutputStream(out);
1192: }
1193: return true;
1194: case PERMISSION_DENIED:
1195: return false;
1196: case COMMAND_NOT_RECOGNIZED:
1197: case SYNTAX_ERROR:
1198: case INTERNAL_ERROR:
1199: default:
1200: throw new NNTPException(response);
1201:
1202: }
1203: }
1204:
1205:
1206:
1207:
1210: public Date date()
1211: throws IOException
1212: {
1213: send(DATE);
1214: StatusResponse response = parseResponse(read());
1215: switch (response.status)
1216: {
1217: case DATE_OK:
1218: String message = response.getMessage();
1219: try
1220: {
1221: DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
1222: return df.parse(message);
1223: }
1224: catch (ParseException e)
1225: {
1226: throw new IOException("Invalid date: " + message);
1227: }
1228: default:
1229: throw new NNTPException(response);
1230: }
1231: }
1232:
1233:
1234:
1235:
1238: protected StatusResponse parseResponse(String line)
1239: throws ProtocolException
1240: {
1241: return parseResponse(line, false);
1242: }
1243:
1244:
1248: protected StatusResponse parseResponse(String line, boolean isListGroup)
1249: throws ProtocolException
1250: {
1251: if (line == null)
1252: {
1253: throw new ProtocolException(hostname + " closed connection");
1254: }
1255: int start = 0, end;
1256: short status = -1;
1257: String statusText = line;
1258: String message = null;
1259: end = line.indexOf(' ', start);
1260: if (end > start)
1261: {
1262: statusText = line.substring(start, end);
1263: message = line.substring(end + 1);
1264: }
1265: try
1266: {
1267: status = Short.parseShort(statusText);
1268: }
1269: catch (NumberFormatException e)
1270: {
1271: throw new ProtocolException(line);
1272: }
1273: StatusResponse response;
1274: switch (status)
1275: {
1276: case ARTICLE_FOLLOWS:
1277: case HEAD_FOLLOWS:
1278: case BODY_FOLLOWS:
1279: case ARTICLE_RETRIEVED:
1280: case GROUP_SELECTED:
1281:
1284: if (status != GROUP_SELECTED || isListGroup)
1285: {
1286: try
1287: {
1288: ArticleResponse aresponse =
1289: new ArticleResponse(status, message);
1290:
1291: start = end + 1;
1292: end = line.indexOf(' ', start);
1293: if (end > start)
1294: {
1295: aresponse.articleNumber =
1296: Integer.parseInt(line.substring(start, end));
1297: }
1298:
1299: start = end + 1;
1300: end = line.indexOf(' ', start);
1301: if (end > start)
1302: {
1303: aresponse.messageId = line.substring(start, end);
1304: }
1305: else
1306: {
1307: aresponse.messageId = line.substring(start);
1308: }
1309: response = aresponse;
1310: }
1311: catch (NumberFormatException e)
1312: {
1313:
1314: response = new StatusResponse(status, message);
1315: }
1316: break;
1317: }
1318:
1319: GroupResponse gresponse = new GroupResponse(status, message);
1320: try
1321: {
1322:
1323: start = end + 1;
1324: end = line.indexOf(' ', start);
1325: if (end > start)
1326: {
1327: gresponse.count =
1328: Integer.parseInt(line.substring(start, end));
1329: }
1330:
1331: start = end + 1;
1332: end = line.indexOf(' ', start);
1333: if (end > start)
1334: {
1335: gresponse.first =
1336: Integer.parseInt(line.substring(start, end));
1337: }
1338:
1339: start = end + 1;
1340: end = line.indexOf(' ', start);
1341: if (end > start)
1342: {
1343: gresponse.last =
1344: Integer.parseInt(line.substring(start, end));
1345: }
1346:
1347: start = end + 1;
1348: end = line.indexOf(' ', start);
1349: if (end > start)
1350: {
1351: gresponse.group = line.substring(start, end);
1352: }
1353: else
1354: {
1355: gresponse.group = line.substring(start);
1356: }
1357: }
1358: catch (NumberFormatException e)
1359: {
1360: throw new ProtocolException(line);
1361: }
1362: response = gresponse;
1363: break;
1364: default:
1365: response = new StatusResponse(status, message);
1366: }
1367: return response;
1368: }
1369:
1370:
1374: protected void send(String line)
1375: throws IOException
1376: {
1377: if (pendingData != null)
1378: {
1379:
1380: pendingData.readToEOF();
1381: pendingData = null;
1382: }
1383: if (debug)
1384: {
1385: Logger logger = Logger.getInstance();
1386: logger.log("nntp", ">" + line);
1387: }
1388: byte[] data = line.getBytes(US_ASCII);
1389: out.write(data);
1390: out.writeln();
1391: out.flush();
1392: }
1393:
1394:
1398: protected String read()
1399: throws IOException
1400: {
1401: String line = in.readLine();
1402: if (debug)
1403: {
1404: Logger logger = Logger.getInstance();
1405: if (line == null)
1406: {
1407: logger.log("nntp", "<EOF");
1408: }
1409: else
1410: {
1411: logger.log("nntp", "<" + line);
1412: }
1413: }
1414: return line;
1415: }
1416:
1417: }