1 package org.apache.commons.net.pop3;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Commons" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.io.IOException;
58 import java.io.Reader;
59 import java.security.MessageDigest;
60 import java.security.NoSuchAlgorithmException;
61 import java.util.Enumeration;
62 import java.util.StringTokenizer;
63 import org.apache.commons.net.io.DotTerminatedMessageReader;
64
65 /****
66 * The POP3Client class implements the client side of the Internet POP3
67 * Protocol defined in RFC 1939. All commands are supported, including
68 * the APOP command which requires MD5 encryption. See RFC 1939 for
69 * more details on the POP3 protocol.
70 * <p>
71 * Rather than list it separately for each method, we mention here that
72 * every method communicating with the server and throwing an IOException
73 * can also throw a
74 * <a href="org.apache.commons.net.MalformedServerReplyException.html">
75 * MalformedServerReplyException </a>, which is a subclass
76 * of IOException. A MalformedServerReplyException will be thrown when
77 * the reply received from the server deviates enough from the protocol
78 * specification that it cannot be interpreted in a useful manner despite
79 * attempts to be as lenient as possible.
80 * <p>
81 * <p>
82 * @author Daniel F. Savarese
83 * @see POP3MessageInfo
84 * @see org.apache.commons.net.io.DotTerminatedMessageReader
85 * @see org.apache.commons.net.MalformedServerReplyException
86 ***/
87
88 public class POP3Client extends POP3
89 {
90
91 private static POP3MessageInfo __parseStatus(String line)
92 {
93 int num, size;
94 StringTokenizer tokenizer;
95
96 tokenizer = new StringTokenizer(line);
97
98 if (!tokenizer.hasMoreElements())
99 return null;
100
101 num = size = 0;
102
103 try
104 {
105 num = Integer.parseInt(tokenizer.nextToken());
106
107 if (!tokenizer.hasMoreElements())
108 return null;
109
110 size = Integer.parseInt(tokenizer.nextToken());
111 }
112 catch (NumberFormatException e)
113 {
114 return null;
115 }
116
117 return new POP3MessageInfo(num, size);
118 }
119
120 private static POP3MessageInfo __parseUID(String line)
121 {
122 int num;
123 StringTokenizer tokenizer;
124
125 tokenizer = new StringTokenizer(line);
126
127 if (!tokenizer.hasMoreElements())
128 return null;
129
130 num = 0;
131
132 try
133 {
134 num = Integer.parseInt(tokenizer.nextToken());
135
136 if (!tokenizer.hasMoreElements())
137 return null;
138
139 line = tokenizer.nextToken();
140 }
141 catch (NumberFormatException e)
142 {
143 return null;
144 }
145
146 return new POP3MessageInfo(num, line);
147 }
148
149 /****
150 * Login to the POP3 server with the given username and password. You
151 * must first connect to the server with
152 * <a href="org.apache.commons.net.SocketClient.html#connect"> connect </a>
153 * before attempting to login. A login attempt is only valid if
154 * the client is in the
155 * <a href="org.apache.commons.net.pop3.POP3.html#AUTHORIZATION_STATE">
156 * AUTHORIZATION_STATE </a>. After logging in, the client enters the
157 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
158 * TRANSACTION_STATE </a>.
159 * <p>
160 * @param username The account name being logged in to.
161 * @param password The plain text password of the account.
162 * @return True if the login attempt was successful, false if not.
163 * @exception IOException If a network I/O error occurs in the process of
164 * logging in.
165 ***/
166 public boolean login(String username, String password) throws IOException
167 {
168 if (getState() != AUTHORIZATION_STATE)
169 return false;
170
171 if (sendCommand(POP3Command.USER, username) != POP3Reply.OK)
172 return false;
173
174 if (sendCommand(POP3Command.PASS, password) != POP3Reply.OK)
175 return false;
176
177 setState(TRANSACTION_STATE);
178
179 return true;
180 }
181
182
183 /****
184 * Login to the POP3 server with the given username and authentication
185 * information. Use this method when connecting to a server requiring
186 * authentication using the APOP command. Because the timestamp
187 * produced in the greeting banner varies from server to server, it is
188 * not possible to consistently extract the information. Therefore,
189 * after connecting to the server, you must call
190 * <a href="org.apache.commons.net.pop3.POP3.html#getReplyString">
191 * getReplyString </a> and parse out the timestamp information yourself.
192 * <p>
193 * You must first connect to the server with
194 * <a href="org.apache.commons.net.SocketClient.html#connect"> connect </a>
195 * before attempting to login. A login attempt is only valid if
196 * the client is in the
197 * <a href="org.apache.commons.net.pop3.POP3.html#AUTHORIZATION_STATE">
198 * AUTHORIZATION_STATE </a>. After logging in, the client enters the
199 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
200 * TRANSACTION_STATE </a>. After connecting, you must parse out the
201 * server specific information to use as a timestamp, and pass that
202 * information to this method. The secret is a shared secret known
203 * to you and the server. See RFC 1939 for more details regarding
204 * the APOP command.
205 * <p>
206 * @param username The account name being logged in to.
207 * @param timestamp The timestamp string to combine with the secret.
208 * @param secret The shared secret which produces the MD5 digest when
209 * combined with the timestamp.
210 * @return True if the login attempt was successful, false if not.
211 * @exception IOException If a network I/O error occurs in the process of
212 * logging in.
213 * @exception NoSuchAlgorithmException If the MD5 encryption algorithm
214 * cannot be instantiated by the Java runtime system.
215 ***/
216 public boolean login(String username, String timestamp, String secret)
217 throws IOException, NoSuchAlgorithmException
218 {
219 int i;
220 byte[] digest;
221 StringBuffer buffer, digestBuffer;
222 MessageDigest md5;
223
224 if (getState() != AUTHORIZATION_STATE)
225 return false;
226
227 md5 = MessageDigest.getInstance("MD5");
228 timestamp += secret;
229 digest = md5.digest(timestamp.getBytes());
230 digestBuffer = new StringBuffer(128);
231
232 for (i = 0; i < digest.length; i++)
233 digestBuffer.append(Integer.toHexString(digest[i] & 0xff));
234
235 buffer = new StringBuffer(256);
236 buffer.append(username);
237 buffer.append(' ');
238 buffer.append(digestBuffer.toString());
239
240 if (sendCommand(POP3Command.APOP, buffer.toString()) != POP3Reply.OK)
241 return false;
242
243 setState(TRANSACTION_STATE);
244
245 return true;
246 }
247
248
249 /****
250 * Logout of the POP3 server. To fully disconnect from the server
251 * you must call
252 * <a href="org.apache.commons.net.pop3.POP3.html#disconnect"> disconnect </a>.
253 * A logout attempt is valid in any state. If
254 * the client is in the
255 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
256 * TRANSACTION_STATE </a>, it enters the
257 * <a href="org.apache.commons.net.pop3.POP3.html#UPDATE_STATE">
258 * UPDATE_STATE </a> on a successful logout.
259 * <p>
260 * @return True if the logout attempt was successful, false if not.
261 * @exception IOException If a network I/O error occurs in the process
262 * of logging out.
263 ***/
264 public boolean logout() throws IOException
265 {
266 if (getState() == TRANSACTION_STATE)
267 setState(UPDATE_STATE);
268 sendCommand(POP3Command.QUIT);
269 return (_replyCode == POP3Reply.OK);
270 }
271
272
273 /****
274 * Send a NOOP command to the POP3 server. This is useful for keeping
275 * a connection alive since most POP3 servers will timeout after 10
276 * minutes of inactivity. A noop attempt will only succeed if
277 * the client is in the
278 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
279 * TRANSACTION_STATE </a>.
280 * <p>
281 * @return True if the noop attempt was successful, false if not.
282 * @exception IOException If a network I/O error occurs in the process of
283 * sending the NOOP command.
284 ***/
285 public boolean noop() throws IOException
286 {
287 if (getState() == TRANSACTION_STATE)
288 return (sendCommand(POP3Command.NOOP) == POP3Reply.OK);
289 return false;
290 }
291
292
293 /****
294 * Delete a message from the POP3 server. The message is only marked
295 * for deletion by the server. If you decide to unmark the message, you
296 * must issuse a <a href="#reset"> reset </a> command. Messages marked
297 * for deletion are only deleted by the server on
298 * <a href="#logout"> logout </a>.
299 * A delete attempt can only succeed if the client is in the
300 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
301 * TRANSACTION_STATE </a>.
302 * <p>
303 * @param messageId The message number to delete.
304 * @return True if the deletion attempt was successful, false if not.
305 * @exception IOException If a network I/O error occurs in the process of
306 * sending the delete command.
307 ***/
308 public boolean deleteMessage(int messageId) throws IOException
309 {
310 if (getState() == TRANSACTION_STATE)
311 return (sendCommand(POP3Command.DELE, Integer.toString(messageId))
312 == POP3Reply.OK);
313 return false;
314 }
315
316
317 /****
318 * Reset the POP3 session. This is useful for undoing any message
319 * deletions that may have been performed. A reset attempt can only
320 * succeed if the client is in the
321 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
322 * TRANSACTION_STATE </a>.
323 * <p>
324 * @return True if the reset attempt was successful, false if not.
325 * @exception IOException If a network I/O error occurs in the process of
326 * sending the reset command.
327 ***/
328 public boolean reset() throws IOException
329 {
330 if (getState() == TRANSACTION_STATE)
331 return (sendCommand(POP3Command.RSET) == POP3Reply.OK);
332 return false;
333 }
334
335 /****
336 * Get the mailbox status. A status attempt can only
337 * succeed if the client is in the
338 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
339 * TRANSACTION_STATE </a>. Returns a POP3MessageInfo instance
340 * containing the number of messages in the mailbox and the total
341 * size of the messages in bytes. Returns null if the status the
342 * attempt fails.
343 * <p>
344 * @return A POP3MessageInfo instance containing the number of
345 * messages in the mailbox and the total size of the messages
346 * in bytes. Returns null if the status the attempt fails.
347 * @exception IOException If a network I/O error occurs in the process of
348 * sending the status command.
349 ***/
350 public POP3MessageInfo status() throws IOException
351 {
352 if (getState() != TRANSACTION_STATE)
353 return null;
354 if (sendCommand(POP3Command.STAT) != POP3Reply.OK)
355 return null;
356 return __parseStatus(_lastReplyLine.substring(3));
357 }
358
359
360 /****
361 * List an individual message. A list attempt can only
362 * succeed if the client is in the
363 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
364 * TRANSACTION_STATE </a>. Returns a POP3MessageInfo instance
365 * containing the number of the listed message and the
366 * size of the message in bytes. Returns null if the list
367 * attempt fails (e.g., if the specified message number does
368 * not exist).
369 * <p>
370 * @param messageId The number of the message list.
371 * @return A POP3MessageInfo instance containing the number of the
372 * listed message and the size of the message in bytes. Returns
373 * null if the list attempt fails.
374 * @exception IOException If a network I/O error occurs in the process of
375 * sending the list command.
376 ***/
377 public POP3MessageInfo listMessage(int messageId) throws IOException
378 {
379 if (getState() != TRANSACTION_STATE)
380 return null;
381 if (sendCommand(POP3Command.LIST, Integer.toString(messageId))
382 != POP3Reply.OK)
383 return null;
384 return __parseStatus(_lastReplyLine.substring(3));
385 }
386
387
388 /****
389 * List all messages. A list attempt can only
390 * succeed if the client is in the
391 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
392 * TRANSACTION_STATE </a>. Returns an array of POP3MessageInfo instances,
393 * each containing the number of a message and its size in bytes.
394 * If there are no messages, this method returns a zero length array.
395 * If the list attempt fails, it returns null.
396 * <p>
397 * @return An array of POP3MessageInfo instances representing all messages
398 * in the order they appear in the mailbox,
399 * each containing the number of a message and its size in bytes.
400 * If there are no messages, this method returns a zero length array.
401 * If the list attempt fails, it returns null.
402 * @exception IOException If a network I/O error occurs in the process of
403 * sending the list command.
404 ***/
405 public POP3MessageInfo[] listMessages() throws IOException
406 {
407 POP3MessageInfo[] messages;
408 Enumeration enum;
409 int line;
410
411 if (getState() != TRANSACTION_STATE)
412 return null;
413 if (sendCommand(POP3Command.LIST) != POP3Reply.OK)
414 return null;
415 getAdditionalReply();
416
417 // This could be a zero length array if no messages present
418 messages = new POP3MessageInfo[_replyLines.size() - 2];
419 enum = _replyLines.elements();
420
421 // Skip first line
422 enum.nextElement();
423
424 // Fetch lines.
425 for (line = 0; line < messages.length; line++)
426 messages[line] = __parseStatus((String)enum.nextElement());
427
428 return messages;
429 }
430
431 /****
432 * List the unique identifier for a message. A list attempt can only
433 * succeed if the client is in the
434 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
435 * TRANSACTION_STATE </a>. Returns a POP3MessageInfo instance
436 * containing the number of the listed message and the
437 * unique identifier for that message. Returns null if the list
438 * attempt fails (e.g., if the specified message number does
439 * not exist).
440 * <p>
441 * @param messageId The number of the message list.
442 * @return A POP3MessageInfo instance containing the number of the
443 * listed message and the unique identifier for that message.
444 * Returns null if the list attempt fails.
445 * @exception IOException If a network I/O error occurs in the process of
446 * sending the list unique identifier command.
447 ***/
448 public POP3MessageInfo listUniqueIdentifier(int messageId)
449 throws IOException
450 {
451 if (getState() != TRANSACTION_STATE)
452 return null;
453 if (sendCommand(POP3Command.UIDL, Integer.toString(messageId))
454 != POP3Reply.OK)
455 return null;
456 return __parseUID(_lastReplyLine.substring(3));
457 }
458
459
460 /****
461 * List the unique identifiers for all messages. A list attempt can only
462 * succeed if the client is in the
463 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
464 * TRANSACTION_STATE </a>. Returns an array of POP3MessageInfo instances,
465 * each containing the number of a message and its unique identifier.
466 * If there are no messages, this method returns a zero length array.
467 * If the list attempt fails, it returns null.
468 * <p>
469 * @return An array of POP3MessageInfo instances representing all messages
470 * in the order they appear in the mailbox,
471 * each containing the number of a message and its unique identifier
472 * If there are no messages, this method returns a zero length array.
473 * If the list attempt fails, it returns null.
474 * @exception IOException If a network I/O error occurs in the process of
475 * sending the list unique identifier command.
476 ***/
477 public POP3MessageInfo[] listUniqueIdentifiers() throws IOException
478 {
479 POP3MessageInfo[] messages;
480 Enumeration enum;
481 int line;
482
483 if (getState() != TRANSACTION_STATE)
484 return null;
485 if (sendCommand(POP3Command.UIDL) != POP3Reply.OK)
486 return null;
487 getAdditionalReply();
488
489 // This could be a zero length array if no messages present
490 messages = new POP3MessageInfo[_replyLines.size() - 2];
491 enum = _replyLines.elements();
492
493 // Skip first line
494 enum.nextElement();
495
496 // Fetch lines.
497 for (line = 0; line < messages.length; line++)
498 messages[line] = __parseUID((String)enum.nextElement());
499
500 return messages;
501 }
502
503
504 /****
505 * Retrieve a message from the POP3 server. A retrieve message attempt
506 * can only succeed if the client is in the
507 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
508 * TRANSACTION_STATE </a>. Returns a DotTerminatedMessageReader instance
509 * from which the entire message can be read.
510 * Returns null if the retrieval attempt fails (e.g., if the specified
511 * message number does not exist).
512 * <p>
513 * You must not issue any commands to the POP3 server (i.e., call any
514 * other methods) until you finish reading the message from the
515 * returned Reader instance.
516 * The POP3 protocol uses the same stream for issuing commands as it does
517 * for returning results. Therefore the returned Reader actually reads
518 * directly from the POP3 connection. After the end of message has been
519 * reached, new commands can be executed and their replies read. If
520 * you do not follow these requirements, your program will not work
521 * properly.
522 * <p>
523 * @param messageId The number of the message to fetch.
524 * @return A DotTerminatedMessageReader instance
525 * from which the entire message can be read.
526 * Returns null if the retrieval attempt fails (e.g., if the specified
527 * message number does not exist).
528 * @exception IOException If a network I/O error occurs in the process of
529 * sending the retrieve message command.
530 ***/
531 public Reader retrieveMessage(int messageId) throws IOException
532 {
533 if (getState() != TRANSACTION_STATE)
534 return null;
535 if (sendCommand(POP3Command.RETR, Integer.toString(messageId))
536 != POP3Reply.OK)
537 return null;
538
539 return new DotTerminatedMessageReader(_reader);
540 }
541
542
543 /****
544 * Retrieve only the specified top number of lines of a message from the
545 * POP3 server. A retrieve top lines attempt
546 * can only succeed if the client is in the
547 * <a href="org.apache.commons.net.pop3.POP3.html#TRANSACTION_STATE">
548 * TRANSACTION_STATE </a>. Returns a DotTerminatedMessageReader instance
549 * from which the specified top number of lines of the message can be
550 * read.
551 * Returns null if the retrieval attempt fails (e.g., if the specified
552 * message number does not exist).
553 * <p>
554 * You must not issue any commands to the POP3 server (i.e., call any
555 * other methods) until you finish reading the message from the returned
556 * Reader instance.
557 * The POP3 protocol uses the same stream for issuing commands as it does
558 * for returning results. Therefore the returned Reader actually reads
559 * directly from the POP3 connection. After the end of message has been
560 * reached, new commands can be executed and their replies read. If
561 * you do not follow these requirements, your program will not work
562 * properly.
563 * <p>
564 * @param messageId The number of the message to fetch.
565 * @param numLines The top number of lines to fetch. This must be >= 0.
566 * @return A DotTerminatedMessageReader instance
567 * from which the specified top number of lines of the message can be
568 * read.
569 * Returns null if the retrieval attempt fails (e.g., if the specified
570 * message number does not exist).
571 * @exception IOException If a network I/O error occurs in the process of
572 * sending the top command.
573 ***/
574 public Reader retrieveMessageTop(int messageId, int numLines)
575 throws IOException
576 {
577 if (numLines < 0 || getState() != TRANSACTION_STATE)
578 return null;
579 if (sendCommand(POP3Command.TOP, Integer.toString(messageId) + " " +
580 Integer.toString(numLines)) != POP3Reply.OK)
581 return null;
582
583 return new DotTerminatedMessageReader(_reader);
584 }
585
586
587 }
588
This page was automatically generated by Maven