001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.net.nntp;
019    
020    import java.io.BufferedReader;
021    import java.io.IOException;
022    import java.io.Reader;
023    import java.io.StringWriter;
024    import java.io.Writer;
025    import java.util.ArrayList;
026    import java.util.Vector;
027    
028    import org.apache.commons.net.MalformedServerReplyException;
029    import org.apache.commons.net.io.DotTerminatedMessageReader;
030    import org.apache.commons.net.io.DotTerminatedMessageWriter;
031    import org.apache.commons.net.io.Util;
032    
033    /***
034     * NNTPClient encapsulates all the functionality necessary to post and
035     * retrieve articles from an NNTP server.  As with all classes derived
036     * from {@link org.apache.commons.net.SocketClient},
037     * you must first connect to the server with
038     * {@link org.apache.commons.net.SocketClient#connect  connect }
039     * before doing anything, and finally
040     * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
041     * after you're completely finished interacting with the server.
042     * Remember that the
043     * {@link org.apache.commons.net.nntp.NNTP#isAllowedToPost isAllowedToPost()}
044     *  method is defined in
045     * {@link org.apache.commons.net.nntp.NNTP}.
046     * <p>
047     * You should keep in mind that the NNTP server may choose to prematurely
048     * close a connection if the client has been idle for longer than a
049     * given time period or if the server is being shutdown by the operator or
050     * some other reason.  The NNTP class will detect a
051     * premature NNTP server connection closing when it receives a
052     * {@link org.apache.commons.net.nntp.NNTPReply#SERVICE_DISCONTINUED NNTPReply.SERVICE_DISCONTINUED }
053     *  response to a command.
054     * When that occurs, the NNTP class method encountering that reply will throw
055     * an {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
056     * .
057     * <code>NNTPConectionClosedException</code>
058     * is a subclass of <code> IOException </code> and therefore need not be
059     * caught separately, but if you are going to catch it separately, its
060     * catch block must appear before the more general <code> IOException </code>
061     * catch block.  When you encounter an
062     * {@link org.apache.commons.net.nntp.NNTPConnectionClosedException}
063     * , you must disconnect the connection with
064     * {@link org.apache.commons.net.nntp.NNTP#disconnect  disconnect() }
065     *  to properly clean up the
066     * system resources used by NNTP.  Before disconnecting, you may check the
067     * last reply code and text with
068     * {@link org.apache.commons.net.nntp.NNTP#getReplyCode  getReplyCode } and
069     * {@link org.apache.commons.net.nntp.NNTP#getReplyString  getReplyString }.
070     * <p>
071     * Rather than list it separately for each method, we mention here that
072     * every method communicating with the server and throwing an IOException
073     * can also throw a
074     * {@link org.apache.commons.net.MalformedServerReplyException}
075     * , which is a subclass
076     * of IOException.  A MalformedServerReplyException will be thrown when
077     * the reply received from the server deviates enough from the protocol
078     * specification that it cannot be interpreted in a useful manner despite
079     * attempts to be as lenient as possible.
080     * <p>
081     * <p>
082     * @author Rory Winston
083     * @author Ted Wise
084     * @see NNTP
085     * @see NNTPConnectionClosedException
086     * @see org.apache.commons.net.MalformedServerReplyException
087     ***/
088    
089    public class NNTPClient extends NNTP
090    {
091    
092        /**
093         * Parse the reply and store the id and number in the pointer.
094         *
095         * @param reply the reply to parse "22n nnn <aaa>"
096         * @param pointer the pointer to update
097         *
098         * @throws MalformedServerReplyException
099         */
100        private void __parseArticlePointer(String reply, ArticleInfo pointer)
101        throws MalformedServerReplyException
102        {
103            String tokens[] = reply.split(" ");
104            if (tokens.length >= 3) { // OK, we can parset the line
105                int i = 1; // skip reply code
106                try
107                {
108                    // Get article number
109                    pointer.articleNumber = Long.parseLong(tokens[i++]);
110                    // Get article id
111                    pointer.articleId = tokens[i++];
112                    return; // done
113                }
114                catch (NumberFormatException e)
115                {
116                    // drop through and raise exception
117                }
118            }
119            throw new MalformedServerReplyException(
120                "Could not parse article pointer.\nServer reply: " + reply);
121        }
122    
123        /*
124         * 211 n f l s group selected
125         *     (n = estimated number of articles in group,
126         *     f = first article number in the group,
127         *     l = last article number in the group,
128         *     s = name of the group.)
129         */
130    
131        private static void __parseGroupReply(String reply, NewsgroupInfo info)
132        throws MalformedServerReplyException
133        {
134            String tokens[] = reply.split(" ");
135            if (tokens.length >= 5) {
136                int i = 1;  // Skip numeric response value
137                try
138                {
139                    // Get estimated article count
140                    info._setArticleCount(Long.parseLong(tokens[i++]));
141                    // Get first article number
142                    info._setFirstArticle(Long.parseLong(tokens[i++]));
143                    // Get last article number
144                    info._setLastArticle(Long.parseLong(tokens[i++]));
145                    // Get newsgroup name
146                    info._setNewsgroup(tokens[i++]);
147    
148                    info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
149                    return ;
150                } catch (NumberFormatException e)
151                {
152                   // drop through to report error
153                }
154            }
155    
156            throw new MalformedServerReplyException(
157                "Could not parse newsgroup info.\nServer reply: " + reply);
158        }
159    
160    
161        // Format: group last first p
162        static NewsgroupInfo __parseNewsgroupListEntry(String entry)
163        {
164            String tokens[] = entry.split(" ");
165            if (tokens.length < 4) {
166                return null;
167            }
168            NewsgroupInfo result = new NewsgroupInfo();
169    
170            int i = 0;
171    
172            result._setNewsgroup(tokens[i++]);
173    
174            try
175            {
176                long lastNum = Long.parseLong(tokens[i++]);
177                long firstNum = Long.parseLong(tokens[i++]);
178                result._setFirstArticle(firstNum);
179                result._setLastArticle(lastNum);
180                if((firstNum == 0) && (lastNum == 0))
181                    result._setArticleCount(0);
182                else
183                    result._setArticleCount(lastNum - firstNum + 1);
184                }
185            catch (NumberFormatException e)
186            {
187                return null;
188            }
189    
190            switch (tokens[i++].charAt(0))
191            {
192            case 'y':
193            case 'Y':
194                result._setPostingPermission(
195                    NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
196                break;
197            case 'n':
198            case 'N':
199                result._setPostingPermission(
200                    NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
201                break;
202            case 'm':
203            case 'M':
204                result._setPostingPermission(
205                    NewsgroupInfo.MODERATED_POSTING_PERMISSION);
206                break;
207            default:
208                result._setPostingPermission(
209                    NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
210                break;
211            }
212    
213            return result;
214        }
215    
216        /**
217         * Parse a response line from {@link #retrieveArticleInfo(long, long)}.
218         *
219         * @param line a response line
220         * @return the parsed {@link Article}, if unparseable then isDummy()
221         * will be true, and the subject will contain the raw info.
222         * @since 3.0
223         */
224        static Article __parseArticleEntry(String line) {
225            // Extract the article information
226            // Mandatory format (from NNTP RFC 2980) is :
227            // articleNumber\tSubject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine Count
228    
229            Article article = new Article();
230            article.setSubject(line); // in case parsing fails
231            String parts[] = line.split("\t");
232            if (parts.length > 6) {
233                int i = 0;
234                try {
235                    article.setArticleNumber(Long.parseLong(parts[i++]));
236                    article.setSubject(parts[i++]);
237                    article.setFrom(parts[i++]);
238                    article.setDate(parts[i++]);
239                    article.setArticleId(parts[i++]);
240                    article.addReference(parts[i++]);
241                } catch (NumberFormatException e) {
242                    // ignored, already handled
243                }
244            }
245            return article;
246        }
247    
248        private NewsgroupInfo[] __readNewsgroupListing() throws IOException
249        {
250    
251            BufferedReader reader = new DotTerminatedMessageReader(_reader_);
252            // Start of with a big vector because we may be reading a very large
253            // amount of groups.
254            Vector<NewsgroupInfo> list = new Vector<NewsgroupInfo>(2048);
255    
256            String line;
257            while ((line = reader.readLine()) != null)
258            {
259                NewsgroupInfo tmp = __parseNewsgroupListEntry(line);
260                if (tmp != null)
261                    list.addElement(tmp);
262                else
263                    throw new MalformedServerReplyException(line);
264            }
265    
266            int size;
267            if ((size = list.size()) < 1)
268                return new NewsgroupInfo[0];
269    
270            NewsgroupInfo[] info = new NewsgroupInfo[size];
271            list.copyInto(info);
272    
273            return info;
274        }
275    
276    
277        private BufferedReader __retrieve(int command,
278                                  String articleId, ArticleInfo pointer)
279        throws IOException
280        {
281            if (articleId != null)
282            {
283                if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId))) {
284                    return null;
285                }
286            }
287            else
288            {
289                if (!NNTPReply.isPositiveCompletion(sendCommand(command))) {
290                    return null;
291                }
292            }
293    
294    
295            if (pointer != null) {
296                __parseArticlePointer(getReplyString(), pointer);
297            }
298    
299            return new DotTerminatedMessageReader(_reader_);
300        }
301    
302    
303        private BufferedReader __retrieve(int command,
304                                  long articleNumber, ArticleInfo pointer)
305        throws IOException
306        {
307            if (!NNTPReply.isPositiveCompletion(sendCommand(command,
308                                                Long.toString(articleNumber)))) {
309                return null;
310            }
311    
312            if (pointer != null) {
313                __parseArticlePointer(getReplyString(), pointer);
314            }
315    
316            return new DotTerminatedMessageReader(_reader_);
317        }
318    
319    
320    
321        /***
322         * Retrieves an article from the NNTP server.  The article is referenced
323         * by its unique article identifier (including the enclosing &lt and &gt).
324         * The article number and identifier contained in the server reply
325         * are returned through an ArticleInfo.  The <code> articleId </code>
326         * field of the ArticleInfo cannot always be trusted because some
327         * NNTP servers do not correctly follow the RFC 977 reply format.
328         * <p>
329         * A DotTerminatedMessageReader is returned from which the article can
330         * be read.  If the article does not exist, null is returned.
331         * <p>
332         * You must not issue any commands to the NNTP server (i.e., call any
333         * other methods) until you finish reading the message from the returned
334         * BufferedReader instance.
335         * The NNTP protocol uses the same stream for issuing commands as it does
336         * for returning results.  Therefore the returned BufferedReader actually reads
337         * directly from the NNTP connection.  After the end of message has been
338         * reached, new commands can be executed and their replies read.  If
339         * you do not follow these requirements, your program will not work
340         * properly.
341         * <p>
342         * @param articleId  The unique article identifier of the article to
343         *     retrieve.  If this parameter is null, the currently selected
344         *     article is retrieved.
345         * @param pointer    A parameter through which to return the article's
346         *   number and unique id.  The articleId field cannot always be trusted
347         *   because of server deviations from RFC 977 reply formats.  You may
348         *   set this parameter to null if you do not desire to retrieve the
349         *   returned article information.
350         * @return A DotTerminatedMessageReader instance from which the article
351         *         be read.  null if the article does not exist.
352         * @exception NNTPConnectionClosedException
353         *      If the NNTP server prematurely closes the connection as a result
354         *      of the client being idle or some other reason causing the server
355         *      to send NNTP reply code 400.  This exception may be caught either
356         *      as an IOException or independently as itself.
357         * @exception IOException  If an I/O error occurs while either sending a
358         *      command to the server or receiving a reply from the server.
359         ***/
360        public BufferedReader retrieveArticle(String articleId, ArticleInfo pointer)
361        throws IOException
362        {
363            return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
364    
365        }
366    
367        /**
368         * Same as <code> retrieveArticle(articleId, (ArticleInfo) null) </code>
369         * Note: the return can be cast to a {@link BufferedReader}
370         */
371        public Reader retrieveArticle(String articleId) throws IOException
372        {
373            return retrieveArticle(articleId, (ArticleInfo) null);
374        }
375    
376        /**
377         * Same as <code> retrieveArticle((String) null) </code>
378         * Note: the return can be cast to a {@link BufferedReader}
379         */
380        public Reader retrieveArticle() throws IOException
381        {
382            return retrieveArticle((String) null);
383        }
384    
385    
386        /***
387         * Retrieves an article from the currently selected newsgroup.  The
388         * article is referenced by its article number.
389         * The article number and identifier contained in the server reply
390         * are returned through an ArticleInfo.  The <code> articleId </code>
391         * field of the ArticleInfo cannot always be trusted because some
392         * NNTP servers do not correctly follow the RFC 977 reply format.
393         * <p>
394         * A DotTerminatedMessageReader is returned from which the article can
395         * be read.  If the article does not exist, null is returned.
396         * <p>
397         * You must not issue any commands to the NNTP server (i.e., call any
398         * other methods) until you finish reading the message from the returned
399         * BufferedReader instance.
400         * The NNTP protocol uses the same stream for issuing commands as it does
401         * for returning results.  Therefore the returned BufferedReader actually reads
402         * directly from the NNTP connection.  After the end of message has been
403         * reached, new commands can be executed and their replies read.  If
404         * you do not follow these requirements, your program will not work
405         * properly.
406         * <p>
407         * @param articleNumber  The number of the the article to
408         *     retrieve.
409         * @param pointer    A parameter through which to return the article's
410         *   number and unique id.  The articleId field cannot always be trusted
411         *   because of server deviations from RFC 977 reply formats.  You may
412         *   set this parameter to null if you do not desire to retrieve the
413         *   returned article information.
414         * @return A DotTerminatedMessageReader instance from which the article
415         *         be read.  null if the article does not exist.
416         * @exception NNTPConnectionClosedException
417         *      If the NNTP server prematurely closes the connection as a result
418         *      of the client being idle or some other reason causing the server
419         *      to send NNTP reply code 400.  This exception may be caught either
420         *      as an IOException or independently as itself.
421         * @exception IOException  If an I/O error occurs while either sending a
422         *      command to the server or receiving a reply from the server.
423         ***/
424        public BufferedReader retrieveArticle(long articleNumber, ArticleInfo pointer)
425        throws IOException
426        {
427            return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
428        }
429    
430        /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
431        public BufferedReader retrieveArticle(long articleNumber) throws IOException
432        {
433            return retrieveArticle(articleNumber, null);
434        }
435    
436    
437    
438        /***
439         * Retrieves an article header from the NNTP server.  The article is
440         * referenced
441         * by its unique article identifier (including the enclosing &lt and &gt).
442         * The article number and identifier contained in the server reply
443         * are returned through an ArticleInfo.  The <code> articleId </code>
444         * field of the ArticleInfo cannot always be trusted because some
445         * NNTP servers do not correctly follow the RFC 977 reply format.
446         * <p>
447         * A DotTerminatedMessageReader is returned from which the article can
448         * be read.  If the article does not exist, null is returned.
449         * <p>
450         * You must not issue any commands to the NNTP server (i.e., call any
451         * other methods) until you finish reading the message from the returned
452         * BufferedReader instance.
453         * The NNTP protocol uses the same stream for issuing commands as it does
454         * for returning results.  Therefore the returned BufferedReader actually reads
455         * directly from the NNTP connection.  After the end of message has been
456         * reached, new commands can be executed and their replies read.  If
457         * you do not follow these requirements, your program will not work
458         * properly.
459         * <p>
460         * @param articleId  The unique article identifier of the article whose
461         *    header is being retrieved.  If this parameter is null, the
462         *    header of the currently selected article is retrieved.
463         * @param pointer    A parameter through which to return the article's
464         *   number and unique id.  The articleId field cannot always be trusted
465         *   because of server deviations from RFC 977 reply formats.  You may
466         *   set this parameter to null if you do not desire to retrieve the
467         *   returned article information.
468         * @return A DotTerminatedMessageReader instance from which the article
469         *         header can be read.  null if the article does not exist.
470         * @exception NNTPConnectionClosedException
471         *      If the NNTP server prematurely closes the connection as a result
472         *      of the client being idle or some other reason causing the server
473         *      to send NNTP reply code 400.  This exception may be caught either
474         *      as an IOException or independently as itself.
475         * @exception IOException  If an I/O error occurs while either sending a
476         *      command to the server or receiving a reply from the server.
477         ***/
478        public BufferedReader retrieveArticleHeader(String articleId, ArticleInfo pointer)
479        throws IOException
480        {
481            return __retrieve(NNTPCommand.HEAD, articleId, pointer);
482    
483        }
484    
485        /**
486         * Same as <code> retrieveArticleHeader(articleId, (ArticleInfo) null) </code>
487         *  Note: the return can be cast to a {@link BufferedReader}
488         */
489        public Reader retrieveArticleHeader(String articleId) throws IOException
490        {
491            return retrieveArticleHeader(articleId, (ArticleInfo) null);
492        }
493    
494        /**
495         * Same as <code> retrieveArticleHeader((String) null) </code>
496         *  Note: the return can be cast to a {@link BufferedReader}
497         */
498        public Reader retrieveArticleHeader() throws IOException
499        {
500            return retrieveArticleHeader((String) null);
501        }
502    
503    
504        /***
505         * Retrieves an article header from the currently selected newsgroup.  The
506         * article is referenced by its article number.
507         * The article number and identifier contained in the server reply
508         * are returned through an ArticleInfo.  The <code> articleId </code>
509         * field of the ArticleInfo cannot always be trusted because some
510         * NNTP servers do not correctly follow the RFC 977 reply format.
511         * <p>
512         * A DotTerminatedMessageReader is returned from which the article can
513         * be read.  If the article does not exist, null is returned.
514         * <p>
515         * You must not issue any commands to the NNTP server (i.e., call any
516         * other methods) until you finish reading the message from the returned
517         * BufferedReader instance.
518         * The NNTP protocol uses the same stream for issuing commands as it does
519         * for returning results.  Therefore the returned BufferedReader actually reads
520         * directly from the NNTP connection.  After the end of message has been
521         * reached, new commands can be executed and their replies read.  If
522         * you do not follow these requirements, your program will not work
523         * properly.
524         * <p>
525         * @param articleNumber  The number of the the article whose header is
526         *     being retrieved.
527         * @param pointer    A parameter through which to return the article's
528         *   number and unique id.  The articleId field cannot always be trusted
529         *   because of server deviations from RFC 977 reply formats.  You may
530         *   set this parameter to null if you do not desire to retrieve the
531         *   returned article information.
532         * @return A DotTerminatedMessageReader instance from which the article
533         *         header can be read.  null if the article does not exist.
534         * @exception NNTPConnectionClosedException
535         *      If the NNTP server prematurely closes the connection as a result
536         *      of the client being idle or some other reason causing the server
537         *      to send NNTP reply code 400.  This exception may be caught either
538         *      as an IOException or independently as itself.
539         * @exception IOException  If an I/O error occurs while either sending a
540         *      command to the server or receiving a reply from the server.
541         ***/
542        public BufferedReader retrieveArticleHeader(long articleNumber,
543                                            ArticleInfo pointer)
544        throws IOException
545        {
546            return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
547        }
548    
549    
550        /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
551        public BufferedReader retrieveArticleHeader(long articleNumber) throws IOException
552        {
553            return retrieveArticleHeader(articleNumber, null);
554        }
555    
556    
557    
558        /***
559         * Retrieves an article body from the NNTP server.  The article is
560         * referenced
561         * by its unique article identifier (including the enclosing &lt and &gt).
562         * The article number and identifier contained in the server reply
563         * are returned through an ArticleInfo.  The <code> articleId </code>
564         * field of the ArticleInfo cannot always be trusted because some
565         * NNTP servers do not correctly follow the RFC 977 reply format.
566         * <p>
567         * A DotTerminatedMessageReader is returned from which the article can
568         * be read.  If the article does not exist, null is returned.
569         * <p>
570         * You must not issue any commands to the NNTP server (i.e., call any
571         * other methods) until you finish reading the message from the returned
572         * BufferedReader instance.
573         * The NNTP protocol uses the same stream for issuing commands as it does
574         * for returning results.  Therefore the returned BufferedReader actually reads
575         * directly from the NNTP connection.  After the end of message has been
576         * reached, new commands can be executed and their replies read.  If
577         * you do not follow these requirements, your program will not work
578         * properly.
579         * <p>
580         * @param articleId  The unique article identifier of the article whose
581         *    body is being retrieved.  If this parameter is null, the
582         *    body of the currently selected article is retrieved.
583         * @param pointer    A parameter through which to return the article's
584         *   number and unique id.  The articleId field cannot always be trusted
585         *   because of server deviations from RFC 977 reply formats.  You may
586         *   set this parameter to null if you do not desire to retrieve the
587         *   returned article information.
588         * @return A DotTerminatedMessageReader instance from which the article
589         *         body can be read.  null if the article does not exist.
590         * @exception NNTPConnectionClosedException
591         *      If the NNTP server prematurely closes the connection as a result
592         *      of the client being idle or some other reason causing the server
593         *      to send NNTP reply code 400.  This exception may be caught either
594         *      as an IOException or independently as itself.
595         * @exception IOException  If an I/O error occurs while either sending a
596         *      command to the server or receiving a reply from the server.
597         ***/
598        public BufferedReader retrieveArticleBody(String articleId, ArticleInfo pointer)
599        throws IOException
600        {
601            return __retrieve(NNTPCommand.BODY, articleId, pointer);
602    
603        }
604    
605        /**
606         * Same as <code> retrieveArticleBody(articleId, (ArticleInfo) null) </code>
607         *  Note: the return can be cast to a {@link BufferedReader}
608         */
609        public Reader retrieveArticleBody(String articleId) throws IOException
610        {
611            return retrieveArticleBody(articleId, (ArticleInfo) null);
612        }
613    
614        /**
615         * Same as <code> retrieveArticleBody(null) </code>
616         *  Note: the return can be cast to a {@link BufferedReader}
617         */
618        public Reader retrieveArticleBody() throws IOException
619        {
620            return retrieveArticleBody(null);
621        }
622    
623    
624        /***
625         * Retrieves an article body from the currently selected newsgroup.  The
626         * article is referenced by its article number.
627         * The article number and identifier contained in the server reply
628         * are returned through an ArticleInfo.  The <code> articleId </code>
629         * field of the ArticleInfo cannot always be trusted because some
630         * NNTP servers do not correctly follow the RFC 977 reply format.
631         * <p>
632         * A DotTerminatedMessageReader is returned from which the article can
633         * be read.  If the article does not exist, null is returned.
634         * <p>
635         * You must not issue any commands to the NNTP server (i.e., call any
636         * other methods) until you finish reading the message from the returned
637         * BufferedReader instance.
638         * The NNTP protocol uses the same stream for issuing commands as it does
639         * for returning results.  Therefore the returned BufferedReader actually reads
640         * directly from the NNTP connection.  After the end of message has been
641         * reached, new commands can be executed and their replies read.  If
642         * you do not follow these requirements, your program will not work
643         * properly.
644         * <p>
645         * @param articleNumber  The number of the the article whose body is
646         *     being retrieved.
647         * @param pointer    A parameter through which to return the article's
648         *   number and unique id.  The articleId field cannot always be trusted
649         *   because of server deviations from RFC 977 reply formats.  You may
650         *   set this parameter to null if you do not desire to retrieve the
651         *   returned article information.
652         * @return A DotTerminatedMessageReader instance from which the article
653         *         body can be read.  null if the article does not exist.
654         * @exception NNTPConnectionClosedException
655         *      If the NNTP server prematurely closes the connection as a result
656         *      of the client being idle or some other reason causing the server
657         *      to send NNTP reply code 400.  This exception may be caught either
658         *      as an IOException or independently as itself.
659         * @exception IOException  If an I/O error occurs while either sending a
660         *      command to the server or receiving a reply from the server.
661         ***/
662        public BufferedReader retrieveArticleBody(long articleNumber,
663                                          ArticleInfo pointer)
664        throws IOException
665        {
666            return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
667        }
668    
669    
670        /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
671        public BufferedReader retrieveArticleBody(long articleNumber) throws IOException
672        {
673            return retrieveArticleBody(articleNumber, null);
674        }
675    
676    
677        /***
678         * Select the specified newsgroup to be the target of for future article
679         * retrieval and posting operations.  Also return the newsgroup
680         * information contained in the server reply through the info parameter.
681         * <p>
682         * @param newsgroup  The newsgroup to select.
683         * @param info  A parameter through which the newsgroup information of
684         *      the selected newsgroup contained in the server reply is returned.
685         *      Set this to null if you do not desire this information.
686         * @return True if the newsgroup exists and was selected, false otherwise.
687         * @exception NNTPConnectionClosedException
688         *      If the NNTP server prematurely closes the connection as a result
689         *      of the client being idle or some other reason causing the server
690         *      to send NNTP reply code 400.  This exception may be caught either
691         *      as an IOException or independently as itself.
692         * @exception IOException  If an I/O error occurs while either sending a
693         *      command to the server or receiving a reply from the server.
694         ***/
695        public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
696        throws IOException
697        {
698            if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
699                return false;
700    
701            if (info != null)
702                __parseGroupReply(getReplyString(), info);
703    
704            return true;
705        }
706    
707        /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
708        public boolean selectNewsgroup(String newsgroup) throws IOException
709        {
710            return selectNewsgroup(newsgroup, null);
711        }
712    
713        /***
714         * List the command help from the server.
715         * <p>
716         * @return The sever help information.
717         * @exception NNTPConnectionClosedException
718         *      If the NNTP server prematurely closes the connection as a result
719         *      of the client being idle or some other reason causing the server
720         *      to send NNTP reply code 400.  This exception may be caught either
721         *      as an IOException or independently as itself.
722         * @exception IOException  If an I/O error occurs while either sending a
723         *      command to the server or receiving a reply from the server.
724         ***/
725        public String listHelp() throws IOException
726        {
727            if (!NNTPReply.isInformational(help())) {
728                return null;
729            }
730    
731            StringWriter help = new StringWriter();
732            BufferedReader reader = new DotTerminatedMessageReader(_reader_);
733            Util.copyReader(reader, help);
734            reader.close();
735            help.close();
736            return help.toString();
737        }
738    
739        /**
740         * Send a "LIST OVERVIEW.FMT" command to the server.
741         *
742         * @return the contents of the Overview format, of {@code null} if the command failed
743         * @throws IOException
744         */
745        public String[] listOverviewFmt() throws IOException
746        {
747            if (!NNTPReply.isPositiveCompletion(sendCommand("LIST", "OVERVIEW.FMT"))){
748                return null;
749            }
750    
751            BufferedReader reader = new DotTerminatedMessageReader(_reader_);
752            String line;
753            ArrayList<String> list = new ArrayList<String>();
754            while((line=reader.readLine()) != null) {
755                list.add(line);
756            }
757            reader.close();
758            return list.toArray(new String[list.size()]);
759        }
760    
761        /***
762         * Select an article by its unique identifier (including enclosing
763         * &lt and &gt) and return its article number and id through the
764         * pointer parameter.  This is achieved through the STAT command.
765         * According to RFC 977, this will NOT set the current article pointer
766         * on the server.  To do that, you must reference the article by its
767         * number.
768         * <p>
769         * @param articleId  The unique article identifier of the article that
770         *    is being selectedd.  If this parameter is null, the
771         *    body of the current article is selected
772         * @param pointer    A parameter through which to return the article's
773         *   number and unique id.  The articleId field cannot always be trusted
774         *   because of server deviations from RFC 977 reply formats.  You may
775         *   set this parameter to null if you do not desire to retrieve the
776         *   returned article information.
777         * @return True if successful, false if not.
778         * @exception NNTPConnectionClosedException
779         *      If the NNTP server prematurely closes the connection as a result
780         *      of the client being idle or some other reason causing the server
781         *      to send NNTP reply code 400.  This exception may be caught either
782         *      as an IOException or independently as itself.
783         * @exception IOException  If an I/O error occurs while either sending a
784         *      command to the server or receiving a reply from the server.
785         ***/
786        public boolean selectArticle(String articleId, ArticleInfo pointer)
787        throws IOException
788        {
789            if (articleId != null)
790            {
791                if (!NNTPReply.isPositiveCompletion(stat(articleId)))
792                    return false;
793            }
794            else
795            {
796                if (!NNTPReply.isPositiveCompletion(stat()))
797                    return false;
798            }
799    
800            if (pointer != null)
801                __parseArticlePointer(getReplyString(), pointer);
802    
803            return true;
804        }
805    
806        /**** Same as <code> selectArticle(articleId, (ArticleInfo) null) </code> ***/
807        public boolean selectArticle(String articleId) throws IOException
808        {
809            return selectArticle(articleId, (ArticleInfo) null);
810        }
811    
812        /****
813         * Same as <code> selectArticle((String) null, articleId) </code>.  Useful
814         * for retrieving the current article number.
815         ***/
816        public boolean selectArticle(ArticleInfo pointer) throws IOException
817        {
818            return selectArticle(null, pointer);
819        }
820    
821    
822        /***
823         * Select an article in the currently selected newsgroup by its number.
824         * and return its article number and id through the
825         * pointer parameter.  This is achieved through the STAT command.
826         * According to RFC 977, this WILL set the current article pointer
827         * on the server.  Use this command to select an article before retrieving
828         * it, or to obtain an article's unique identifier given its number.
829         * <p>
830         * @param articleNumber The number of the article to select from the
831         *       currently selected newsgroup.
832         * @param pointer    A parameter through which to return the article's
833         *   number and unique id.  Although the articleId field cannot always
834         *   be trusted because of server deviations from RFC 977 reply formats,
835         *   we haven't found a server that misformats this information in response
836         *   to this particular command.  You may set this parameter to null if
837         *   you do not desire to retrieve the returned article information.
838         * @return True if successful, false if not.
839         * @exception NNTPConnectionClosedException
840         *      If the NNTP server prematurely closes the connection as a result
841         *      of the client being idle or some other reason causing the server
842         *      to send NNTP reply code 400.  This exception may be caught either
843         *      as an IOException or independently as itself.
844         * @exception IOException  If an I/O error occurs while either sending a
845         *      command to the server or receiving a reply from the server.
846         ***/
847        public boolean selectArticle(long articleNumber, ArticleInfo pointer)
848        throws IOException
849        {
850            if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
851                return false;
852    
853            if (pointer != null)
854                __parseArticlePointer(getReplyString(), pointer);
855    
856            return true;
857        }
858    
859    
860        /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
861        public boolean selectArticle(long articleNumber) throws IOException
862        {
863            return selectArticle(articleNumber, null);
864        }
865    
866    
867        /***
868         * Select the article preceeding the currently selected article in the
869         * currently selected newsgroup and return its number and unique id
870         * through the pointer parameter.  Because of deviating server
871         * implementations, the articleId information cannot be trusted.  To
872         * obtain the article identifier, issue a
873         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
874         * afterward.
875         * <p>
876         * @param pointer    A parameter through which to return the article's
877         *   number and unique id.  The articleId field cannot always be trusted
878         *   because of server deviations from RFC 977 reply formats.  You may
879         *   set this parameter to null if you do not desire to retrieve the
880         *   returned article information.
881         * @return True if successful, false if not (e.g., there is no previous
882         *     article).
883         * @exception NNTPConnectionClosedException
884         *      If the NNTP server prematurely closes the connection as a result
885         *      of the client being idle or some other reason causing the server
886         *      to send NNTP reply code 400.  This exception may be caught either
887         *      as an IOException or independently as itself.
888         * @exception IOException  If an I/O error occurs while either sending a
889         *      command to the server or receiving a reply from the server.
890         ***/
891        public boolean selectPreviousArticle(ArticleInfo pointer)
892        throws IOException
893        {
894            if (!NNTPReply.isPositiveCompletion(last()))
895                return false;
896    
897            if (pointer != null)
898                __parseArticlePointer(getReplyString(), pointer);
899    
900            return true;
901        }
902    
903        /*** Same as <code> selectPreviousArticle((ArticleInfo) null) </code> ***/
904        public boolean selectPreviousArticle() throws IOException
905        {
906            return selectPreviousArticle((ArticleInfo) null);
907        }
908    
909    
910        /***
911         * Select the article following the currently selected article in the
912         * currently selected newsgroup and return its number and unique id
913         * through the pointer parameter.  Because of deviating server
914         * implementations, the articleId information cannot be trusted.  To
915         * obtain the article identifier, issue a
916         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
917         * afterward.
918         * <p>
919         * @param pointer    A parameter through which to return the article's
920         *   number and unique id.  The articleId field cannot always be trusted
921         *   because of server deviations from RFC 977 reply formats.  You may
922         *   set this parameter to null if you do not desire to retrieve the
923         *   returned article information.
924         * @return True if successful, false if not (e.g., there is no following
925         *         article).
926         * @exception NNTPConnectionClosedException
927         *      If the NNTP server prematurely closes the connection as a result
928         *      of the client being idle or some other reason causing the server
929         *      to send NNTP reply code 400.  This exception may be caught either
930         *      as an IOException or independently as itself.
931         * @exception IOException  If an I/O error occurs while either sending a
932         *      command to the server or receiving a reply from the server.
933         ***/
934        public boolean selectNextArticle(ArticleInfo pointer) throws IOException
935        {
936            if (!NNTPReply.isPositiveCompletion(next()))
937                return false;
938    
939            if (pointer != null)
940                __parseArticlePointer(getReplyString(), pointer);
941    
942            return true;
943        }
944    
945    
946        /*** Same as <code> selectNextArticle((ArticleInfo) null) </code> ***/
947        public boolean selectNextArticle() throws IOException
948        {
949            return selectNextArticle((ArticleInfo) null);
950        }
951    
952    
953        /***
954         * List all newsgroups served by the NNTP server.  If no newsgroups
955         * are served, a zero length array will be returned.  If the command
956         * fails, null will be returned.
957         * The method uses the "LIST" command.
958         * <p>
959         * @return An array of NewsgroupInfo instances containing the information
960         *    for each newsgroup served by the NNTP server.   If no newsgroups
961         *    are served, a zero length array will be returned.  If the command
962         *    fails, null will be returned.
963         * @exception NNTPConnectionClosedException
964         *      If the NNTP server prematurely closes the connection as a result
965         *      of the client being idle or some other reason causing the server
966         *      to send NNTP reply code 400.  This exception may be caught either
967         *      as an IOException or independently as itself.
968         * @exception IOException  If an I/O error occurs while either sending a
969         *      command to the server or receiving a reply from the server.
970         * @see #iterateNewsgroupListing()
971         * @see #iterateNewsgroups()
972         ***/
973        public NewsgroupInfo[] listNewsgroups() throws IOException
974        {
975            if (!NNTPReply.isPositiveCompletion(list()))
976                return null;
977    
978            return __readNewsgroupListing();
979        }
980    
981        /**
982         * List all newsgroups served by the NNTP server.  If no newsgroups
983         * are served, no entries will be returned.
984         * The method uses the "LIST" command.
985         * <p>
986         * @return An iterable of NewsgroupInfo instances containing the information
987         *    for each newsgroup served by the NNTP server.   If no newsgroups
988         *    are served, no entries will be returned.
989         * @exception NNTPConnectionClosedException
990         *      If the NNTP server prematurely closes the connection as a result
991         *      of the client being idle or some other reason causing the server
992         *      to send NNTP reply code 400.  This exception may be caught either
993         *      as an IOException or independently as itself.
994         * @exception IOException  If an I/O error occurs while either sending a
995         *      command to the server or receiving a reply from the server.
996         * @since 3.0
997         */
998        public Iterable<String> iterateNewsgroupListing() throws IOException {
999            if (NNTPReply.isPositiveCompletion(list())) {
1000                return new ReplyIterator(_reader_);
1001            }
1002            throw new IOException("LIST command failed: "+getReplyString());
1003        }
1004    
1005        /**
1006         * List all newsgroups served by the NNTP server.  If no newsgroups
1007         * are served, no entries will be returned.
1008         * The method uses the "LIST" command.
1009         * <p>
1010         * @return An iterable of Strings containing the raw information
1011         *    for each newsgroup served by the NNTP server.   If no newsgroups
1012         *    are served, no entries will be returned.
1013         * @exception NNTPConnectionClosedException
1014         *      If the NNTP server prematurely closes the connection as a result
1015         *      of the client being idle or some other reason causing the server
1016         *      to send NNTP reply code 400.  This exception may be caught either
1017         *      as an IOException or independently as itself.
1018         * @exception IOException  If an I/O error occurs while either sending a
1019         *      command to the server or receiving a reply from the server.
1020         * @since 3.0
1021         */
1022        public Iterable<NewsgroupInfo> iterateNewsgroups() throws IOException {
1023            return new NewsgroupIterator(iterateNewsgroupListing());
1024        }
1025    
1026        /**
1027         * List the newsgroups that match a given pattern.
1028         * Uses the "LIST ACTIVE" command.
1029         * <p>
1030         * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1031         * @return An array of NewsgroupInfo instances containing the information
1032         *    for each newsgroup served by the NNTP server corresponding to the
1033         *    supplied pattern.   If no such newsgroups are served, a zero length
1034         *    array will be returned.  If the command fails, null will be returned.
1035         * @throws IOException
1036         * @see #iterateNewsgroupListing(String)
1037         * @see #iterateNewsgroups(String)
1038         */
1039        public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
1040        {
1041            if(!NNTPReply.isPositiveCompletion(listActive(wildmat)))
1042                return null;
1043            return __readNewsgroupListing();
1044        }
1045    
1046    
1047        /**
1048         * List the newsgroups that match a given pattern.
1049         * Uses the "LIST ACTIVE" command.
1050         * <p>
1051         * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1052         * @return An iterable of Strings containing the raw information
1053         *    for each newsgroup served by the NNTP server corresponding to the
1054         *    supplied pattern.   If no such newsgroups are served, no entries
1055         *    will be returned.
1056         * @throws IOException
1057         * @since 3.0
1058         */
1059        public Iterable<String> iterateNewsgroupListing(String wildmat) throws IOException {
1060            if(NNTPReply.isPositiveCompletion(listActive(wildmat))) {
1061                return new ReplyIterator(_reader_);
1062            }
1063            throw new IOException("LIST ACTIVE "+wildmat+" command failed: "+getReplyString());
1064        }
1065    
1066        /**
1067         * List the newsgroups that match a given pattern.
1068         * Uses the "LIST ACTIVE" command.
1069         * <p>
1070         * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
1071         * @return An iterable NewsgroupInfo instances containing the information
1072         *    for each newsgroup served by the NNTP server corresponding to the
1073         *    supplied pattern.   If no such newsgroups are served, no entries
1074         *    will be returned.
1075         * @throws IOException
1076         * @since 3.0
1077         */
1078        public Iterable<NewsgroupInfo> iterateNewsgroups(String wildmat) throws IOException {
1079            return new NewsgroupIterator(iterateNewsgroupListing(wildmat));
1080        }
1081    
1082        /***
1083         * List all new newsgroups added to the NNTP server since a particular
1084         * date subject to the conditions of the specified query.  If no new
1085         * newsgroups were added, a zero length array will be returned.  If the
1086         * command fails, null will be returned.
1087         * This uses the "NEWGROUPS" command.
1088         * <p>
1089         * @param query  The query restricting how to search for new newsgroups.
1090         * @return An array of NewsgroupInfo instances containing the information
1091         *    for each new newsgroup added to the NNTP server.   If no newsgroups
1092         *    were added, a zero length array will be returned.  If the command
1093         *    fails, null will be returned.
1094         * @exception NNTPConnectionClosedException
1095         *      If the NNTP server prematurely closes the connection as a result
1096         *      of the client being idle or some other reason causing the server
1097         *      to send NNTP reply code 400.  This exception may be caught either
1098         *      as an IOException or independently as itself.
1099         * @exception IOException  If an I/O error occurs while either sending a
1100         *      command to the server or receiving a reply from the server.
1101         * @see #iterateNewNewsgroups(NewGroupsOrNewsQuery)
1102         * @see #iterateNewNewsgroupListing(NewGroupsOrNewsQuery)
1103         ***/
1104        public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
1105        throws IOException
1106        {
1107            if (!NNTPReply.isPositiveCompletion(newgroups(
1108                                                    query.getDate(), query.getTime(),
1109                                                    query.isGMT(), query.getDistributions())))
1110                return null;
1111    
1112            return __readNewsgroupListing();
1113        }
1114    
1115        /**
1116         * List all new newsgroups added to the NNTP server since a particular
1117         * date subject to the conditions of the specified query.  If no new
1118         * newsgroups were added, no entries will be returned.
1119         * This uses the "NEWGROUPS" command.
1120         * <p>
1121         * @param query  The query restricting how to search for new newsgroups.
1122         * @return An iterable of Strings containing the raw information
1123         *    for each new newsgroup added to the NNTP server.   If no newsgroups
1124         *    were added, no entries will be returned.
1125         * @exception NNTPConnectionClosedException
1126         *      If the NNTP server prematurely closes the connection as a result
1127         *      of the client being idle or some other reason causing the server
1128         *      to send NNTP reply code 400.  This exception may be caught either
1129         *      as an IOException or independently as itself.
1130         * @exception IOException  If an I/O error occurs while either sending a
1131         *      command to the server or receiving a reply from the server.
1132         * @since 3.0
1133         */
1134        public Iterable<String> iterateNewNewsgroupListing(NewGroupsOrNewsQuery query) throws IOException {
1135            if (NNTPReply.isPositiveCompletion(newgroups(
1136                    query.getDate(), query.getTime(),
1137                    query.isGMT(), query.getDistributions()))) {
1138                return new ReplyIterator(_reader_);
1139            }
1140            throw new IOException("NEWGROUPS command failed: "+getReplyString());
1141        }
1142    
1143        /**
1144         * List all new newsgroups added to the NNTP server since a particular
1145         * date subject to the conditions of the specified query.  If no new
1146         * newsgroups were added, no entries will be returned.
1147         * This uses the "NEWGROUPS" command.
1148         * <p>
1149         * @param query  The query restricting how to search for new newsgroups.
1150         * @return An iterable of NewsgroupInfo instances containing the information
1151         *    for each new newsgroup added to the NNTP server.   If no newsgroups
1152         *    were added, no entries will be returned.
1153         * @exception NNTPConnectionClosedException
1154         *      If the NNTP server prematurely closes the connection as a result
1155         *      of the client being idle or some other reason causing the server
1156         *      to send NNTP reply code 400.  This exception may be caught either
1157         *      as an IOException or independently as itself.
1158         * @exception IOException  If an I/O error occurs while either sending a
1159         *      command to the server or receiving a reply from the server.
1160         * @since 3.0
1161         */
1162        public Iterable<NewsgroupInfo> iterateNewNewsgroups(NewGroupsOrNewsQuery query) throws IOException {
1163            return new NewsgroupIterator(iterateNewNewsgroupListing(query));
1164        }
1165    
1166        /***
1167         * List all new articles added to the NNTP server since a particular
1168         * date subject to the conditions of the specified query.  If no new
1169         * new news is found, a zero length array will be returned.  If the
1170         * command fails, null will be returned.  You must add at least one
1171         * newsgroup to the query, else the command will fail.  Each String
1172         * in the returned array is a unique message identifier including the
1173         * enclosing &lt and &gt.
1174         * This uses the "NEWNEWS" command.
1175         * <p>
1176         * @param query  The query restricting how to search for new news.  You
1177         *    must add at least one newsgroup to the query.
1178         * @return An array of String instances containing the unique message
1179         *    identifiers for each new article added to the NNTP server.  If no
1180         *    new news is found, a zero length array will be returned.  If the
1181         *    command fails, null will be returned.
1182         * @exception NNTPConnectionClosedException
1183         *      If the NNTP server prematurely closes the connection as a result
1184         *      of the client being idle or some other reason causing the server
1185         *      to send NNTP reply code 400.  This exception may be caught either
1186         *      as an IOException or independently as itself.
1187         * @exception IOException  If an I/O error occurs while either sending a
1188         *      command to the server or receiving a reply from the server.
1189         *
1190         * @see #iterateNewNews(NewGroupsOrNewsQuery)
1191         ***/
1192        public String[] listNewNews(NewGroupsOrNewsQuery query)
1193        throws IOException
1194        {
1195            if (!NNTPReply.isPositiveCompletion(
1196                    newnews(query.getNewsgroups(), query.getDate(), query.getTime(),
1197                            query.isGMT(), query.getDistributions()))) {
1198                return null;
1199            }
1200    
1201            Vector<String> list = new Vector<String>();
1202            BufferedReader reader = new DotTerminatedMessageReader(_reader_);
1203    
1204            String line;
1205            while ((line = reader.readLine()) != null) {
1206                list.addElement(line);
1207            }
1208    
1209            int size = list.size();
1210            if (size < 1) {
1211                return new String[0];
1212            }
1213    
1214            String[] result = new String[size];
1215            list.copyInto(result);
1216    
1217            return result;
1218        }
1219    
1220        /**
1221         * List all new articles added to the NNTP server since a particular
1222         * date subject to the conditions of the specified query.  If no new
1223         * new news is found, no entries will be returned.
1224         * This uses the "NEWNEWS" command.
1225         * You must add at least one newsgroup to the query, else the command will fail.
1226         * Each String which is returned is a unique message identifier including the
1227         * enclosing &lt and &gt.
1228         * <p>
1229         * @param query  The query restricting how to search for new news.  You
1230         *    must add at least one newsgroup to the query.
1231         * @return An iterator of String instances containing the unique message
1232         *    identifiers for each new article added to the NNTP server.  If no
1233         *    new news is found, no strings will be returned.
1234         * @exception NNTPConnectionClosedException
1235         *      If the NNTP server prematurely closes the connection as a result
1236         *      of the client being idle or some other reason causing the server
1237         *      to send NNTP reply code 400.  This exception may be caught either
1238         *      as an IOException or independently as itself.
1239         * @exception IOException  If an I/O error occurs while either sending a
1240         *      command to the server or receiving a reply from the server.
1241         * @since 3.0
1242         */
1243        public Iterable<String> iterateNewNews(NewGroupsOrNewsQuery query) throws IOException {
1244            if (NNTPReply.isPositiveCompletion(newnews(
1245                    query.getNewsgroups(), query.getDate(), query.getTime(),
1246                    query.isGMT(), query.getDistributions()))) {
1247                return new ReplyIterator(_reader_);
1248            }
1249            throw new IOException("NEWNEWS command failed: "+getReplyString());
1250        }
1251    
1252        /***
1253         * There are a few NNTPClient methods that do not complete the
1254         * entire sequence of NNTP commands to complete a transaction.  These
1255         * commands require some action by the programmer after the reception
1256         * of a positive preliminary command.  After the programmer's code
1257         * completes its actions, it must call this method to receive
1258         * the completion reply from the server and verify the success of the
1259         * entire transaction.
1260         * <p>
1261         * For example
1262         * <pre>
1263         * writer = client.postArticle();
1264         * if(writer == null) // failure
1265         *   return false;
1266         * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
1267         * header.addNewsgroup("alt.test");
1268         * writer.write(header.toString());
1269         * writer.write("This is just a test");
1270         * writer.close();
1271         * if(!client.completePendingCommand()) // failure
1272         *   return false;
1273         * </pre>
1274         * <p>
1275         * @return True if successfully completed, false if not.
1276         * @exception NNTPConnectionClosedException
1277         *      If the NNTP server prematurely closes the connection as a result
1278         *      of the client being idle or some other reason causing the server
1279         *      to send NNTP reply code 400.  This exception may be caught either
1280         *      as an IOException or independently as itself.
1281         * @exception IOException  If an I/O error occurs while either sending a
1282         *      command to the server or receiving a reply from the server.
1283         ***/
1284        public boolean completePendingCommand() throws IOException
1285        {
1286            return NNTPReply.isPositiveCompletion(getReply());
1287        }
1288    
1289        /***
1290         * Post an article to the NNTP server.  This method returns a
1291         * DotTerminatedMessageWriter instance to which the article can be
1292         * written.  Null is returned if the posting attempt fails.  You
1293         * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
1294         *  before trying to post.  However, a posting
1295         * attempt can fail due to malformed headers.
1296         * <p>
1297         * You must not issue any commands to the NNTP server (i.e., call any
1298         * (other methods) until you finish writing to the returned Writer
1299         * instance and close it.  The NNTP protocol uses the same stream for
1300         * issuing commands as it does for returning results.  Therefore the
1301         * returned Writer actually writes directly to the NNTP connection.
1302         * After you close the writer, you can execute new commands.  If you
1303         * do not follow these requirements your program will not work properly.
1304         * <p>
1305         * Different NNTP servers will require different header formats, but
1306         * you can use the provided
1307         * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
1308         * class to construct the bare minimum acceptable header for most
1309         * news readers.  To construct more complicated headers you should
1310         * refer to RFC 822.  When the Java Mail API is finalized, you will be
1311         * able to use it to compose fully compliant Internet text messages.
1312         * The DotTerminatedMessageWriter takes care of doubling line-leading
1313         * dots and ending the message with a single dot upon closing, so all
1314         * you have to worry about is writing the header and the message.
1315         * <p>
1316         * Upon closing the returned Writer, you need to call
1317         * {@link #completePendingCommand  completePendingCommand() }
1318         * to finalize the posting and verify its success or failure from
1319         * the server reply.
1320         * <p>
1321         * @return A DotTerminatedMessageWriter to which the article (including
1322         *      header) can be written.  Returns null if the command fails.
1323         * @exception IOException  If an I/O error occurs while either sending a
1324         *      command to the server or receiving a reply from the server.
1325         ***/
1326    
1327        public Writer postArticle() throws IOException
1328        {
1329            if (!NNTPReply.isPositiveIntermediate(post()))
1330                return null;
1331    
1332            return new DotTerminatedMessageWriter(_writer_);
1333        }
1334    
1335    
1336        public Writer forwardArticle(String articleId) throws IOException
1337        {
1338            if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
1339                return null;
1340    
1341            return new DotTerminatedMessageWriter(_writer_);
1342        }
1343    
1344    
1345        /***
1346         * Logs out of the news server gracefully by sending the QUIT command.
1347         * However, you must still disconnect from the server before you can open
1348         * a new connection.
1349         * <p>
1350         * @return True if successfully completed, false if not.
1351         * @exception IOException  If an I/O error occurs while either sending a
1352         *      command to the server or receiving a reply from the server.
1353         ***/
1354        public boolean logout() throws IOException
1355        {
1356            return NNTPReply.isPositiveCompletion(quit());
1357        }
1358    
1359    
1360        /**
1361         * Log into a news server by sending the AUTHINFO USER/AUTHINFO
1362         * PASS command sequence. This is usually sent in response to a
1363         * 480 reply code from the NNTP server.
1364         * <p>
1365         * @param username a valid username
1366         * @param password the corresponding password
1367         * @return True for successful login, false for a failure
1368         * @throws IOException
1369         */
1370        public boolean authenticate(String username, String password)
1371            throws IOException
1372        {
1373            int replyCode = authinfoUser(username);
1374    
1375            if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
1376                {
1377                    replyCode = authinfoPass(password);
1378    
1379                    if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
1380                        {
1381                            _isAllowedToPost = true;
1382                            return true;
1383                        }
1384                }
1385            return false;
1386        }
1387    
1388        /***
1389         * Private implementation of XOVER functionality.
1390         *
1391         * See {@link NNTP#xover}
1392         * for legal agument formats. Alternatively, read RFC 2980 :-)
1393         * <p>
1394         * @param articleRange
1395         * @return Returns a DotTerminatedMessageReader if successful, null
1396         *         otherwise
1397         * @exception IOException
1398         */
1399        private BufferedReader __retrieveArticleInfo(String articleRange)
1400            throws IOException
1401        {
1402            if (!NNTPReply.isPositiveCompletion(xover(articleRange))) {
1403                return null;
1404            }
1405    
1406            return new DotTerminatedMessageReader(_reader_);
1407        }
1408    
1409        /**
1410         * Return article headers for a specified post.
1411         * <p>
1412         * @param articleNumber the article to retrieve headers for
1413         * @return a DotTerminatedReader if successful, null otherwise
1414         * @throws IOException
1415         */
1416        public BufferedReader retrieveArticleInfo(long articleNumber) throws IOException
1417        {
1418            return __retrieveArticleInfo(Long.toString(articleNumber));
1419        }
1420    
1421        /**
1422         * Return article headers for all articles between lowArticleNumber
1423         * and highArticleNumber, inclusively. Uses the XOVER command.
1424         * <p>
1425         * @param lowArticleNumber
1426         * @param highArticleNumber
1427         * @return a DotTerminatedReader if successful, null otherwise
1428         * @throws IOException
1429         */
1430        public BufferedReader retrieveArticleInfo(long lowArticleNumber,
1431                long highArticleNumber)
1432            throws IOException
1433        {
1434            return
1435                __retrieveArticleInfo(lowArticleNumber + "-" +
1436                                                 highArticleNumber);
1437        }
1438    
1439        /**
1440         * Return article headers for all articles between lowArticleNumber
1441         * and highArticleNumber, inclusively, using the XOVER command.
1442         * <p>
1443         * @param lowArticleNumber
1444         * @param highArticleNumber
1445         * @return an Iterable of Articles
1446         * @throws IOException if the command failed
1447         * @since 3.0
1448         */
1449        public Iterable<Article> iterateArticleInfo(long lowArticleNumber, long highArticleNumber)
1450            throws IOException
1451        {
1452            BufferedReader info = retrieveArticleInfo(lowArticleNumber,highArticleNumber);
1453            if (info == null) {
1454                throw new IOException("XOVER command failed: "+getReplyString());
1455            }
1456            // N.B. info is already DotTerminated, so don't rewrap
1457            return new ArticleIterator(new ReplyIterator(info, false));
1458        }
1459    
1460        /***
1461         * Private implementation of XHDR functionality.
1462         *
1463         * See {@link NNTP#xhdr}
1464         * for legal agument formats. Alternatively, read RFC 1036.
1465         * <p>
1466         * @param header
1467         * @param articleRange
1468         * @return Returns a DotTerminatedMessageReader if successful, null
1469         *         otherwise
1470         * @exception IOException
1471         */
1472        private BufferedReader __retrieveHeader(String header, String articleRange)
1473            throws IOException
1474        {
1475            if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange))) {
1476                return null;
1477            }
1478    
1479            return new DotTerminatedMessageReader(_reader_);
1480        }
1481    
1482        /**
1483         * Return an article header for a specified post.
1484         * <p>
1485         * @param header the header to retrieve
1486         * @param articleNumber the article to retrieve the header for
1487         * @return a DotTerminatedReader if successful, null otherwise
1488         * @throws IOException
1489         */
1490        public BufferedReader retrieveHeader(String header, long articleNumber)
1491            throws IOException
1492        {
1493            return __retrieveHeader(header, Long.toString(articleNumber));
1494        }
1495    
1496        /**
1497         * Return an article header for all articles between lowArticleNumber
1498         * and highArticleNumber, inclusively.
1499         * <p>
1500         * @param header
1501         * @param lowArticleNumber
1502         * @param highArticleNumber
1503         * @return a DotTerminatedReader if successful, null otherwise
1504         * @throws IOException
1505         */
1506        public BufferedReader retrieveHeader(String header, long lowArticleNumber,
1507                                     long highArticleNumber)
1508            throws IOException
1509        {
1510            return
1511                __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
1512        }
1513    
1514    
1515    
1516    
1517    
1518        // DEPRECATED METHODS - for API compatibility only - DO NOT USE
1519        // ============================================================
1520    
1521    
1522    
1523        /**
1524         * @deprecated 3.0 use {@link #retrieveHeader(String, long, long)} instead
1525         */
1526        @Deprecated
1527        public Reader retrieveHeader(String s, int l, int h)
1528            throws IOException
1529        {
1530            return retrieveHeader(s, (long) l, (long) h);
1531        }
1532    
1533        /**
1534         * @deprecated 3.0 use {@link #retrieveArticleInfo(long, long)} instead
1535         */
1536        @Deprecated
1537        public Reader retrieveArticleInfo(int a, int b) throws IOException {
1538            return retrieveArticleInfo((long) a, (long) b);
1539        }
1540    
1541        /**
1542         * @deprecated 3.0 use {@link #retrieveHeader(String, long)} instead
1543         */
1544        @Deprecated
1545        public Reader retrieveHeader(String a, int b) throws IOException {
1546            return retrieveHeader(a, (long) b);
1547        }
1548    
1549        /**
1550         * @deprecated 3.0 use {@link #selectArticle(long, ArticleInfo)} instead
1551         */
1552        @Deprecated
1553        public boolean selectArticle(int a, ArticlePointer ap) throws IOException {
1554            ArticleInfo ai =  __ap2ai(ap);
1555            boolean b = selectArticle(a, ai);
1556            __ai2ap(ai, ap);
1557            return b;
1558        }
1559    
1560        /**
1561         * @deprecated 3.0 use {@link #retrieveArticleInfo(long)} instead
1562         */
1563        @Deprecated
1564        public Reader retrieveArticleInfo(int a) throws IOException {
1565            return retrieveArticleInfo((long) a);
1566        }
1567    
1568        /**
1569         * @deprecated 3.0 use {@link #selectArticle(long)} instead
1570         */
1571        @Deprecated
1572        public boolean selectArticle(int a) throws IOException {
1573            return selectArticle((long) a);
1574        }
1575    
1576        /**
1577         * @deprecated 3.0 use {@link #retrieveArticleHeader(long)} instead
1578         */
1579        @Deprecated
1580        public Reader retrieveArticleHeader(int a) throws IOException {
1581            return retrieveArticleHeader((long) a);
1582        }
1583    
1584        /**
1585         * @deprecated 3.0 use {@link #retrieveArticleHeader(long, ArticleInfo)} instead
1586         */
1587        @Deprecated
1588        public Reader retrieveArticleHeader(int a, ArticlePointer ap) throws IOException {
1589            ArticleInfo ai =  __ap2ai(ap);
1590            Reader rdr = retrieveArticleHeader(a, ai);
1591            __ai2ap(ai, ap);
1592            return rdr;
1593        }
1594    
1595        /**
1596         * @deprecated 3.0 use {@link #retrieveArticleBody(long)} instead
1597         */
1598        @Deprecated
1599        public Reader retrieveArticleBody(int a) throws IOException {
1600            return retrieveArticleBody((long) a);
1601        }
1602    
1603        /**
1604         * @deprecated 3.0 use {@link #retrieveArticle(long, ArticleInfo)} instead
1605         */
1606        @Deprecated
1607        public Reader retrieveArticle(int a, ArticlePointer ap) throws IOException {
1608            ArticleInfo ai =  __ap2ai(ap);
1609            Reader rdr = retrieveArticle(a, ai);
1610            __ai2ap(ai, ap);
1611            return rdr;
1612        }
1613    
1614        /**
1615         * @deprecated 3.0 use {@link #retrieveArticle(long)} instead
1616         */
1617        @Deprecated
1618        public Reader retrieveArticle(int a) throws IOException {
1619            return retrieveArticle((long) a);
1620        }
1621    
1622        /**
1623         * @deprecated 3.0 use {@link #retrieveArticleBody(long, ArticleInfo)} instead
1624         */
1625        @Deprecated
1626        public Reader retrieveArticleBody(int a, ArticlePointer ap) throws IOException {
1627            ArticleInfo ai =  __ap2ai(ap);
1628            Reader rdr = retrieveArticleBody(a, ai);
1629            __ai2ap(ai, ap);
1630            return rdr;
1631        }
1632    
1633        /**
1634         * @deprecated 3.0 use {@link #retrieveArticle(String, ArticleInfo)} instead
1635         */
1636        @Deprecated
1637        public Reader retrieveArticle(String a, ArticlePointer ap) throws IOException {
1638            ArticleInfo ai =  __ap2ai(ap);
1639            Reader rdr = retrieveArticle(a, ai);
1640            __ai2ap(ai, ap);
1641            return rdr;
1642        }
1643    
1644        /**
1645         * @deprecated 3.0 use {@link #retrieveArticleBody(String, ArticleInfo)} instead
1646         */
1647        @Deprecated
1648        public Reader retrieveArticleBody(String a, ArticlePointer ap) throws IOException {
1649            ArticleInfo ai =  __ap2ai(ap);
1650            Reader rdr = retrieveArticleBody(a, ai);
1651            __ai2ap(ai, ap);
1652            return rdr;
1653        }
1654    
1655        /**
1656         * @deprecated 3.0 use {@link #retrieveArticleHeader(String, ArticleInfo)} instead
1657         */
1658        @Deprecated
1659        public Reader retrieveArticleHeader(String a, ArticlePointer ap) throws IOException {
1660            ArticleInfo ai =  __ap2ai(ap);
1661            Reader rdr = retrieveArticleHeader(a, ai);
1662            __ai2ap(ai, ap);
1663            return rdr;
1664        }
1665    
1666        /**
1667         * @deprecated 3.0 use {@link #selectArticle(String, ArticleInfo)} instead
1668         */
1669        @Deprecated
1670        public boolean selectArticle(String a, ArticlePointer ap) throws IOException {
1671            ArticleInfo ai =  __ap2ai(ap);
1672            boolean b = selectArticle(a, ai);
1673            __ai2ap(ai, ap);
1674            return b;
1675    
1676        }
1677    
1678        /**
1679         * @deprecated 3.0 use {@link #selectArticle(ArticleInfo)} instead
1680         */
1681        @Deprecated
1682        public boolean selectArticle(ArticlePointer ap) throws IOException {
1683            ArticleInfo ai =  __ap2ai(ap);
1684            boolean b = selectArticle(ai);
1685            __ai2ap(ai, ap);
1686            return b;
1687    
1688        }
1689    
1690        /**
1691         * @deprecated 3.0 use {@link #selectNextArticle(ArticleInfo)} instead
1692         */
1693        @Deprecated
1694        public boolean selectNextArticle(ArticlePointer ap) throws IOException {
1695            ArticleInfo ai =  __ap2ai(ap);
1696            boolean b = selectNextArticle(ai);
1697            __ai2ap(ai, ap);
1698            return b;
1699    
1700        }
1701    
1702        /**
1703         * @deprecated 3.0 use {@link #selectPreviousArticle(ArticleInfo)} instead
1704         */
1705        @Deprecated
1706        public boolean selectPreviousArticle(ArticlePointer ap) throws IOException {
1707            ArticleInfo ai =  __ap2ai(ap);
1708            boolean b = selectPreviousArticle(ai);
1709            __ai2ap(ai, ap);
1710            return b;
1711        }
1712    
1713       // Helper methods
1714    
1715        private ArticleInfo __ap2ai(@SuppressWarnings("deprecation") ArticlePointer ap) {
1716            if (ap == null) return null;
1717            ArticleInfo ai = new ArticleInfo();
1718            return ai;
1719        }
1720    
1721        @SuppressWarnings("deprecation")
1722        private void __ai2ap(ArticleInfo ai, ArticlePointer ap){
1723            if (ap != null) { // ai cannot be null
1724                ap.articleId = ai.articleId;
1725                ap.articleNumber = (int) ai.articleNumber;
1726            }
1727        }
1728    }
1729    
1730    
1731    /* Emacs configuration
1732     * Local variables:        **
1733     * mode:             java  **
1734     * c-basic-offset:   4     **
1735     * indent-tabs-mode: nil   **
1736     * End:                    **
1737     */