1bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/****************************************************************
2bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Licensed to the Apache Software Foundation (ASF) under one   *
3bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * or more contributor license agreements.  See the NOTICE file *
4bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * distributed with this work for additional information        *
5bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * regarding copyright ownership.  The ASF licenses this file   *
6bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * to you under the Apache License, Version 2.0 (the            *
7bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * "License"); you may not use this file except in compliance   *
8bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * with the License.  You may obtain a copy of the License at   *
9bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *                                                              *
10bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *   http://www.apache.org/licenses/LICENSE-2.0                 *
11bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *                                                              *
12bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Unless required by applicable law or agreed to in writing,   *
13bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * software distributed under the License is distributed on an  *
14bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * KIND, either express or implied.  See the License for the    *
16bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * specific language governing permissions and limitations      *
17bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * under the License.                                           *
18bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ****************************************************************/
19bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
20bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpackage org.apache.james.mime4j;
21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.HashMap;
23bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.Map;
24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/**
26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Encapsulates the values of the MIME-specific header fields
27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * (which starts with <code>Content-</code>).
28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
29bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook *
30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $
31bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */
32bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic class BodyDescriptor {
33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private static Log log = LogFactory.getLog(BodyDescriptor.class);
34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
35bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String mimeType = "text/plain";
36bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String boundary = null;
37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String charset = "us-ascii";
38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private String transferEncoding = "7bit";
39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Map<String, String> parameters = new HashMap<String, String>();
40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean contentTypeSet = false;
41bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private boolean contentTransferEncSet = false;
42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
44bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Creates a new root <code>BodyDescriptor</code> instance.
45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public BodyDescriptor() {
47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        this(null);
48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
51bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Creates a new <code>BodyDescriptor</code> instance.
52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
53bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param parent the descriptor of the parent or <code>null</code> if this
54bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *        is the root descriptor.
55bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public BodyDescriptor(BodyDescriptor parent) {
57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (parent != null && parent.isMimeType("multipart/digest")) {
58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mimeType = "message/rfc822";
59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            mimeType = "text/plain";
61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Should be called for each <code>Content-</code> header field of
66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * a MIME message or part.
67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     *
68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param name the field name.
69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * @param value the field value.
70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public void addField(String name, String value) {
72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        name = name.trim().toLowerCase();
74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {
76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            contentTransferEncSet = true;
77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            value = value.trim().toLowerCase();
79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (value.length() > 0) {
80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                transferEncoding = value;
81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else if (name.equals("content-type") && !contentTypeSet) {
84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            contentTypeSet = true;
85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            value = value.trim();
87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            /*
89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             * Unfold Content-Type value
90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             */
91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            StringBuffer sb = new StringBuffer();
92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            for (int i = 0; i < value.length(); i++) {
93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                char c = value.charAt(i);
94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (c == '\r' || c == '\n') {
95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    continue;
96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                sb.append(c);
98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            Map<String, String> params = getHeaderParams(sb.toString());
101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String main = params.get("");
103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (main != null) {
104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                main = main.toLowerCase().trim();
105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                int index = main.indexOf('/');
106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                boolean valid = false;
107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (index != -1) {
108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    String type = main.substring(0, index).trim();
109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    String subtype = main.substring(index + 1).trim();
110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    if (type.length() > 0 && subtype.length() > 0) {
111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        main = type + "/" + subtype;
112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        valid = true;
113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    }
114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (!valid) {
117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    main = null;
118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String b = params.get("boundary");
121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (main != null
123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    && ((main.startsWith("multipart/") && b != null)
124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            || !main.startsWith("multipart/"))) {
125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                mimeType = main;
127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (isMultipart()) {
130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                boundary = b;
131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            String c = params.get("charset");
134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (c != null) {
135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                c = c.trim();
136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                if (c.length() > 0) {
137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    charset = c.toLowerCase();
138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            /*
142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             * Add all other parameters to parameters.
143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook             */
144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            parameters.putAll(params);
145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            parameters.remove("");
146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            parameters.remove("boundary");
147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            parameters.remove("charset");
148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    private Map<String, String> getHeaderParams(String headerValue) {
152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        Map<String, String> result = new HashMap<String, String>();
153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        // split main value and parameters
155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        String main;
156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        String rest;
157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (headerValue.indexOf(";") == -1) {
158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            main = headerValue;
159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            rest = null;
160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        } else {
161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            main = headerValue.substring(0, headerValue.indexOf(";"));
162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            rest = headerValue.substring(main.length() + 1);
163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        result.put("", main);
166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        if (rest != null) {
167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            char[] chars = rest.toCharArray();
168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            StringBuffer paramName = new StringBuffer();
169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            StringBuffer paramValue = new StringBuffer();
170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte READY_FOR_NAME = 0;
172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte IN_NAME = 1;
173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte READY_FOR_VALUE = 2;
174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte IN_VALUE = 3;
175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte IN_QUOTED_VALUE = 4;
176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte VALUE_DONE = 5;
177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            final byte ERROR = 99;
178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            byte state = READY_FOR_NAME;
180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            boolean escaped = false;
181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            for (int i = 0; i < chars.length; i++) {
182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                char c = chars[i];
183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                switch (state) {
185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case ERROR:
186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (c == ';')
187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            state = READY_FOR_NAME;
188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case READY_FOR_NAME:
191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (c == '=') {
192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            log.error("Expected header param name, got '='");
193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            state = ERROR;
194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        paramName = new StringBuffer();
198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        paramValue = new StringBuffer();
199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        state = IN_NAME;
201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // $FALL-THROUGH$
202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case IN_NAME:
204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (c == '=') {
205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            if (paramName.length() == 0)
206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = ERROR;
207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            else
208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = READY_FOR_VALUE;
209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // not '='... just add to name
213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        paramName.append(c);
214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case READY_FOR_VALUE:
217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        boolean fallThrough = false;
218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        switch (c) {
219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case ' ':
220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '\t':
221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;  // ignore spaces, especially before '"'
222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '"':
224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = IN_QUOTED_VALUE;
225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            default:
228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = IN_VALUE;
229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                fallThrough = true;
230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (!fallThrough)
233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // $FALL-THROUGH$
236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case IN_VALUE:
238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        fallThrough = false;
239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        switch (c) {
240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case ';':
241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case ' ':
242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '\t':
243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                result.put(
244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                   paramName.toString().trim().toLowerCase(),
245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                   paramValue.toString().trim());
246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = VALUE_DONE;
247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                fallThrough = true;
248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            default:
250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                paramValue.append(c);
251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        if (!fallThrough)
254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            break;
255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        // $FALL-THROUGH$
257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case VALUE_DONE:
259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        switch (c) {
260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case ';':
261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = READY_FOR_NAME;
262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case ' ':
265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '\t':
266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            default:
269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                state = ERROR;
270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
274bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                    case IN_QUOTED_VALUE:
275bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        switch (c) {
276bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '"':
277bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                if (!escaped) {
278bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    // don't trim quoted strings; the spaces could be intentional.
279bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    result.put(
280bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                            paramName.toString().trim().toLowerCase(),
281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                            paramValue.toString());
282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    state = VALUE_DONE;
283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                } else {
284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    escaped = false;
285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    paramValue.append(c);
286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                }
287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            case '\\':
290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                if (escaped) {
291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    paramValue.append('\\');
292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                }
293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                escaped = !escaped;
294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                            default:
297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                if (escaped) {
298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                    paramValue.append('\\');
299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                }
300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                escaped = false;
301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                paramValue.append(c);
302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                                break;
303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        }
304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        break;
305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                }
307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            // done looping.  check if anything is left over.
310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            if (state == IN_VALUE) {
311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                result.put(
312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        paramName.toString().trim().toLowerCase(),
313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook                        paramValue.toString().trim());
314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook            }
315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        }
316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return result;
318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isMimeType(String mimeType) {
322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return this.mimeType.equals(mimeType.toLowerCase());
323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return true if the BodyDescriptor belongs to a message
327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isMessage() {
329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mimeType.equals("message/rfc822");
330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return true if the BodyDescripotro belongs to a multipart
334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isMultipart() {
336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mimeType.startsWith("multipart/");
337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return the MimeType
341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public String getMimeType() {
343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mimeType;
344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return the boundary
348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public String getBoundary() {
350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return boundary;
351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return the charset
355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public String getCharset() {
357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return charset;
358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return all parameters for the BodyDescriptor
362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public Map<String, String> getParameters() {
364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return parameters;
365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return the TransferEncoding
369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public String getTransferEncoding() {
371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return transferEncoding;
372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return true if it's base64 encoded
376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isBase64Encoded() {
378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return "base64".equals(transferEncoding);
379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    /**
382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     * Return true if it's quoted-printable
383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook     */
384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public boolean isQuotedPrintableEncoded() {
385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return "quoted-printable".equals(transferEncoding);
386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook
388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    @Override
389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    public String toString() {
390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook        return mimeType;
391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook    }
392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook}
393