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