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:  */
cli@37: public class ArticleReader
chris@1: {
chris@1: 
cli@37: 	private BufferedOutputStream out;
cli@37: 	private BufferedInputStream in;
cli@37: 	private String messageID;
chris@1: 
cli@37: 	public ArticleReader(String host, int port, String messageID)
cli@37: 		throws IOException, UnknownHostException
cli@37: 	{
cli@37: 		this.messageID = messageID;
chris@3: 
cli@37: 		// Connect to NNTP server
cli@37: 		Socket socket = new Socket(host, port);
cli@37: 		this.out = new BufferedOutputStream(socket.getOutputStream());
cli@37: 		this.in = new BufferedInputStream(socket.getInputStream());
cli@37: 		String line = readln(this.in);
cli@37: 		if (!line.startsWith("200 ")) {
cli@37: 			throw new IOException("Invalid hello from server: " + line);
cli@37: 		}
cli@37: 	}
chris@1: 
cli@37: 	private boolean eofArticle(byte[] buf)
cli@37: 	{
cli@37: 		if (buf.length < 4) {
cli@37: 			return false;
cli@37: 		}
chris@1: 
cli@37: 		int l = buf.length - 1;
cli@37: 		return buf[l - 3] == 10 // '*\n'
cli@37: 			&& buf[l - 2] == '.' // '.'
cli@37: 			&& buf[l - 1] == 13 && buf[l] == 10;  // '\r\n'
cli@37: 	}
chris@1: 
cli@37: 	public byte[] getArticleData()
cli@37: 		throws IOException, UnsupportedEncodingException
cli@37: 	{
cli@37: 		long maxSize = Config.inst().get(Config.ARTICLE_MAXSIZE, 1024) * 1024L;
cli@37: 
cli@37: 		try {
cli@37: 			this.out.write(("ARTICLE " + this.messageID + "\r\n").getBytes("UTF-8"));
cli@37: 			this.out.flush();
cli@37: 
cli@37: 			String line = readln(this.in);
cli@37: 			if (line.startsWith("220 ")) {
cli@37: 				ByteArrayOutputStream buf = new ByteArrayOutputStream();
cli@37: 
cli@37: 				while (!eofArticle(buf.toByteArray())) {
cli@37: 					for (int b = in.read(); b != 10; b = in.read()) {
cli@37: 						buf.write(b);
cli@37: 					}
cli@37: 
cli@37: 					buf.write(10);
cli@37: 					if (buf.size() > maxSize) {
cli@37: 						Log.get().warning("Skipping message that is too large: " + buf.size());
cli@37: 						return null;
cli@37: 					}
cli@37: 				}
cli@37: 
cli@37: 				return buf.toByteArray();
cli@37: 			} else {
cli@37: 				Log.get().warning("ArticleReader: " + line);
cli@37: 				return null;
cli@37: 			}
cli@37: 		} catch (IOException ex) {
cli@37: 			throw ex;
cli@37: 		} finally {
cli@37: 			this.out.write("QUIT\r\n".getBytes("UTF-8"));
cli@37: 			this.out.flush();
cli@37: 			this.out.close();
cli@37: 		}
cli@37: 	}
cli@37: 
cli@37: 	private String readln(InputStream in)
cli@37: 		throws IOException
cli@37: 	{
cli@37: 		ByteArrayOutputStream buf = new ByteArrayOutputStream();
cli@37: 		for (int b = in.read(); b != 10 /* \n */; b = in.read()) {
cli@37: 			buf.write(b);
cli@37: 		}
cli@37: 
cli@37: 		return new String(buf.toByteArray());
cli@37: 	}
chris@1: }