1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
29 #include "gmime-message-partial.h"
30 #include "gmime-stream-cat.h"
31 #include "gmime-stream-mem.h"
32 #include "gmime-parser.h"
36 * SECTION: gmime-message-partial
37 * @title: GMimeMessagePartial
38 * @short_description: Partial MIME parts
41 * A #GMimeMessagePartial represents the message/partial MIME part.
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);
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);
59 static GMimePartClass *parent_class = NULL;
63 g_mime_message_partial_get_type (void)
65 static GType type = 0;
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),
77 (GInstanceInitFunc) g_mime_message_partial_init,
80 type = g_type_register_static (GMIME_TYPE_PART, "GMimeMessagePartial", &info, 0);
88 g_mime_message_partial_class_init (GMimeMessagePartialClass *klass)
90 GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
91 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
93 parent_class = g_type_class_ref (GMIME_TYPE_PART);
95 gobject_class->finalize = g_mime_message_partial_finalize;
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;
106 g_mime_message_partial_init (GMimeMessagePartial *partial, GMimeMessagePartialClass *klass)
109 partial->number = -1;
114 g_mime_message_partial_finalize (GObject *object)
116 GMimeMessagePartial *partial = (GMimeMessagePartial *) object;
118 g_free (partial->id);
120 G_OBJECT_CLASS (parent_class)->finalize (object);
124 message_partial_prepend_header (GMimeObject *object, const char *header, const char *value)
126 /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
127 if (!g_ascii_strcasecmp ("Content-MD5", header))
130 GMIME_OBJECT_CLASS (parent_class)->prepend_header (object, header, value);
134 message_partial_append_header (GMimeObject *object, const char *header, const char *value)
136 /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
137 if (!g_ascii_strcasecmp ("Content-MD5", header))
140 GMIME_OBJECT_CLASS (parent_class)->append_header (object, header, value);
144 message_partial_set_header (GMimeObject *object, const char *header, const char *value)
146 /* RFC 1864 states that you cannot set a Content-MD5 on a message part */
147 if (!g_ascii_strcasecmp ("Content-MD5", header))
150 GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
154 message_partial_get_header (GMimeObject *object, const char *header)
156 return GMIME_OBJECT_CLASS (parent_class)->get_header (object, header);
160 message_partial_remove_header (GMimeObject *object, const char *header)
162 return GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
166 message_partial_set_content_type (GMimeObject *object, GMimeContentType *content_type)
168 GMimeMessagePartial *partial = (GMimeMessagePartial *) object;
171 value = g_mime_content_type_get_parameter (content_type, "id");
172 g_free (partial->id);
173 partial->id = g_strdup (value);
175 value = g_mime_content_type_get_parameter (content_type, "number");
176 partial->number = value ? strtol (value, NULL, 10) : -1;
178 value = g_mime_content_type_get_parameter (content_type, "total");
179 partial->total = value ? strtol (value, NULL, 10) : -1;
181 GMIME_OBJECT_CLASS (parent_class)->set_content_type (object, content_type);
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
191 * Creates a new MIME message/partial object.
193 * Returns: an empty MIME message/partial object.
195 GMimeMessagePartial *
196 g_mime_message_partial_new (const char *id, int number, int total)
198 GMimeContentType *content_type;
199 GMimeMessagePartial *partial;
202 partial = g_object_newv (GMIME_TYPE_MESSAGE_PARTIAL, 0, NULL);
204 content_type = g_mime_content_type_new ("message", "partial");
206 partial->id = g_strdup (id);
207 g_mime_content_type_set_parameter (content_type, "id", id);
209 partial->number = number;
210 num = g_strdup_printf ("%d", number);
211 g_mime_content_type_set_parameter (content_type, "number", num);
214 partial->total = total;
215 num = g_strdup_printf ("%d", total);
216 g_mime_content_type_set_parameter (content_type, "total", num);
219 g_mime_object_set_content_type (GMIME_OBJECT (partial), content_type);
220 g_object_unref (content_type);
227 * g_mime_message_partial_get_id:
228 * @partial: message/partial object
230 * Gets the message/partial id parameter value.
232 * Returns: the message/partial id or %NULL on fail.
235 g_mime_message_partial_get_id (GMimeMessagePartial *partial)
237 g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), NULL);
244 * g_mime_message_partial_get_number:
245 * @partial: message/partial object
247 * Gets the message/partial part number.
249 * Returns: the message/partial part number or %-1 on fail.
252 g_mime_message_partial_get_number (GMimeMessagePartial *partial)
254 g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), -1);
256 return partial->number;
261 * g_mime_message_partial_get_total:
262 * @partial: message/partial object
264 * Gets the total number of message/partial parts needed to
265 * reconstruct the original message.
267 * Returns: the total number of message/partial parts needed to
268 * reconstruct the original message or -1 on fail.
271 g_mime_message_partial_get_total (GMimeMessagePartial *partial)
273 g_return_val_if_fail (GMIME_IS_MESSAGE_PARTIAL (partial), -1);
275 return partial->total;
280 partial_compare (const void *v1, const void *v2)
282 GMimeMessagePartial **partial1 = (GMimeMessagePartial **) v1;
283 GMimeMessagePartial **partial2 = (GMimeMessagePartial **) v2;
286 num1 = g_mime_message_partial_get_number (*partial1);
287 num2 = g_mime_message_partial_get_number (*partial2);
294 * g_mime_message_partial_reconstruct_message:
295 * @partials: an array of message/partial mime parts
296 * @num: the number of elements in @partials
298 * Reconstructs the GMimeMessage from the given message/partial parts
301 * Returns: a GMimeMessage object on success or %NULL on fail.
304 g_mime_message_partial_reconstruct_message (GMimeMessagePartial **partials, size_t num)
306 GMimeMessagePartial *partial;
307 GMimeDataWrapper *wrapper;
308 GMimeStream *cat, *stream;
309 GMimeMessage *message;
315 if (num == 0 || !(id = g_mime_message_partial_get_id (partials[0])))
318 /* get them into the correct order... */
319 qsort ((void *) partials, num, sizeof (gpointer), partial_compare);
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)
325 if (num != (size_t) total)
328 cat = g_mime_stream_cat_new ();
330 for (i = 0; i < num; i++) {
331 const char *partial_id;
333 partial = partials[i];
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))
340 /* sanity check to make sure we aren't missing any parts */
341 if ((number = g_mime_message_partial_get_number (partial)) == -1)
344 if ((size_t) number != i + 1)
347 wrapper = g_mime_part_get_content_object (GMIME_PART (partial));
348 stream = g_mime_data_wrapper_get_stream (wrapper);
350 g_mime_stream_reset (stream);
351 g_mime_stream_cat_add_source (GMIME_STREAM_CAT (cat), stream);
354 parser = g_mime_parser_new ();
355 g_mime_parser_init_with_stream (parser, cat);
356 g_object_unref (cat);
358 message = g_mime_parser_construct_message (parser);
359 g_object_unref (parser);
365 g_object_unref (cat);
371 static GMimeMessage *
372 message_partial_message_new (GMimeMessage *base)
374 const char *name, *value;
375 GMimeMessage *message;
376 GMimeHeaderList *list;
377 GMimeHeaderIter iter;
379 message = g_mime_message_new (FALSE);
381 list = ((GMimeObject *) base)->headers;
383 if (g_mime_header_list_get_iter (list, &iter)) {
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));
396 * g_mime_message_partial_split_message:
397 * @message: message object
398 * @max_size: max size
399 * @nparts: number of parts
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.
406 * Returns: an array of #GMimeMessage objects and sets @nparts to th
407 * number of messages returned or %NULL on fail.
410 g_mime_message_partial_split_message (GMimeMessage *message, size_t max_size, size_t *nparts)
412 GMimeMessage **messages;
413 GMimeMessagePartial *partial;
414 GMimeStream *stream, *substream;
415 GMimeDataWrapper *wrapper;
416 const unsigned char *buf;
425 g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
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);
433 g_mime_stream_reset (stream);
435 len = g_mime_stream_length (stream);
438 if (len <= max_size) {
439 g_object_unref (stream);
440 g_object_ref (message);
442 messages = g_malloc (sizeof (void *));
443 messages[0] = message;
450 parts = g_ptr_array_new ();
451 buf = ((GMimeStreamMem *) stream)->buffer->data;
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 */
460 while (ebx > (start + 1) && ebx[buf] != '\n')
463 if (ebx[buf] == '\n')
467 substream = g_mime_stream_substream (stream, start, end);
468 g_ptr_array_add (parts, substream);
472 id = g_mime_message_get_message_id (message);
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);
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);
487 g_object_unref (stream);
489 messages = (GMimeMessage **) parts->pdata;
490 *nparts = parts->len;
492 g_ptr_array_free (parts, FALSE);