/[classpath]/inetlib/source/gnu/inet/smtp/SMTPConnection.java
ViewVC logotype

Diff of /inetlib/source/gnu/inet/smtp/SMTPConnection.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by dog, Sun Oct 19 08:51:37 2003 UTC revision 1.2 by dog, Sun Oct 19 16:16:50 2003 UTC
# Line 129  public class SMTPConnection Line 129  public class SMTPConnection
129     * port.     * port.
130     * @param host the server hostname     * @param host the server hostname
131     */     */
132    public SMTPConnection(String host)    public SMTPConnection(String host) throws IOException
     throws IOException  
133    {    {
134      this(host, DEFAULT_PORT);      this(host, DEFAULT_PORT);
135    }    }
136      
137    /**    /**
138     * Creates a new connection to the specified host, using the specified     * Creates a new connection to the specified host, using the specified
139     * port.     * port.
140     * @param host the server hostname     * @param host the server hostname
141     * @param port the port to connect to     * @param port the port to connect to
142     */     */
143    public SMTPConnection(String host, int port)    public SMTPConnection(String host, int port) throws IOException
     throws IOException  
144    {    {
145      this(host, port, -1, -1, false);      this(host, port, -1, -1, false);
146    }    }
147      
148    /**    /**
149     * Creates a new connection to the specified host, using the specified     * Creates a new connection to the specified host, using the specified
150     * port.     * port.
# Line 156  public class SMTPConnection Line 154  public class SMTPConnection
154     * @param timeout the I/O timeout in milliseconds     * @param timeout the I/O timeout in milliseconds
155     * @param debug whether to log progress     * @param debug whether to log progress
156     */     */
157    public SMTPConnection(String host, int port,    public SMTPConnection(String host, int port,
158        int connectionTimeout, int timeout, boolean debug)                          int connectionTimeout, int timeout, boolean debug)
159      throws IOException      throws IOException
160    {    {
161      if (port<=0)      if (port <= 0)
162        port = DEFAULT_PORT;        port = DEFAULT_PORT;
163      response = null;      response = null;
164      continuation = false;      continuation = false;
165      this.debug = debug;      this.debug = debug;
166        
167      // Initialise socket      // Initialise socket
168      // TODO connectionTimeout      // TODO connectionTimeout
169      socket = new Socket(host, port);      socket = new Socket(host, port);
170      if (timeout>0)      if (timeout > 0)
171        socket.setSoTimeout(timeout);        socket.setSoTimeout(timeout);
172        
173      // Initialise streams      // Initialise streams
174      InputStream in = socket.getInputStream();      InputStream in = socket.getInputStream();
175      in = new BufferedInputStream(in);        in = new BufferedInputStream(in);
176      in = new CRLFInputStream(in);        in = new CRLFInputStream(in);
177                  this.in = new LineInputStream(in);        this.in = new LineInputStream(in);
178      OutputStream out = socket.getOutputStream();      OutputStream out = socket.getOutputStream();
179      out = new BufferedOutputStream(out);        out = new BufferedOutputStream(out);
180      this.out = new CRLFOutputStream(out);        this.out = new CRLFOutputStream(out);
181        
182      // Greeting      // Greeting
183      if (getResponse()!=READY)      if (getResponse() != READY)
184        throw new ProtocolException(response);        throw new ProtocolException(response);
185    }    }
186    
# Line 198  public class SMTPConnection Line 196  public class SMTPConnection
196      throws IOException      throws IOException
197    {    {
198      StringBuffer command = new StringBuffer(MAIL_FROM);      StringBuffer command = new StringBuffer(MAIL_FROM);
199      command.append('<');        command.append('<');
200      command.append(reversePath);        command.append(reversePath);
201      command.append('>');        command.append('>');
202      if (parameters!=null)      if (parameters != null)
203      {      {
204        command.append(SP);        command.append(SP);
205        command.append(parameters);        command.append(parameters);
# Line 209  public class SMTPConnection Line 207  public class SMTPConnection
207      send(command.toString());      send(command.toString());
208      switch (getResponse())      switch (getResponse())
209      {      {
210        case OK:      case OK:
211        case OK_NOT_LOCAL:      case OK_NOT_LOCAL:
212        case OK_UNVERIFIED:      case OK_UNVERIFIED:
213          return true;        return true;
214        default:        default:return false;
         return false;  
215      }      }
216    }    }
217    
# Line 228  public class SMTPConnection Line 225  public class SMTPConnection
225      throws IOException      throws IOException
226    {    {
227      StringBuffer command = new StringBuffer(RCPT_TO);      StringBuffer command = new StringBuffer(RCPT_TO);
228      command.append('<');        command.append('<');
229      command.append(forwardPath);        command.append(forwardPath);
230      command.append('>');        command.append('>');
231      if (parameters!=null)      if (parameters != null)
232      {      {
233        command.append(SP);        command.append(SP);
234        command.append(parameters);        command.append(parameters);
# Line 239  public class SMTPConnection Line 236  public class SMTPConnection
236      send(command.toString());      send(command.toString());
237      switch (getResponse())      switch (getResponse())
238      {      {
239        case OK:      case OK:
240        case OK_NOT_LOCAL:      case OK_NOT_LOCAL:
241        case OK_UNVERIFIED:      case OK_UNVERIFIED:
242          return true;        return true;
243        default:      default:
244          return false;        return false;
245      }      }
246    }    }
247    
# Line 257  public class SMTPConnection Line 254  public class SMTPConnection
254     * must be called to complete the transfer and determine its success.     * must be called to complete the transfer and determine its success.
255     * @return a stream for writing messages to     * @return a stream for writing messages to
256     */     */
257    public OutputStream data()    public OutputStream data() throws IOException
     throws IOException  
258    {    {
259      send(DATA);      send(DATA);
260      switch (getResponse())      switch (getResponse())
261      {      {
262        case SEND_DATA:      case SEND_DATA:
263          return new MessageOutputStream(out);        return new MessageOutputStream(out);
264        default:        default:throw new ProtocolException(response);
         throw new ProtocolException(response);  
265      }      }
266    }    }
267    
# Line 275  public class SMTPConnection Line 270  public class SMTPConnection
270     * @see #data     * @see #data
271     * @return true id transfer was successful, false otherwise     * @return true id transfer was successful, false otherwise
272     */     */
273    public boolean finishData()    public boolean finishData() throws IOException
     throws IOException  
274    {    {
275      send(FINISH_DATA);      send(FINISH_DATA);
276      switch (getResponse())      switch (getResponse())
277      {      {
278        case OK:      case OK:
279          return true;        return true;
280        default:        default:return false;
         return false;  
281      }      }
282    }    }
283    
284    /**    /**
285     * Aborts the current mail transaction.     * Aborts the current mail transaction.
286     */     */
287    public void rset()    public void rset() throws IOException
     throws IOException  
288    {    {
289      send(RSET);      send(RSET);
290      if (getResponse()!=OK)      if (getResponse() != OK)
291        throw new ProtocolException(response);        throw new ProtocolException(response);
292    }    }
293    
# Line 306  public class SMTPConnection Line 298  public class SMTPConnection
298     * null on failure.     * null on failure.
299     * @param address a mailbox, or real name and mailbox     * @param address a mailbox, or real name and mailbox
300     */     */
301    public List vrfy(String address)    public List vrfy(String address) throws IOException
     throws IOException  
302    {    {
303      String command = new StringBuffer(VRFY)      String command =
304        .append(' ')        new StringBuffer(VRFY).append(' ').append(address).toString();
305        .append(address)        send(command);
       .toString();  
     send(command);  
306      List list = new ArrayList();      List list = new ArrayList();
307      do      do
308      {      {
309        switch (getResponse())        switch (getResponse())
310        {        {
311          case OK:        case OK:
312          case AMBIGUOUS:        case AMBIGUOUS:
313            response = response.trim();          response = response.trim();
314            if (response.indexOf('@')!=-1)          if (response.indexOf('@') != -1)
315              list.add(response);            list.add(response);
316            else if (response.indexOf('<')!=-1)          else if (response.indexOf('<') != -1)
317              list.add(response);            list.add(response);
318            else if (response.indexOf(' ')==-1)          else if (response.indexOf(' ') == -1)
319              list.add(response);            list.add(response);
320            break;          break;
321          default:          default:return null;
           return null;  
322        }        }
323      }      }
324      while (continuation);      while (continuation);
# Line 342  public class SMTPConnection Line 330  public class SMTPConnection
330     * or null on failure.     * or null on failure.
331     * @param address a mailing list name     * @param address a mailing list name
332     */     */
333    public List expn(String address)    public List expn(String address) throws IOException
     throws IOException  
334    {    {
335      String command = new StringBuffer(EXPN)      String command =
336        .append(' ')        new StringBuffer(EXPN).append(' ').append(address).toString();
337        .append(address)        send(command);
       .toString();  
     send(command);  
338      List list = new ArrayList();      List list = new ArrayList();
339      do      do
340      {      {
341        switch (getResponse())        switch (getResponse())
342        {        {
343          case OK:        case OK:
344            response = response.trim();          response = response.trim();
345            list.add(response);          list.add(response);
346            break;          break;
347          default:          default:return null;
           return null;  
348        }        }
349      }      }
350      while (continuation);      while (continuation);
# Line 374  public class SMTPConnection Line 358  public class SMTPConnection
358     * @return a list of possibly useful information, or null if the command     * @return a list of possibly useful information, or null if the command
359     * failed.     * failed.
360     */     */
361    public List help(String arg)    public List help(String arg) throws IOException
     throws IOException  
362    {    {
363      String command = (arg==null) ? HELP :      String command = (arg == null) ? HELP :
364        new StringBuffer(HELP).append(' ').append(arg).toString();        new StringBuffer(HELP).append(' ').append(arg).toString();
365      send(command);        send(command);
366      List list = new ArrayList();      List list = new ArrayList();
367      do      do
368      {      {
369        switch (getResponse())        switch (getResponse())
370        {        {
371          case INFO:        case INFO:
372            list.add(response);          list.add(response);
373            break;          break;
374          default:          default:return null;
           return null;  
375        }        }
376      }      }
377      while (continuation);      while (continuation);
378      return Collections.unmodifiableList(list);      return Collections.unmodifiableList(list);
379    }    }
380      
381    /**    /**
382     * Issues a NOOP command.     * Issues a NOOP command.
383     * This does nothing, but can be used to keep the connection alive.     * This does nothing, but can be used to keep the connection alive.
384     */     */
385    public void noop()    public void noop() throws IOException
     throws IOException  
386    {    {
387      send(NOOP);      send(NOOP);
388      do      do
# Line 414  public class SMTPConnection Line 395  public class SMTPConnection
395    /**    /**
396     * Close the connection to the server.     * Close the connection to the server.
397     */     */
398    public void quit()    public void quit() throws IOException
     throws IOException  
399    {    {
400      try      try
401      {      {
# Line 425  public class SMTPConnection Line 405  public class SMTPConnection
405         * many don't: postfix, for instance, sends 221.         * many don't: postfix, for instance, sends 221.
406         * In any case we have done our best. */         * In any case we have done our best. */
407      }      }
408      catch (IOException e)      catch(IOException e)
409      {      {
410      }      }
411      finally      finally
# Line 439  public class SMTPConnection Line 419  public class SMTPConnection
419     * Issues a HELO command.     * Issues a HELO command.
420     * @param hostname the local host name     * @param hostname the local host name
421     */     */
422    public boolean helo(String hostname)    public boolean helo(String hostname) throws IOException
     throws IOException  
423    {    {
424      String command = new StringBuffer(HELO)      String command =
425        .append(' ')        new StringBuffer(HELO).append(' ').append(hostname).toString();
426        .append(hostname)        send(command);
427        .toString();        return (getResponse() == OK);
     send(command);  
     return (getResponse()==OK);  
428    }    }
429    
430    /**    /**
# Line 457  public class SMTPConnection Line 434  public class SMTPConnection
434     * Otherwise returns null, and HELO should be called.     * Otherwise returns null, and HELO should be called.
435     * @param hostname the local host name     * @param hostname the local host name
436     */     */
437    public List ehlo(String hostname)    public List ehlo(String hostname) throws IOException
     throws IOException  
438    {    {
439      String command = new StringBuffer(EHLO)      String command =
440        .append(' ')        new StringBuffer(EHLO).append(' ').append(hostname).toString();
441        .append(hostname)        send(command);
       .toString();  
     send(command);  
442      List extensions = new ArrayList();      List extensions = new ArrayList();
443      do      do
444      {      {
445        switch (getResponse())        switch (getResponse())
446        {        {
447          case OK:        case OK:
448            extensions.add(response);          extensions.add(response);
449            break;          break;
450          default:          default:return null;
           return null;  
451        }        }
452      }      }
453      while (continuation);      while (continuation);
# Line 486  public class SMTPConnection Line 459  public class SMTPConnection
459     * This depends on many features, such as the JSSE classes being in the     * This depends on many features, such as the JSSE classes being in the
460     * classpath. Returns true if successful, false otherwise.     * classpath. Returns true if successful, false otherwise.
461     */     */
462    public boolean starttls()    public boolean starttls() throws IOException
     throws IOException  
463    {    {
464      try      try
465      {      {
466        // Use SSLSocketFactory to negotiate a TLS session and wrap the        // Use SSLSocketFactory to negotiate a TLS session and wrap the
467                          // current socket.        // current socket.
468                          SSLSocketFactory factory =        SSLSocketFactory factory =
469                                  (SSLSocketFactory)SSLSocketFactory.getDefault();          (SSLSocketFactory) SSLSocketFactory.getDefault();
470                            
471                          send(STARTTLS);          send(STARTTLS);
472                          if (getResponse()!=OK)        if (getResponse() != OK)
473                                  return false;            return false;
474                            
475                          socket = factory.createSocket(socket,          socket = factory.createSocket(socket,
476                                          socket.getInetAddress().getHostName(),                                        socket.getInetAddress().getHostName(),
477                                          socket.getPort(),                                        socket.getPort(), true);
                                         true);  
478        // Set up streams        // Set up streams
479        InputStream in = socket.getInputStream();        InputStream in = socket.getInputStream();
480        in = new BufferedInputStream(in);          in = new BufferedInputStream(in);
481        in = new CRLFInputStream(in);          in = new CRLFInputStream(in);
482                          this.in = new LineInputStream(in);          this.in = new LineInputStream(in);
483        OutputStream out = socket.getOutputStream();        OutputStream out = socket.getOutputStream();
484        out = new BufferedOutputStream(out);          out = new BufferedOutputStream(out);
485        this.out = new CRLFOutputStream(out);          this.out = new CRLFOutputStream(out);
486        return true;          return true;
487      }      }
488      catch (ClassNotFoundException e)      catch(ClassNotFoundException e)
489      {      {
490        return false; // No javax.net classes in runtime        return false;             // No javax.net classes in runtime
491      }      }
492    }    }
493    
494    // -- Authentication --    // -- Authentication --
495    
496          /**          /**
497           * Authenticates the connection using the specified SASL mechanism,           * Authenticates the connection using the specified SASL mechanism,
498           * username, and password.           * username, and password.
499           * @param mechanism a SASL authentication mechanism, e.g. LOGIN, PLAIN,           * @param mechanism a SASL authentication mechanism, e.g. LOGIN, PLAIN,
# Line 531  public class SMTPConnection Line 502  public class SMTPConnection
502           * @param password the authentication credentials           * @param password the authentication credentials
503           * @return true if authentication was successful, false otherwise           * @return true if authentication was successful, false otherwise
504           */           */
505          public boolean authenticate(String mechanism, String username, String password)    public boolean authenticate(String mechanism, String username,
506                  throws IOException                                String password) throws IOException
507          {    {
508                  try      try
509                  {      {
510                          String[] m = new String[] { mechanism };        String[]m = new String[]
511                          CallbackHandler ch = new SaslCallbackHandler(username, password);        {
512                          // Avoid lengthy callback procedure for GNU Crypto        mechanism};
513                          Properties p = new Properties();        CallbackHandler ch = new SaslCallbackHandler(username, password);
514                          p.put("gnu.crypto.sasl.username", username);        // Avoid lengthy callback procedure for GNU Crypto
515                          p.put("gnu.crypto.sasl.password", password);        Properties p = new Properties();
516                          SaslClient sasl = Sasl.createSaslClient(m, null, "smtp",        p.put("gnu.crypto.sasl.username", username);
517                                          socket.getInetAddress().getHostName(), p, ch);        p.put("gnu.crypto.sasl.password", password);
518          SaslClient sasl = Sasl.createSaslClient(m, null, "smtp",
519                          StringBuffer cmd = new StringBuffer(AUTH);                                                socket.getInetAddress().
520                          cmd.append(' ');                                                getHostName(), p, ch);
521                          cmd.append(mechanism);  
522                          if (sasl.hasInitialResponse())        StringBuffer cmd = new StringBuffer(AUTH);
523                          {        cmd.append(' ');
524                                  cmd.append(' ');        cmd.append(mechanism);
525                                  byte[] init = sasl.evaluateChallenge(new byte[0]);        if (sasl.hasInitialResponse())
526                                  cmd.append(new String(init, "US-ASCII"));        {
527                          }          cmd.append(' ');
528                          send(cmd.toString());          byte[]init = sasl.evaluateChallenge(new byte[0]);
529                          while (true)          cmd.append(new String(init, "US-ASCII"));
530                          {        }
531                                  switch (getResponse())        send(cmd.toString());
532                                  {        while (true)
533                                          case 334:        {
534                                                  try          switch (getResponse())
535                                                  {          {
536                                                          byte[] c0 = response.getBytes("US-ASCII");          case 334:
537                                                          byte[] c1 = BASE64.decode(c0); // challenge            try
538                                                          byte[] r0 = sasl.evaluateChallenge(c1);            {
539                                                          byte[] r1 = BASE64.encode(r0); // response              byte[]c0 = response.getBytes("US-ASCII");
540                                                          out.write(r1);              byte[]c1 = BASE64.decode(c0);       // challenge
541                                                          out.write(0x0d);              byte[]r0 = sasl.evaluateChallenge(c1);
542                                                          out.flush();              byte[]r1 = BASE64.encode(r0);       // response
543                                                  }              out.write(r1);
544                                                  catch (SaslException e)              out.write(0x0d);
545                                                  {              out.flush();
546                                                          // Error in SASL challenge evaluation - cancel exchange            }
547                                                          out.write(0x2a);            catch(SaslException e)
548                                                          out.write(0x0d);            {
549                                                          out.flush();              // Error in SASL challenge evaluation - cancel exchange
550                                                  }              out.write(0x2a);
551                                                  break;              out.write(0x0d);
552                                          case 235:              out.flush();
553                                                  String qop = (String)sasl.getNegotiatedProperty(Sasl.QOP);            }
554                                                  if ("auth-int".equalsIgnoreCase(qop)            break;
555                                                                  || "auth-conf".equalsIgnoreCase(qop))          case 235:
556                                                  {            String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP);
557                                                          InputStream in = socket.getInputStream();            if ("auth-int".equalsIgnoreCase(qop)
558                                                          in = new BufferedInputStream(in);                || "auth-conf".equalsIgnoreCase(qop))
559                                                          in = new SaslInputStream(sasl, in);            {
560                                                          in = new CRLFInputStream(in);              InputStream in = socket.getInputStream();
561                                                          this.in = new LineInputStream(in);              in = new BufferedInputStream(in);
562                                                          OutputStream out = socket.getOutputStream();              in = new SaslInputStream(sasl, in);
563                                                          out = new BufferedOutputStream(out);              in = new CRLFInputStream(in);
564                                                          out = new SaslOutputStream(sasl, out);              this.in = new LineInputStream(in);
565                                                          this.out = new CRLFOutputStream(out);              OutputStream out = socket.getOutputStream();
566                                                  }              out = new BufferedOutputStream(out);
567                                                  return true;              out = new SaslOutputStream(sasl, out);
568                                          default:              this.out = new CRLFOutputStream(out);
569                                                  return false;            }
570                                  }            return true;
571                          }          default:
572                  }            return false;
573                  catch (SaslException e)          }
574                  {        }
575                          return false; // No provider for mechanism      }
576                  }      catch(SaslException e)
577                  catch (ClassNotFoundException e)      {
578                  {        return false;             // No provider for mechanism
579                          return false; // No javax.security.sasl classes      }
580                  }      catch(ClassNotFoundException e)
581          }      {
582                                  return false;             // No javax.security.sasl classes
583        }
584      }
585    
586    /**    /**
587     * LOGIN authentication mechanism.     * LOGIN authentication mechanism.
588     * If this returns true, EHLO should be re-issued.     * If this returns true, EHLO should be re-issued.
# Line 663  public class SMTPConnection Line 637  public class SMTPConnection
637     * Send the specified command string to the server.     * Send the specified command string to the server.
638     * @param command the command to send     * @param command the command to send
639     */     */
640    protected void send(String command)    protected void send(String command) throws IOException
641      throws IOException    {
642          {      if (debug)
643                  if (debug)      {
644                  {        Logger logger = Logger.getInstance();
645                          Logger logger = Logger.getInstance();          logger.log("smtp", "> " + command);
646                          logger.log("smtp", "> "+command);      }
647                  }      out.write(command.getBytes("US-ASCII"));    // TODO check encoding
648                  out.write(command.getBytes("US-ASCII")); // TODO check encoding      out.write(0x0d);
649                  out.write(0x0d);      out.flush();
650                  out.flush();    }
         }  
651    
652    /**    /**
653     * Returns the next response from the server.     * Returns the next response from the server.
654     */     */
655    protected int getResponse()    protected int getResponse() throws IOException
656      throws IOException    {
657          {      String line = null;
658                  String line = null;        try
659                  try      {
660                  {        line = in.readLine();
661                          line = in.readLine();        // Handle special case eg 334 where CRLF occurs after code.
662                          // Handle special case eg 334 where CRLF occurs after code.        if (line.length() < 4)
663                          if (line.length()<4)          line =
664                                  line = new StringBuffer(line)            new StringBuffer(line).append('\n').append(in.readLine()).
665                                          .append('\n')            toString();
666                                          .append(in.readLine())        if (debug)
667                                          .toString();        {
668                          if (debug)          Logger logger = Logger.getInstance();
669                          {            logger.log("smtp", "< " + line);
670                                  Logger logger = Logger.getInstance();        }
671                                  logger.log("smtp", "< "+line);        int code = Integer.parseInt(line.substring(0, 3));
672                          }        continuation = (line.charAt(3) == '-');
673                          int code = Integer.parseInt(line.substring(0, 3));        response = line.substring(4);
674                          continuation = (line.charAt(3)=='-');        return code;
675                          response = line.substring(4);      }
676                          return code;      catch(NumberFormatException e)
677                  }      {
678                  catch (NumberFormatException e)        throw new ProtocolException("Unexpected response: " + line);
679                  {      }
680                          throw new ProtocolException("Unexpected response: "+line);    }
                 }  
         }  
681    
682  }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26