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 }