View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  package org.apache.james.jms.builder;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Random;
27  
28  import javax.mail.MessagingException;
29  import javax.mail.internet.ParseException;
30  
31  import org.apache.commons.io.IOUtils;
32  import org.apache.james.api.jms.MailBuilder;
33  import org.apache.james.core.MailImpl;
34  import org.apache.james.mime4j.field.ParsedField;
35  import org.apache.james.mime4j.field.AddressListField;
36  import org.apache.james.mime4j.field.DefaultFieldParser;
37  import org.apache.james.mime4j.field.MailboxListField;
38  import org.apache.james.mime4j.field.address.Mailbox;
39  import org.apache.james.mime4j.field.address.MailboxList;
40  import org.apache.james.services.MailServer;
41  import org.apache.mailet.Mail;
42  import org.apache.mailet.MailAddress;
43  
44  /**
45   * <p>Basic implementation that builds {@link Mail} 
46   * from basic non-mime <code>ASCII</code> mail.
47   * Naively uses the <code>to</code> header
48   * to determine the recipent and <code>from</code>
49   * to determine the sender.
50   * </p><p>
51   * <strong>Note</strong> this implementation is too
52   * simple to cope well with wild emails. It may be useful
53   * when dealing with generated ASCII emails.
54   * </p>
55   */
56  public class SimpleMailBuilder implements MailBuilder {
57      
58      private static final String US_ASCII = "US-ASCII";
59  
60      private final IdGenerator generator;
61      
62      public SimpleMailBuilder() {
63          this(new IdGenerator() {
64              private final Random random = new Random();
65              public String getId() {
66                  return "SimpleMailBuilder#" + random.nextLong();
67              }
68          });
69      }
70      
71      public SimpleMailBuilder(final IdGenerator generator) {
72          this.generator = generator;
73      }
74      
75      public Mail build(String text) throws MessagingException {
76          final Collection recipients = new ArrayList();
77          MailAddress sender = null;
78          final int length = text.length();
79          int position = 0;
80          while (position < length) {
81              position = text.indexOf('\n', position);
82              if (position == -1) {
83                  position = length;
84              }
85              final char nextChar = text.charAt(++position);
86              
87              switch (nextChar) {
88                  case '\r':
89                      // end of headers
90                      position = length;
91                      break;
92                  case 'F':
93                  case 'f':
94                      // from header?
95                      sender = parseFrom(text, sender, length, position);
96                      break; 
97                  case 'T':
98                  case 't':
99                      parseTo(text, recipients, length, position);
100                   break; 
101               default:
102                   break;
103           }
104       }
105       final String key = generator.getId();
106       final InputStream input = toInputStream(text);
107       final Mail result = new MailImpl(key, sender, recipients, input);
108       return result;
109   }
110 
111   private MailAddress parseFrom(final String text, final MailAddress defaultAddress, final int length, int position) throws ParseException {
112       MailAddress result = defaultAddress;
113       int eol = text.indexOf('\r', position);
114       if (eol == -1) {
115           eol = length;
116       }
117         int nameEnds = text.indexOf(':', position);
118         if (nameEnds == -1) {
119           nameEnds = length;
120         }
121       final String name = text.substring(position, nameEnds);
122         final String body = text.substring(nameEnds+1, eol);
123       final ParsedField parsedField = new DefaultFieldParser().parse(name, body, null);
124       if ("from".equalsIgnoreCase(parsedField.getName())) {
125           if (parsedField instanceof AddressListField) {
126               final AddressListField field = (AddressListField) parsedField; 
127               final MailboxList mailboxes = field.getAddressList().flatten();
128               result = firstMailAddress(mailboxes);
129           } else if (parsedField instanceof MailboxListField) {
130               final MailboxListField field = (MailboxListField) parsedField;
131               final MailboxList mailboxes = field.getMailboxList();
132               result = firstMailAddress(mailboxes);
133           }
134       }
135       return result;
136   }
137   
138   private void parseTo(final String text, Collection addresses, final int length, int position) throws ParseException {
139       int eol = text.indexOf('\r', position);
140       if (eol == -1) {
141           eol = length;
142       }
143         int nameEnds = text.indexOf(':', position);
144         if (nameEnds == -1) {
145           nameEnds = length;
146         }
147         final String name = text.substring(position, nameEnds);
148         final String body = text.substring(nameEnds+1, eol);
149         final ParsedField parsedField = new DefaultFieldParser().parse(name, body, null);
150       if ("to".equalsIgnoreCase(parsedField.getName())) {
151           if (parsedField instanceof AddressListField) {
152               final AddressListField field = (AddressListField) parsedField; 
153               final MailboxList mailboxes = field.getAddressList().flatten();
154               addMailAddresses(addresses, mailboxes);
155           } else if (parsedField instanceof MailboxListField) {
156               final MailboxListField field = (MailboxListField) parsedField;
157               final MailboxList mailboxes = field.getMailboxList();
158               addMailAddresses(addresses, mailboxes);
159           }
160       }
161   }
162 
163   private void addMailAddresses(Collection addresses, final MailboxList mailboxes) throws ParseException {
164       int size = mailboxes.size();
165       for (int i=0;i< size;i++) {
166           final MailAddress address = toMailAddress(mailboxes.get(i));
167           addresses.add(address);
168       }
169   }
170 
171   private MailAddress firstMailAddress(final MailboxList mailboxes) throws ParseException {
172       MailAddress result = null;
173       if (mailboxes.size() > 0) {
174           final Mailbox first = mailboxes.get(0);
175           result = toMailAddress(first);
176       }
177       return result;
178   }
179 
180   private MailAddress toMailAddress(final Mailbox mailbox) throws ParseException {
181       MailAddress result;
182       final String domain = mailbox.getDomain();
183       final String local = mailbox.getLocalPart();
184       result = new MailAddress(local, domain);
185       return result;
186   }
187 
188   private InputStream toInputStream(String text) throws MessagingException {
189       try {
190           final InputStream result = IOUtils.toInputStream(text, US_ASCII);
191           return result;
192       } catch (IOException e) {
193           throw new MessagingException("Conversion to ASCII stream failed.", e);
194       }
195   }
196 
197   
198   
199   public interface IdGenerator {
200       
201       /**
202         * Generate a new identifier/name for a mail being processed by this server.
203         *
204         * @return the new identifier
205         */
206       String getId();
207   }
208   
209   public final static class JamesIdGenerator implements IdGenerator {
210       private final MailServer mailServer;
211       
212       public JamesIdGenerator(final MailServer mailServer) {
213           super();
214           this.mailServer = mailServer;
215       }
216 
217       public String getId() {
218           return mailServer.getId();
219       }
220 
221       /**
222         * Renders suitably for logging.
223         * @return a <code>String</code> representation 
224         * of this object.
225         */
226       public String toString()
227       {
228           final String TAB = " ";
229           final String retValue = "JamesIdGenerator ( "
230               + "mailServer = " + this.mailServer + TAB
231               + " )";     
232           return retValue;
233       }
234       
235       
236   }
237 
238   /**
239     * Renders suitably for logging.
240     * @return a <code>String</code> representation 
241     * of this object.
242     */
243   public String toString()
244   {
245       final String TAB = " ";
246       
247       final String retValue = "SimpleMailBuilder ( "
248           + "generator = " + this.generator + TAB
249           + " )";
250   
251       return retValue;
252   }
253   
254   
255 }