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 }