1 /*
2  * This library is free software: you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This library is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this library. If not, see <http://www.gnu.org/licenses/>.
13  */
14 
15 #include <string.h>
16 
17 #include "camel-test.h"
18 #include "folders.h"
19 #include "messages.h"
20 
21 /* check the total/unread is what we think it should be */
22 void
test_folder_counts(CamelFolder * folder,gint total,gint unread)23 test_folder_counts (CamelFolder *folder,
24                     gint total,
25                     gint unread)
26 {
27 	GPtrArray *s;
28 	gint i, myunread;
29 	CamelMessageInfo *info;
30 
31 	push ("test folder counts %d total %d unread", total, unread);
32 
33 	/* use the summary */
34 	s = camel_folder_get_summary (folder);
35 	check (s != NULL);
36 	check (s->len == total);
37 	myunread = s->len;
38 	for (i = 0; i < s->len; i++) {
39 		info = s->pdata[i];
40 		if (camel_message_info_get_flags (info) & CAMEL_MESSAGE_SEEN)
41 			myunread--;
42 	}
43 	check (unread == myunread);
44 	camel_folder_free_summary (folder, s);
45 
46 	/* use the uid list */
47 	s = camel_folder_get_uids (folder);
48 	check (s != NULL);
49 	check (s->len == total);
50 	myunread = s->len;
51 	for (i = 0; i < s->len; i++) {
52 		info = camel_folder_get_message_info (folder, s->pdata[i]);
53 		if (camel_message_info_get_flags (info) & CAMEL_MESSAGE_SEEN)
54 			myunread--;
55 		g_clear_object (&info);
56 	}
57 	check (unread == myunread);
58 	camel_folder_free_uids (folder, s);
59 
60 	pull ();
61 }
62 
63 static gint
safe_strcmp(const gchar * a,const gchar * b)64 safe_strcmp (const gchar *a,
65              const gchar *b)
66 {
67 	if (a == NULL && b == NULL)
68 		return 0;
69 	if (a == NULL)
70 		return 1;
71 	if (b == NULL)
72 		return -1;
73 	return strcmp (a, b);
74 }
75 
76 void
test_message_info(CamelMimeMessage * msg,const CamelMessageInfo * info)77 test_message_info (CamelMimeMessage *msg,
78                    const CamelMessageInfo *info)
79 {
80 	check_msg (
81 		safe_strcmp (camel_message_info_get_subject (info), camel_mime_message_get_subject (msg)) == 0,
82 		"info->subject = '%s', get_subject () = '%s'", camel_message_info_get_subject (info), camel_mime_message_get_subject (msg));
83 
84 	/* FIXME: testing from/cc/to, etc is more tricky */
85 
86 	check (camel_message_info_get_date_sent (info) == camel_mime_message_get_date (msg, NULL));
87 
88 	/* date received isn't set for messages that haven't been sent anywhere ... */
89 	/*check (info->date_received == camel_mime_message_get_date_received (msg, NULL));*/
90 
91 	/* so is messageid/references, etc */
92 }
93 
94 /* check a message is present */
95 void
test_folder_message(CamelFolder * folder,const gchar * uid)96 test_folder_message (CamelFolder *folder,
97                      const gchar *uid)
98 {
99 	CamelMimeMessage *msg;
100 	CamelMessageInfo *info;
101 	GPtrArray *s;
102 	gint i;
103 	gint found;
104 	GError *error = NULL;
105 
106 	push ("uid %s is in folder", uid);
107 
108 	/* first try getting info */
109 	info = camel_folder_get_message_info (folder, uid);
110 	check (info != NULL);
111 	check (strcmp (camel_message_info_get_uid (info), uid) == 0);
112 	g_clear_object (&info);
113 
114 	/* then, getting message */
115 	msg = camel_folder_get_message_sync (folder, uid, NULL, &error);
116 	check_msg (error == NULL, "%s", error->message);
117 	check (msg != NULL);
118 
119 	/* cross check with info */
120 	test_message_info (msg, info);
121 
122 	g_object_unref (msg);
123 
124 	/* see if it is in the summary (only once) */
125 	s = camel_folder_get_summary (folder);
126 	check (s != NULL);
127 	found = 0;
128 	for (i = 0; i < s->len; i++) {
129 		info = s->pdata[i];
130 		if (strcmp (camel_message_info_get_uid (info), uid) == 0)
131 			found++;
132 	}
133 	check (found == 1);
134 	camel_folder_free_summary (folder, s);
135 
136 	/* check it is in the uid list */
137 	s = camel_folder_get_uids (folder);
138 	check (s != NULL);
139 	found = 0;
140 	for (i = 0; i < s->len; i++) {
141 		if (strcmp (s->pdata[i], uid) == 0)
142 			found++;
143 	}
144 	check (found == 1);
145 	camel_folder_free_uids (folder, s);
146 
147 	g_clear_error (&error);
148 
149 	pull ();
150 }
151 
152 /* check message not present */
153 void
test_folder_not_message(CamelFolder * folder,const gchar * uid)154 test_folder_not_message (CamelFolder *folder,
155                          const gchar *uid)
156 {
157 	CamelMimeMessage *msg;
158 	CamelMessageInfo *info;
159 	GPtrArray *s;
160 	gint i;
161 	gint found;
162 	GError *error = NULL;
163 
164 	push ("uid '%s' is not in folder", uid);
165 
166 	/* first try getting info */
167 	push ("no message info");
168 	info = camel_folder_get_message_info (folder, uid);
169 	check (info == NULL);
170 	pull ();
171 
172 	/* then, getting message */
173 	push ("no message");
174 	msg = camel_folder_get_message_sync (folder, uid, NULL, &error);
175 	check (error != NULL);
176 	check (msg == NULL);
177 	g_clear_error (&error);
178 	pull ();
179 
180 	/* see if it is not in the summary (only once) */
181 	push ("not in summary list");
182 	s = camel_folder_get_summary (folder);
183 	check (s != NULL);
184 	found = 0;
185 	for (i = 0; i < s->len; i++) {
186 		info = s->pdata[i];
187 		if (strcmp (camel_message_info_get_uid (info), uid) == 0)
188 			found++;
189 	}
190 	check (found == 0);
191 	camel_folder_free_summary (folder, s);
192 	pull ();
193 
194 	/* check it is not in the uid list */
195 	push ("not in uid list");
196 	s = camel_folder_get_uids (folder);
197 	check (s != NULL);
198 	found = 0;
199 	for (i = 0; i < s->len; i++) {
200 		if (strcmp (s->pdata[i], uid) == 0)
201 			found++;
202 	}
203 	check (found == 0);
204 	camel_folder_free_uids (folder, s);
205 	pull ();
206 
207 	g_clear_error (&error);
208 
209 	pull ();
210 }
211 
212 /* test basic store operations on folders */
213 /* TODO: Add subscription stuff */
214 void
test_folder_basic(CamelSession * session,const gchar * storename,gint local,gint spool)215 test_folder_basic (CamelSession *session,
216                    const gchar *storename,
217                    gint local,
218                    gint spool)
219 {
220 	CamelStore *store;
221 	CamelFolder *folder;
222 	CamelService *service;
223 	gchar *what = g_strdup_printf ("testing store: %s", storename);
224 	GError *error = NULL;
225 
226 	camel_test_start (what);
227 	test_free (what);
228 
229 	push ("getting store");
230 	service = camel_session_add_service (
231 		session, storename, storename, CAMEL_PROVIDER_STORE, &error);
232 	check_msg (error == NULL, "adding store: %s", error->message);
233 	check (CAMEL_IS_STORE (service));
234 	store = CAMEL_STORE (service);
235 	pull ();
236 
237 	/* local providers == no inbox */
238 	push ("getting inbox folder");
239 	folder = camel_store_get_inbox_folder_sync (store, NULL, &error);
240 	if (local) {
241 		/* Well, maildir can have an inbox */
242 		if (folder) {
243 			check (error == NULL);
244 			check_unref (folder, 1);
245 		} else {
246 			check (error != NULL);
247 		}
248 	} else {
249 		check_msg (error == NULL, "%s", error->message);
250 		check (folder != NULL);
251 		check_unref (folder, 2);
252 	}
253 	g_clear_error (&error);
254 	pull ();
255 
256 	push ("getting a non-existant folder, no create");
257 	folder = camel_store_get_folder_sync (
258 		store, "unknown", 0, NULL, &error);
259 	check (error != NULL);
260 	check (folder == NULL);
261 	g_clear_error (&error);
262 	pull ();
263 
264 	if (!spool) {
265 		push ("getting a non-existant folder, with create");
266 		folder = camel_store_get_folder_sync (
267 			store, "testbox", CAMEL_STORE_FOLDER_CREATE,
268 			NULL, &error);
269 		check_msg (error == NULL, "%s", error->message);
270 		check (folder != NULL);
271 		if (local)
272 			check_unref (folder, 1);
273 		else
274 			check_unref (folder, 2);
275 		g_clear_error (&error);
276 		pull ();
277 
278 		push ("getting an existing folder");
279 		folder = camel_store_get_folder_sync (
280 			store, "testbox", 0, NULL, &error);
281 		check_msg (error == NULL, "%s", error->message);
282 		check (folder != NULL);
283 		if (local)
284 			check_unref (folder, 1);
285 		else
286 			check_unref (folder, 2);
287 		g_clear_error (&error);
288 		pull ();
289 
290 		push ("renaming a non-existant folder");
291 		camel_store_rename_folder_sync (
292 			store, "unknown1", "unknown2", NULL, &error);
293 		check (error != NULL);
294 		g_clear_error (&error);
295 		pull ();
296 
297 		push ("renaming an existing folder");
298 		camel_store_rename_folder_sync (
299 			store, "testbox", "testbox2", NULL, &error);
300 		check_msg (error == NULL, "%s", error->message);
301 		g_clear_error (&error);
302 		pull ();
303 
304 		push ("opening the old name of a renamed folder");
305 		folder = camel_store_get_folder_sync (
306 			store, "testbox", 0, NULL, &error);
307 		check (error != NULL);
308 		check (folder == NULL);
309 		g_clear_error (&error);
310 		pull ();
311 
312 		push ("opening the new name of a renamed folder");
313 		folder = camel_store_get_folder_sync (
314 			store, "testbox2", 0, NULL, &error);
315 		check_msg (error == NULL, "%s", error->message);
316 		check (folder != NULL);
317 		if (local)
318 			check_unref (folder, 1);
319 		else
320 			check_unref (folder, 2);
321 		pull ();
322 	}
323 
324 	push ("deleting a non-existant folder");
325 	camel_store_delete_folder_sync (store, "unknown", NULL, &error);
326 	check (error != NULL);
327 	g_clear_error (&error);
328 	pull ();
329 
330 	if (!spool) {
331 		push ("deleting an existing folder");
332 		camel_store_delete_folder_sync (
333 			store, "testbox2", NULL, &error);
334 		check_msg (error == NULL, "%s", error->message);
335 		g_clear_error (&error);
336 		pull ();
337 	}
338 
339 	push ("opening a folder that has been deleted");
340 	folder = camel_store_get_folder_sync (
341 		store, "testbox2", 0, NULL, &error);
342 	check (error != NULL);
343 	check (folder == NULL);
344 	g_clear_error (&error);
345 	pull ();
346 
347 	check_unref (store, 1);
348 
349 	camel_test_end ();
350 }
351 
352 /* todo: cross-check everything with folder_info checks as well */
353 /* this should probably take a folder instead of a session ... */
354 void
test_folder_message_ops(CamelSession * session,const gchar * name,gint local,const gchar * mailbox)355 test_folder_message_ops (CamelSession *session,
356                          const gchar *name,
357                          gint local,
358                          const gchar *mailbox)
359 {
360 	CamelStore *store;
361 	CamelService *service;
362 	CamelFolder *folder;
363 	CamelMimeMessage *msg;
364 	gint j;
365 	gint indexed, max;
366 	GPtrArray *uids;
367 	CamelMessageInfo *info;
368 	GError *error = NULL;
369 
370 	max = local ? 2 : 1;
371 
372 	for (indexed = 0; indexed < max; indexed++) {
373 		gchar *what = g_strdup_printf ("folder ops: %s %s", name, local ? (indexed?"indexed":"non-indexed"):"");
374 		gint flags;
375 
376 		camel_test_start (what);
377 		test_free (what);
378 
379 		push ("getting store");
380 		service = camel_session_add_service (
381 			session, name, name, CAMEL_PROVIDER_STORE, &error);
382 		check_msg (error == NULL, "adding store: %s", error->message);
383 		check (CAMEL_IS_STORE (service));
384 		store = CAMEL_STORE (service);
385 		g_clear_error (&error);
386 		pull ();
387 
388 		push ("creating %sindexed folder", indexed?"":"non-");
389 		if (indexed)
390 			flags = CAMEL_STORE_FOLDER_CREATE | CAMEL_STORE_FOLDER_BODY_INDEX;
391 		else
392 			flags = CAMEL_STORE_FOLDER_CREATE;
393 		folder = camel_store_get_folder_sync (
394 			store, mailbox, flags, NULL, &error);
395 
396 		/* we can't create mailbox outside of namespace, since we have no api for it, try
397 		 * using inbox namespace, works for courier */
398 		if (folder == NULL) {
399 			gchar *mbox = g_strdup_printf ("INBOX/%s", mailbox);
400 			mailbox = mbox;
401 			g_clear_error (&error);
402 			folder = camel_store_get_folder_sync (
403 				store, mailbox, flags, NULL, &error);
404 		}
405 
406 		check_msg (error == NULL, "%s", error->message);
407 		check (folder != NULL);
408 
409 		/* verify empty/can't get nonexistant stuff */
410 		test_folder_counts (folder, 0, 0);
411 		test_folder_not_message (folder, "0");
412 		test_folder_not_message (folder, "");
413 
414 		for (j = 0; j < 10; j++) {
415 			gchar *content, *subject;
416 
417 			push ("creating test message");
418 			msg = test_message_create_simple ();
419 			content = g_strdup_printf ("Test message %d contents\n\n", j);
420 			test_message_set_content_simple (
421 				(CamelMimePart *) msg, 0, "text/plain",
422 							content, strlen (content));
423 			test_free (content);
424 			subject = g_strdup_printf ("Test message %d", j);
425 			camel_mime_message_set_subject (msg, subject);
426 			pull ();
427 
428 			push ("appending simple message %d", j);
429 			camel_folder_append_message_sync (
430 				folder, msg, NULL, NULL, NULL, &error);
431 			check_msg (error == NULL, "%s", error->message);
432 
433 #if 0
434 			/* sigh, this shouldn't be required, but the imap code is too dumb to do it itself */
435 			if (!local) {
436 				push ("forcing a refresh of folder updates");
437 				camel_folder_refresh_info (folder, ex);
438 				check_msg (error == NULL, "%s", error->message);
439 				pull ();
440 			}
441 #endif
442 			/*if (!local)
443 			  camel_test_nonfatal ("unread counts dont seem right for imap");*/
444 
445 			test_folder_counts (folder, j + 1, j + 1);
446 
447 			/*if (!local)
448 			  camel_test_fatal ();*/
449 
450 			push ("checking it is in the right uid slot & exists");
451 			uids = camel_folder_get_uids (folder);
452 			check (uids != NULL);
453 			check (uids->len == j + 1);
454 			if (uids->len > j)
455 				test_folder_message (folder, uids->pdata[j]);
456 			pull ();
457 
458 			push ("checking it is the right message (subject): %s", subject);
459 			if (uids->len > j) {
460 				info = camel_folder_get_message_info (folder, uids->pdata[j]);
461 				check (info != NULL);
462 				check_msg (
463 					strcmp (camel_message_info_get_subject (info), subject) == 0,
464 					"info->subject %s", camel_message_info_get_subject (info));
465 				g_clear_object (&info);
466 			}
467 			camel_folder_free_uids (folder, uids);
468 			pull ();
469 
470 			test_free (subject);
471 
472 			/*if (!local)
473 			  camel_test_fatal ();*/
474 
475 			check_unref (msg, 1);
476 			pull ();
477 		}
478 
479 		if (local)
480 			check_unref (folder, 1);
481 		else
482 			check_unref (folder, 2);
483 		pull ();
484 
485 #if 0
486 		push ("deleting test folder, with messages in it");
487 		camel_store_delete_folder (store, mailbox, ex);
488 		check (camel_exception_is_set (ex));
489 		camel_exception_clear (ex);
490 		pull ();
491 #endif
492 
493 		push ("re-opening folder");
494 		folder = camel_store_get_folder_sync (
495 			store, mailbox, flags, NULL, &error);
496 		check_msg (error == NULL, "%s", error->message);
497 		check (folder != NULL);
498 		g_clear_error (&error);
499 
500 			/* verify counts */
501 		test_folder_counts (folder, 10, 10);
502 
503 		/* re-check uid's, after a reload */
504 		uids = camel_folder_get_uids (folder);
505 		check (uids != NULL);
506 		check (uids->len == 10);
507 		for (j = 0; j < 10; j++) {
508 			gchar *subject = g_strdup_printf ("Test message %d", j);
509 
510 			push ("verify reload of %s", subject);
511 			test_folder_message (folder, uids->pdata[j]);
512 
513 			info = camel_folder_get_message_info (folder, uids->pdata[j]);
514 			check_msg (
515 				strcmp (camel_message_info_get_subject (info), subject) == 0,
516 				"info->subject %s", camel_message_info_get_subject (info));
517 			test_free (subject);
518 			g_clear_object (&info);
519 			pull ();
520 		}
521 
522 		push ("deleting first message & expunging");
523 		camel_folder_delete_message (folder, uids->pdata[0]);
524 		test_folder_counts (folder, 10, 9);
525 		camel_folder_expunge_sync (folder, NULL, &error);
526 		check_msg (error == NULL, "%s", error->message);
527 		g_clear_error (&error);
528 		test_folder_not_message (folder, uids->pdata[0]);
529 		test_folder_counts (folder, 9, 9);
530 
531 		camel_folder_free_uids (folder, uids);
532 
533 		uids = camel_folder_get_uids (folder);
534 		check (uids != NULL);
535 		check (uids->len == 9);
536 		for (j = 0; j < 9; j++) {
537 			gchar *subject = g_strdup_printf ("Test message %d", j + 1);
538 
539 			push ("verify after expunge of %s", subject);
540 			test_folder_message (folder, uids->pdata[j]);
541 
542 			info = camel_folder_get_message_info (folder, uids->pdata[j]);
543 			check_msg (
544 				strcmp (camel_message_info_get_subject (info), subject) == 0,
545 				"info->subject %s", camel_message_info_get_subject (info));
546 			test_free (subject);
547 			g_clear_object (&info);
548 			pull ();
549 		}
550 		pull ();
551 
552 		push ("deleting last message & expunging");
553 		camel_folder_delete_message (folder, uids->pdata[8]);
554 		/* sync? */
555 		test_folder_counts (folder, 9, 8);
556 		camel_folder_expunge_sync (folder, NULL, &error);
557 		check_msg (error == NULL, "%s", error->message);
558 		g_clear_error (&error);
559 		test_folder_not_message (folder, uids->pdata[8]);
560 		test_folder_counts (folder, 8, 8);
561 
562 		camel_folder_free_uids (folder, uids);
563 
564 		uids = camel_folder_get_uids (folder);
565 		check (uids != NULL);
566 		check (uids->len == 8);
567 		for (j = 0; j < 8; j++) {
568 			gchar *subject = g_strdup_printf ("Test message %d", j + 1);
569 
570 			push ("verify after expunge of %s", subject);
571 			test_folder_message (folder, uids->pdata[j]);
572 
573 			info = camel_folder_get_message_info (folder, uids->pdata[j]);
574 			check_msg (
575 				strcmp (camel_message_info_get_subject (info), subject) == 0,
576 				"info->subject %s", camel_message_info_get_subject (info));
577 			test_free (subject);
578 			g_clear_object (&info);
579 			pull ();
580 		}
581 		pull ();
582 
583 		push ("deleting all messages & expunging");
584 		for (j = 0; j < 8; j++) {
585 			camel_folder_delete_message (folder, uids->pdata[j]);
586 		}
587 		/* sync? */
588 		test_folder_counts (folder, 8, 0);
589 		camel_folder_expunge_sync (folder, NULL, &error);
590 		check_msg (error == NULL, "%s", error->message);
591 		g_clear_error (&error);
592 		for (j = 0; j < 8; j++) {
593 			test_folder_not_message (folder, uids->pdata[j]);
594 		}
595 		test_folder_counts (folder, 0, 0);
596 
597 		camel_folder_free_uids (folder, uids);
598 		pull ();
599 
600 		if (local)
601 			check_unref (folder, 1);
602 		else
603 			check_unref (folder, 2);
604 		pull (); /* re-opening folder */
605 
606 		if (g_ascii_strcasecmp (mailbox, "INBOX") != 0) {
607 			push ("deleting test folder, with no messages in it");
608 			camel_store_delete_folder_sync (
609 				store, mailbox, NULL, &error);
610 			check_msg (error == NULL, "%s", error->message);
611 			g_clear_error (&error);
612 			pull ();
613 		}
614 
615 		if (!local) {
616 			push ("disconneect service");
617 			camel_service_disconnect_sync (
618 				CAMEL_SERVICE (store), TRUE, NULL, &error);
619 			check_msg (error == NULL, "%s", error->message);
620 			g_clear_error (&error);
621 			pull ();
622 		}
623 
624 		check_unref (store, 1);
625 		camel_test_end ();
626 	}
627 }
628