1 /*
2  * Copyright (C) 2015 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 package com.android.phone.common.mail.internet;
17 
18 import com.android.phone.common.mail.Body;
19 import com.android.phone.common.mail.BodyPart;
20 import com.android.phone.common.mail.MessagingException;
21 
22 import java.io.BufferedWriter;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.io.OutputStreamWriter;
26 import java.util.regex.Pattern;
27 
28 /**
29  * TODO this is a close approximation of Message, need to update along with
30  * Message.
31  */
32 public class MimeBodyPart extends BodyPart {
33     protected MimeHeader mHeader = new MimeHeader();
34     protected MimeHeader mExtendedHeader;
35     protected Body mBody;
36     protected int mSize;
37 
38     // regex that matches content id surrounded by "<>" optionally.
39     private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
40     // regex that matches end of line.
41     private static final Pattern END_OF_LINE = Pattern.compile("\r?\n");
42 
MimeBodyPart()43     public MimeBodyPart() throws MessagingException {
44         this(null);
45     }
46 
MimeBodyPart(Body body)47     public MimeBodyPart(Body body) throws MessagingException {
48         this(body, null);
49     }
50 
MimeBodyPart(Body body, String mimeType)51     public MimeBodyPart(Body body, String mimeType) throws MessagingException {
52         if (mimeType != null) {
53             setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
54         }
55         setBody(body);
56     }
57 
getFirstHeader(String name)58     protected String getFirstHeader(String name) throws MessagingException {
59         return mHeader.getFirstHeader(name);
60     }
61 
62     @Override
addHeader(String name, String value)63     public void addHeader(String name, String value) throws MessagingException {
64         mHeader.addHeader(name, value);
65     }
66 
67     @Override
setHeader(String name, String value)68     public void setHeader(String name, String value) throws MessagingException {
69         mHeader.setHeader(name, value);
70     }
71 
72     @Override
getHeader(String name)73     public String[] getHeader(String name) throws MessagingException {
74         return mHeader.getHeader(name);
75     }
76 
77     @Override
removeHeader(String name)78     public void removeHeader(String name) throws MessagingException {
79         mHeader.removeHeader(name);
80     }
81 
82     @Override
getBody()83     public Body getBody() throws MessagingException {
84         return mBody;
85     }
86 
87     @Override
setBody(Body body)88     public void setBody(Body body) throws MessagingException {
89         this.mBody = body;
90         if (body instanceof com.android.phone.common.mail.Multipart) {
91             com.android.phone.common.mail.Multipart multipart =
92                 ((com.android.phone.common.mail.Multipart)body);
93             multipart.setParent(this);
94             setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
95         }
96         else if (body instanceof TextBody) {
97             String contentType = String.format("%s;\n charset=utf-8", getMimeType());
98             String name = MimeUtility.getHeaderParameter(getContentType(), "name");
99             if (name != null) {
100                 contentType += String.format(";\n name=\"%s\"", name);
101             }
102             setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
103             setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
104         }
105     }
106 
107     @Override
getContentType()108     public String getContentType() throws MessagingException {
109         String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
110         if (contentType == null) {
111             return "text/plain";
112         } else {
113             return contentType;
114         }
115     }
116 
117     @Override
getDisposition()118     public String getDisposition() throws MessagingException {
119         String contentDisposition = getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
120         if (contentDisposition == null) {
121             return null;
122         } else {
123             return contentDisposition;
124         }
125     }
126 
127     @Override
getContentId()128     public String getContentId() throws MessagingException {
129         String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
130         if (contentId == null) {
131             return null;
132         } else {
133             // remove optionally surrounding brackets.
134             return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
135         }
136     }
137 
138     @Override
getMimeType()139     public String getMimeType() throws MessagingException {
140         return MimeUtility.getHeaderParameter(getContentType(), null);
141     }
142 
143     @Override
isMimeType(String mimeType)144     public boolean isMimeType(String mimeType) throws MessagingException {
145         return getMimeType().equals(mimeType);
146     }
147 
setSize(int size)148     public void setSize(int size) {
149         this.mSize = size;
150     }
151 
152     @Override
getSize()153     public int getSize() throws MessagingException {
154         return mSize;
155     }
156 
157     /**
158      * Set extended header
159      *
160      * @param name Extended header name
161      * @param value header value - flattened by removing CR-NL if any
162      * remove header if value is null
163      * @throws MessagingException
164      */
165     @Override
setExtendedHeader(String name, String value)166     public void setExtendedHeader(String name, String value) throws MessagingException {
167         if (value == null) {
168             if (mExtendedHeader != null) {
169                 mExtendedHeader.removeHeader(name);
170             }
171             return;
172         }
173         if (mExtendedHeader == null) {
174             mExtendedHeader = new MimeHeader();
175         }
176         mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
177     }
178 
179     /**
180      * Get extended header
181      *
182      * @param name Extended header name
183      * @return header value - null if header does not exist
184      * @throws MessagingException
185      */
186     @Override
getExtendedHeader(String name)187     public String getExtendedHeader(String name) throws MessagingException {
188         if (mExtendedHeader == null) {
189             return null;
190         }
191         return mExtendedHeader.getFirstHeader(name);
192     }
193 
194     /**
195      * Write the MimeMessage out in MIME format.
196      */
197     @Override
writeTo(OutputStream out)198     public void writeTo(OutputStream out) throws IOException, MessagingException {
199         BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
200         mHeader.writeTo(out);
201         writer.write("\r\n");
202         writer.flush();
203         if (mBody != null) {
204             mBody.writeTo(out);
205         }
206     }
207 }
208