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