1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.emailcommon.internet;
18
19import com.android.emailcommon.mail.MessagingException;
20
21import java.io.BufferedWriter;
22import java.io.IOException;
23import java.io.OutputStream;
24import java.io.OutputStreamWriter;
25import java.util.ArrayList;
26
27public class MimeHeader {
28    /**
29     * Application specific header that contains Store specific information about an attachment.
30     * In IMAP this contains the IMAP BODYSTRUCTURE part id so that the ImapStore can later
31     * retrieve the attachment at will from the server.
32     * The info is recorded from this header on LocalStore.appendMessages and is put back
33     * into the MIME data by LocalStore.fetch.
34     */
35    public static final String HEADER_ANDROID_ATTACHMENT_STORE_DATA = "X-Android-Attachment-StoreData";
36    /**
37     * Application specific header that is used to tag body parts for quoted/forwarded messages.
38     */
39    public static final String HEADER_ANDROID_BODY_QUOTED_PART = "X-Android-Body-Quoted-Part";
40
41    public static final String HEADER_CONTENT_TYPE = "Content-Type";
42    public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
43    public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
44    public static final String HEADER_CONTENT_ID = "Content-ID";
45
46    /**
47     * Fields that should be omitted when writing the header using writeTo()
48     */
49    private static final String[] WRITE_OMIT_FIELDS = {
50//        HEADER_ANDROID_ATTACHMENT_DOWNLOADED,
51//        HEADER_ANDROID_ATTACHMENT_ID,
52        HEADER_ANDROID_ATTACHMENT_STORE_DATA
53    };
54
55    protected final ArrayList<Field> mFields = new ArrayList<Field>();
56
57    public void clear() {
58        mFields.clear();
59    }
60
61    public String getFirstHeader(String name) throws MessagingException {
62        String[] header = getHeader(name);
63        if (header == null) {
64            return null;
65        }
66        return header[0];
67    }
68
69    public void addHeader(String name, String value) throws MessagingException {
70        mFields.add(new Field(name, value));
71    }
72
73    public void setHeader(String name, String value) throws MessagingException {
74        if (name == null || value == null) {
75            return;
76        }
77        removeHeader(name);
78        addHeader(name, value);
79    }
80
81    public String[] getHeader(String name) throws MessagingException {
82        ArrayList<String> values = new ArrayList<String>();
83        for (Field field : mFields) {
84            if (field.name.equalsIgnoreCase(name)) {
85                values.add(field.value);
86            }
87        }
88        if (values.size() == 0) {
89            return null;
90        }
91        return values.toArray(new String[] {});
92    }
93
94    public void removeHeader(String name) throws MessagingException {
95        ArrayList<Field> removeFields = new ArrayList<Field>();
96        for (Field field : mFields) {
97            if (field.name.equalsIgnoreCase(name)) {
98                removeFields.add(field);
99            }
100        }
101        mFields.removeAll(removeFields);
102    }
103
104    /**
105     * Write header into String
106     *
107     * @return CR-NL separated header string except the headers in writeOmitFields
108     * null if header is empty
109     */
110    public String writeToString() {
111        if (mFields.size() == 0) {
112            return null;
113        }
114        StringBuilder builder = new StringBuilder();
115        for (Field field : mFields) {
116            if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) {
117                builder.append(field.name + ": " + field.value + "\r\n");
118            }
119        }
120        return builder.toString();
121    }
122
123    public void writeTo(OutputStream out) throws IOException, MessagingException {
124        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
125        for (Field field : mFields) {
126            if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) {
127                writer.write(field.name + ": " + field.value + "\r\n");
128            }
129        }
130        writer.flush();
131    }
132
133    private static class Field {
134        final String name;
135        final String value;
136
137        public Field(String name, String value) {
138            this.name = name;
139            this.value = value;
140        }
141
142        @Override
143        public String toString() {
144            return name + "=" + value;
145        }
146    }
147
148    @Override
149    public String toString() {
150        return (mFields == null) ? null : mFields.toString();
151    }
152
153    public final static boolean arrayContains(Object[] a, Object o) {
154        int index = arrayIndex(a, o);
155        return (index >= 0);
156    }
157
158    public final static int arrayIndex(Object[] a, Object o) {
159        for (int i = 0, count = a.length; i < count; i++) {
160            if (a[i].equals(o)) {
161                return i;
162            }
163        }
164        return -1;
165    }
166}
167