1 //
2 // VMime library (http://www.vmime.org)
3 // Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 3 of
8 // the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // Linking this library statically or dynamically with other modules is making
20 // a combined work based on this library. Thus, the terms and conditions of
21 // the GNU General Public License cover the whole combination.
22 //
23
24 #include "vmime/attachmentHelper.hpp"
25
26 #include "vmime/bodyPartAttachment.hpp"
27 #include "vmime/parsedMessageAttachment.hpp"
28 #include "vmime/generatedMessageAttachment.hpp"
29
30 #include "vmime/disposition.hpp"
31 #include "vmime/emptyContentHandler.hpp"
32
33 #include <iterator>
34
35
36 namespace vmime
37 {
38
39
40 // static
isBodyPartAnAttachment(shared_ptr<const bodyPart> part,const unsigned int options)41 bool attachmentHelper::isBodyPartAnAttachment
42 (shared_ptr <const bodyPart> part, const unsigned int options)
43 {
44 // First, try with "Content-Disposition" field.
45 // If not present, we will try with "Content-Type" field.
46 shared_ptr <const contentDispositionField> cdf =
47 part->getHeader()->findField <contentDispositionField>(fields::CONTENT_DISPOSITION);
48
49 if (cdf)
50 {
51 const contentDisposition disp = *cdf->getValue <contentDisposition>();
52
53 if (disp.getName() != contentDispositionTypes::INLINE)
54 return true;
55
56 if ((options & INLINE_OBJECTS) == 0)
57 {
58 // If the Content-Disposition is 'inline' and there is no
59 // Content-Id or Content-Location field, it may be an attachment
60 if (!part->getHeader()->hasField(vmime::fields::CONTENT_ID) &&
61 !part->getHeader()->hasField(vmime::fields::CONTENT_LOCATION))
62 {
63 // If this is the root part, it might not be an attachment
64 if (part->getParentPart() == NULL)
65 return false;
66
67 return true;
68 }
69
70 return false;
71 }
72 }
73
74 // Assume "attachment" if type is not "text/..." or "multipart/...".
75 mediaType type;
76 bool hasContentTypeName = false;
77
78 shared_ptr <const contentTypeField> ctf =
79 part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
80
81 if (ctf)
82 {
83 type = *ctf->getValue <mediaType>();
84
85 if (ctf->hasParameter("name"))
86 hasContentTypeName = true;
87 }
88 else
89 {
90 // If this is the root part and no Content-Type field is present,
91 // then this may not be a MIME message, so do not assume it is
92 // an attachment
93 if (part->getParentPart() == NULL)
94 return false;
95
96 // No "Content-type" field: assume "application/octet-stream".
97 type = mediaType(mediaTypes::APPLICATION,
98 mediaTypes::APPLICATION_OCTET_STREAM);
99 }
100
101 if (type.getType() != mediaTypes::TEXT &&
102 type.getType() != mediaTypes::MULTIPART)
103 {
104 // Compatibility with (obsolete) RFC-1341: if there is a "name" parameter
105 // on the "Content-Type" field, then we assume it is an attachment
106 if (hasContentTypeName)
107 return true;
108
109 if ((options & INLINE_OBJECTS) == 0)
110 {
111 // If a "Content-Id" field is present, it might be an
112 // embedded object (MHTML messages)
113 if (part->getHeader()->hasField(vmime::fields::CONTENT_ID))
114 return false;
115 }
116
117 return true;
118 }
119
120 return false;
121 }
122
123
124 // static
getBodyPartAttachment(shared_ptr<const bodyPart> part,const unsigned int options)125 shared_ptr <const attachment> attachmentHelper::getBodyPartAttachment
126 (shared_ptr <const bodyPart> part, const unsigned int options)
127 {
128 if (!isBodyPartAnAttachment(part, options))
129 return null;
130
131 mediaType type;
132
133 shared_ptr <const contentTypeField> ctf =
134 part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
135
136 if (ctf)
137 {
138 type = *ctf->getValue <mediaType>();
139 }
140 else
141 {
142 // No "Content-type" field: assume "application/octet-stream".
143 type = mediaType(mediaTypes::APPLICATION,
144 mediaTypes::APPLICATION_OCTET_STREAM);
145 }
146
147 if (type.getType() == mediaTypes::MESSAGE &&
148 type.getSubType() == mediaTypes::MESSAGE_RFC822)
149 {
150 return make_shared <generatedMessageAttachment>(part);
151 }
152 else
153 {
154 return make_shared <bodyPartAttachment>(part);
155 }
156 }
157
158
159 // static
160 const std::vector <shared_ptr <const attachment> >
findAttachmentsInMessage(shared_ptr<const message> msg,const unsigned int options)161 attachmentHelper::findAttachmentsInMessage
162 (shared_ptr <const message> msg, const unsigned int options)
163 {
164 return findAttachmentsInBodyPart(msg, options);
165 }
166
167
168 // static
169 const std::vector <shared_ptr <const attachment> >
findAttachmentsInBodyPart(shared_ptr<const bodyPart> part,const unsigned int options)170 attachmentHelper::findAttachmentsInBodyPart
171 (shared_ptr <const bodyPart> part, const unsigned int options)
172 {
173 std::vector <shared_ptr <const attachment> > atts;
174
175 // Test this part
176 if (isBodyPartAnAttachment(part, options))
177 {
178 atts.push_back(getBodyPartAttachment(part, options));
179 }
180 // Find in sub-parts
181 else
182 {
183 shared_ptr <const body> bdy = part->getBody();
184
185 for (size_t i = 0 ; i < bdy->getPartCount() ; ++i)
186 {
187 std::vector <shared_ptr <const attachment> > partAtts =
188 findAttachmentsInBodyPart(bdy->getPartAt(i), options);
189
190 std::copy(partAtts.begin(), partAtts.end(), std::back_inserter(atts));
191 }
192 }
193
194 return atts;
195 }
196
197
198 // static
addAttachment(shared_ptr<message> msg,shared_ptr<attachment> att)199 void attachmentHelper::addAttachment(shared_ptr <message> msg, shared_ptr <attachment> att)
200 {
201 // We simply search for a "multipart/mixed" part. If no one exists,
202 // create it in the root part. This (very simple) algorithm should
203 // work in the most cases.
204
205 vmime::mediaType mpMixed(vmime::mediaTypes::MULTIPART,
206 vmime::mediaTypes::MULTIPART_MIXED);
207
208 shared_ptr <bodyPart> part = findBodyPart(msg, mpMixed);
209
210 if (part == NULL) // create it
211 {
212 if (msg->getBody()->getPartCount() != 0)
213 {
214 // Create a new container part for the parts that were in
215 // the root part of the message
216 shared_ptr <bodyPart> container = make_shared <bodyPart>();
217
218 if (msg->getHeader()->hasField(fields::CONTENT_TYPE))
219 {
220 container->getHeader()->ContentType()->setValue
221 (msg->getHeader()->ContentType()->getValue());
222 }
223
224 if (msg->getHeader()->hasField(fields::CONTENT_TRANSFER_ENCODING))
225 {
226 container->getHeader()->ContentTransferEncoding()->setValue
227 (msg->getHeader()->ContentTransferEncoding()->getValue());
228 }
229
230 // Move parts from the root part to this new part
231 const std::vector <shared_ptr <bodyPart> > partList =
232 msg->getBody()->getPartList();
233
234 msg->getBody()->removeAllParts();
235
236 for (unsigned int i = 0 ; i < partList.size() ; ++i)
237 container->getBody()->appendPart(partList[i]);
238
239 msg->getBody()->appendPart(container);
240 }
241 else
242 {
243 // The message is a simple (RFC-822) message, and do not
244 // contains any MIME part. Move the contents from the
245 // root to a new child part.
246 shared_ptr <bodyPart> child = make_shared <bodyPart>();
247
248 if (msg->getHeader()->hasField(fields::CONTENT_TYPE))
249 {
250 child->getHeader()->ContentType()->setValue
251 (msg->getHeader()->ContentType()->getValue());
252 }
253
254 if (msg->getHeader()->hasField(fields::CONTENT_TRANSFER_ENCODING))
255 {
256 child->getHeader()->ContentTransferEncoding()->setValue
257 (msg->getHeader()->ContentTransferEncoding()->getValue());
258 }
259
260 child->getBody()->setContents(msg->getBody()->getContents());
261 msg->getBody()->setContents(make_shared <emptyContentHandler>());
262
263 msg->getBody()->appendPart(child);
264 }
265
266 // Set the root part to 'multipart/mixed'
267 msg->getHeader()->ContentType()->setValue(mpMixed);
268
269 msg->getHeader()->removeAllFields(vmime::fields::CONTENT_DISPOSITION);
270 msg->getHeader()->removeAllFields(vmime::fields::CONTENT_TRANSFER_ENCODING);
271
272 part = msg;
273 }
274
275 // Generate the attachment part
276 att->generateIn(part);
277 }
278
279
280 // static
findBodyPart(shared_ptr<bodyPart> part,const mediaType & type)281 shared_ptr <bodyPart> attachmentHelper::findBodyPart
282 (shared_ptr <bodyPart> part, const mediaType& type)
283 {
284 if (part->getBody()->getContentType() == type)
285 return part;
286
287 // Try in sub-parts
288 shared_ptr <body> bdy = part->getBody();
289
290 for (size_t i = 0 ; i < bdy->getPartCount() ; ++i)
291 {
292 shared_ptr <bodyPart> found =
293 findBodyPart(bdy->getPartAt(i), type);
294
295 if (found != NULL)
296 return found;
297 }
298
299 return null;
300 }
301
302
303 // static
addAttachment(shared_ptr<message> msg,shared_ptr<message> amsg)304 void attachmentHelper::addAttachment(shared_ptr <message> msg, shared_ptr <message> amsg)
305 {
306 shared_ptr <attachment> att = make_shared <parsedMessageAttachment>(amsg);
307 addAttachment(msg, att);
308 }
309
310
311 } // vmime
312
313