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.jdkim.impl;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.james.jdkim.api.Headers;
32  import org.apache.james.mime4j.MimeException;
33  import org.apache.james.mime4j.io.EOLConvertingInputStream;
34  import org.apache.james.mime4j.parser.MimeEntityConfig;
35  import org.apache.james.mime4j.parser.MimeTokenStream;
36  
37  /**
38   * The header of an entity (see RFC 2045).
39   */
40  public class Message implements Headers {
41  
42      private List fields = new LinkedList();
43      private Map fieldMap = new HashMap();
44      private InputStream bodyIs = null;
45  
46      /**
47       * Creates a new empty <code>Header</code>.
48       */
49      public Message() {
50      }
51  
52      /**
53       * Creates a new <code>Header</code> from the specified stream.
54       * 
55       * @param is
56       *                the stream to read the header from.
57       * 
58       * @throws IOException
59       *                 on I/O errors.
60       * @throws MimeIOException
61       *                 on MIME protocol violations.
62       */
63      public Message(InputStream is) throws IOException, MimeException {
64          MimeEntityConfig mec = new MimeEntityConfig();
65          mec.setMaxLineLen(10000);
66          MimeTokenStream stream = new ExtendedMimeTokenStream(mec);
67          stream.setRecursionMode(MimeTokenStream.M_FLAT);
68          // DKIM requires no isolated CR or LF, so we alter them at source.
69          stream.parse(new EOLConvertingInputStream(is));
70          for (int state = stream.getState(); state != MimeTokenStream.T_END_OF_STREAM; state = stream
71                  .next()) {
72              switch (state) {
73              // a field
74              case MimeTokenStream.T_FIELD:
75                  addField(stream.getFieldName(), stream.getField());
76                  break;
77  
78              // expected ignored tokens
79              case MimeTokenStream.T_START_MESSAGE:
80              case MimeTokenStream.T_END_MESSAGE:
81              case MimeTokenStream.T_START_HEADER:
82              case MimeTokenStream.T_END_HEADER:
83                  break;
84  
85              // the body stream
86              case MimeTokenStream.T_BODY:
87                  this.bodyIs = stream.getInputStream();
88                  break;
89  
90              default:
91                  throw new IllegalStateException("Unexpected stream message: "
92                          + state);
93              }
94              // stop parsing after header
95              if (bodyIs != null)
96                  break;
97          }
98  
99      }
100 
101     public InputStream getBodyInputStream() {
102         return bodyIs;
103     }
104 
105     public void setBodyInputStream(InputStream is) {
106         bodyIs = is;
107     }
108 
109     /**
110      * Adds a field to the end of the list of fields.
111      * 
112      * @param field
113      *                the field to add.
114      */
115     public void addField(String fieldName, String field) {
116         List values = (List) fieldMap.get(fieldName.toLowerCase());
117         if (values == null) {
118             values = new LinkedList();
119             fieldMap.put(fieldName.toLowerCase(), values);
120         }
121         values.add(field);
122         fields.add(field);
123     }
124 
125     /**
126      * @see org.apache.james.jdkim.api.Headers#getFields()
127      */
128     public List getFields() {
129         return Collections.unmodifiableList(fields);
130     }
131 
132     /**
133      * @see org.apache.james.jdkim.api.Headers#getFields(java.lang.String)
134      */
135     public List getFields(final String name) {
136         final String lowerCaseName = name.toLowerCase();
137         final List l = (List) fieldMap.get(lowerCaseName);
138         final List results;
139         if (l == null || l.isEmpty()) {
140             results = null;
141         } else {
142             results = Collections.unmodifiableList(l);
143         }
144         return results;
145     }
146 
147     /**
148      * Return Header Object as String representation. Each headerline is
149      * seperated by "\r\n"
150      * 
151      * @return headers
152      */
153     public String toString() {
154         StringBuffer str = new StringBuffer(128);
155         for (Iterator i = fields.iterator(); i.hasNext();) {
156             String field = (String) i.next();
157             str.append(field);
158         }
159         InputStream is = getBodyInputStream();
160         if (is != null) {
161             str.append("\r\n");
162             byte[] buff = new byte[128];
163             int read;
164             try {
165                 while ((read = is.read(buff)) > 0) {
166                     str.append(new String(buff, 0, read));
167                 }
168             } catch (IOException e) {
169             }
170         }
171         return str.toString();
172     }
173 
174     /**
175      * Extends this to publish the constructor
176      */
177     private final class ExtendedMimeTokenStream extends MimeTokenStream {
178 
179         public ExtendedMimeTokenStream(MimeEntityConfig mec) {
180             super(mec);
181         }
182     }
183 
184 }