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.ArrayList;
042: import java.util.Date;
043: import java.util.Enumeration;
044: import java.util.HashMap;
045: import java.util.Iterator;
046: import java.util.Properties;
047: import java.util.List;
048:
049: import java.io.DataInputStream;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.io.OutputStream;
053:
054: import java.net.InetAddress;
055:
056: import com.quadcap.io.HeaderEnumeration;
057: import com.quadcap.io.IO;
058:
059: import com.quadcap.util.Debug;
060:
061: /**
062: * This simple Pop3 agent transfers all new messages in a remote Pop3
063: * mailbox into the specified folder in the quadcap message store.
064: *
065: * <p>A more sophisticated agent would scan the headers first and apply
066: * filters to certain messages without having to fetch the bodies.
067: *
068: * @author Stan Bailes
069: */
070: public class Pop3Agent {
071: HashMap uidlMap = new HashMap();
072: String hostName = null;
073: int portNumber = 110;
074: String login = null;
075: String passwd = null;
076: int daysKeep = 999999;
077: boolean enabled = true;
078:
079: Session pop3 = null;
080:
081: public void write(OutputStream w) throws IOException {
082: Iterator iter = uidlMap.keySet().iterator();
083: while (iter.hasNext()) {
084: UidlEntry u = (UidlEntry) uidlMap.get(iter.next());
085: IO.write(w, "UIDL: " + u.toString() + "\n");
086: }
087: }
088:
089: public void read(InputStream r) throws IOException {
090: DataInputStream d = new DataInputStream(r);
091: String line;
092: while ((line = d.readLine()) != null) {
093: if (line.startsWith("UIDL: ")) {
094: line = line.substring(6).trim();
095: UidlEntry uidl = new UidlEntry(line);
096: uidlMap.put(uidl.getUidl(), uidl);
097: }
098: }
099: }
100:
101: /**
102: * Default constructor for factory
103: */
104: public Pop3Agent() {
105: }
106:
107: /**
108: * Check for mail
109: */
110: public void run(Properties props, MessageHook hook) {
111: try {
112: hostName = props.getProperty("host");
113: portNumber = Integer.parseInt(props.getProperty("port",
114: "110"));
115: login = props.getProperty("user");
116: passwd = props.getProperty("passwd");
117: daysKeep = Integer.parseInt(props.getProperty("daysKeep",
118: "3"));
119: enabled = "true".equalsIgnoreCase(props.getProperty(
120: "enabled", "true"));
121: if (!enabled)
122: return;
123:
124: if (pop3 == null) {
125: pop3 = new Session(hostName, portNumber);
126: }
127: pop3.connect();
128:
129: int ret = pop3.user(login);
130: if (ret != Session.OK) {
131: throw new IOException("login failed");
132: }
133:
134: ret = pop3.pass(passwd);
135: if (ret != Session.OK) {
136: throw new IOException("login (passwd) failed");
137: }
138:
139: getMail(hook);
140: } catch (Throwable t) {
141: Debug.print(t);
142: } finally {
143: try {
144: if (pop3 != null)
145: pop3.quit();
146: } catch (IOException e) {
147: Debug.print(e);
148: }
149: // ---- we're about to "go to sleep", potentially for a long time,
150: // ---- so allow these objects to be gc'ed.
151: pop3 = null;
152: }
153: }
154:
155: /**
156: * Figure out what's new and deliver it. Figure out what's old
157: * and delete it.
158: */
159: void getMail(MessageHook hook) throws IOException {
160: InputStream uidls = pop3.uidl();
161: ArrayList newMessages = new ArrayList(); // list of uidls
162: ArrayList delMessages = new ArrayList(); // list of msgnums
163: int msgNum = 0;
164: String uidl = null;
165: StringBuffer sb = new StringBuffer();
166: Date now = new Date();
167: long ms = daysKeep * 24L * 60 * 60 * 1000;
168: Date windowStart = new Date(now.getTime() - ms);
169: int c = uidls.read();
170: while (c >= 0) {
171: while (Character.isDigit((char) c)) {
172: sb.append((char) c);
173: c = uidls.read();
174: }
175: if (sb.length() == 0) {
176: if (c < 0)
177: break;
178: else {
179: c = uidls.read();
180: continue;
181: }
182: }
183: msgNum = Integer.parseInt(sb.toString());
184:
185: sb.setLength(0);
186: while (Character.isWhitespace((char) c)) {
187: c = uidls.read();
188: }
189: if (c < 0)
190: continue;
191: while (c >= 0x21 && c <= 0x7e) {
192: sb.append((char) c);
193: c = uidls.read();
194: }
195: uidl = sb.toString();
196: sb.setLength(0);
197: UidlEntry e = getUidlEntry(uidl);
198: if (e == null) {
199: e = putUidlEntry(uidl, msgNum, now);
200: newMessages.add(e);
201: } else {
202: e.setMessageNumber(msgNum);
203: if (e.getFirstSeen().before(windowStart)) {
204: if (!e.markedForDeletion) {
205: delMessages.add(e);
206: e.markedForDeletion = true;
207: }
208: }
209: }
210: while (c >= 0 && c != 0x0a) {
211: c = uidls.read();
212: }
213: if (c == 0x0a)
214: c = uidls.read();
215: }
216: getMessages(hook, newMessages);
217: deleteMessages(delMessages);
218: }
219:
220: /**
221: * Get the specified list of messages.
222: *
223: * @param v a ArrayList of UidlEntry types, specifying the messages to get.
224: */
225: void getMessages(MessageHook hook, ArrayList v) throws IOException {
226: for (int i = 0; i < v.size(); i++) {
227: UidlEntry e = (UidlEntry) v.get(i);
228: int msg = e.getMessageNumber();
229: boolean pass = hook.passAllHeaders();
230: if (!pass) {
231: InputStream is = pop3.top("" + msg, 0);
232: try {
233: HashMap map = new HashMap();
234: new HeaderEnumeration(is).getHeaderMap(map);
235: pass = hook.passHeaders(map);
236: } finally {
237: is.close();
238: }
239: }
240: if (pass) {
241: InputStream body = pop3.retr("" + msg);
242: try {
243: if (hook.passMessage(body)) {
244: if (!e.markedForDeletion) {
245: pop3.dele(msg);
246: e.markedForDeletion = true;
247: }
248: }
249: } catch (IOException e1) {
250: Debug.print(e1);
251: throw e1;
252: } catch (Exception e2) {
253: //#ifdef DEBUG
254: Debug.print(e2);
255: //#endif
256: throw new IOException(e2.toString());
257: } finally {
258: body.close();
259: }
260: }
261: }
262: }
263:
264: /**
265: * Delete the specified list of messages.
266: *
267: * @param v a ArrayList of UidlEntry types, specifying the
268: * messages to delete.
269: */
270: void deleteMessages(ArrayList v) throws IOException {
271: for (int i = 0; i < v.size(); i++) {
272: UidlEntry e = (UidlEntry) v.get(i);
273: int msg = e.getMessageNumber();
274: pop3.dele(msg);
275: uidlMap.remove(e.getUidl());
276: }
277: }
278:
279: /**
280: * Search the map for a specified <b>UIDL</b> entry.
281: *
282: * @param uidl the UIDL text to search for
283: *
284: * @return if found, the corresponding entry in the map. Otherwise, null.
285: */
286: UidlEntry getUidlEntry(String uidl) {
287: return (UidlEntry) uidlMap.get(uidl);
288: }
289:
290: /**
291: * Create and store a new UidlEntry in the map.
292: *
293: * @param uidl the UIDL text
294: * @param msgNum the current POP3 session's message number.
295: * @param d the date for this entry.
296: *
297: * @return the new UidlEntry
298: */
299: UidlEntry putUidlEntry(String uidl, int msgNum, Date d) {
300: UidlEntry e = new UidlEntry(uidl, msgNum, d);
301: uidlMap.put(uidl, e);
302: return e;
303: }
304: }
|