Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-message-partial.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library 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  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "gmime-message-partial.h"
30 #include "gmime-stream-cat.h"
31 #include "gmime-stream-mem.h"
32 #include "gmime-parser.h"
33
34
35 /**
36  * SECTION: gmime-message-partial
37  * @title: GMimeMessagePartial
38  * @short_description: Partial MIME parts
39  * @se_also:
40  *
41  * A #GMimeMessagePartial represents the message/partial MIME part.
42  **/
43
44
45 /* GObject class methods */
46 static void g_mime_message_partial_class_init (GMimeMessagePartialClass *klass);
47 static void g_mime_message_partial_init (GMimeMessagePartial *catpart, GMimeMessagePartialClass *klass);
48 static void g_mime_message_partial_finalize (GObject *object);
49
50 /* GMimeObject class methods */
51 static void message_partial_prepend_header (GMimeObject *object, const char *header, const char *value);
52 static void message_partial_append_header (GMimeObject *object, const char *header, const char *value);
53 static void message_partial_set_header (GMimeObject *object, const char *header, const char *value);
54 static const char *message_partial_get_header (GMimeObject *object, const char *header);
55 static gboolean message_partial_remove_header (GMimeObject *object, const char *header);
56 static void message_partial_set_content_type (GMimeObject *object, GMimeContentType *content_type);
57
58
59 static GMimePartClass *parent_class = NULL;
60
61
62 GType
63 g_mime_message_partial_get_type (void)
64 {
65         static GType type = 0;
66         
67         if (!type) {
68                 static const GTypeInfo info = {
69                         sizeof (GMimeMessagePartialClass),
70                         NULL, /* base_class_init */
71                         NULL, /* base_class_finalize */
72                         (GClassInitFunc) g_mime_message_partial_class_init,
73                         NULL, /* class_finalize */
74                         NULL, /* class_data */
75                         sizeof (GMimeMessagePartial),
76                         0,    /* n_preallocs */
77                         (GInstanceInitFunc) g_mime_message_partial_init,
78                 };
79                 
80                 type = g_type_register_static (GMIME_TYPE_PART, "GMimeMessagePartial", &info, 0);
81         }
82         
83         return type;
84 }
85
86
87 static void
88 g_mime_message_partial_class_init (GMimeMessagePartialClass *klass)
89 {
90         GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
91         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
92         
93         parent_class = g_type_class_ref (GMIME_TYPE_PART);
94         
95         gobject_class->finalize = g_mime_message_partial_finalize;
96         
97         object_class->prepend_header = message_partial_prepend_header;
98         object_class->append_header = message_partial_append_header;
99         object_class->remove_header = message_partial_remove_header;
100         object_class->set_header = message_partial_set_header;
101         object_class->get_header = message_partial_get_header;
102         object_class->set_content_type = message_partial_set_content_type;
103 }
104
105 static void
106 g_mime_message_partial_init (GMimeMessagePartial *partial, GMimeMessagePartialClass *klass)
107 {
108         partial->id = NULL;
109         partial->number = -1;
110         partial->total = -1;
111 }
112
113 static void
114 g_mime_message_partial_finalize (GObject *object)
115 {
116         GMimeMessagePartial *partial = (GMimeMessagePartial *) object;
117         
118         g_free (partial->id);
119         
120         G_OBJECT_CLASS (parent_class)->finalize (object);
121 }
122
123 static void
124 message_partial_prepend_header (GMimeObject *object, const char *header, const char *value)
125 {
126         /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
127         if (!g_ascii_strcasecmp ("Content-MD5", header))
128                 return;
129         
130         GMIME_OBJECT_CLASS (parent_class)->prepend_header (object, header, value);
131 }
132
133 static void
134 message_partial_append_header (GMimeObject *object, const char *header, const char *value)
135 {
136         /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
137         if (!g_ascii_strcasecmp ("Content-MD5", header))
138                 return;
139         
140         GMIME_OBJECT_CLASS (parent_class)->append_header (object, header, value);
141 }
142
143 static void
144 message_partial_set_header (GMimeObject *object, const char *header, const char *value)
145 {
146         /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
147         if (!g_ascii_strcasecmp ("Content-MD5", header))
148                 return;
149         
150         GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
151 }
152
153 static const char *
154 message_partial_get_header (GMimeObject *object, const char *header)
155 {
156         return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
157 }
158
159 static gboolean
160 message_partial_remove_header (GMimeObject *object, const char *header)
161 {
162         return GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
163 }
164
165 static void
166 message_partial_set_content_type (GMimeObject *object, GMimeContentType *content_type)
167 {
168         GMimeMessagePartial *partial = (GMimeMessagePartial *) object;
169         const char *value;
170         
171         value = g_mime_content_type_get_parameter (content_type, "id");
172         g_free (partial->id);
173         partial->id = g_strdup (value);
174         
175         value = g_mime_content_type_get_parameter (content_type, "number");
176         partial->number = value ? strtol (value, NULL, 10) : -1;
177         
178         value = g_mime_content_type_get_parameter (content_type, "total");
179         partial->total = value ? strtol (value, NULL, 10) : -1;
180         
181         GMIME_OBJECT_CLASS (parent_class)->set_content_type (object, content_type);
182 }
183
184
185 /**
186  * g_mime_message_partial_new:
187  * @id: message/partial part id
188  * @number: message/partial part number
189  * @total: total number of message/partial parts
190  *
191  * Creates a new MIME message/partial object.
192  *
193  * Returns: an empty MIME message/partial object.
194  **/
195 GMimeMessagePartial *
196 g_mime_message_partial_new (const char *id, int number, int total)
197 {
198         GMimeContentType *content_type;
199         GMimeMessagePartial *partial;
200         char *num;
201         
202         partial = g_object_newv (GMIME_TYPE_MESSAGE_PARTIAL, 0, NULL);
203         
204         content_type = g_mime_content_type_new ("message", "partial");
205         
206         partial->id = g_strdup (id);
207         g_mime_content_type_set_parameter (content_type, "id", id);
208         
209         partial->number = number;
210         num = g_strdup_printf ("%d", number);
211         g_mime_content_type_set_parameter (content_type, "number", num);
212         g_free (num);
213         
214         partial->total = total;
215         num = g_strdup_printf ("%d", total);
216         g_mime_content_type_set_parameter (content_type, "total", num);
217         g_free (num);
218         
219         g_mime_object_set_content_type (GMIME_OBJECT (partial), content_type);
220         g_object_unref (content_type);
221         
222         return partial;
223 }
224
225
226 /**
227  * g_mime_message_partial_get_id:
228  * @partial: message/partial object
229  *
230  * Gets the message/partial id parameter value.
231  *
232  * Returns: the message/partial id or %NULL on fail.
233  **/
234 const char *
235 g_mime_message_partial_get_id (GMimeMessagePartial *partial)
236 {
237         g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), NULL);
238         
239         return partial->id;
240 }
241
242
243 /**
244  * g_mime_message_partial_get_number:
245  * @partial: message/partial object
246  *
247  * Gets the message/partial part number.
248  *
249  * Returns: the message/partial part number or %-1 on fail.
250  **/
251 int
252 g_mime_message_partial_get_number (GMimeMessagePartial *partial)
253 {
254         g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), -1);
255         
256         return partial->number;
257 }
258
259
260 /**
261  * g_mime_message_partial_get_total:
262  * @partial: message/partial object
263  *
264  * Gets the total number of message/partial parts needed to
265  * reconstruct the original message.
266  *
267  * Returns: the total number of message/partial parts needed to
268  * reconstruct the original message or -1 on fail.
269  **/
270 int
271 g_mime_message_partial_get_total (GMimeMessagePartial *partial)
272 {
273         g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), -1);
274         
275         return partial->total;
276 }
277
278
279 static int
280 partial_compare (const void *v1, const void *v2)
281 {
282         GMimeMessagePartial **partial1 = (GMimeMessagePartial **) v1;
283         GMimeMessagePartial **partial2 = (GMimeMessagePartial **) v2;
284         int num1, num2;
285         
286         num1 = g_mime_message_partial_get_number (*partial1);
287         num2 = g_mime_message_partial_get_number (*partial2);
288         
289         return num1 - num2;
290 }
291
292
293 /**
294  * g_mime_message_partial_reconstruct_message:
295  * @partials: an array of message/partial mime parts
296  * @num: the number of elements in @partials
297  *
298  * Reconstructs the GMimeMessage from the given message/partial parts
299  * in @partials.
300  *
301  * Returns: a GMimeMessage object on success or %NULL on fail.
302  **/
303 GMimeMessage *
304 g_mime_message_partial_reconstruct_message (GMimeMessagePartial **partials, size_t num)
305 {
306         GMimeMessagePartial *partial;
307         GMimeDataWrapper *wrapper;
308         GMimeStream *cat, *stream;
309         GMimeMessage *message;
310         GMimeParser *parser;
311         int total, number;
312         const char *id;
313         size_t i;
314         
315         if (num == 0 || !(id = g_mime_message_partial_get_id (partials[0])))
316                 return NULL;
317         
318         /* get them into the correct order... */
319         qsort ((void *) partials, num, sizeof (gpointer), partial_compare);
320         
321         /* only the last message/partial part is REQUIRED to have the total parameter */
322         if ((total = g_mime_message_partial_get_total (partials[num - 1])) == -1)
323                 return NULL;
324         
325         if (num != (size_t) total)
326                 return NULL;
327         
328         cat = g_mime_stream_cat_new ();
329         
330         for (i = 0; i < num; i++) {
331                 const char *partial_id;
332                 
333                 partial = partials[i];
334                 
335                 /* sanity check to make sure this part belongs... */
336                 partial_id = g_mime_message_partial_get_id (partial);
337                 if (!partial_id || strcmp (id, partial_id))
338                         goto exception;
339                 
340                 /* sanity check to make sure we aren't missing any parts */
341                 if ((number = g_mime_message_partial_get_number (partial)) == -1)
342                         goto exception;
343                 
344                 if ((size_t) number != i + 1)
345                         goto exception;
346                 
347                 wrapper = g_mime_part_get_content_object (GMIME_PART (partial));
348                 stream = g_mime_data_wrapper_get_stream (wrapper);
349                 
350                 g_mime_stream_reset (stream);
351                 g_mime_stream_cat_add_source (GMIME_STREAM_CAT (cat), stream);
352         }
353         
354         parser = g_mime_parser_new ();
355         g_mime_parser_init_with_stream (parser, cat);
356         g_object_unref (cat);
357         
358         message = g_mime_parser_construct_message (parser);
359         g_object_unref (parser);
360         
361         return message;
362         
363  exception:
364         
365         g_object_unref (cat);
366         
367         return NULL;
368 }
369
370
371 static GMimeMessage *
372 message_partial_message_new (GMimeMessage *base)
373 {
374         const char *name, *value;
375         GMimeMessage *message;
376         GMimeHeaderList *list;
377         GMimeHeaderIter iter;
378         
379         message = g_mime_message_new (FALSE);
380         
381         list = ((GMimeObject *) base)->headers;
382         
383         if (g_mime_header_list_get_iter (list, &iter)) {
384                 do {
385                         name = g_mime_header_iter_get_name (&iter);
386                         value = g_mime_header_iter_get_value (&iter);
387                         g_mime_object_append_header ((GMimeObject *) message, name, value);
388                 } while (g_mime_header_iter_next (&iter));
389         }
390         
391         return message;
392 }
393
394
395 /**
396  * g_mime_message_partial_split_message:
397  * @message: message object
398  * @max_size: max size
399  * @nparts: number of parts
400  *
401  * Splits @message into an array of #GMimeMessage objects each
402  * containing a single #GMimeMessagePartial object containing
403  * @max_size bytes or fewer. @nparts is set to the number of
404  * #GMimeMessagePartial objects created.
405  *
406  * Returns: an array of #GMimeMessage objects and sets @nparts to th
407  * number of messages returned or %NULL on fail.
408  **/
409 GMimeMessage **
410 g_mime_message_partial_split_message (GMimeMessage *message, size_t max_size, size_t *nparts)
411 {
412         GMimeMessage **messages;
413         GMimeMessagePartial *partial;
414         GMimeStream *stream, *substream;
415         GMimeDataWrapper *wrapper;
416         const unsigned char *buf;
417         GPtrArray *parts;
418         gint64 len, end;
419         const char *id;
420         gint64 start;
421         guint i;
422         
423         *nparts = 0;
424         
425         g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
426         
427         stream = g_mime_stream_mem_new ();
428         if (g_mime_object_write_to_stream (GMIME_OBJECT (message), stream) == -1) {
429                 g_object_unref (stream);
430                 return NULL;
431         }
432         
433         g_mime_stream_reset (stream);
434         
435         len = g_mime_stream_length (stream);
436         
437         /* optimization */
438         if (len <= max_size) {
439                 g_object_unref (stream);
440                 g_object_ref (message);
441                 
442                 messages = g_malloc (sizeof (void *));
443                 messages[0] = message;
444                 *nparts = 1;
445                 
446                 return messages;
447         }
448         
449         start = 0;
450         parts = g_ptr_array_new ();
451         buf = ((GMimeStreamMem *) stream)->buffer->data;
452         
453         while (start < len) {
454                 /* Preferably, we'd split on whole-lines if we can,
455                  * but if that's not possible, split on max size */
456                 if ((end = MIN (len, start + max_size)) < len) {
457                         register gint64 ebx; /* end boundary */
458                         
459                         ebx = end;
460                         while (ebx > (start + 1) && ebx[buf] != '\n')
461                                 ebx--;
462                         
463                         if (ebx[buf] == '\n')
464                                 end = ebx + 1;
465                 }
466                 
467                 substream = g_mime_stream_substream (stream, start, end);
468                 g_ptr_array_add (parts, substream);
469                 start = end;
470         }
471         
472         id = g_mime_message_get_message_id (message);
473         
474         for (i = 0; i < parts->len; i++) {
475                 partial = g_mime_message_partial_new (id, i + 1, parts->len);
476                 wrapper = g_mime_data_wrapper_new_with_stream (GMIME_STREAM (parts->pdata[i]),
477                                                                GMIME_CONTENT_ENCODING_DEFAULT);
478                 g_object_unref (parts->pdata[i]);
479                 g_mime_part_set_content_object (GMIME_PART (partial), wrapper);
480                 g_object_unref (wrapper);
481                 
482                 parts->pdata[i] = message_partial_message_new (message);
483                 g_mime_message_set_mime_part (GMIME_MESSAGE (parts->pdata[i]), GMIME_OBJECT (partial));
484                 g_object_unref (partial);
485         }
486         
487         g_object_unref (stream);
488         
489         messages = (GMimeMessage **) parts->pdata;
490         *nparts = parts->len;
491         
492         g_ptr_array_free (parts, FALSE);
493         
494         return messages;
495 }