1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2014 Jeffrey Stedfast
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (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
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <gmime/gmime.h>
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include "testsuite.h"
31 
32 extern int verbose;
33 
34 #define d(x)
35 #define v(x) if (verbose > 3) x
36 
37 typedef struct {
38 	const char *name;
39 	const char *value;
40 } Header;
41 
42 static Header initial[] = {
43 	{ "Received",   "first received header"            },
44 	{ "Received",   "second received header"           },
45 	{ "Received",   "third received header"            },
46 	{ "Date",       "Sat, 31 May 2008 08:56:43 EST"    },
47 	{ "From",       "someone@somewhere.com"            },
48 	{ "Sender",     "someoneelse@somewhere.com"        },
49 	{ "To",         "coworker@somewhere.com"           },
50 	{ "Subject",    "hey, check this out"              },
51 	{ "Message-Id", "<136734928.123728@localhost.com>" },
52 };
53 
54 static GMimeHeaderList *
header_list_new(void)55 header_list_new (void)
56 {
57 	GMimeHeaderList *list;
58 	guint i;
59 
60 	list = g_mime_header_list_new ();
61 	for (i = 0; i < G_N_ELEMENTS (initial); i++)
62 		g_mime_header_list_append (list, initial[i].name, initial[i].value);
63 
64 	return list;
65 }
66 
67 static void
test_iter_forward_back(void)68 test_iter_forward_back (void)
69 {
70 	const char *name, *value;
71 	GMimeHeaderList *list;
72 	GMimeHeaderIter iter;
73 	guint i;
74 
75 	list = header_list_new ();
76 
77 	/* make sure initial iter is valid */
78 	testsuite_check ("initial iter");
79 	try {
80 		if (!g_mime_header_list_get_iter (list, &iter))
81 			throw (exception_new ("get_iter() failed"));
82 
83 		if (!g_mime_header_iter_is_valid (&iter))
84 			throw (exception_new ("invalid iter"));
85 
86 		name = g_mime_header_iter_get_name (&iter);
87 		value = g_mime_header_iter_get_value (&iter);
88 
89 		if (strcmp (initial[0].name, name) != 0 || strcmp (initial[0].value, value) != 0)
90 			throw (exception_new ("resulted in unexpected header"));
91 		testsuite_check_passed ();
92 	} catch (ex) {
93 		testsuite_check_failed ("initial iter: %s", ex->message);
94 	} finally;
95 
96 	/* make sure iter->next works as expected */
97 	for (i = 1; i < G_N_ELEMENTS (initial); i++) {
98 		testsuite_check ("next iter[%u]", i);
99 		try {
100 			if (!g_mime_header_iter_next (&iter))
101 				throw (exception_new ("failed to advance"));
102 
103 			if (!g_mime_header_iter_is_valid (&iter))
104 				throw (exception_new ("advanced but is invalid"));
105 
106 			name = g_mime_header_iter_get_name (&iter);
107 			value = g_mime_header_iter_get_value (&iter);
108 
109 			if (strcmp (initial[i].name, name) != 0 ||
110 			    strcmp (initial[i].value, value) != 0)
111 				throw (exception_new ("resulted in unexpected header"));
112 			testsuite_check_passed ();
113 		} catch (ex) {
114 			testsuite_check_failed ("next iter[%u]: %s", i, ex->message);
115 		} finally;
116 	}
117 
118 	/* make sure trying to advance past the last header fails */
119 	testsuite_check ("iter->next past end of headers");
120 	try {
121 		if (g_mime_header_iter_next (&iter))
122 			throw (exception_new ("should not have worked"));
123 		testsuite_check_passed ();
124 	} catch (ex) {
125 		testsuite_check_failed ("iter->next past end of headers: %s", ex->message);
126 	} finally;
127 
128 	/* make sure iter->prev works as expected */
129 	i--;
130 	while (i > 0) {
131 		testsuite_check ("prev iter[%u]", i);
132 		try {
133 			if (!g_mime_header_iter_prev (&iter))
134 				throw (exception_new ("failed to advance"));
135 
136 			if (!g_mime_header_iter_is_valid (&iter))
137 				throw (exception_new ("advanced but is invalid"));
138 
139 			name = g_mime_header_iter_get_name (&iter);
140 			value = g_mime_header_iter_get_value (&iter);
141 
142 			if (strcmp (initial[i - 1].name, name) != 0 ||
143 			    strcmp (initial[i - 1].value, value) != 0)
144 				throw (exception_new ("resulted in unexpected header"));
145 			testsuite_check_passed ();
146 		} catch (ex) {
147 			testsuite_check_failed ("prev iter[%u]: %s", i, ex->message);
148 		} finally;
149 
150 		i--;
151 	}
152 
153 	/* make sure trying to advance prev of the first header fails */
154 	testsuite_check ("iter->prev past beginning of headers");
155 	try {
156 		if (g_mime_header_iter_prev (&iter))
157 			throw (exception_new ("should not have worked"));
158 		testsuite_check_passed ();
159 	} catch (ex) {
160 		testsuite_check_failed ("iter->prev past beginning of headers: %s", ex->message);
161 	} finally;
162 
163 	g_mime_header_list_destroy (list);
164 }
165 
166 static void
test_iter_remove_all(void)167 test_iter_remove_all (void)
168 {
169 	GMimeHeaderList *list;
170 	GMimeHeaderIter iter;
171 	guint i = 0;
172 
173 	list = header_list_new ();
174 
175 	g_mime_header_list_get_iter (list, &iter);
176 
177 	testsuite_check ("removing all headers");
178 	try {
179 		while (g_mime_header_iter_remove (&iter))
180 			i++;
181 
182 		if (i != G_N_ELEMENTS (initial))
183 			throw (exception_new ("only removed %u of %u", i, G_N_ELEMENTS (initial)));
184 
185 		if (g_mime_header_iter_is_valid (&iter))
186 			throw (exception_new ("expected invalid iter"));
187 
188 		testsuite_check_passed ();
189 	} catch (ex) {
190 		testsuite_check_failed ("removing all headers: %s", ex->message);
191 	} finally;
192 
193 	g_mime_header_list_get_iter (list, &iter);
194 
195 	testsuite_check ("empty list iter");
196 	try {
197 		if (g_mime_header_iter_is_valid (&iter))
198 			throw (exception_new ("expected invalid iter"));
199 
200 		testsuite_check_passed ();
201 	} catch (ex) {
202 		testsuite_check_failed ("empty list iter: %s", ex->message);
203 	} finally;
204 
205 	g_mime_header_list_destroy (list);
206 }
207 
208 static void
test_iter_remove(void)209 test_iter_remove (void)
210 {
211 	GMimeHeaderIter iter, iter1, iter2, iter3;
212 	const char *name, *value;
213 	GMimeHeaderList *list;
214 	guint i;
215 
216 	list = header_list_new ();
217 
218 	g_mime_header_list_get_iter (list, &iter1);
219 
220 	testsuite_check ("iter copying");
221 	try {
222 		/* make iter2 point to the second header */
223 		g_mime_header_iter_copy_to (&iter1, &iter2);
224 		if (!g_mime_header_iter_next (&iter2))
225 			throw (exception_new ("iter2->next failed"));
226 
227 		name = g_mime_header_iter_get_name (&iter2);
228 		value = g_mime_header_iter_get_value (&iter2);
229 
230 		if (strcmp (initial[1].name, name) != 0 ||
231 		    strcmp (initial[1].value, value) != 0)
232 			throw (exception_new ("iter2 resulted in unexpected header"));
233 
234 		/* make iter3 point to the third header */
235 		g_mime_header_iter_copy_to (&iter2, &iter3);
236 		if (!g_mime_header_iter_next (&iter3))
237 			throw (exception_new ("iter3->next failed"));
238 
239 		name = g_mime_header_iter_get_name (&iter3);
240 		value = g_mime_header_iter_get_value (&iter3);
241 
242 		if (strcmp (initial[2].name, name) != 0 ||
243 		    strcmp (initial[2].value, value) != 0)
244 			throw (exception_new ("iter3 resulted in unexpected header"));
245 
246 		testsuite_check_passed ();
247 	} catch (ex) {
248 		testsuite_check_failed ("iter copying: %s", ex->message);
249 	} finally;
250 
251 	testsuite_check ("remove first header");
252 	try {
253 		/* remove the first header */
254 		g_mime_header_iter_copy_to (&iter1, &iter);
255 		if (!g_mime_header_iter_remove (&iter))
256 			throw (exception_new ("iter::remove() failed"));
257 
258 		/* make sure iter now points to the 2nd header */
259 		name = g_mime_header_iter_get_name (&iter);
260 		value = g_mime_header_iter_get_value (&iter);
261 
262 		if (strcmp (initial[1].name, name) != 0 ||
263 		    strcmp (initial[1].value, value) != 0)
264 			throw (exception_new ("iter doesn't point to 2nd header as expected"));
265 
266 		/* make sure that the other iters have been invalidated */
267 		if (g_mime_header_iter_is_valid (&iter1))
268 			throw (exception_new ("iter::remove() iter1::isvalid() incorrect"));
269 		if (g_mime_header_iter_is_valid (&iter2))
270 			throw (exception_new ("iter::remove() iter2::isvalid() incorrect"));
271 		if (g_mime_header_iter_is_valid (&iter3))
272 			throw (exception_new ("iter::remove() iter3::isvalid() incorrect"));
273 
274 		testsuite_check_passed ();
275 	} catch (ex) {
276 		testsuite_check_failed ("remove first header: %s", ex->message);
277 	} finally;
278 
279 	testsuite_check ("remove last header");
280 	try {
281 		/* remove the last header */
282 		g_mime_header_iter_last (&iter);
283 
284 		if (!g_mime_header_iter_remove (&iter))
285 			throw (exception_new ("iter::remove() failed"));
286 
287 		/* iter should be invalid now because it couldn't advance to a header beyond the last */
288 		if (g_mime_header_iter_is_valid (&iter))
289 			throw (exception_new ("iter::remove() iter is valid when it shouldn't be"));
290 
291 		testsuite_check_passed ();
292 	} catch (ex) {
293 		testsuite_check_failed ("remove last header: %s", ex->message);
294 	} finally;
295 
296 	testsuite_check ("remove middle header");
297 	try {
298 		g_mime_header_list_get_iter (list, &iter);
299 
300 		/* advance to a header in the middle somewhere... */
301 		g_mime_header_iter_next (&iter);
302 		g_mime_header_iter_next (&iter);
303 
304 		/* we should now be pointing to the 3rd header (4th from the initial headers) */
305 		name = g_mime_header_iter_get_name (&iter);
306 		value = g_mime_header_iter_get_value (&iter);
307 
308 		if (strcmp (initial[3].name, name) != 0 ||
309 		    strcmp (initial[3].value, value) != 0)
310 			throw (exception_new ("iter doesn't point to 3rd header as expected"));
311 
312 		/* remove it */
313 		if (!g_mime_header_iter_remove (&iter))
314 			throw (exception_new ("iter::remove() failed"));
315 
316 		/* make sure the iter is still valid */
317 		if (!g_mime_header_iter_is_valid (&iter))
318 			throw (exception_new ("iter::remove() iter isn't valid when it should be"));
319 
320 		/* make sure iter now points to the 4th header */
321 		name = g_mime_header_iter_get_name (&iter);
322 		value = g_mime_header_iter_get_value (&iter);
323 
324 		if (strcmp (initial[4].name, name) != 0 ||
325 		    strcmp (initial[4].value, value) != 0)
326 			throw (exception_new ("iter doesn't point to 4th header as expected"));
327 
328 		testsuite_check_passed ();
329 	} catch (ex) {
330 		testsuite_check_failed ("remove first header: %s", ex->message);
331 	} finally;
332 
333 	testsuite_check ("resulting lists match");
334 	try {
335 		g_mime_header_list_get_iter (list, &iter);
336 		i = 1;
337 
338 		do {
339 			name = g_mime_header_iter_get_name (&iter);
340 			value = g_mime_header_iter_get_value (&iter);
341 
342 			if (i == 3)
343 				i++;
344 
345 			if (strcmp (initial[i].name, name) != 0 ||
346 			    strcmp (initial[i].value, value) != 0)
347 				throw (exception_new ("iter vs array mismatch @ index %u", i));
348 
349 			i++;
350 		} while (g_mime_header_iter_next (&iter));
351 
352 		if (++i != G_N_ELEMENTS (initial))
353 			throw (exception_new ("iter didn't have as many headers as expected"));
354 
355 		testsuite_check_passed ();
356 	} catch (ex) {
357 		testsuite_check_failed ("resulting lists match: %s", ex->message);
358 	} finally;
359 
360 	g_mime_header_list_destroy (list);
361 }
362 
363 static void
test_header_sync(void)364 test_header_sync (void)
365 {
366 	InternetAddressList *list;
367 	InternetAddress *addr, *ia;
368 	GMimeMessage *message;
369 	GMimeObject *object;
370 	const char *value;
371 	GMimePart *part;
372 
373 	part = g_mime_part_new_with_type ("application", "octet-stream");
374 	object = (GMimeObject *) part;
375 
376 	testsuite_check ("content-type synchronization");
377 	try {
378 		/* first, check that the current Content-Type header
379 		 * value is "application/octet-stream" as expected */
380 		if (!(value = g_mime_object_get_header (object, "Content-Type")))
381 			throw (exception_new ("initial content-type header was unexpectedly null"));
382 
383 		if (strcmp ("application/octet-stream", value) != 0)
384 			throw (exception_new ("initial content-type header had unexpected value"));
385 
386 		/* now change the content-type's media type... */
387 		g_mime_content_type_set_media_type (object->content_type, "text");
388 		if (!(value = g_mime_object_get_header (object, "Content-Type")))
389 			throw (exception_new ("content-type header was unexpectedly null after changing type"));
390 		if (strcmp ("text/octet-stream", value) != 0)
391 			throw (exception_new ("content-type header had unexpected value after changing type"));
392 
393 		/* now change the content-type's media subtype... */
394 		g_mime_content_type_set_media_subtype (object->content_type, "plain");
395 		if (!(value = g_mime_object_get_header (object, "Content-Type")))
396 			throw (exception_new ("content-type header was unexpectedly null after changing subtype"));
397 		if (strcmp ("text/plain", value) != 0)
398 			throw (exception_new ("content-type header had unexpected value after changing subtype"));
399 
400 		/* now change the content-type's parameters by setting a param */
401 		g_mime_content_type_set_parameter (object->content_type, "format", "flowed");
402 		if (!(value = g_mime_object_get_header (object, "Content-Type")))
403 			throw (exception_new ("content-type header was unexpectedly null after setting a param"));
404 		if (strcmp ("text/plain; format=flowed", value) != 0)
405 			throw (exception_new ("content-type header had unexpected value after setting a param"));
406 
407 		/* now change the content-type's parameters by setting a param list */
408 		g_mime_content_type_set_params (object->content_type, NULL);
409 		if (!(value = g_mime_object_get_header (object, "Content-Type")))
410 			throw (exception_new ("content-type header was unexpectedly null after setting params"));
411 		if (strcmp ("text/plain", value) != 0)
412 			throw (exception_new ("content-type header had unexpected value after setting params"));
413 
414 		testsuite_check_passed ();
415 	} catch (ex) {
416 		testsuite_check_failed ("content-type header not synchronized: %s", ex->message);
417 	} finally;
418 
419 	testsuite_check ("content-disposition synchronization");
420 	try {
421 		g_mime_object_set_disposition (object, "attachment");
422 
423 		/* first, check that the current Content-Disposition header
424 		 * value is "application/octet-stream" as expected */
425 		if (!(value = g_mime_object_get_header (object, "Content-Disposition")))
426 			throw (exception_new ("initial content-disposition header was unexpectedly null"));
427 
428 		if (strcmp ("attachment", value) != 0)
429 			throw (exception_new ("initial content-disposition header had unexpected value"));
430 
431 		/* now change the content-disposition's disposition */
432 		g_mime_content_disposition_set_disposition (object->disposition, "inline");
433 		if (!(value = g_mime_object_get_header (object, "Content-Disposition")))
434 			throw (exception_new ("content-disposition header was unexpectedly null after changing type"));
435 		if (strcmp ("inline", value) != 0)
436 			throw (exception_new ("content-disposition header had unexpected value after changing type"));
437 
438 		/* now change the content-disposition's parameters by setting a param */
439 		g_mime_content_disposition_set_parameter (object->disposition, "filename", "hello.txt");
440 		if (!(value = g_mime_object_get_header (object, "Content-Disposition")))
441 			throw (exception_new ("content-disposition header was unexpectedly null after setting a param"));
442 		if (strcmp ("inline; filename=hello.txt", value) != 0)
443 			throw (exception_new ("content-disposition header had unexpected value after setting a param"));
444 
445 		/* now change the content-disposition's parameters by setting a param list */
446 		g_mime_content_disposition_set_params (object->disposition, NULL);
447 		if (!(value = g_mime_object_get_header (object, "Content-Disposition")))
448 			throw (exception_new ("content-disposition header was unexpectedly null after setting params"));
449 		if (strcmp ("inline", value) != 0)
450 			throw (exception_new ("content-disposition header had unexpected value after setting params"));
451 
452 		testsuite_check_passed ();
453 	} catch (ex) {
454 		testsuite_check_failed ("content-disposition header not synchronized: %s", ex->message);
455 	} finally;
456 
457 	g_object_unref (part);
458 
459 	message = g_mime_message_new (TRUE);
460 	list = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
461 	object = (GMimeObject *) message;
462 
463 	testsuite_check ("address header synchronization");
464 	try {
465 		/* first, check that the To recipients are empty */
466 		if (list == NULL || internet_address_list_length (list) != 0)
467 			throw (exception_new ("unexpected initial internet address list"));
468 
469 		/* now check that the initial header value is null */
470 		if ((value = g_mime_object_get_header (object, "To")) != NULL)
471 			throw (exception_new ("unexpected initial address list header"));
472 
473 		/* now try adding an address */
474 		addr = internet_address_mailbox_new ("Tester", "tester@localhost.com");
475 		internet_address_list_add (list, addr);
476 
477 		if (!(value = g_mime_object_get_header (object, "To")))
478 			throw (exception_new ("address list header unexpectedly null after adding recipient"));
479 
480 		if (strcmp ("Tester <tester@localhost.com>", value) != 0)
481 			throw (exception_new ("unexpected address list header after adding recipient"));
482 
483 		/* now let's try changing the address name to make sure signals properly chain up */
484 		internet_address_set_name (addr, "Eva Lucy-Ann Tester");
485 		if (!(value = g_mime_object_get_header (object, "To")))
486 			throw (exception_new ("address list header unexpectedly null after changing name"));
487 
488 		if (strcmp ("Eva Lucy-Ann Tester <tester@localhost.com>", value) != 0)
489 			throw (exception_new ("unexpected address list header after changing name"));
490 
491 		/* now let's try changing the address mailbox... */
492 		internet_address_mailbox_set_addr ((InternetAddressMailbox *) addr,
493 						   "evalucyann@ximian.com");
494 		if (!(value = g_mime_object_get_header (object, "To")))
495 			throw (exception_new ("address list header unexpectedly null after changing mailbox"));
496 
497 		if (strcmp ("Eva Lucy-Ann Tester <evalucyann@ximian.com>", value) != 0)
498 			throw (exception_new ("unexpected address list header after changing mailbox"));
499 
500 		/* now let's try inserting a group address */
501 		g_object_unref (addr);
502 		addr = internet_address_group_new ("Group");
503 		internet_address_list_insert (list, 0, addr);
504 
505 		if (!(value = g_mime_object_get_header (object, "To")))
506 			throw (exception_new ("address list header unexpectedly null after inserting group"));
507 
508 		if (strcmp ("Group: ;, Eva Lucy-Ann Tester <evalucyann@ximian.com>", value) != 0)
509 			throw (exception_new ("unexpected address list header after inserting group"));
510 
511 		/* now let's try removing the original recipient */
512 		internet_address_list_remove_at (list, 1);
513 		if (!(value = g_mime_object_get_header (object, "To")))
514 			throw (exception_new ("address list header unexpectedly null after removing recipient"));
515 
516 		if (strcmp ("Group: ;", value) != 0)
517 			throw (exception_new ("unexpected address list header after removing recipient"));
518 
519 		/* now let's try adding an address to the group... */
520 		ia = internet_address_mailbox_new ("Tester", "tester@hotmail.com");
521 		internet_address_list_add (((InternetAddressGroup *) addr)->members, ia);
522 		if (!(value = g_mime_object_get_header (object, "To")))
523 			throw (exception_new ("address list header unexpectedly null after adding addr to group"));
524 
525 		if (strcmp ("Group: Tester <tester@hotmail.com>;", value) != 0)
526 			throw (exception_new ("unexpected address list header after adding addr to group"));
527 
528 		testsuite_check_passed ();
529 	} catch (ex) {
530 		testsuite_check_failed ("address header not synchronized: %s", ex->message);
531 	} finally;
532 
533 	g_object_unref (message);
534 }
535 
main(int argc,char ** argv)536 int main (int argc, char **argv)
537 {
538 	g_mime_init (0);
539 
540 	testsuite_init (argc, argv);
541 
542 	testsuite_start ("iterating forward and backward");
543 	test_iter_forward_back ();
544 	testsuite_end ();
545 
546 	testsuite_start ("removing all headers");
547 	test_iter_remove_all ();
548 	testsuite_end ();
549 
550 	testsuite_start ("removing individual headers");
551 	test_iter_remove ();
552 	testsuite_end ();
553 
554 	testsuite_start ("header synchronization");
555 	test_header_sync ();
556 	testsuite_end ();
557 
558 	g_mime_shutdown ();
559 
560 	return testsuite_exit ();
561 }
562