001    /*
002     * Copyright 2010 the original author or authors.
003     * 
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package com.hs.mail.deliver;
017    
018    import java.io.File;
019    import java.io.FileInputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.util.HashSet;
023    import java.util.Properties;
024    import java.util.Set;
025    
026    import org.apache.commons.cli.CommandLine;
027    import org.apache.commons.cli.HelpFormatter;
028    import org.apache.commons.cli.OptionBuilder;
029    import org.apache.commons.cli.Options;
030    import org.apache.commons.cli.ParseException;
031    import org.apache.commons.cli.PosixParser;
032    import org.apache.commons.io.FileUtils;
033    import org.apache.commons.io.IOUtils;
034    import org.apache.commons.lang.ArrayUtils;
035    import org.apache.james.mime4j.field.address.Address;
036    import org.apache.james.mime4j.field.address.AddressList;
037    import org.apache.james.mime4j.field.address.Mailbox;
038    import org.apache.log4j.Logger;
039    import org.springframework.core.io.FileSystemResource;
040    import org.springframework.core.io.support.PropertiesLoaderUtils;
041    
042    import com.hs.mail.container.config.Config;
043    import com.hs.mail.imap.message.MessageHeader;
044    import com.hs.mail.smtp.message.MailAddress;
045    import com.hs.mail.smtp.message.Recipient;
046    import com.hs.mail.smtp.message.SmtpMessage;
047    
048    /**
049     * 
050     * @author Won Chul Doh
051     * @since Jun 21, 2010
052     * 
053     */
054    
055    @SuppressWarnings("static-access")
056    public class Deliver {
057            
058            static Logger logger = Logger.getLogger(Deliver.class);
059    
060            /**
061             * Delivery was successful.
062             */
063            private static final int EX_OK = 0;
064            /**
065             * Invalid parameter given.
066             */
067            private static final int EX_USAGE = 64;
068            /**
069             * A temporary failure. This is returned for almost all failures. See the
070             * log file for details.
071             */
072            private static final int EX_TEMPFAIL = 75;
073            /**
074             * Mail was rejected. Typically this happens when user is over quota.
075             */
076            //private static final int EX_NOPERM = 77;
077            /**
078             * Failed to read configuration file, a missing configuration setting.
079             */
080            private static final int EX_CONFIG = 78;
081    
082            private static final String DEFAULT_CONFIG_LOCATION = "../conf/default.properties";
083            
084            private File spool;
085    
086            private static final Options OPTS = new Options();
087            static {
088                    OPTS.addOption(OptionBuilder.withArgName("file")
089                                                            .hasArg()
090                                                            .withDescription("Configuration file path")
091                                                            .create("c"));
092                    OPTS.addOption(OptionBuilder.withArgName("file")
093                                                            .hasArg()
094                                                            .isRequired()
095                                                            .withDescription("Path to the mail to be delivered")
096                                                            .create("p"));
097                    OPTS.addOption(OptionBuilder.withArgName("address")
098                                                            .hasArg()
099                                                            .withDescription("Envelope sender address")
100                                                            .create("f"));
101                    OPTS.addOption(OptionBuilder.withArgName("addresses")
102                                                            .hasArgs()
103                                                            .withDescription("List of destination addresses")
104                                                            .create("r"));
105            }
106            
107            private void init(String path) {
108                    try {
109                            File configFile = new File(path);
110                            Properties props = PropertiesLoaderUtils
111                                            .loadProperties(new FileSystemResource(configFile));
112                            String dir = props.getProperty("queue_directory");
113                            spool = (dir != null) 
114                                            ? new File(dir) 
115                                            : new File(configFile.getParentFile().getParentFile(), "spool");
116                            if (!spool.isDirectory()) {
117                                    logger.error("Spool directory does not exist: "
118                                                    + spool.getAbsolutePath());
119                                    System.exit(EX_CONFIG);
120                            }
121                            Config.setSpoolDirectory(spool);
122                    } catch (IOException ex) {
123                            logger.error(ex.getMessage(), ex);
124                            System.exit(EX_CONFIG);
125                    }
126            }
127            
128            private void deliver(String from, String[] rcpts, File file) {
129                    SmtpMessage message = null;
130                    try {
131                            MailAddress sender = new MailAddress(from, false);
132                            message = new SmtpMessage(sender, SmtpMessage.LOCAL);
133                            for (int i = 0; i < rcpts.length; i++) {
134                                    Recipient rcpt = new Recipient(rcpts[i], false);
135                                    message.addRecipient(rcpt);
136                            }
137                            FileUtils.moveFile(file, message.getDataFile());
138                            message.store();
139                            message.createTrigger();
140                    } catch (Exception ex) {
141                            logger.error(ex.getMessage(), ex);
142                            // If exception caught, remove temporary files
143                            if (message != null) {
144                                    message.dispose();
145                            }
146                            System.exit(EX_TEMPFAIL);
147                    }
148            }
149            
150            public static void main(String[] args) {
151                    CommandLine cli = null;
152                    try {
153                            cli = new PosixParser().parse(OPTS, args);
154                    } catch (ParseException e) {
155                            usage();
156                            System.exit(EX_USAGE);
157                    }
158                    
159                    // Configuration file path
160                    String config = cli.getOptionValue("c", DEFAULT_CONFIG_LOCATION);
161                    // Message file path
162                    File file = new File(cli.getOptionValue("p"));
163                    // Envelope sender address
164                    String from = cli.getOptionValue("f");
165                    // Destination mailboxes
166                    String[] rcpts = cli.getOptionValues("r");
167                    
168                    if (!file.exists()) {
169                            // Message file must exist
170                            logger.error("File not exist: " + file.getAbsolutePath());
171                            System.exit(EX_TEMPFAIL);
172                    }
173                    
174                    if (from == null || rcpts == null) {
175                            // If sender or recipients address was not specified, get the
176                            // addresses from the message header.
177                            InputStream is = null;
178                            try {
179                                    is = new FileInputStream(file);
180                                    MessageHeader header = new MessageHeader(is);
181                                    if (from == null) {
182                                            from = header.getFrom().getAddress();
183                                    }
184                                    if (rcpts == null) {
185                                            rcpts = getRecipients(header);
186                                    }
187                            } catch (IOException ex) {
188                                    logger.error(ex.getMessage(), ex);
189                                    System.exit(EX_TEMPFAIL);
190                            } finally {
191                                    IOUtils.closeQuietly(is);
192                            }
193                    }
194                    
195                    if (from == null || ArrayUtils.isEmpty(rcpts)) {
196                            usage();
197                            System.exit(EX_USAGE);
198                    }
199                    
200                    Deliver deliver = new Deliver();
201    
202                    deliver.init(config);
203                    // Spool the incoming message
204                    deliver.deliver(from, rcpts, file);
205    
206                    System.exit(EX_OK);
207            }
208    
209            private static String[] getRecipients(MessageHeader header) {
210                    Set<String> rcpts = new HashSet<String>();
211                    getRecipients(header.getTo(), rcpts);
212                    getRecipients(header.getCc(), rcpts);
213                    getRecipients(header.getBcc(), rcpts);
214                    return rcpts.toArray(new String[0]);
215            }
216            
217            private static void getRecipients(AddressList addresses, Set<String> rcpts) {
218                    if (addresses != null) {
219                            for (Address address : addresses) {
220                                    if (address instanceof Mailbox) {
221                                            Mailbox mailbox = (Mailbox) address;
222                                            rcpts.add(mailbox.getAddress());
223                                    }
224                            }
225                    }
226            }
227            
228            private static void usage() {
229                    HelpFormatter hf = new HelpFormatter();
230                    String runProgram = "java " + Deliver.class.getName() + " [options]";
231                    hf.printHelp(runProgram, OPTS);
232            }
233            
234    }