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.StringTokenizer;
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 Daniel F. Savarese
083     * @author Rory Winston
084     * @author Ted Wise
085     * @see NNTP
086     * @see NNTPConnectionClosedException
087     * @see org.apache.commons.net.MalformedServerReplyException
088     ***/
089    
090    public class NNTPClient extends NNTP
091    {
092    
093        private void __parseArticlePointer(String reply, ArticlePointer pointer)
094        throws MalformedServerReplyException
095        {
096            StringTokenizer tokenizer;
097    
098            // Do loop is a kluge to simulate goto
099            do
100            {
101                tokenizer = new StringTokenizer(reply);
102    
103                if (tokenizer.countTokens() < 3)
104                    break;
105    
106                // Skip numeric response value
107                tokenizer.nextToken();
108                // Get article number
109                try
110                {
111                    pointer.articleNumber = Integer.parseInt(tokenizer.nextToken());
112                }
113                catch (NumberFormatException e)
114                {
115                    break;
116                }
117    
118                // Get article id
119                pointer.articleId = tokenizer.nextToken();
120                return ;
121            }
122            while (false);
123    
124            throw new MalformedServerReplyException(
125                "Could not parse article pointer.\nServer reply: " + reply);
126        }
127    
128    
129        private void __parseGroupReply(String reply, NewsgroupInfo info)
130        throws MalformedServerReplyException
131        {
132            String count, first, last;
133            StringTokenizer tokenizer;
134    
135            // Do loop is a kluge to simulate goto
136            do
137            {
138                tokenizer = new StringTokenizer(reply);
139    
140                if (tokenizer.countTokens() < 5)
141                    break;
142    
143                // Skip numeric response value
144                tokenizer.nextToken();
145                // Get estimated article count
146                count = tokenizer.nextToken();
147                // Get first article number
148                first = tokenizer.nextToken();
149                // Get last article number
150                last = tokenizer.nextToken();
151                // Get newsgroup name
152                info._setNewsgroup(tokenizer.nextToken());
153    
154                try
155                {
156                    info._setArticleCount(Integer.parseInt(count));
157                    info._setFirstArticle(Integer.parseInt(first));
158                    info._setLastArticle(Integer.parseInt(last));
159                }
160                catch (NumberFormatException e)
161                {
162                    break;
163                }
164    
165                info._setPostingPermission(NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
166                return ;
167            }
168            while (false);
169    
170            throw new MalformedServerReplyException(
171                "Could not parse newsgroup info.\nServer reply: " + reply);
172        }
173    
174    
175        private NewsgroupInfo __parseNewsgroupListEntry(String entry)
176        {
177            NewsgroupInfo result;
178            StringTokenizer tokenizer;
179            int lastNum, firstNum;
180            String last, first, permission;
181    
182            result = new NewsgroupInfo();
183            tokenizer = new StringTokenizer(entry);
184    
185            if (tokenizer.countTokens() < 4)
186                return null;
187    
188            result._setNewsgroup(tokenizer.nextToken());
189            last = tokenizer.nextToken();
190            first = tokenizer.nextToken();
191            permission = tokenizer.nextToken();
192    
193            try
194            {
195                lastNum = Integer.parseInt(last);
196                firstNum = Integer.parseInt(first);
197                result._setFirstArticle(firstNum);
198                result._setLastArticle(lastNum);
199    
200            if((firstNum == 0) && (lastNum == 0))
201                result._setArticleCount(0);
202            else
203                result._setArticleCount(lastNum - firstNum + 1);
204            }
205            catch (NumberFormatException e)
206            {
207                return null;
208            }
209    
210            switch (permission.charAt(0))
211            {
212            case 'y':
213            case 'Y':
214                result._setPostingPermission(
215                    NewsgroupInfo.PERMITTED_POSTING_PERMISSION);
216                break;
217            case 'n':
218            case 'N':
219                result._setPostingPermission(
220                    NewsgroupInfo.PROHIBITED_POSTING_PERMISSION);
221                break;
222            case 'm':
223            case 'M':
224                result._setPostingPermission(
225                    NewsgroupInfo.MODERATED_POSTING_PERMISSION);
226                break;
227            default:
228                result._setPostingPermission(
229                    NewsgroupInfo.UNKNOWN_POSTING_PERMISSION);
230                break;
231            }
232    
233            return result;
234        }
235    
236        private NewsgroupInfo[] __readNewsgroupListing() throws IOException
237        {
238            int size;
239            String line;
240            Vector<NewsgroupInfo> list;
241            BufferedReader reader;
242            NewsgroupInfo tmp, info[];
243    
244            reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
245            // Start of with a big vector because we may be reading a very large
246            // amount of groups.
247            list = new Vector<NewsgroupInfo>(2048);
248    
249            while ((line = reader.readLine()) != null)
250            {
251                tmp = __parseNewsgroupListEntry(line);
252                if (tmp != null)
253                    list.addElement(tmp);
254                else
255                    throw new MalformedServerReplyException(line);
256            }
257    
258            if ((size = list.size()) < 1)
259                return new NewsgroupInfo[0];
260    
261            info = new NewsgroupInfo[size];
262            list.copyInto(info);
263    
264            return info;
265        }
266    
267    
268        private Reader __retrieve(int command,
269                                  String articleId, ArticlePointer pointer)
270        throws IOException
271        {
272            Reader reader;
273    
274            if (articleId != null)
275            {
276                if (!NNTPReply.isPositiveCompletion(sendCommand(command, articleId)))
277                    return null;
278            }
279            else
280            {
281                if (!NNTPReply.isPositiveCompletion(sendCommand(command)))
282                    return null;
283            }
284    
285    
286            if (pointer != null)
287                __parseArticlePointer(getReplyString(), pointer);
288    
289            reader = new DotTerminatedMessageReader(_reader_);
290            return reader;
291        }
292    
293    
294        private Reader __retrieve(int command,
295                                  int articleNumber, ArticlePointer pointer)
296        throws IOException
297        {
298            Reader reader;
299    
300            if (!NNTPReply.isPositiveCompletion(sendCommand(command,
301                                                Integer.toString(articleNumber))))
302                return null;
303    
304            if (pointer != null)
305                __parseArticlePointer(getReplyString(), pointer);
306    
307            reader = new DotTerminatedMessageReader(_reader_);
308            return reader;
309        }
310    
311    
312    
313        /***
314         * Retrieves an article from the NNTP server.  The article is referenced
315         * by its unique article identifier (including the enclosing &lt and &gt).
316         * The article number and identifier contained in the server reply
317         * are returned through an ArticlePointer.  The <code> articleId </code>
318         * field of the ArticlePointer cannot always be trusted because some
319         * NNTP servers do not correctly follow the RFC 977 reply format.
320         * <p>
321         * A DotTerminatedMessageReader is returned from which the article can
322         * be read.  If the article does not exist, null is returned.
323         * <p>
324         * You must not issue any commands to the NNTP server (i.e., call any
325         * other methods) until you finish reading the message from the returned
326         * Reader instance.
327         * The NNTP protocol uses the same stream for issuing commands as it does
328         * for returning results.  Therefore the returned Reader actually reads
329         * directly from the NNTP connection.  After the end of message has been
330         * reached, new commands can be executed and their replies read.  If
331         * you do not follow these requirements, your program will not work
332         * properly.
333         * <p>
334         * @param articleId  The unique article identifier of the article to
335         *     retrieve.  If this parameter is null, the currently selected
336         *     article is retrieved.
337         * @param pointer    A parameter through which to return the article's
338         *   number and unique id.  The articleId field cannot always be trusted
339         *   because of server deviations from RFC 977 reply formats.  You may
340         *   set this parameter to null if you do not desire to retrieve the
341         *   returned article information.
342         * @return A DotTerminatedMessageReader instance from which the article
343         *         be read.  null if the article does not exist.
344         * @exception NNTPConnectionClosedException
345         *      If the NNTP server prematurely closes the connection as a result
346         *      of the client being idle or some other reason causing the server
347         *      to send NNTP reply code 400.  This exception may be caught either
348         *      as an IOException or independently as itself.
349         * @exception IOException  If an I/O error occurs while either sending a
350         *      command to the server or receiving a reply from the server.
351         ***/
352        public Reader retrieveArticle(String articleId, ArticlePointer pointer)
353        throws IOException
354        {
355            return __retrieve(NNTPCommand.ARTICLE, articleId, pointer);
356    
357        }
358    
359        /*** Same as <code> retrieveArticle(articleId, null) </code> ***/
360        public Reader retrieveArticle(String articleId) throws IOException
361        {
362            return retrieveArticle(articleId, null);
363        }
364    
365        /*** Same as <code> retrieveArticle(null) </code> ***/
366        public Reader retrieveArticle() throws IOException
367        {
368            return retrieveArticle(null);
369        }
370    
371    
372        /***
373         * Retrieves an article from the currently selected newsgroup.  The
374         * article is referenced by its article number.
375         * The article number and identifier contained in the server reply
376         * are returned through an ArticlePointer.  The <code> articleId </code>
377         * field of the ArticlePointer cannot always be trusted because some
378         * NNTP servers do not correctly follow the RFC 977 reply format.
379         * <p>
380         * A DotTerminatedMessageReader is returned from which the article can
381         * be read.  If the article does not exist, null is returned.
382         * <p>
383         * You must not issue any commands to the NNTP server (i.e., call any
384         * other methods) until you finish reading the message from the returned
385         * Reader instance.
386         * The NNTP protocol uses the same stream for issuing commands as it does
387         * for returning results.  Therefore the returned Reader actually reads
388         * directly from the NNTP connection.  After the end of message has been
389         * reached, new commands can be executed and their replies read.  If
390         * you do not follow these requirements, your program will not work
391         * properly.
392         * <p>
393         * @param articleNumber  The number of the the article to
394         *     retrieve.
395         * @param pointer    A parameter through which to return the article's
396         *   number and unique id.  The articleId field cannot always be trusted
397         *   because of server deviations from RFC 977 reply formats.  You may
398         *   set this parameter to null if you do not desire to retrieve the
399         *   returned article information.
400         * @return A DotTerminatedMessageReader instance from which the article
401         *         be read.  null if the article does not exist.
402         * @exception NNTPConnectionClosedException
403         *      If the NNTP server prematurely closes the connection as a result
404         *      of the client being idle or some other reason causing the server
405         *      to send NNTP reply code 400.  This exception may be caught either
406         *      as an IOException or independently as itself.
407         * @exception IOException  If an I/O error occurs while either sending a
408         *      command to the server or receiving a reply from the server.
409         ***/
410        public Reader retrieveArticle(int articleNumber, ArticlePointer pointer)
411        throws IOException
412        {
413            return __retrieve(NNTPCommand.ARTICLE, articleNumber, pointer);
414        }
415    
416        /*** Same as <code> retrieveArticle(articleNumber, null) </code> ***/
417        public Reader retrieveArticle(int articleNumber) throws IOException
418        {
419            return retrieveArticle(articleNumber, null);
420        }
421    
422    
423    
424        /***
425         * Retrieves an article header from the NNTP server.  The article is
426         * referenced
427         * by its unique article identifier (including the enclosing &lt and &gt).
428         * The article number and identifier contained in the server reply
429         * are returned through an ArticlePointer.  The <code> articleId </code>
430         * field of the ArticlePointer cannot always be trusted because some
431         * NNTP servers do not correctly follow the RFC 977 reply format.
432         * <p>
433         * A DotTerminatedMessageReader is returned from which the article can
434         * be read.  If the article does not exist, null is returned.
435         * <p>
436         * You must not issue any commands to the NNTP server (i.e., call any
437         * other methods) until you finish reading the message from the returned
438         * Reader instance.
439         * The NNTP protocol uses the same stream for issuing commands as it does
440         * for returning results.  Therefore the returned Reader actually reads
441         * directly from the NNTP connection.  After the end of message has been
442         * reached, new commands can be executed and their replies read.  If
443         * you do not follow these requirements, your program will not work
444         * properly.
445         * <p>
446         * @param articleId  The unique article identifier of the article whose
447         *    header is being retrieved.  If this parameter is null, the
448         *    header of the currently selected article is retrieved.
449         * @param pointer    A parameter through which to return the article's
450         *   number and unique id.  The articleId field cannot always be trusted
451         *   because of server deviations from RFC 977 reply formats.  You may
452         *   set this parameter to null if you do not desire to retrieve the
453         *   returned article information.
454         * @return A DotTerminatedMessageReader instance from which the article
455         *         header can be read.  null if the article does not exist.
456         * @exception NNTPConnectionClosedException
457         *      If the NNTP server prematurely closes the connection as a result
458         *      of the client being idle or some other reason causing the server
459         *      to send NNTP reply code 400.  This exception may be caught either
460         *      as an IOException or independently as itself.
461         * @exception IOException  If an I/O error occurs while either sending a
462         *      command to the server or receiving a reply from the server.
463         ***/
464        public Reader retrieveArticleHeader(String articleId, ArticlePointer pointer)
465        throws IOException
466        {
467            return __retrieve(NNTPCommand.HEAD, articleId, pointer);
468    
469        }
470    
471        /*** Same as <code> retrieveArticleHeader(articleId, null) </code> ***/
472        public Reader retrieveArticleHeader(String articleId) throws IOException
473        {
474            return retrieveArticleHeader(articleId, null);
475        }
476    
477        /*** Same as <code> retrieveArticleHeader(null) </code> ***/
478        public Reader retrieveArticleHeader() throws IOException
479        {
480            return retrieveArticleHeader(null);
481        }
482    
483    
484        /***
485         * Retrieves an article header from the currently selected newsgroup.  The
486         * article is referenced by its article number.
487         * The article number and identifier contained in the server reply
488         * are returned through an ArticlePointer.  The <code> articleId </code>
489         * field of the ArticlePointer cannot always be trusted because some
490         * NNTP servers do not correctly follow the RFC 977 reply format.
491         * <p>
492         * A DotTerminatedMessageReader is returned from which the article can
493         * be read.  If the article does not exist, null is returned.
494         * <p>
495         * You must not issue any commands to the NNTP server (i.e., call any
496         * other methods) until you finish reading the message from the returned
497         * Reader instance.
498         * The NNTP protocol uses the same stream for issuing commands as it does
499         * for returning results.  Therefore the returned Reader actually reads
500         * directly from the NNTP connection.  After the end of message has been
501         * reached, new commands can be executed and their replies read.  If
502         * you do not follow these requirements, your program will not work
503         * properly.
504         * <p>
505         * @param articleNumber  The number of the the article whose header is
506         *     being retrieved.
507         * @param pointer    A parameter through which to return the article's
508         *   number and unique id.  The articleId field cannot always be trusted
509         *   because of server deviations from RFC 977 reply formats.  You may
510         *   set this parameter to null if you do not desire to retrieve the
511         *   returned article information.
512         * @return A DotTerminatedMessageReader instance from which the article
513         *         header can be read.  null if the article does not exist.
514         * @exception NNTPConnectionClosedException
515         *      If the NNTP server prematurely closes the connection as a result
516         *      of the client being idle or some other reason causing the server
517         *      to send NNTP reply code 400.  This exception may be caught either
518         *      as an IOException or independently as itself.
519         * @exception IOException  If an I/O error occurs while either sending a
520         *      command to the server or receiving a reply from the server.
521         ***/
522        public Reader retrieveArticleHeader(int articleNumber,
523                                            ArticlePointer pointer)
524        throws IOException
525        {
526            return __retrieve(NNTPCommand.HEAD, articleNumber, pointer);
527        }
528    
529    
530        /*** Same as <code> retrieveArticleHeader(articleNumber, null) </code> ***/
531        public Reader retrieveArticleHeader(int articleNumber) throws IOException
532        {
533            return retrieveArticleHeader(articleNumber, null);
534        }
535    
536    
537    
538        /***
539         * Retrieves an article body from the NNTP server.  The article is
540         * referenced
541         * by its unique article identifier (including the enclosing &lt and &gt).
542         * The article number and identifier contained in the server reply
543         * are returned through an ArticlePointer.  The <code> articleId </code>
544         * field of the ArticlePointer cannot always be trusted because some
545         * NNTP servers do not correctly follow the RFC 977 reply format.
546         * <p>
547         * A DotTerminatedMessageReader is returned from which the article can
548         * be read.  If the article does not exist, null is returned.
549         * <p>
550         * You must not issue any commands to the NNTP server (i.e., call any
551         * other methods) until you finish reading the message from the returned
552         * Reader instance.
553         * The NNTP protocol uses the same stream for issuing commands as it does
554         * for returning results.  Therefore the returned Reader actually reads
555         * directly from the NNTP connection.  After the end of message has been
556         * reached, new commands can be executed and their replies read.  If
557         * you do not follow these requirements, your program will not work
558         * properly.
559         * <p>
560         * @param articleId  The unique article identifier of the article whose
561         *    body is being retrieved.  If this parameter is null, the
562         *    body of the currently selected article is retrieved.
563         * @param pointer    A parameter through which to return the article's
564         *   number and unique id.  The articleId field cannot always be trusted
565         *   because of server deviations from RFC 977 reply formats.  You may
566         *   set this parameter to null if you do not desire to retrieve the
567         *   returned article information.
568         * @return A DotTerminatedMessageReader instance from which the article
569         *         body can be read.  null if the article does not exist.
570         * @exception NNTPConnectionClosedException
571         *      If the NNTP server prematurely closes the connection as a result
572         *      of the client being idle or some other reason causing the server
573         *      to send NNTP reply code 400.  This exception may be caught either
574         *      as an IOException or independently as itself.
575         * @exception IOException  If an I/O error occurs while either sending a
576         *      command to the server or receiving a reply from the server.
577         ***/
578        public Reader retrieveArticleBody(String articleId, ArticlePointer pointer)
579        throws IOException
580        {
581            return __retrieve(NNTPCommand.BODY, articleId, pointer);
582    
583        }
584    
585        /*** Same as <code> retrieveArticleBody(articleId, null) </code> ***/
586        public Reader retrieveArticleBody(String articleId) throws IOException
587        {
588            return retrieveArticleBody(articleId, null);
589        }
590    
591        /*** Same as <code> retrieveArticleBody(null) </code> ***/
592        public Reader retrieveArticleBody() throws IOException
593        {
594            return retrieveArticleBody(null);
595        }
596    
597    
598        /***
599         * Retrieves an article body from the currently selected newsgroup.  The
600         * article is referenced by its article number.
601         * The article number and identifier contained in the server reply
602         * are returned through an ArticlePointer.  The <code> articleId </code>
603         * field of the ArticlePointer cannot always be trusted because some
604         * NNTP servers do not correctly follow the RFC 977 reply format.
605         * <p>
606         * A DotTerminatedMessageReader is returned from which the article can
607         * be read.  If the article does not exist, null is returned.
608         * <p>
609         * You must not issue any commands to the NNTP server (i.e., call any
610         * other methods) until you finish reading the message from the returned
611         * Reader instance.
612         * The NNTP protocol uses the same stream for issuing commands as it does
613         * for returning results.  Therefore the returned Reader actually reads
614         * directly from the NNTP connection.  After the end of message has been
615         * reached, new commands can be executed and their replies read.  If
616         * you do not follow these requirements, your program will not work
617         * properly.
618         * <p>
619         * @param articleNumber  The number of the the article whose body is
620         *     being retrieved.
621         * @param pointer    A parameter through which to return the article's
622         *   number and unique id.  The articleId field cannot always be trusted
623         *   because of server deviations from RFC 977 reply formats.  You may
624         *   set this parameter to null if you do not desire to retrieve the
625         *   returned article information.
626         * @return A DotTerminatedMessageReader instance from which the article
627         *         body can be read.  null if the article does not exist.
628         * @exception NNTPConnectionClosedException
629         *      If the NNTP server prematurely closes the connection as a result
630         *      of the client being idle or some other reason causing the server
631         *      to send NNTP reply code 400.  This exception may be caught either
632         *      as an IOException or independently as itself.
633         * @exception IOException  If an I/O error occurs while either sending a
634         *      command to the server or receiving a reply from the server.
635         ***/
636        public Reader retrieveArticleBody(int articleNumber,
637                                          ArticlePointer pointer)
638        throws IOException
639        {
640            return __retrieve(NNTPCommand.BODY, articleNumber, pointer);
641        }
642    
643    
644        /*** Same as <code> retrieveArticleBody(articleNumber, null) </code> ***/
645        public Reader retrieveArticleBody(int articleNumber) throws IOException
646        {
647            return retrieveArticleBody(articleNumber, null);
648        }
649    
650    
651        /***
652         * Select the specified newsgroup to be the target of for future article
653         * retrieval and posting operations.  Also return the newsgroup
654         * information contained in the server reply through the info parameter.
655         * <p>
656         * @param newsgroup  The newsgroup to select.
657         * @param info  A parameter through which the newsgroup information of
658         *      the selected newsgroup contained in the server reply is returned.
659         *      Set this to null if you do not desire this information.
660         * @return True if the newsgroup exists and was selected, false otherwise.
661         * @exception NNTPConnectionClosedException
662         *      If the NNTP server prematurely closes the connection as a result
663         *      of the client being idle or some other reason causing the server
664         *      to send NNTP reply code 400.  This exception may be caught either
665         *      as an IOException or independently as itself.
666         * @exception IOException  If an I/O error occurs while either sending a
667         *      command to the server or receiving a reply from the server.
668         ***/
669        public boolean selectNewsgroup(String newsgroup, NewsgroupInfo info)
670        throws IOException
671        {
672            if (!NNTPReply.isPositiveCompletion(group(newsgroup)))
673                return false;
674    
675            if (info != null)
676                __parseGroupReply(getReplyString(), info);
677    
678            return true;
679        }
680    
681        /*** Same as <code> selectNewsgroup(newsgroup, null) </code> ***/
682        public boolean selectNewsgroup(String newsgroup) throws IOException
683        {
684            return selectNewsgroup(newsgroup, null);
685        }
686    
687        /***
688         * List the command help from the server.
689         * <p>
690         * @return The sever help information.
691         * @exception NNTPConnectionClosedException
692         *      If the NNTP server prematurely closes the connection as a result
693         *      of the client being idle or some other reason causing the server
694         *      to send NNTP reply code 400.  This exception may be caught either
695         *      as an IOException or independently as itself.
696         * @exception IOException  If an I/O error occurs while either sending a
697         *      command to the server or receiving a reply from the server.
698         ***/
699        public String listHelp() throws IOException
700        {
701            StringWriter help;
702            Reader reader;
703    
704            if (!NNTPReply.isInformational(help()))
705                return null;
706    
707            help = new StringWriter();
708            reader = new DotTerminatedMessageReader(_reader_);
709            Util.copyReader(reader, help);
710            reader.close();
711            help.close();
712            return help.toString();
713        }
714    
715    
716        /***
717         * Select an article by its unique identifier (including enclosing
718         * &lt and &gt) and return its article number and id through the
719         * pointer parameter.  This is achieved through the STAT command.
720         * According to RFC 977, this will NOT set the current article pointer
721         * on the server.  To do that, you must reference the article by its
722         * number.
723         * <p>
724         * @param articleId  The unique article identifier of the article that
725         *    is being selectedd.  If this parameter is null, the
726         *    body of the current article is selected
727         * @param pointer    A parameter through which to return the article's
728         *   number and unique id.  The articleId field cannot always be trusted
729         *   because of server deviations from RFC 977 reply formats.  You may
730         *   set this parameter to null if you do not desire to retrieve the
731         *   returned article information.
732         * @return True if successful, false if not.
733         * @exception NNTPConnectionClosedException
734         *      If the NNTP server prematurely closes the connection as a result
735         *      of the client being idle or some other reason causing the server
736         *      to send NNTP reply code 400.  This exception may be caught either
737         *      as an IOException or independently as itself.
738         * @exception IOException  If an I/O error occurs while either sending a
739         *      command to the server or receiving a reply from the server.
740         ***/
741        public boolean selectArticle(String articleId, ArticlePointer pointer)
742        throws IOException
743        {
744            if (articleId != null)
745            {
746                if (!NNTPReply.isPositiveCompletion(stat(articleId)))
747                    return false;
748            }
749            else
750            {
751                if (!NNTPReply.isPositiveCompletion(stat()))
752                    return false;
753            }
754    
755            if (pointer != null)
756                __parseArticlePointer(getReplyString(), pointer);
757    
758            return true;
759        }
760    
761        /**** Same as <code> selectArticle(articleId, null) </code> ***/
762        public boolean selectArticle(String articleId) throws IOException
763        {
764            return selectArticle(articleId, null);
765        }
766    
767        /****
768         * Same as <code> selectArticle(null, articleId) </code>.  Useful
769         * for retrieving the current article number.
770         ***/
771        public boolean selectArticle(ArticlePointer pointer) throws IOException
772        {
773            return selectArticle(null, pointer);
774        }
775    
776    
777        /***
778         * Select an article in the currently selected newsgroup by its number.
779         * and return its article number and id through the
780         * pointer parameter.  This is achieved through the STAT command.
781         * According to RFC 977, this WILL set the current article pointer
782         * on the server.  Use this command to select an article before retrieving
783         * it, or to obtain an article's unique identifier given its number.
784         * <p>
785         * @param articleNumber The number of the article to select from the
786         *       currently selected newsgroup.
787         * @param pointer    A parameter through which to return the article's
788         *   number and unique id.  Although the articleId field cannot always
789         *   be trusted because of server deviations from RFC 977 reply formats,
790         *   we haven't found a server that misformats this information in response
791         *   to this particular command.  You may set this parameter to null if
792         *   you do not desire to retrieve the returned article information.
793         * @return True if successful, false if not.
794         * @exception NNTPConnectionClosedException
795         *      If the NNTP server prematurely closes the connection as a result
796         *      of the client being idle or some other reason causing the server
797         *      to send NNTP reply code 400.  This exception may be caught either
798         *      as an IOException or independently as itself.
799         * @exception IOException  If an I/O error occurs while either sending a
800         *      command to the server or receiving a reply from the server.
801         ***/
802        public boolean selectArticle(int articleNumber, ArticlePointer pointer)
803        throws IOException
804        {
805            if (!NNTPReply.isPositiveCompletion(stat(articleNumber)))
806                return false;
807    
808            if (pointer != null)
809                __parseArticlePointer(getReplyString(), pointer);
810    
811            return true;
812        }
813    
814    
815        /*** Same as <code> selectArticle(articleNumber, null) </code> ***/
816        public boolean selectArticle(int articleNumber) throws IOException
817        {
818            return selectArticle(articleNumber, null);
819        }
820    
821    
822        /***
823         * Select the article preceeding the currently selected article in the
824         * currently selected newsgroup and return its number and unique id
825         * through the pointer parameter.  Because of deviating server
826         * implementations, the articleId information cannot be trusted.  To
827         * obtain the article identifier, issue a
828         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
829         * afterward.
830         * <p>
831         * @param pointer    A parameter through which to return the article's
832         *   number and unique id.  The articleId field cannot always be trusted
833         *   because of server deviations from RFC 977 reply formats.  You may
834         *   set this parameter to null if you do not desire to retrieve the
835         *   returned article information.
836         * @return True if successful, false if not (e.g., there is no previous
837         *     article).
838         * @exception NNTPConnectionClosedException
839         *      If the NNTP server prematurely closes the connection as a result
840         *      of the client being idle or some other reason causing the server
841         *      to send NNTP reply code 400.  This exception may be caught either
842         *      as an IOException or independently as itself.
843         * @exception IOException  If an I/O error occurs while either sending a
844         *      command to the server or receiving a reply from the server.
845         ***/
846        public boolean selectPreviousArticle(ArticlePointer pointer)
847        throws IOException
848        {
849            if (!NNTPReply.isPositiveCompletion(last()))
850                return false;
851    
852            if (pointer != null)
853                __parseArticlePointer(getReplyString(), pointer);
854    
855            return true;
856        }
857    
858        /*** Same as <code> selectPreviousArticle(null) </code> ***/
859        public boolean selectPreviousArticle() throws IOException
860        {
861            return selectPreviousArticle(null);
862        }
863    
864    
865        /***
866         * Select the article following the currently selected article in the
867         * currently selected newsgroup and return its number and unique id
868         * through the pointer parameter.  Because of deviating server
869         * implementations, the articleId information cannot be trusted.  To
870         * obtain the article identifier, issue a
871         * <code> selectArticle(pointer.articleNumber, pointer) </code> immediately
872         * afterward.
873         * <p>
874         * @param pointer    A parameter through which to return the article's
875         *   number and unique id.  The articleId field cannot always be trusted
876         *   because of server deviations from RFC 977 reply formats.  You may
877         *   set this parameter to null if you do not desire to retrieve the
878         *   returned article information.
879         * @return True if successful, false if not (e.g., there is no following
880         *         article).
881         * @exception NNTPConnectionClosedException
882         *      If the NNTP server prematurely closes the connection as a result
883         *      of the client being idle or some other reason causing the server
884         *      to send NNTP reply code 400.  This exception may be caught either
885         *      as an IOException or independently as itself.
886         * @exception IOException  If an I/O error occurs while either sending a
887         *      command to the server or receiving a reply from the server.
888         ***/
889        public boolean selectNextArticle(ArticlePointer pointer) throws IOException
890        {
891            if (!NNTPReply.isPositiveCompletion(next()))
892                return false;
893    
894            if (pointer != null)
895                __parseArticlePointer(getReplyString(), pointer);
896    
897            return true;
898        }
899    
900    
901        /*** Same as <code> selectNextArticle(null) </code> ***/
902        public boolean selectNextArticle() throws IOException
903        {
904            return selectNextArticle(null);
905        }
906    
907    
908        /***
909         * List all newsgroups served by the NNTP server.  If no newsgroups
910         * are served, a zero length array will be returned.  If the command
911         * fails, null will be returned.
912         * <p>
913         * @return An array of NewsgroupInfo instances containing the information
914         *    for each newsgroup served by the NNTP server.   If no newsgroups
915         *    are served, a zero length array will be returned.  If the command
916         *    fails, null will be returned.
917         * @exception NNTPConnectionClosedException
918         *      If the NNTP server prematurely closes the connection as a result
919         *      of the client being idle or some other reason causing the server
920         *      to send NNTP reply code 400.  This exception may be caught either
921         *      as an IOException or independently as itself.
922         * @exception IOException  If an I/O error occurs while either sending a
923         *      command to the server or receiving a reply from the server.
924         ***/
925        public NewsgroupInfo[] listNewsgroups() throws IOException
926        {
927            if (!NNTPReply.isPositiveCompletion(list()))
928                return null;
929    
930            return __readNewsgroupListing();
931        }
932    
933        /**
934         * An overloaded listNewsgroups() command that allows us to
935         * specify with a pattern what groups we want to list. Wraps the
936         * LIST ACTIVE command.
937         * <p>
938         * @param wildmat a pseudo-regex pattern (cf. RFC 2980)
939         * @return An array of NewsgroupInfo instances containing the information
940         *    for each newsgroup served by the NNTP server corresponding to the
941         *    supplied pattern.   If no such newsgroups are served, a zero length
942         *    array will be returned.  If the command fails, null will be returned.
943         * @throws IOException
944         */
945        public NewsgroupInfo[] listNewsgroups(String wildmat) throws IOException
946        {
947            if(!NNTPReply.isPositiveCompletion(listActive(wildmat)))
948                return null;
949            return __readNewsgroupListing();
950        }
951    
952    
953        /***
954         * List all new newsgroups added to the NNTP server since a particular
955         * date subject to the conditions of the specified query.  If no new
956         * newsgroups were added, a zero length array will be returned.  If the
957         * command fails, null will be returned.
958         * <p>
959         * @param query  The query restricting how to search for new newsgroups.
960         * @return An array of NewsgroupInfo instances containing the information
961         *    for each new newsgroup added to the NNTP server.   If no newsgroups
962         *    were added, a zero length array will be returned.  If the command
963         *    fails, null will be returned.
964         * @exception NNTPConnectionClosedException
965         *      If the NNTP server prematurely closes the connection as a result
966         *      of the client being idle or some other reason causing the server
967         *      to send NNTP reply code 400.  This exception may be caught either
968         *      as an IOException or independently as itself.
969         * @exception IOException  If an I/O error occurs while either sending a
970         *      command to the server or receiving a reply from the server.
971         ***/
972        public NewsgroupInfo[] listNewNewsgroups(NewGroupsOrNewsQuery query)
973        throws IOException
974        {
975            if (!NNTPReply.isPositiveCompletion(newgroups(
976                                                    query.getDate(), query.getTime(),
977                                                    query.isGMT(), query.getDistributions())))
978                return null;
979    
980            return __readNewsgroupListing();
981        }
982    
983    
984        /***
985         * List all new articles added to the NNTP server since a particular
986         * date subject to the conditions of the specified query.  If no new
987         * new news is found, a zero length array will be returned.  If the
988         * command fails, null will be returned.  You must add at least one
989         * newsgroup to the query, else the command will fail.  Each String
990         * in the returned array is a unique message identifier including the
991         * enclosing &lt and &gt.
992         * <p>
993         * @param query  The query restricting how to search for new news.  You
994         *    must add at least one newsgroup to the query.
995         * @return An array of String instances containing the unique message
996         *    identifiers for each new article added to the NNTP server.  If no
997         *    new news is found, a zero length array will be returned.  If the
998         *    command fails, null 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         ***/
1007        public String[] listNewNews(NewGroupsOrNewsQuery query)
1008        throws IOException
1009        {
1010            int size;
1011            String line;
1012            Vector<String> list;
1013            String[] result;
1014            BufferedReader reader;
1015    
1016            if (!NNTPReply.isPositiveCompletion(newnews(
1017                                                    query.getNewsgroups(), query.getDate(), query.getTime(),
1018                                                    query.isGMT(), query.getDistributions())))
1019                return null;
1020    
1021            list = new Vector<String>();
1022            reader = new BufferedReader(new DotTerminatedMessageReader(_reader_));
1023    
1024            while ((line = reader.readLine()) != null)
1025                list.addElement(line);
1026    
1027            size = list.size();
1028    
1029            if (size < 1)
1030                return new String[0];
1031    
1032            result = new String[size];
1033            list.copyInto(result);
1034    
1035            return result;
1036        }
1037    
1038        /***
1039         * There are a few NNTPClient methods that do not complete the
1040         * entire sequence of NNTP commands to complete a transaction.  These
1041         * commands require some action by the programmer after the reception
1042         * of a positive preliminary command.  After the programmer's code
1043         * completes its actions, it must call this method to receive
1044         * the completion reply from the server and verify the success of the
1045         * entire transaction.
1046         * <p>
1047         * For example
1048         * <pre>
1049         * writer = client.postArticle();
1050         * if(writer == null) // failure
1051         *   return false;
1052         * header = new SimpleNNTPHeader("foobar@foo.com", "Just testing");
1053         * header.addNewsgroup("alt.test");
1054         * writer.write(header.toString());
1055         * writer.write("This is just a test");
1056         * writer.close();
1057         * if(!client.completePendingCommand()) // failure
1058         *   return false;
1059         * </pre>
1060         * <p>
1061         * @return True if successfully completed, false if not.
1062         * @exception NNTPConnectionClosedException
1063         *      If the NNTP server prematurely closes the connection as a result
1064         *      of the client being idle or some other reason causing the server
1065         *      to send NNTP reply code 400.  This exception may be caught either
1066         *      as an IOException or independently as itself.
1067         * @exception IOException  If an I/O error occurs while either sending a
1068         *      command to the server or receiving a reply from the server.
1069         ***/
1070        public boolean completePendingCommand() throws IOException
1071        {
1072            return NNTPReply.isPositiveCompletion(getReply());
1073        }
1074    
1075        /***
1076         * Post an article to the NNTP server.  This method returns a
1077         * DotTerminatedMessageWriter instance to which the article can be
1078         * written.  Null is returned if the posting attempt fails.  You
1079         * should check {@link NNTP#isAllowedToPost isAllowedToPost() }
1080         *  before trying to post.  However, a posting
1081         * attempt can fail due to malformed headers.
1082         * <p>
1083         * You must not issue any commands to the NNTP server (i.e., call any
1084         * (other methods) until you finish writing to the returned Writer
1085         * instance and close it.  The NNTP protocol uses the same stream for
1086         * issuing commands as it does for returning results.  Therefore the
1087         * returned Writer actually writes directly to the NNTP connection.
1088         * After you close the writer, you can execute new commands.  If you
1089         * do not follow these requirements your program will not work properly.
1090         * <p>
1091         * Different NNTP servers will require different header formats, but
1092         * you can use the provided
1093         * {@link org.apache.commons.net.nntp.SimpleNNTPHeader}
1094         * class to construct the bare minimum acceptable header for most
1095         * news readers.  To construct more complicated headers you should
1096         * refer to RFC 822.  When the Java Mail API is finalized, you will be
1097         * able to use it to compose fully compliant Internet text messages.
1098         * The DotTerminatedMessageWriter takes care of doubling line-leading
1099         * dots and ending the message with a single dot upon closing, so all
1100         * you have to worry about is writing the header and the message.
1101         * <p>
1102         * Upon closing the returned Writer, you need to call
1103         * {@link #completePendingCommand  completePendingCommand() }
1104         * to finalize the posting and verify its success or failure from
1105         * the server reply.
1106         * <p>
1107         * @return A DotTerminatedMessageWriter to which the article (including
1108         *      header) can be written.  Returns null if the command fails.
1109         * @exception IOException  If an I/O error occurs while either sending a
1110         *      command to the server or receiving a reply from the server.
1111         ***/
1112    
1113        public Writer postArticle() throws IOException
1114        {
1115            if (!NNTPReply.isPositiveIntermediate(post()))
1116                return null;
1117    
1118            return new DotTerminatedMessageWriter(_writer_);
1119        }
1120    
1121    
1122        public Writer forwardArticle(String articleId) throws IOException
1123        {
1124            if (!NNTPReply.isPositiveIntermediate(ihave(articleId)))
1125                return null;
1126    
1127            return new DotTerminatedMessageWriter(_writer_);
1128        }
1129    
1130    
1131        /***
1132         * Logs out of the news server gracefully by sending the QUIT command.
1133         * However, you must still disconnect from the server before you can open
1134         * a new connection.
1135         * <p>
1136         * @return True if successfully completed, false if not.
1137         * @exception IOException  If an I/O error occurs while either sending a
1138         *      command to the server or receiving a reply from the server.
1139         ***/
1140        public boolean logout() throws IOException
1141        {
1142            return NNTPReply.isPositiveCompletion(quit());
1143        }
1144    
1145    
1146        /**
1147         * Log into a news server by sending the AUTHINFO USER/AUTHINFO
1148         * PASS command sequence. This is usually sent in response to a
1149         * 480 reply code from the NNTP server.
1150         * <p>
1151         * @param username a valid username
1152         * @param password the corresponding password
1153         * @return True for successful login, false for a failure
1154         * @throws IOException
1155         */
1156        public boolean authenticate(String username, String password)
1157            throws IOException
1158        {
1159            int replyCode = authinfoUser(username);
1160    
1161            if (replyCode == NNTPReply.MORE_AUTH_INFO_REQUIRED)
1162                {
1163                    replyCode = authinfoPass(password);
1164    
1165                    if (replyCode == NNTPReply.AUTHENTICATION_ACCEPTED)
1166                        {
1167                            _isAllowedToPost = true;
1168                            return true;
1169                        }
1170                }
1171            return false;
1172        }
1173    
1174        /***
1175         * Private implementation of XOVER functionality.
1176         *
1177         * See {@link NNTP#xover}
1178         * for legal agument formats. Alternatively, read RFC 2980 :-)
1179         * <p>
1180         * @param articleRange
1181         * @return Returns a DotTerminatedMessageReader if successful, null
1182         *         otherwise
1183         * @exception IOException
1184         */
1185        private Reader __retrieveArticleInfo(String articleRange)
1186            throws IOException
1187        {
1188            if (!NNTPReply.isPositiveCompletion(xover(articleRange)))
1189                return null;
1190    
1191            return new DotTerminatedMessageReader(_reader_);
1192        }
1193    
1194        /**
1195         * Return article headers for a specified post.
1196         * <p>
1197         * @param articleNumber the article to retrieve headers for
1198         * @return a DotTerminatedReader if successful, null otherwise
1199         * @throws IOException
1200         */
1201        public Reader retrieveArticleInfo(int articleNumber) throws IOException
1202        {
1203            return __retrieveArticleInfo(Integer.toString(articleNumber));
1204        }
1205    
1206        /**
1207         * Return article headers for all articles between lowArticleNumber
1208         * and highArticleNumber, inclusively.
1209         * <p>
1210         * @param lowArticleNumber
1211         * @param highArticleNumber
1212         * @return a DotTerminatedReader if successful, null otherwise
1213         * @throws IOException
1214         */
1215        public Reader retrieveArticleInfo(int lowArticleNumber,
1216                                          int highArticleNumber)
1217            throws IOException
1218        {
1219            return
1220                __retrieveArticleInfo(lowArticleNumber + "-" +
1221                                                 highArticleNumber);
1222        }
1223    
1224        /***
1225         * Private implementation of XHDR functionality.
1226         *
1227         * See {@link NNTP#xhdr}
1228         * for legal agument formats. Alternatively, read RFC 1036.
1229         * <p>
1230         * @param header
1231         * @param articleRange
1232         * @return Returns a DotTerminatedMessageReader if successful, null
1233         *         otherwise
1234         * @exception IOException
1235         */
1236        private Reader __retrieveHeader(String header, String articleRange)
1237            throws IOException
1238        {
1239            if (!NNTPReply.isPositiveCompletion(xhdr(header, articleRange)))
1240                return null;
1241    
1242            return new DotTerminatedMessageReader(_reader_);
1243        }
1244    
1245        /**
1246         * Return an article header for a specified post.
1247         * <p>
1248         * @param header the header to retrieve
1249         * @param articleNumber the article to retrieve the header for
1250         * @return a DotTerminatedReader if successful, null otherwise
1251         * @throws IOException
1252         */
1253        public Reader retrieveHeader(String header, int articleNumber)
1254            throws IOException
1255        {
1256            return __retrieveHeader(header, Integer.toString(articleNumber));
1257        }
1258    
1259        /**
1260         * Return an article header for all articles between lowArticleNumber
1261         * and highArticleNumber, inclusively.
1262         * <p>
1263         * @param header
1264         * @param lowArticleNumber
1265         * @param highArticleNumber
1266         * @return a DotTerminatedReader if successful, null otherwise
1267         * @throws IOException
1268         */
1269        public Reader retrieveHeader(String header, int lowArticleNumber,
1270                                     int highArticleNumber)
1271            throws IOException
1272        {
1273            return
1274                __retrieveHeader(header,lowArticleNumber + "-" + highArticleNumber);
1275        }
1276    }
1277    
1278    
1279    /* Emacs configuration
1280     * Local variables:        **
1281     * mode:             java  **
1282     * c-basic-offset:   4     **
1283     * indent-tabs-mode: nil   **
1284     * End:                    **
1285     */