001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net.smtp;
019
020import java.io.BufferedReader;
021import java.io.BufferedWriter;
022import java.io.IOException;
023import java.io.InputStreamReader;
024import java.io.OutputStreamWriter;
025import java.util.ArrayList;
026
027import org.apache.commons.net.MalformedServerReplyException;
028import org.apache.commons.net.ProtocolCommandSupport;
029import org.apache.commons.net.SocketClient;
030import org.apache.commons.net.io.CRLFLineReader;
031import org.apache.commons.net.util.NetConstants;
032
033/**
034 * SMTP provides the basic the functionality necessary to implement your own SMTP client. To derive the full benefits of the SMTP class requires some knowledge
035 * of the FTP protocol defined in RFC 821. However, there is no reason why you should have to use the SMTP class. The
036 * {@link org.apache.commons.net.smtp.SMTPClient} class, derived from SMTP, implements all the functionality required of an SMTP client. The SMTP class is made
037 * public to provide access to various SMTP constants and to make it easier for adventurous programmers (or those with special needs) to interact with the SMTP
038 * protocol and implement their own clients. A set of methods with names corresponding to the SMTP command names are provided to facilitate this interaction.
039 * <p>
040 * You should keep in mind that the SMTP server may choose to prematurely close a connection for various reasons. The SMTP class will detect a premature SMTP
041 * server connection closing when it receives a {@link org.apache.commons.net.smtp.SMTPReply#SERVICE_NOT_AVAILABLE SMTPReply.SERVICE_NOT_AVAILABLE } response to
042 * a command. When that occurs, the SMTP class method encountering that reply will throw an {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} .
043 * <code>SMTPConectionClosedException</code> is a subclass of <code> IOException </code> and therefore need not be caught separately, but if you are going to
044 * catch it separately, its catch block must appear before the more general <code> IOException </code> catch block. When you encounter an
045 * {@link org.apache.commons.net.smtp.SMTPConnectionClosedException} , you must disconnect the connection with
046 * {@link org.apache.commons.net.SocketClient#disconnect disconnect() } to properly clean up the system resources used by SMTP. Before disconnecting, you may
047 * check the last reply code and text with {@link #getReplyCode getReplyCode }, {@link #getReplyString getReplyString }, and {@link #getReplyStrings
048 * getReplyStrings}.
049 * <p>
050 * Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a
051 * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the
052 * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as
053 * lenient as possible.
054 *
055 * @see SMTPClient
056 * @see SMTPConnectionClosedException
057 * @see org.apache.commons.net.MalformedServerReplyException
058 */
059
060public class SMTP extends SocketClient {
061    /** The default SMTP port (25). */
062    public static final int DEFAULT_PORT = 25;
063
064    // We have to ensure that the protocol communication is in ASCII
065    // but we use ISO-8859-1 just in case 8-bit characters cross
066    // the wire.
067    private static final String DEFAULT_ENCODING = "ISO-8859-1";
068
069    /**
070     * The encoding to use (user-settable).
071     *
072     * @since 3.1 (changed from private to protected)
073     */
074    protected final String encoding;
075
076    /**
077     * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and te firing of ProtocolCommandEvents.
078     */
079    protected ProtocolCommandSupport _commandSupport_;
080
081    BufferedReader reader;
082    BufferedWriter writer;
083
084    private int replyCode;
085    private final ArrayList<String> replyLines;
086    private boolean newReplyString;
087    private String replyString;
088
089    /**
090     * The default SMTP constructor. Sets the default port to <code>DEFAULT_PORT</code> and initializes internal data structures for saving SMTP reply
091     * information.
092     */
093    public SMTP() {
094        this(DEFAULT_ENCODING);
095    }
096
097    /**
098     * Overloaded constructor where the user may specify a default encoding.
099     *
100     * @param encoding the encoing to use
101     * @since 2.0
102     */
103    public SMTP(final String encoding) {
104        setDefaultPort(DEFAULT_PORT);
105        replyLines = new ArrayList<>();
106        newReplyString = false;
107        replyString = null;
108        _commandSupport_ = new ProtocolCommandSupport(this);
109        this.encoding = encoding;
110    }
111
112    /** Initiates control connections and gets initial reply. */
113    @Override
114    protected void _connectAction_() throws IOException {
115        super._connectAction_();
116        reader = new CRLFLineReader(new InputStreamReader(_input_, encoding));
117        writer = new BufferedWriter(new OutputStreamWriter(_output_, encoding));
118        getReply();
119    }
120
121    /**
122     * A convenience method to send the SMTP DATA command to the server, receive the reply, and return the reply code.
123     * <p>
124     *
125     * @return The reply code received from the server.
126     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
127     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
128     *                                       independently as itself.
129     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
130     */
131    public int data() throws IOException {
132        return sendCommand(SMTPCommand.DATA);
133    }
134
135    /**
136     * Closes the connection to the SMTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The reply text
137     * and code information from the last command is voided so that the memory it used may be reclaimed.
138     * <p>
139     *
140     * @throws IOException If an error occurs while disconnecting.
141     */
142    @Override
143    public void disconnect() throws IOException {
144        super.disconnect();
145        reader = null;
146        writer = null;
147        replyString = null;
148        replyLines.clear();
149        newReplyString = false;
150    }
151
152    /**
153     * A convenience method to send the SMTP VRFY command to the server, receive the reply, and return the reply code.
154     * <p>
155     *
156     * @param name The name to expand.
157     * @return The reply code received from the server.
158     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
159     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
160     *                                       independently as itself.
161     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
162     */
163    public int expn(final String name) throws IOException {
164        return sendCommand(SMTPCommand.EXPN, name);
165    }
166
167    /**
168     * Provide command support to super-class
169     */
170    @Override
171    protected ProtocolCommandSupport getCommandSupport() {
172        return _commandSupport_;
173    }
174
175    /**
176     * Fetches a reply from the SMTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from either
177     * calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. Only use this method if you are implementing your own SMTP
178     * client or if you need to fetch a secondary response from the SMTP server.
179     * <p>
180     *
181     * @return The integer value of the reply code of the fetched SMTP reply.
182     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
183     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
184     *                                       independently as itself.
185     * @throws IOException                   If an I/O error occurs while receiving the server reply.
186     */
187    public int getReply() throws IOException {
188        final int length;
189
190        newReplyString = true;
191        replyLines.clear();
192
193        String line = reader.readLine();
194
195        if (line == null) {
196            throw new SMTPConnectionClosedException("Connection closed without indication.");
197        }
198
199        // In case we run into an anomaly we don't want fatal index exceptions
200        // to be thrown.
201        length = line.length();
202        if (length < 3) {
203            throw new MalformedServerReplyException("Truncated server reply: " + line);
204        }
205
206        try {
207            final String code = line.substring(0, 3);
208            replyCode = Integer.parseInt(code);
209        } catch (final NumberFormatException e) {
210            throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + line);
211        }
212
213        replyLines.add(line);
214
215        // Get extra lines if message continues.
216        if (length > 3 && line.charAt(3) == '-') {
217            do {
218                line = reader.readLine();
219
220                if (line == null) {
221                    throw new SMTPConnectionClosedException("Connection closed without indication.");
222                }
223
224                replyLines.add(line);
225
226                // The length() check handles problems that could arise from readLine()
227                // returning too soon after encountering a naked CR or some other
228                // anomaly.
229            } while (!(line.length() >= 4 && line.charAt(3) != '-' && Character.isDigit(line.charAt(0))));
230            // This is too strong a condition because a non-conforming server
231            // could screw things up like ftp.funet.fi does for FTP
232            // line.startsWith(code)));
233        }
234
235        fireReplyReceived(replyCode, getReplyString());
236
237        if (replyCode == SMTPReply.SERVICE_NOT_AVAILABLE) {
238            throw new SMTPConnectionClosedException("SMTP response 421 received.  Server closed connection.");
239        }
240        return replyCode;
241    }
242
243    /**
244     * Returns the integer value of the reply code of the last SMTP reply. You will usually only use this method after you connect to the SMTP server to check
245     * that the connection was successful since <code> connect </code> is of type void.
246     * <p>
247     *
248     * @return The integer value of the reply code of the last SMTP reply.
249     */
250    public int getReplyCode() {
251        return replyCode;
252    }
253
254    /**
255     * Returns the entire text of the last SMTP server response exactly as it was received, including all end of line markers in NETASCII format.
256     * <p>
257     *
258     * @return The entire text from the last SMTP response as a String.
259     */
260    public String getReplyString() {
261        final StringBuilder buffer;
262
263        if (!newReplyString) {
264            return replyString;
265        }
266
267        buffer = new StringBuilder();
268
269        for (final String line : replyLines) {
270            buffer.append(line);
271            buffer.append(SocketClient.NETASCII_EOL);
272        }
273
274        newReplyString = false;
275
276        replyString = buffer.toString();
277        return replyString;
278    }
279
280    /**
281     * Returns the lines of text from the last SMTP server response as an array of strings, one entry per line. The end of line markers of each are stripped
282     * from each line.
283     * <p>
284     *
285     * @return The lines of text from the last SMTP response as an array.
286     */
287    public String[] getReplyStrings() {
288        return replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY);
289    }
290
291    /**
292     * A convenience method to send the SMTP HELO command to the server, receive the reply, and return the reply code.
293     * <p>
294     *
295     * @param hostname The hostname of the sender.
296     * @return The reply code received from the server.
297     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
298     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
299     *                                       independently as itself.
300     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
301     */
302    public int helo(final String hostname) throws IOException {
303        return sendCommand(SMTPCommand.HELO, hostname);
304    }
305
306    /**
307     * A convenience method to send the SMTP HELP command to the server, receive the reply, and return the reply code.
308     * <p>
309     *
310     * @return The reply code received from the server.
311     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
312     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
313     *                                       independently as itself.
314     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
315     */
316    public int help() throws IOException {
317        return sendCommand(SMTPCommand.HELP);
318    }
319
320    /**
321     * A convenience method to send the SMTP HELP command to the server, receive the reply, and return the reply code.
322     * <p>
323     *
324     * @param command The command name on which to request help.
325     * @return The reply code received from the server.
326     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
327     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
328     *                                       independently as itself.
329     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
330     */
331    public int help(final String command) throws IOException {
332        return sendCommand(SMTPCommand.HELP, command);
333    }
334
335    /**
336     * A convenience method to send the SMTP MAIL command to the server, receive the reply, and return the reply code.
337     * <p>
338     *
339     * @param reversePath The reverese path.
340     * @return The reply code received from the server.
341     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
342     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
343     *                                       independently as itself.
344     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
345     */
346    public int mail(final String reversePath) throws IOException {
347        return sendCommand(SMTPCommand.MAIL, reversePath, false);
348    }
349
350    /**
351     * A convenience method to send the SMTP NOOP command to the server, receive the reply, and return the reply code.
352     * <p>
353     *
354     * @return The reply code received from the server.
355     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
356     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
357     *                                       independently as itself.
358     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
359     */
360    public int noop() throws IOException {
361        return sendCommand(SMTPCommand.NOOP);
362    }
363
364    /**
365     * A convenience method to send the SMTP QUIT command to the server, receive the reply, and return the reply code.
366     * <p>
367     *
368     * @return The reply code received from the server.
369     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
370     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
371     *                                       independently as itself.
372     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
373     */
374    public int quit() throws IOException {
375        return sendCommand(SMTPCommand.QUIT);
376    }
377
378    /**
379     * A convenience method to send the SMTP RCPT command to the server, receive the reply, and return the reply code.
380     * <p>
381     *
382     * @param forwardPath The forward path.
383     * @return The reply code received from the server.
384     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
385     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
386     *                                       independently as itself.
387     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
388     */
389    public int rcpt(final String forwardPath) throws IOException {
390        return sendCommand(SMTPCommand.RCPT, forwardPath, false);
391    }
392
393    /**
394     * Removes a ProtocolCommandListener.
395     *
396     * Delegates this incorrectly named method - removeProtocolCommandistener (note the missing "L")- to the correct method
397     * {@link SocketClient#removeProtocolCommandListener}
398     *
399     * @param listener The ProtocolCommandListener to remove
400     */
401    public void removeProtocolCommandistener(final org.apache.commons.net.ProtocolCommandListener listener) {
402        removeProtocolCommandListener(listener);
403    }
404
405    /**
406     * A convenience method to send the SMTP RSET command to the server, receive the reply, and return the reply code.
407     * <p>
408     *
409     * @return The reply code received from the server.
410     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
411     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
412     *                                       independently as itself.
413     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
414     */
415    public int rset() throws IOException {
416        return sendCommand(SMTPCommand.RSET);
417    }
418
419    /**
420     * A convenience method to send the SMTP SAML command to the server, receive the reply, and return the reply code.
421     * <p>
422     *
423     * @param reversePath The reverese path.
424     * @return The reply code received from the server.
425     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
426     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
427     *                                       independently as itself.
428     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
429     */
430    public int saml(final String reversePath) throws IOException {
431        return sendCommand(SMTPCommand.SAML, reversePath);
432    }
433
434    /**
435     * A convenience method to send the SMTP SEND command to the server, receive the reply, and return the reply code.
436     * <p>
437     *
438     * @param reversePath The reverese path.
439     * @return The reply code received from the server.
440     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
441     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
442     *                                       independently as itself.
443     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
444     */
445    public int send(final String reversePath) throws IOException {
446        return sendCommand(SMTPCommand.SEND, reversePath);
447    }
448
449    /**
450     * Sends an SMTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
451     * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
452     * <p>
453     *
454     * @param command The SMTPCommand constant corresponding to the SMTP command to send.
455     * @return The integer value of the SMTP reply code returned by the server in response to the command.
456     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
457     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
458     *                                       independently as itself.
459     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
460     */
461    public int sendCommand(final int command) throws IOException {
462        return sendCommand(command, null);
463    }
464
465    /**
466     * Sends an SMTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
467     * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
468     * <p>
469     *
470     * @param command The SMTPCommand constant corresponding to the SMTP command to send.
471     * @param args    The arguments to the SMTP command. If this parameter is set to null, then the command is sent with no argument.
472     * @return The integer value of the SMTP reply code returned by the server in response to the command.
473     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
474     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
475     *                                       independently as itself.
476     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
477     */
478    public int sendCommand(final int command, final String args) throws IOException {
479        return sendCommand(SMTPCommand.getCommand(command), args);
480    }
481
482    /**
483     *
484     * @param command      the command to send (as an int defined in {@link SMPTCommand})
485     * @param args         the command arguments, may be {@code null}
486     * @param includeSpace if {@code true}, add a space between the command and its arguments
487     * @return the reply code
488     * @throws IOException
489     */
490    private int sendCommand(final int command, final String args, final boolean includeSpace) throws IOException {
491        return sendCommand(SMTPCommand.getCommand(command), args, includeSpace);
492    }
493
494    /**
495     * Sends an SMTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
496     * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
497     * <p>
498     *
499     * @param command The text representation of the SMTP command to send.
500     * @return The integer value of the SMTP reply code returned by the server in response to the command.
501     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
502     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
503     *                                       independently as itself.
504     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
505     */
506    public int sendCommand(final String command) throws IOException {
507        return sendCommand(command, null);
508    }
509
510    /**
511     * Sends an SMTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
512     * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
513     * <p>
514     *
515     * @param command The text representation of the SMTP command to send.
516     * @param args    The arguments to the SMTP command. If this parameter is set to null, then the command is sent with no argument.
517     * @return The integer value of the SMTP reply code returned by the server in response to the command.
518     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
519     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
520     *                                       independently as itself.
521     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
522     */
523    public int sendCommand(final String command, final String args) throws IOException {
524        return sendCommand(command, args, true);
525    }
526
527    /**
528     * Send a command to the server. May also be used to send text data.
529     *
530     * @param command      the command to send (as a plain String)
531     * @param args         the command arguments, may be {@code null}
532     * @param includeSpace if {@code true}, add a space between the command and its arguments
533     * @return the reply code
534     * @throws IOException
535     */
536    private int sendCommand(final String command, final String args, final boolean includeSpace) throws IOException {
537        final StringBuilder __commandBuffer = new StringBuilder();
538        __commandBuffer.append(command);
539
540        if (args != null) {
541            if (includeSpace) {
542                __commandBuffer.append(' ');
543            }
544            __commandBuffer.append(args);
545        }
546
547        __commandBuffer.append(SocketClient.NETASCII_EOL);
548
549        final String message = __commandBuffer.toString();
550        writer.write(message);
551        writer.flush();
552
553        fireCommandSent(command, message);
554
555        return getReply();
556    }
557
558    /**
559     * A convenience method to send the SMTP SOML command to the server, receive the reply, and return the reply code.
560     * <p>
561     *
562     * @param reversePath The reverese path.
563     * @return The reply code received from the server.
564     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
565     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
566     *                                       independently as itself.
567     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
568     */
569    public int soml(final String reversePath) throws IOException {
570        return sendCommand(SMTPCommand.SOML, reversePath);
571    }
572
573    /**
574     * A convenience method to send the SMTP TURN command to the server, receive the reply, and return the reply code.
575     * <p>
576     *
577     * @return The reply code received from the server.
578     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
579     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
580     *                                       independently as itself.
581     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
582     */
583    public int turn() throws IOException {
584        return sendCommand(SMTPCommand.TURN);
585    }
586
587    /**
588     * A convenience method to send the SMTP VRFY command to the server, receive the reply, and return the reply code.
589     * <p>
590     *
591     * @param user The user address to verify.
592     * @return The reply code received from the server.
593     * @throws SMTPConnectionClosedException If the SMTP server prematurely closes the connection as a result of the client being idle or some other reason
594     *                                       causing the server to send SMTP reply code 421. This exception may be caught either as an IOException or
595     *                                       independently as itself.
596     * @throws IOException                   If an I/O error occurs while either sending the command or receiving the server reply.
597     */
598    public int vrfy(final String user) throws IOException {
599        return sendCommand(SMTPCommand.VRFY, user);
600    }
601}