001: package com.quadcap.pop3.client;
002:
003: /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.util.Enumeration;
042: import java.util.Vector;
043:
044: import java.net.Socket;
045: import java.net.UnknownHostException;
046:
047: import java.io.BufferedInputStream;
048: import java.io.BufferedOutputStream;
049: import java.io.ByteArrayOutputStream;
050: import java.io.FileOutputStream;
051: import java.io.IOException;
052: import java.io.InputStream;
053: import java.io.OutputStream;
054:
055: import com.quadcap.util.Debug;
056: import com.quadcap.util.Util;
057:
058: import com.quadcap.io.DotStuffInputStream;
059: import com.quadcap.io.LogInputStream;
060: import com.quadcap.io.LogOutputStream;
061:
062: /**
063: * This class implementes a simple mapping of the POP3 protocol onto methods
064: * of a java class.
065: *
066: * @author Stan Bailes
067: */
068: public class Session {
069: /**
070: * server portion of host address.
071: */
072: String host = null;
073: /**
074: * the port portion of the host address.
075: */
076: int port = -1;
077:
078: /**
079: * Sockect used to communicate with the server.
080: */
081: Socket socket = null;
082:
083: /**
084: * Input (responses) from server.
085: */
086: InputStream in = null;
087:
088: /**
089: * Output (commands) to the server.
090: */
091: OutputStream out = null;
092:
093: /**
094: * A temporary area used to collect server responses.
095: */
096: ByteArrayOutputStream resp = new ByteArrayOutputStream();
097:
098: /**
099: * The last response, as a string.
100: */
101: String response = null;
102:
103: /**
104: * Construct a POP3 client object.
105: * Initialize the host/port information, but don't actually open the
106: * socket.
107: *
108: * @param host the server's host name
109: * @param port the server's port number
110: */
111: public Session(String host, int port) {
112: this .host = host;
113: this .port = port;
114: }
115:
116: /**
117: * Open the socket to the server and return the status code for the
118: * server's greeting.
119: */
120: public int connect() throws IOException, UnknownHostException {
121: socket = new Socket(host, port);
122: // XXX Not buffered, why?
123: in = new BufferedInputStream(socket.getInputStream());
124: out = new BufferedOutputStream(socket.getOutputStream());
125:
126: if (true) {
127: FileOutputStream log = new FileOutputStream("pop3.log",
128: true);
129: in = new LogInputStream(in, log, "S: ");
130: out = new LogOutputStream(out, log, "C: ");
131: }
132: int ret = getResponse();
133: return ret;
134: }
135:
136: /** Useful constant for CR. */
137: public static final int CR = '\r';
138: /** Useful constant for LF. */
139: public static final int LF = '\n';
140:
141: /** Useful constant for OK status. */
142: public static final int OK = 0;
143: /** Useful constant for ERR status. */
144: public static final int ERR = 1;
145:
146: /**
147: * Read a response line from the server. We expect this to be a string
148: * of the form '+OK ...' or '-ERR ...'.
149: *
150: * @return OK or ERR.
151: */
152: public int getResponse() throws IOException {
153: out.flush();
154: resp.reset();
155: int state = 0;
156: ByteArrayOutputStream bo = new ByteArrayOutputStream();
157: while (state < 2) {
158: int c = in.read();
159: if (c < 0) {
160: throw new IOException("End of file in getResponse()");
161: }
162: bo.write(c);
163: resp.write(c);
164: switch (state) {
165: case 0:
166: if (c == CR)
167: state = 1;
168: break;
169: case 1:
170: if (c == LF)
171: state = 2;
172: break;
173: }
174: }
175: response = resp.toString();
176: if (response.indexOf("+OK") == 0) {
177: return OK;
178: } else if (response.indexOf("-ERR") == 0) {
179: return ERR;
180: } else {
181: Debug.println("getResponse: ???");
182: return ERR;
183: }
184: }
185:
186: /**
187: * Prepare to read multi-line response. Return an input stream
188: * which must be read to actually get the response.
189: */
190: public InputStream getResponse(boolean get) throws IOException {
191: int ret = getResponse();
192: return (get && ret == OK) ? new DotStuffInputStream(in) : null;
193: }
194:
195: /** Handy constant for CRLF sequence. */
196: private static final byte[] CRLF = { CR, LF };
197:
198: /**
199: * Write a POP3 command and arguments.
200: * @param cmd a byte array containing the command literal
201: * @param val a String to be appended to the 'cmd'
202: */
203: void writeCmd(byte[] cmd, String val) throws IOException {
204: out.write(cmd);
205: out.write(Util.bytes(val));
206: out.write(CRLF);
207: }
208:
209: /**
210: * Execute a simple command which just expects an OR or ERR response.
211: */
212: int simpleCmd(byte[] cmd, String val) throws IOException {
213: writeCmd(cmd, val);
214: return getResponse();
215: }
216:
217: /**
218: * Execute a command which expects a multi-line response.
219: */
220: InputStream responseCmd(byte[] cmd, String val) throws IOException {
221: writeCmd(cmd, val);
222: return getResponse(true);
223: }
224:
225: /**
226: * Execute a command which needs to parse the response line.
227: */
228: Vector vectorCmd(byte[] cmd, String val) throws IOException {
229: writeCmd(cmd, val);
230: int ret = getResponse();
231: return Util.split(response, ' ');
232: }
233:
234: /** The USER command. */
235: private static final byte[] userBytes = { (byte) 'U', (byte) 'S',
236: (byte) 'E', (byte) 'R', (byte) ' ' };
237:
238: public int user(String name) throws IOException {
239: return simpleCmd(userBytes, name);
240: }
241:
242: /** The PASS command. */
243: private static final byte[] passBytes = { (byte) 'P', (byte) 'A',
244: (byte) 'S', (byte) 'S', (byte) ' ' };
245:
246: public int pass(String pass) throws IOException {
247: return simpleCmd(passBytes, pass);
248: }
249:
250: /** The LIST command. */
251: private static final byte[] listBytes = { (byte) 'L', (byte) 'I',
252: (byte) 'S', (byte) 'T', (byte) ' ' };
253:
254: public InputStream list() throws IOException {
255: return responseCmd(listBytes, "");
256: }
257:
258: public Vector list(String msg) throws IOException {
259: return vectorCmd(listBytes, msg);
260: }
261:
262: /** The STAT command. */
263: private static final byte[] statBytes = { (byte) 'S', (byte) 'T',
264: (byte) 'A', (byte) 'T', (byte) ' ' };
265:
266: public Vector stat() throws IOException {
267: return vectorCmd(statBytes, "");
268: }
269:
270: /** The UIDL command. */
271: private static final byte[] uidlBytes = { (byte) 'U', (byte) 'I',
272: (byte) 'D', (byte) 'L', (byte) ' ' };
273:
274: public InputStream uidl() throws IOException {
275: return responseCmd(uidlBytes, "");
276: }
277:
278: public Vector uidl(String msg) throws IOException {
279: return vectorCmd(uidlBytes, msg);
280: }
281:
282: /** The RETR command. */
283: private static final byte[] retrBytes = { (byte) 'R', (byte) 'E',
284: (byte) 'T', (byte) 'R', (byte) ' ' };
285:
286: public InputStream retr(String msg) throws IOException {
287: return responseCmd(retrBytes, msg);
288: }
289:
290: public InputStream retr(int i) throws IOException {
291: return retr("" + i);
292: }
293:
294: /** The DELE command. */
295: private static final byte[] deleBytes = { (byte) 'D', (byte) 'E',
296: (byte) 'L', (byte) 'E', (byte) ' ' };
297:
298: public int dele(String msg) throws IOException {
299: return simpleCmd(deleBytes, msg);
300: }
301:
302: public int dele(int i) throws IOException {
303: return dele("" + i);
304: }
305:
306: /** The TOP command. */
307: private static final byte[] topBytes = { (byte) 'T', (byte) 'O',
308: (byte) 'P', (byte) ' ' };
309:
310: public InputStream top(String msg, int lines) throws IOException {
311: return responseCmd(topBytes, msg + " " + lines);
312: }
313:
314: /** The RSET command. */
315: private static final byte[] rsetBytes = { (byte) 'R', (byte) 'S',
316: (byte) 'E', (byte) 'T', (byte) ' ' };
317:
318: public int rset() throws IOException {
319: return simpleCmd(rsetBytes, "");
320: }
321:
322: /** THE QUIT command. */
323: private static final byte[] quitBytes = { (byte) 'Q', (byte) 'U',
324: (byte) 'I', (byte) 'T', (byte) ' ' };
325:
326: public int quit() throws IOException {
327: try {
328: simpleCmd(quitBytes, "");
329: } finally {
330: socket.close();
331: }
332: return OK;
333: }
334:
335: /** The NOOP command. */
336: private static final byte[] noopBytes = { (byte) 'N', (byte) 'O',
337: (byte) 'O', (byte) 'P', (byte) ' ' };
338:
339: /** THE APOP command. */
340: private static final byte[] apopBytes = { (byte) 'A', (byte) 'P',
341: (byte) 'O', (byte) 'P', (byte) ' ' };
342:
343: }
|