1 21 22 27 28 package com.sun.mail.pop3; 29 30 import java.util.*; 31 import java.net.*; 32 import java.io.*; 33 import java.security.*; 34 35 import com.sun.mail.util.LineInputStream; 36 import com.sun.mail.util.SocketFetcher; 37 import javax.mail.util.SharedByteArrayInputStream; 38 39 class Response { 40 boolean ok = false; String data = null; InputStream bytes = null; } 44 45 53 class Protocol { 54 private Socket socket; private DataInputStream input; private PrintWriter output; private static final int POP3_PORT = 110; private static final String CRLF = "\r\n"; 59 private boolean debug = false; 60 private PrintStream out; 61 private String apopChallenge = null; 62 63 66 Protocol(String host, int port, boolean debug, PrintStream out, 67 Properties props, String prefix, boolean isSSL) 68 throws IOException { 69 this.debug = debug; 70 this.out = out; 71 Response r; 72 String apop = props.getProperty(prefix + ".apop.enable"); 73 boolean enableAPOP = apop != null && apop.equalsIgnoreCase("true"); 74 try { 75 if (port == -1) 76 port = POP3_PORT; 77 if (debug) 78 out.println("DEBUG POP3: connecting to host \"" + host + 79 "\", port " + port + ", isSSL " + isSSL); 80 81 socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL); 82 83 input = new DataInputStream( 84 new BufferedInputStream(socket.getInputStream())); 85 output = new PrintWriter( 86 new BufferedWriter( 87 new OutputStreamWriter(socket.getOutputStream(), 88 "iso-8859-1"))); 89 91 r = simpleCommand(null); 92 } catch (IOException ioe) { 93 try { 94 socket.close(); 95 } finally { 96 throw ioe; 97 } 98 } 99 100 if (!r.ok) { 101 try { 102 socket.close(); 103 } finally { 104 throw new IOException("Connect failed"); 105 } 106 } 107 if (enableAPOP) { 108 int challStart = r.data.indexOf('<'); int challEnd = r.data.indexOf('>', challStart); if (challStart != -1 && challEnd != -1) 111 apopChallenge = r.data.substring(challStart, challEnd + 1); 112 if (debug) 113 out.println("DEBUG POP3: APOP challenge: " + apopChallenge); 114 } 115 } 116 117 protected void finalize() throws Throwable { 118 super.finalize(); 119 if (socket != null) { quit(); 121 } 122 } 123 124 127 synchronized String login(String user, String password) 128 throws IOException { 129 Response r; 130 String dpw = null; 131 if (apopChallenge != null) 132 dpw = getDigest(password); 133 if (apopChallenge != null && dpw != null) { 134 r = simpleCommand("APOP " + user + " " + dpw); 135 } else { 136 r = simpleCommand("USER " + user); 137 if (!r.ok) 138 return r.data != null ? r.data : "USER command failed"; 139 r = simpleCommand("PASS " + password); 140 } 141 if (!r.ok) 142 return r.data != null ? r.data : "login failed"; 143 return null; 144 } 145 146 159 private String getDigest(String password) { 160 String key = apopChallenge + password; 161 byte[] digest; 162 try { 163 MessageDigest md = MessageDigest.getInstance("MD5"); 164 digest = md.digest(key.getBytes("iso-8859-1")); } catch (NoSuchAlgorithmException nsae) { 166 return null; 167 } catch (UnsupportedEncodingException uee) { 168 return null; 169 } 170 return toHex(digest); 171 } 172 173 private static char[] digits = { 174 '0', '1', '2', '3', '4', '5', '6', '7', 175 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 176 }; 177 178 181 private static String toHex(byte[] bytes) { 182 char[] result = new char[bytes.length * 2]; 183 184 for (int index = 0, i = 0; index < bytes.length; index++) { 185 int temp = bytes[index] & 0xFF; 186 result[i++] = digits[temp >> 4]; 187 result[i++] = digits[temp & 0xF]; 188 } 189 return new String (result); 190 } 191 192 196 synchronized boolean quit() throws IOException { 197 boolean ok = false; 198 try { 199 Response r = simpleCommand("QUIT"); 200 ok = r.ok; 201 } finally { 202 try { 203 socket.close(); 204 } finally { 205 socket = null; 206 input = null; 207 output = null; 208 } 209 } 210 return ok; 211 } 212 213 217 synchronized Status stat() throws IOException { 218 Response r = simpleCommand("STAT"); 219 Status s = new Status(); 220 if (r.ok && r.data != null) { 221 try { 222 StringTokenizer st = new StringTokenizer(r.data); 223 s.total = Integer.parseInt(st.nextToken()); 224 s.size = Integer.parseInt(st.nextToken()); 225 } catch (Exception e) { 226 } 227 } 228 return s; 229 } 230 231 234 synchronized int list(int msg) throws IOException { 235 Response r = simpleCommand("LIST " + msg); 236 int size = -1; 237 if (r.ok && r.data != null) { 238 try { 239 StringTokenizer st = new StringTokenizer(r.data); 240 st.nextToken(); size = Integer.parseInt(st.nextToken()); 242 } catch (Exception e) { 243 } 244 } 245 return size; 246 } 247 248 251 synchronized InputStream list() throws IOException { 252 Response r = multilineCommand("LIST", 128); return r.bytes; 254 } 255 256 262 synchronized InputStream retr(int msg, int size) throws IOException { 263 Response r = multilineCommand("RETR " + msg, size); 264 return r.bytes; 265 } 266 267 270 synchronized InputStream top(int msg, int n) throws IOException { 271 Response r = multilineCommand("TOP " + msg + " " + n, 0); 272 return r.bytes; 273 } 274 275 278 synchronized boolean dele(int msg) throws IOException { 279 Response r = simpleCommand("DELE " + msg); 280 return r.ok; 281 } 282 283 286 synchronized String uidl(int msg) throws IOException { 287 Response r = simpleCommand("UIDL " + msg); 288 if (!r.ok) 289 return null; 290 int i = r.data.indexOf(' '); 291 if (i > 0) 292 return r.data.substring(i + 1); 293 else 294 return null; 295 } 296 297 301 synchronized boolean uidl(String [] uids) throws IOException { 302 Response r = multilineCommand("UIDL", 15 * uids.length); 303 if (!r.ok) 304 return false; 305 LineInputStream lis = new LineInputStream(r.bytes); 306 String line = null; 307 while ((line = lis.readLine()) != null) { 308 int i = line.indexOf(' '); 309 if (i < 1 || i >= line.length()) 310 continue; 311 int n = Integer.parseInt(line.substring(0, i)); 312 if (n > 0 && n <= uids.length) 313 uids[n - 1] = line.substring(i + 1); 314 } 315 return true; 316 } 317 318 321 synchronized boolean noop() throws IOException { 322 Response r = simpleCommand("NOOP"); 323 return r.ok; 324 } 325 326 329 synchronized boolean rset() throws IOException { 330 Response r = simpleCommand("RSET"); 331 return r.ok; 332 } 333 334 337 private Response simpleCommand(String cmd) throws IOException { 338 if (socket == null) 339 throw new IOException("Folder is closed"); if (cmd != null) { 341 if (debug) 342 out.println("C: " + cmd); 343 cmd += CRLF; 344 output.print(cmd); output.flush(); 346 } 347 String line = input.readLine(); if (line == null) { 349 if (debug) 350 out.println("S: EOF"); 351 throw new EOFException("EOF on socket"); 352 } 353 if (debug) 354 out.println("S: " + line); 355 Response r = new Response(); 356 if (line.startsWith("+OK")) 357 r.ok = true; 358 else if (line.startsWith("-ERR")) 359 r.ok = false; 360 else 361 throw new IOException("Unexpected response: " + line); 362 int i; 363 if ((i = line.indexOf(' ')) >= 0) 364 r.data = line.substring(i + 1); 365 return r; 366 } 367 368 372 private Response multilineCommand(String cmd, int size) throws IOException { 373 Response r = simpleCommand(cmd); 374 if (!r.ok) 375 return (r); 376 377 SharedByteArrayOutputStream buf = new SharedByteArrayOutputStream(size); 378 int b, lastb = '\n'; 379 while ((b = input.read()) >= 0) { 380 if (lastb == '\n' && b == '.') { 381 if (debug) 382 out.write(b); 383 b = input.read(); 384 if (b == '\r') { 385 if (debug) 386 out.write(b); 387 b = input.read(); 389 if (debug) 390 out.write(b); 391 break; 392 } 393 } 394 buf.write(b); 395 if (debug) 396 out.write(b); 397 lastb = b; 398 } 399 if (b < 0) 400 throw new EOFException("EOF on socket"); 401 r.bytes = buf.toStream(); 402 return r; 403 } 404 } 405 406 411 class SharedByteArrayOutputStream extends ByteArrayOutputStream { 412 public SharedByteArrayOutputStream(int size) { 413 super(size); 414 } 415 416 public InputStream toStream() { 417 return new SharedByteArrayInputStream(buf, 0, count); 418 } 419 } 420 | Popular Tags |