chris@1: /*
chris@1:  *   SONEWS News Server
chris@1:  *   see AUTHORS for the list of contributors
chris@1:  *
chris@1:  *   This program is free software: you can redistribute it and/or modify
chris@1:  *   it under the terms of the GNU General Public License as published by
chris@1:  *   the Free Software Foundation, either version 3 of the License, or
chris@1:  *   (at your option) any later version.
chris@1:  *
chris@1:  *   This program is distributed in the hope that it will be useful,
chris@1:  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
chris@1:  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
chris@1:  *   GNU General Public License for more details.
chris@1:  *
chris@1:  *   You should have received a copy of the GNU General Public License
chris@1:  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
chris@1:  */
chris@1: 
chris@1: package org.sonews.util.io;
chris@1: 
chris@1: import java.io.BufferedInputStream;
chris@1: import java.io.BufferedOutputStream;
chris@1: import java.io.ByteArrayOutputStream;
chris@1: import java.io.IOException;
chris@1: import java.io.InputStream;
chris@1: import java.io.UnsupportedEncodingException;
chris@1: import java.net.Socket;
chris@1: import java.net.UnknownHostException;
chris@3: import org.sonews.config.Config;
chris@1: import org.sonews.util.Log;
chris@1: 
chris@1: /**
chris@1:  * Reads an news article from a NNTP server.
chris@1:  * @author Christian Lins
chris@1:  * @since sonews/0.5.0
chris@1:  */
chris@1: public class ArticleReader 
chris@1: {
chris@1: 
chris@1:   private BufferedOutputStream out;
chris@1:   private BufferedInputStream  in;
chris@1:   private String               messageID;
chris@1:   
chris@1:   public ArticleReader(String host, int port, String messageID)
chris@1:     throws IOException, UnknownHostException
chris@1:   {
chris@1:     this.messageID = messageID;
chris@1: 
chris@1:     // Connect to NNTP server
chris@1:     Socket socket = new Socket(host, port);
chris@1:     this.out = new BufferedOutputStream(socket.getOutputStream());
chris@1:     this.in  = new BufferedInputStream(socket.getInputStream());
chris@1:     String line = readln(this.in);
chris@1:     if(!line.startsWith("200 "))
chris@1:     {
chris@1:       throw new IOException("Invalid hello from server: " + line);
chris@1:     }
chris@1:   }
chris@1:   
chris@1:   private boolean eofArticle(byte[] buf)
chris@1:   {
chris@1:     if(buf.length < 4)
chris@1:     {
chris@1:       return false;
chris@1:     }
chris@1:     
chris@1:     int l = buf.length - 1;
chris@1:     return buf[l-3] == 10 // '*\n'
chris@1:         && buf[l-2] == '.'                   // '.'
chris@1:         && buf[l-1] == 13 && buf[l] == 10;  // '\r\n'
chris@1:   }
chris@1:   
chris@1:   public byte[] getArticleData()
chris@1:     throws IOException, UnsupportedEncodingException
chris@1:   {
chris@3:     long maxSize = Config.inst().get(Config.ARTICLE_MAXSIZE, 1024) * 1024L;
chris@3: 
chris@1:     try
chris@1:     {
chris@1:       this.out.write(("ARTICLE " + this.messageID + "\r\n").getBytes("UTF-8"));
chris@1:       this.out.flush();
chris@1: 
chris@1:       String line = readln(this.in);
chris@1:       if(line.startsWith("220 "))
chris@1:       {
chris@1:         ByteArrayOutputStream buf = new ByteArrayOutputStream();
chris@1:         
chris@1:         while(!eofArticle(buf.toByteArray()))
chris@1:         {
chris@1:           for(int b = in.read(); b != 10; b = in.read())
chris@1:           {
chris@1:             buf.write(b);
chris@1:           }
chris@1: 
chris@1:           buf.write(10);
chris@3:           if(buf.size() > maxSize)
chris@3:           {
chris@3:             Log.msg("Skipping message that is too large: " + buf.size(), false);
chris@3:             return null;
chris@3:           }
chris@1:         }
chris@1:         
chris@1:         return buf.toByteArray();
chris@1:       }
chris@1:       else
chris@1:       {
chris@1:         Log.msg("ArticleReader: " + line, false);
chris@1:         return null;
chris@1:       }
chris@1:     }
chris@1:     catch(IOException ex)
chris@1:     {
chris@1:       throw ex;
chris@1:     }
chris@1:     finally
chris@1:     {
chris@1:       this.out.write("QUIT\r\n".getBytes("UTF-8"));
chris@1:       this.out.flush();
chris@1:       this.out.close();
chris@1:     }
chris@1:   }
chris@1:   
chris@1:   private String readln(InputStream in)
chris@1:     throws IOException
chris@1:   {
chris@1:     ByteArrayOutputStream buf = new ByteArrayOutputStream();
chris@1:     for(int b = in.read(); b != 10 /* \n */; b = in.read())
chris@1:     {
chris@1:       buf.write(b);
chris@1:     }
chris@1:     
chris@1:     return new String(buf.toByteArray());
chris@1:   }
chris@1: 
chris@1: }