Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-search-sql-sexp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Copyright (C) 2008 Novell, Inc. (www.novell.com)
4  *
5  *  Author: Srinivasa Ragavan  <sragavan@novell.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /* This is a helper class for folders to implement the search function.
23  * It implements enough to do basic searches on folders that can provide
24  * an in-memory summary and a body index. */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include "camel-search-sql-sexp.h"
34 #define d(x) /* x;printf("\n"); */
35
36 #ifdef TEST_MAIN
37 #include <sqlite3.h>
38 typedef enum {
39         CAMEL_SEARCH_MATCH_EXACT,
40         CAMEL_SEARCH_MATCH_CONTAINS,
41         CAMEL_SEARCH_MATCH_STARTS,
42         CAMEL_SEARCH_MATCH_ENDS,
43         CAMEL_SEARCH_MATCH_SOUNDEX
44 } camel_search_match_t;
45 gchar * camel_db_get_column_name (const gchar *raw_name);
46
47 gchar *
48 camel_db_sqlize_string (const gchar *string)
49 {
50         return sqlite3_mprintf ("%Q", string);
51 }
52
53 void
54 camel_db_free_sqlized_string (gchar *string)
55 {
56         sqlite3_free (string);
57         string = NULL;
58 }
59
60 #else
61 #include "camel-db.h"
62 #include "camel-folder-search.h"
63 #include "camel-search-private.h"
64 #endif
65
66 static gchar *
67 get_db_safe_string (const gchar *str)
68 {
69         gchar *tmp = camel_db_sqlize_string (str);
70         gchar *ret;
71
72         ret = g_strdup (tmp);
73         camel_db_free_sqlized_string (tmp);
74
75         return ret;
76 }
77
78 /* Configuration of your sexp expression */
79
80 static CamelSExpResult *
81 func_and (CamelSExp *f,
82           gint argc,
83           struct _CamelSExpTerm **argv,
84           gpointer data)
85 {
86         CamelSExpResult *r, *r1;
87         GString *string;
88         gint i;
89
90         d (printf ("executing and: %d", argc));
91
92         string = g_string_new ("( ");
93         for (i = 0; i < argc; i++) {
94                 r1 = camel_sexp_term_eval (f, argv[i]);
95
96                 if (r1->type != CAMEL_SEXP_RES_STRING) {
97                         camel_sexp_result_free (f, r1);
98                         continue;
99                 }
100                 if (r1->value.string && *r1->value.string)
101                         g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 1)) ?  " AND ":"");
102                 camel_sexp_result_free (f, r1);
103         }
104         g_string_append (string, " )");
105         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
106
107         if (strlen (string->str) == 4)
108                 r->value.string = g_strdup ("");
109         else
110                 r->value.string = string->str;
111         g_string_free (string, FALSE);
112
113         return r;
114 }
115
116 static CamelSExpResult *
117 func_or (CamelSExp *f,
118          gint argc,
119          struct _CamelSExpTerm **argv,
120          gpointer data)
121 {
122         CamelSExpResult *r, *r1;
123         GString *string;
124         gint i;
125
126         d (printf ("executing or: %d", argc));
127
128         string = g_string_new ("( ");
129         for (i = 0; i < argc; i++) {
130                 r1 = camel_sexp_term_eval (f, argv[i]);
131
132                 if (r1->type != CAMEL_SEXP_RES_STRING) {
133                         camel_sexp_result_free (f, r1);
134                         continue;
135                 }
136                 g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 1)) ?  " OR ":"");
137                 camel_sexp_result_free (f, r1);
138         }
139         g_string_append (string, " )");
140
141         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
142         r->value.string = string->str;
143         g_string_free (string, FALSE);
144         return r;
145 }
146
147 static CamelSExpResult *
148 func_not (CamelSExp *f,
149           gint argc,
150           struct _CamelSExpTerm **argv,
151           gpointer data)
152 {
153         CamelSExpResult *r = NULL, *r1;
154
155         d (printf ("executing not: %d", argc));
156         r1 = camel_sexp_term_eval (f, argv[0]);
157
158         if (r1->type == CAMEL_SEXP_RES_STRING) {
159                 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
160                 /* HACK: Fix and handle completed-on better. */
161                 if (g_strcmp0 (r1->value.string, "( (usertags LIKE '%completed-on 0%' AND usertags LIKE '%completed-on%') )") == 0)
162                         r->value.string = g_strdup ("( (not (usertags LIKE '%completed-on 0%')) AND usertags LIKE '%completed-on%' )");
163                 else
164                         r->value.string = g_strdup_printf (
165                                 "(NOT (%s))", r1->value.string);
166         }
167         camel_sexp_result_free (f, r1);
168
169         return r;
170 }
171
172 /* this should support all arguments ...? */
173 static CamelSExpResult *
174 eval_eq (struct _CamelSExp *f,
175          gint argc,
176          struct _CamelSExpTerm **argv,
177          gpointer data)
178 {
179         struct _CamelSExpResult *r, *r1, *r2;
180
181         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
182
183         if (argc == 2) {
184                 GString *str = g_string_new ("( ");
185                 r1 = camel_sexp_term_eval (f, argv[0]);
186                 r2 = camel_sexp_term_eval (f, argv[1]);
187
188                 if (r1->type == CAMEL_SEXP_RES_INT)
189                         g_string_append_printf (str, "%d", r1->value.number);
190                 else if (r1->type == CAMEL_SEXP_RES_TIME)
191                         g_string_append_printf (str, "%ld", r1->value.time);
192                 else if (r1->type == CAMEL_SEXP_RES_STRING)
193                         g_string_append_printf (str, "%s", r1->value.string);
194
195                 if (!strstr (str->str, "completed-on") && !strstr (str->str, "follow-up")) {
196                         gboolean ut = FALSE;
197
198                         if (strstr (str->str, "usertags"))
199                                 ut = TRUE;
200                         if (ut)
201                                 g_string_append_printf (str, " LIKE ");
202                         else
203                                 g_string_append_printf (str, " = ");
204                         if (r2->type == CAMEL_SEXP_RES_INT)
205                                 g_string_append_printf (str, "%d", r2->value.number);
206                         if (r2->type == CAMEL_SEXP_RES_BOOL)
207                                 g_string_append_printf (str, "%d", r2->value.boolean);
208                         else if (r2->type == CAMEL_SEXP_RES_TIME)
209                                 g_string_append_printf (str, "%ld", r2->value.time);
210                         else if (r2->type == CAMEL_SEXP_RES_STRING) {
211                                 gchar *tmp = g_strdup_printf ("%c%s%c", ut ? '%':' ', r2->value.string, ut ? '%':' ');
212                                 gchar *safe = get_db_safe_string (tmp);
213                                 g_string_append_printf (str, "%s", safe);
214                                 g_free (safe);
215                                 g_free (tmp);
216                         }
217                 }
218                 camel_sexp_result_free (f, r1);
219                 camel_sexp_result_free (f, r2);
220                 g_string_append (str, " )");
221                 r->value.string = str->str;
222                 g_string_free (str, FALSE);
223         } else {
224                 r->value.string = g_strdup ("(0)");
225         }
226         return r;
227 }
228
229 static CamelSExpResult *
230 eval_lt (struct _CamelSExp *f,
231          gint argc,
232          struct _CamelSExpTerm **argv,
233          gpointer data)
234 {
235         struct _CamelSExpResult *r, *r1, *r2;
236
237         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
238
239         if (argc == 2) {
240                 GString *str = g_string_new ("( ");
241                 r1 = camel_sexp_term_eval (f, argv[0]);
242                 r2 = camel_sexp_term_eval (f, argv[1]);
243
244                 if (r1->type == CAMEL_SEXP_RES_INT)
245                         g_string_append_printf (str, "%d", r1->value.number);
246                 else if (r1->type == CAMEL_SEXP_RES_TIME)
247                         g_string_append_printf (str, "%ld", r1->value.time);
248                 else if (r1->type == CAMEL_SEXP_RES_STRING)
249                         g_string_append_printf (str, "%s", r1->value.string);
250
251                 g_string_append_printf (str, " < ");
252                 if (r2->type == CAMEL_SEXP_RES_INT)
253                         g_string_append_printf (str, "%d", r2->value.number);
254                 if (r2->type == CAMEL_SEXP_RES_BOOL)
255                         g_string_append_printf (str, "%d", r2->value.boolean);
256                 else if (r2->type == CAMEL_SEXP_RES_TIME)
257                         g_string_append_printf (str, "%ld", r2->value.time);
258                 else if (r2->type == CAMEL_SEXP_RES_STRING)
259                         g_string_append_printf (str, "%s", r2->value.string);
260                 camel_sexp_result_free (f, r1);
261                 camel_sexp_result_free (f, r2);
262                 g_string_append (str, " )");
263
264                 r->value.string = str->str;
265                 g_string_free (str, FALSE);
266         }
267         return r;
268 }
269
270 /* this should support all arguments ...? */
271 static CamelSExpResult *
272 eval_gt (struct _CamelSExp *f,
273          gint argc,
274          struct _CamelSExpTerm **argv,
275          gpointer data)
276 {
277         struct _CamelSExpResult *r, *r1, *r2;
278
279         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
280
281         if (argc == 2) {
282                 GString *str = g_string_new ("( ");
283                 r1 = camel_sexp_term_eval (f, argv[0]);
284                 r2 = camel_sexp_term_eval (f, argv[1]);
285
286                 if (r1->type == CAMEL_SEXP_RES_INT)
287                         g_string_append_printf (str, "%d", r1->value.number);
288                 else if (r1->type == CAMEL_SEXP_RES_TIME)
289                         g_string_append_printf (str, "%ld", r1->value.time);
290                 else if (r1->type == CAMEL_SEXP_RES_STRING)
291                         g_string_append_printf (str, "%s", r1->value.string);
292
293                 g_string_append_printf (str, " > ");
294                 if (r2->type == CAMEL_SEXP_RES_INT)
295                         g_string_append_printf (str, "%d", r2->value.number);
296                 if (r2->type == CAMEL_SEXP_RES_BOOL)
297                         g_string_append_printf (str, "%d", r2->value.boolean);
298                 else if (r2->type == CAMEL_SEXP_RES_TIME)
299                         g_string_append_printf (str, "%ld", r2->value.time);
300                 else if (r2->type == CAMEL_SEXP_RES_STRING)
301                         g_string_append_printf (str, "%s", r2->value.string);
302                 camel_sexp_result_free (f, r1);
303                 camel_sexp_result_free (f, r2);
304                 g_string_append (str, " )");
305
306                 r->value.string = str->str;
307                 g_string_free (str, FALSE);
308         }
309         return r;
310 }
311
312 static CamelSExpResult *
313 match_all (struct _CamelSExp *f,
314            gint argc,
315            struct _CamelSExpTerm **argv,
316            gpointer data)
317 {
318         CamelSExpResult *r;
319
320         d (printf ("executing match-all: %d", argc));
321         if (argc == 0) {
322                 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
323                 r->value.string = g_strdup ("1");
324         } else if (argv[0]->type != CAMEL_SEXP_TERM_BOOL)
325                 r = camel_sexp_term_eval (f, argv[0]);
326         else {
327                 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
328                 r->value.string = g_strdup (argv[0]->value.boolean ? "1" : "0");
329         }
330
331         return r;
332
333 }
334
335 static CamelSExpResult *
336 match_threads (struct _CamelSExp *f,
337                gint argc,
338                struct _CamelSExpTerm **argv,
339                gpointer data)
340 {
341         CamelSExpResult *r;
342         gint i;
343         GString *str = g_string_new ("( ");
344
345         d (printf ("executing match-threads: %d", argc));
346
347         for (i = 1; i < argc; i++) {
348                 r = camel_sexp_term_eval (f, argv[i]);
349                 g_string_append_printf (str, "%s%s", r->value.string, ((argc > 1) && (i != argc - 1)) ?  " AND ":"");
350                 camel_sexp_result_free (f, r);
351         }
352
353         g_string_append (str, " )");
354         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
355         r->value.string = str->str;
356         g_string_free (str, FALSE);
357
358         return r;
359 }
360
361 static CamelSExpResult *
362 check_header (struct _CamelSExp *f,
363               gint argc,
364               struct _CamelSExpResult **argv,
365               gpointer data,
366               camel_search_match_t how)
367 {
368         CamelSExpResult *r;
369         gchar *str = NULL;
370
371         d (printf ("executing check-header %d\n", how));
372
373         /* are we inside a match-all? */
374         if (argc > 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
375                 gchar *headername;
376                 gint i;
377
378                 /* only a subset of headers are supported .. */
379                 headername = camel_db_get_column_name (argv[0]->value.string);
380
381                 /* performs an OR of all words */
382                 for (i = 1; i < argc; i++) {
383                         if (argv[i]->type == CAMEL_SEXP_RES_STRING) {
384                                 gchar *value = NULL, *tstr = NULL;
385                                 if (argv[i]->value.string[0] == 0)
386                                         continue;
387                                 if (how == CAMEL_SEARCH_MATCH_CONTAINS || how == CAMEL_SEARCH_MATCH_WORD) {
388                                         tstr = g_strdup_printf ("%c%s%c", '%', argv[i]->value.string, '%');
389                                         value = get_db_safe_string (tstr);
390                                         g_free (tstr);
391                                 } else if (how == CAMEL_SEARCH_MATCH_ENDS) {
392                                         tstr = g_strdup_printf ("%c%s", '%', argv[i]->value.string);
393                                         value = get_db_safe_string (tstr);
394                                         g_free (tstr);
395                                 } else if (how == CAMEL_SEARCH_MATCH_STARTS) {
396                                         tstr = g_strdup_printf ("%s%c", argv[i]->value.string, '%');
397                                         value = get_db_safe_string (tstr);
398                                         g_free (tstr);
399                                 } else if (how == CAMEL_SEARCH_MATCH_EXACT) {
400                                         tstr = g_strdup_printf ("%c%s%c", '%', argv[i]->value.string, '%');
401                                         value = get_db_safe_string (tstr);
402                                         g_free (tstr);
403                                 }
404                                 str = g_strdup_printf ("(%s IS NOT NULL AND %s LIKE %s)", headername, headername, value);
405                                 g_free (value);
406                         }
407                 }
408                 g_free (headername);
409         }
410         /* TODO: else, find all matches */
411
412         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
413         r->value.string = str;
414
415         return r;
416 }
417
418 static CamelSExpResult *
419 header_contains (struct _CamelSExp *f,
420                  gint argc,
421                  struct _CamelSExpResult **argv,
422                  gpointer data)
423 {
424         d (printf ("executing header-contains: %d", argc));
425
426         return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_CONTAINS);
427 }
428
429 static CamelSExpResult *
430 header_has_words (struct _CamelSExp *f,
431                   gint argc,
432                   struct _CamelSExpResult **argv,
433                   gpointer data)
434 {
435         d (printf ("executing header-has-word: %d", argc));
436
437         return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_WORD);
438 }
439
440 static CamelSExpResult *
441 header_matches (struct _CamelSExp *f,
442                 gint argc,
443                 struct _CamelSExpResult **argv,
444                 gpointer data)
445 {
446         d (printf ("executing header-matches: %d", argc));
447
448         return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_EXACT);
449 }
450
451 static CamelSExpResult *
452 header_starts_with (struct _CamelSExp *f,
453                     gint argc,
454                     struct _CamelSExpResult **argv,
455                     gpointer data)
456 {
457         d (printf ("executing header-starts-with: %d", argc));
458
459         return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_STARTS);
460 }
461
462 static CamelSExpResult *
463 header_ends_with (struct _CamelSExp *f,
464                   gint argc,
465                   struct _CamelSExpResult **argv,
466                   gpointer data)
467 {
468         d (printf ("executing header-ends-with: %d", argc));
469
470         return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_ENDS);
471 }
472
473 static CamelSExpResult *
474 header_exists (struct _CamelSExp *f,
475                gint argc,
476                struct _CamelSExpResult **argv,
477                gpointer data)
478 {
479         CamelSExpResult *r;
480         gchar *headername;
481
482         d (printf ("executing header-exists: %d", argc));
483
484         headername = camel_db_get_column_name (argv[0]->value.string);
485         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
486         r->value.string = g_strdup_printf ("(%s NOTNULL)", headername);
487         g_free (headername);
488         return r;
489 }
490
491 static CamelSExpResult *
492 user_tag (struct _CamelSExp *f,
493           gint argc,
494           struct _CamelSExpResult **argv,
495           gpointer data)
496 {
497         CamelSExpResult *r;
498
499         d (printf ("executing user-tag: %d", argc));
500
501         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
502         /* Hacks no otherway to fix these really :( */
503         if (g_strcmp0 (argv[0]->value.string, "completed-on") == 0)
504                 r->value.string = g_strdup_printf ("(usertags LIKE '%ccompleted-on 0%c' AND usertags LIKE '%ccompleted-on%c')", '%', '%', '%', '%');
505         else if (g_strcmp0 (argv[0]->value.string, "follow-up") == 0)
506                 r->value.string = g_strdup_printf ("usertags NOT LIKE '%cfollow-up%c'", '%', '%');
507         else
508                 r->value.string = g_strdup ("usertags");
509
510         return r;
511 }
512
513 static CamelSExpResult *
514 user_flag (struct _CamelSExp *f,
515            gint argc,
516            struct _CamelSExpResult **argv,
517            gpointer data)
518 {
519         CamelSExpResult *r;
520         gchar *tstr, *qstr;
521
522         d (printf ("executing user-flag: %d", argc));
523
524         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
525
526         if (argc != 1) {
527                 r->value.string = g_strdup ("(0)");
528         } else {
529                 tstr = g_strdup_printf ("%s", argv[0]->value.string);
530                 qstr = get_db_safe_string (tstr);
531                 g_free (tstr);
532                 r->value.string = g_strdup_printf ("(labels MATCH %s)", qstr);
533                 g_free (qstr);
534         }
535
536         return r;
537 }
538
539 static CamelSExpResult *
540 system_flag (struct _CamelSExp *f,
541              gint argc,
542              struct _CamelSExpResult **argv,
543              gpointer data)
544 {
545         CamelSExpResult *r;
546         gchar *tstr;
547
548         d (printf ("executing system-flag: %d", argc));
549
550         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
551
552         if (argc != 1) {
553                 r->value.string = g_strdup ("(0)");
554         } else {
555                 tstr = camel_db_get_column_name (argv[0]->value.string);
556                 r->value.string = g_strdup_printf ("(%s = 1)", tstr);
557                 g_free (tstr);
558         }
559
560         return r;
561 }
562
563 static CamelSExpResult *
564 get_sent_date (struct _CamelSExp *f,
565                gint argc,
566                struct _CamelSExpResult **argv,
567                gpointer data)
568 {
569         CamelSExpResult *r;
570
571         d (printf ("executing get-sent-date\n"));
572
573         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
574         r->value.string = g_strdup ("dsent");
575
576         return r;
577 }
578
579 static CamelSExpResult *
580 get_received_date (struct _CamelSExp *f,
581                    gint argc,
582                    struct _CamelSExpResult **argv,
583                    gpointer data)
584 {
585         CamelSExpResult *r;
586
587         d (printf ("executing get-received-date\n"));
588
589         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
590         r->value.string = g_strdup ("dreceived");
591
592         return r;
593 }
594
595 static CamelSExpResult *
596 get_current_date (struct _CamelSExp *f,
597                   gint argc,
598                   struct _CamelSExpResult **argv,
599                   gpointer data)
600 {
601         CamelSExpResult *r;
602
603         d (printf ("executing get-current-date\n"));
604
605         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
606         r->value.number = time (NULL);
607         return r;
608 }
609
610 static CamelSExpResult *
611 get_relative_months (struct _CamelSExp *f,
612                      gint argc,
613                      struct _CamelSExpResult **argv,
614                      gpointer data)
615 {
616         CamelSExpResult *r;
617
618         d (printf ("executing get-relative-months\n"));
619
620         if (argc != 1 || argv[0]->type != CAMEL_SEXP_RES_INT) {
621                 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
622                 r->value.boolean = FALSE;
623
624                 g_debug ("%s: Expecting 1 argument, an integer, but got %d arguments", G_STRFUNC, argc);
625         } else {
626                 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
627                 r->value.number = camel_folder_search_util_add_months (time (NULL), argv[0]->value.number);
628         }
629
630         return r;
631 }
632
633 static CamelSExpResult *
634 get_size (struct _CamelSExp *f,
635           gint argc,
636           struct _CamelSExpResult **argv,
637           gpointer data)
638 {
639         CamelSExpResult *r;
640
641         d (printf ("executing get-size\n"));
642
643         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
644         r->value.string = g_strdup ("size/1024");
645
646         return r;
647 }
648
649 static CamelSExpResult *
650 sql_exp (struct _CamelSExp *f,
651          gint argc,
652          struct _CamelSExpResult **argv,
653          gpointer data)
654 {
655         CamelSExpResult *r;
656         gint i;
657         GString *str = g_string_new (NULL);
658
659         d (printf ("executing sql-exp\n"));
660
661         r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
662         for (i = 0; i < argc; i++) {
663                 if (argv[i]->type == CAMEL_SEXP_RES_STRING && argv[i]->value.string)
664                         g_string_append (str, argv[i]->value.string);
665         }
666         r->value.string = str->str;
667         g_string_free (str, FALSE);
668
669         return r;
670 }
671
672 /* 'builtin' functions */
673 static struct {
674         const gchar *name;
675         CamelSExpFunc func;
676         guint immediate :1;
677 } symbols[] = {
678         { "and", (CamelSExpFunc) func_and, 1 },
679         { "or", (CamelSExpFunc) func_or, 1},
680         { "not", (CamelSExpFunc) func_not, 1},
681         { "=", (CamelSExpFunc) eval_eq, 1},
682         { ">", (CamelSExpFunc) eval_gt, 1},
683         { "<", (CamelSExpFunc) eval_lt, 1},
684
685         { "match-all", (CamelSExpFunc) match_all, 1 },
686         { "match-threads", (CamelSExpFunc) match_threads, 1 },
687 /*      { "body-contains", body_contains}, */ /* We don't store body on the db. */
688         { "header-contains", header_contains, 0},
689         { "header-has-words", header_has_words, 0},
690         { "header-matches", header_matches, 0},
691         { "header-starts-with", header_starts_with, 0},
692         { "header-ends-with", header_ends_with, 0},
693         { "header-exists", header_exists, 0},
694         { "user-tag", user_tag, 0},
695         { "user-flag", user_flag, 0},
696         { "system-flag", system_flag, 0},
697         { "get-sent-date", get_sent_date, 0},
698         { "get-received-date", get_received_date, 0},
699         { "get-current-date", get_current_date, 0},
700         { "get-relative-months", get_relative_months, 0},
701         { "get-size", get_size, 0},
702         { "sql-exp", sql_exp, 0},
703
704 /*      { "uid", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, uid), 1 }, */
705 };
706
707 /**
708  * camel_sexp_to_sql_sexp:
709  *
710  * Since: 2.26
711  **/
712 gchar *
713 camel_sexp_to_sql_sexp (const gchar *sql)
714 {
715         CamelSExp *sexp;
716         CamelSExpResult *r;
717         gint i;
718         gchar *res;
719
720         sexp = camel_sexp_new ();
721
722         for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
723                 if (symbols[i].immediate)
724                         camel_sexp_add_ifunction (sexp, 0, symbols[i].name,
725                                              (CamelSExpIFunc) symbols[i].func, NULL);
726                 else
727                         camel_sexp_add_function (
728                                 sexp, 0, symbols[i].name,
729                                 symbols[i].func, NULL);
730         }
731
732         camel_sexp_input_text (sexp, sql, strlen (sql));
733         if (camel_sexp_parse (sexp)) {
734                 g_object_unref (sexp);
735                 return NULL;
736         }
737
738         r = camel_sexp_eval (sexp);
739         if (!r) {
740                 g_object_unref (sexp);
741                 return NULL;
742         }
743
744         if (r->type == CAMEL_SEXP_RES_STRING) {
745                 res = g_strdup (r->value.string);
746         } else
747                 g_assert (0);
748
749         camel_sexp_result_free (sexp, r);
750         g_object_unref (sexp);
751
752         return res;
753 }
754
755 #ifdef TEST_MAIN
756 /*
757  *
758  * (and (match-all (and (not (system-flag "deleted")) (not (system-flag "junk"))))
759  * (and   (or
760  *
761  *     (match-all (not (system-flag "Attachments")))
762  *
763  *  )
764  * ))
765  *
766  *"
767  * replied INTEGER ,                (match-all (system-flag  "Answered"))
768  * size INTEGER ,                   (match-all (< (get-size) 100))
769  * dsent NUMERIC ,                  (match-all (< (get-sent-date) (- (get-current-date) 10)))
770  * dreceived NUMERIC ,               (match-all (< (get-received-date) (- (get-current-date) 10)))
771  * //mlist TEXT ,                      x-camel-mlist   (match-all (header-matches "x-camel-mlist"  "gnome.org"))
772  * //attachment,                      system-flag "Attachments"   (match-all (system-flag "Attachments"))
773  * //followup_flag TEXT ,             (match-all (not (= (user-tag "follow-up") "")))
774  * //followup_completed_on TEXT ,      (match-all (not (= (user-tag "completed-on") "")))
775  * //followup_due_by TEXT ," //NOTREQD
776  */
777
778 gchar * camel_db_get_column_name (const gchar *raw_name)
779 {
780         /* d(g_print ("\n\aRAW name is : [%s] \n\a", raw_name)); */
781         if (!g_ascii_strcasecmp (raw_name, "Subject"))
782                 return g_strdup ("subject");
783         else if (!g_ascii_strcasecmp (raw_name, "from"))
784                 return g_strdup ("mail_from");
785         else if (!g_ascii_strcasecmp (raw_name, "Cc"))
786                 return g_strdup ("mail_cc");
787         else if (!g_ascii_strcasecmp (raw_name, "To"))
788                 return g_strdup ("mail_to");
789         else if (!g_ascii_strcasecmp (raw_name, "Flagged"))
790                 return g_strdup ("important");
791         else if (!g_ascii_strcasecmp (raw_name, "deleted"))
792                 return g_strdup ("deleted");
793         else if (!g_ascii_strcasecmp (raw_name, "junk"))
794                 return g_strdup ("junk");
795         else if (!g_ascii_strcasecmp (raw_name, "Answered"))
796                 return g_strdup ("replied");
797         else if (!g_ascii_strcasecmp (raw_name, "Seen"))
798                 return g_strdup ("read");
799         else if (!g_ascii_strcasecmp (raw_name, "user-tag"))
800                 return g_strdup ("usertags");
801         else if (!g_ascii_strcasecmp (raw_name, "user-flag"))
802                 return g_strdup ("labels");
803         else if (!g_ascii_strcasecmp (raw_name, "Attachments"))
804                 return g_strdup ("attachment");
805         else if (!g_ascii_strcasecmp (raw_name, "x-camel-mlist"))
806                 return g_strdup ("mlist");
807         else {
808                 /* Let it crash for all unknown columns for now.
809                  * We need to load the messages into memory and search etc.
810                  * We should extend this for camel-folder-search system flags search as well
811                  * otherwise, search-for-signed-messages will not work etc.*/
812
813                 return g_strdup (raw_name);
814         }
815
816 }
817
818 gint main ()
819 {
820
821         gint i = 0;
822         gchar *txt[] = {
823 #if 0
824         "(match-all (header-contains \"From\"  \"org\"))",
825         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (match-all (or (= (user-tag \"label\")  \"work\")  (user-flag  \"work\"))) )))",
826
827         "(and  (and   (match-all (header-contains \"From\"  \"org\"))   )  (match-all (not (system-flag \"junk\"))))",
828
829         "(and  (and (match-all (header-contains \"From\"  \"org\"))) (and (match-all (not (system-flag \"junk\"))) (and   (or (match-all (header-contains \"Subject\"  \"test\")) (match-all (header-contains \"From\"  \"test\"))))))",
830         "(and  (and   (match-all (header-exists \"From\"))   )  (match-all (not (system-flag \"junk\"))))",
831         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (header-contains \"Subject\"  \"org\")) (match-all (header-contains \"From\"  \"org\")) (match-all (system-flag  \"Flagged\")) (match-all (system-flag  \"Seen\")) )))",
832         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (= (user-tag \"label\")  \"work\") )))",
833         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (user-flag  \"work\") )))",
834         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (user-flag  (+ \"$Label\"  \"work\")) )))"
835         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (user-tag \"follow-up\") \"\"))) )))",
836         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (user-tag \"follow-up\") \"\")) )))",
837         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (user-tag \"completed-on\") \"\"))) )))",
838         "(match-all (and  (match-all #t) (system-flag \"deleted\")))",
839         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (= (user-tag \"label\")  \"important\") (user-flag (+ \"$Label\"  \"important\")) (user-flag  \"important\"))) )))",
840         "(or (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")) (not (system-flag \"Attachments\")) (not (system-flag \"Answered\")))) (and   (or (match-all (= (user-tag \"completed-on\") \"\")) )))",
841         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (user-tag \"completed-on\") \"\")) (match-all (= (user-tag \"follow-up\") \"\")) )))",
842         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (> (get-sent-date) (- (get-current-date) 100))) )))",
843         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (< (get-sent-date) (+ (get-current-date) 100))) )))",
844         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (get-sent-date) 1216146600))) )))",
845         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (get-sent-date) 1216146600)) )))" ,
846         "(match-threads \"all\"  (or (match-all (header-contains \"From\"  \"@edesvcs.com\")) (match-all (or (header-contains \"To\"  \"@edesvcs.com\") (header-contains \"Cc\"  \"@edesvcs.com\"))) ))",
847         "(match-all (not (system-flag \"deleted\")))",
848         "(match-all (system-flag \"seen\"))",
849         "(match-all (and  (match-all #t) (system-flag \"deleted\")))",
850         "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))",
851
852         "(and (or (match-all (header-contains \"Subject\"  \"lin\")) ) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (header-contains \"Subject\"  \"case\")) (match-all (header-contains \"From\"  \"case\"))))))",
853         "(and ( match-all(or (match-all (header-contains \"Subject\"  \"lin\")) (match-all (header-contains \"From\"  \"in\")))) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (header-contains \"Subject\"  \"proc\")) (match-all (header-contains \"From\"  \"proc\"))))))",
854         "(and  (or (match-all (header-contains \"Subject\"  \"[LDTP-NOSIP]\")) ) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (header-contains \"Subject\"  \"vamsi\")) (match-all (header-contains \"From\"  \"vamsi\"))))))",
855         /* Last one doesn't work so well and fails on one case. But I doubt, you can create a query like that in Evo. */
856         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (match-all (or (= (user-tag \"label\") \"_office\") (user-flag \"$Label_office\") (user-flag \"_office\"))))",
857         "(and  (and (match-all #t))(and(match-all #t)))",
858         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (and (match-all (header-contains \"Subject\"  \"mysubject\")) (match-all (not (header-matches \"From\"  \"mysender\"))) (match-all (= (get-sent-date) (+ (get-current-date) 1))) (match-all (= (get-received-date) (- (get-current-date) 604800))) (match-all (or (= (user-tag \"label\")  \"important\") (user-flag (+ \"$Label\"  \"important\")) (match-all (< (get-size) 7000)) (match-all (not (= (get-sent-date) 1216146600)))  (match-all (> (cast-int (user-tag \"score\")) 3))  (user-flag  \"important\"))) (match-all (system-flag  \"Deleted\")) (match-all (not (= (user-tag \"follow-up\") \"\"))) (match-all (= (user-tag \"completed-on\") \"\")) (match-all (system-flag \"Attachments\")) (match-all (header-contains \"x-camel-mlist\"  \"evo-hackers\")) )))",
859         "(and (or  (match-all (or (= (user-tag \"label\") \"important\") (user-flag (+ \"$Label\" \"important\")) (user-flag \"important\")))    (match-all (or (= (user-tag \"label\") \"work\") (user-flag (+ \"$Label\" \"work\")) (user-flag \"work\")))    (match-all (or (= (user-tag \"label\") \"personal\") (user-flag (+ \"$Label\" \"personal\")) (user-flag \"personal\")))    (match-all (or (= (user-tag \"label\") \"todo\") (user-flag (+ \"$Label\" \"todo\")) (user-flag \"todo\")))    (match-all (or (= (user-tag \"label\") \"later\") (user-flag (+ \"$Label\" \"later\")) (user-flag \"later\")))  )  (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))))",
860         "(or (header-matches \"to\" \"maw@ximian.com\") (header-matches \"to\" \"mw@ximian.com\")   (header-matches \"to\" \"maw@novell.com\")   (header-matches \"to\" \"maw.AMERICAS3.AMERICAS@novell.com\") (header-matches \"cc\" \"maw@ximian.com\") (header-matches \"cc\" \"mw@ximian.com\")     (header-matches \"cc\" \"maw@novell.com\")   (header-matches \"cc\" \"maw.AMERICAS3.AMERICAS@novell.com\"))",
861         "(not (or (header-matches \"from\" \"bugzilla-daemon@bugzilla.ximian.com\") (header-matches \"from\" \"bugzilla-daemon@bugzilla.gnome.org\") (header-matches \"from\" \"bugzilla_noreply@novell.com\") (header-matches \"from\" \"bugzilla-daemon@mozilla.org\") (header-matches \"from\" \"root@dist.suse.de\") (header-matches \"from\" \"root@hilbert3.suse.de\") (header-matches \"from\" \"root@hilbert4.suse.de\") (header-matches \"from\" \"root@hilbert5.suse.de\") (header-matches \"from\" \"root@hilbert6.suse.de\") (header-matches \"from\" \"root@suse.de\") (header-matches \"from\" \"swamp_noreply@suse.de\") (and (header-matches \"from\" \"hermes@opensuse.org\") (header-starts-with \"subject\" \"submit-Request\"))))",
862         "(and (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\") (header-matches \"to\" \"mw@ximian.com\")   (header-matches \"to\" \"maw@novell.com\")   (header-matches \"to\" \"maw.AMERICAS3.AMERICAS@novell.com\") (header-matches \"cc\" \"maw@ximian.com\") (header-matches \"cc\" \"mw@ximian.com\")     (header-matches \"cc\" \"maw@novell.com\")   (header-matches \"cc\" \"maw.AMERICAS3.AMERICAS@novell.com\"))) (match-all (not (or (header-matches \"from\" \"bugzilla-daemon@bugzilla.ximian.com\") (header-matches \"from\" \"bugzilla-daemon@bugzilla.gnome.org\") (header-matches \"from\" \"bugzilla_noreply@novell.com\") (header-matches \"from\" \"bugzilla-daemon@mozilla.org\") (header-matches \"from\" \"root@dist.suse.de\") (header-matches \"from\" \"root@hilbert3.suse.de\") (header-matches \"from\" \"root@hilbert4.suse.de\") (header-matches \"from\" \"root@hilbert5.suse.de\") (header-matches \"from\" \"root@hilbert6.suse.de\") (header-matches \"from\" \"root@suse.de\") (header-matches \"from\" \"swamp_noreply@suse.de\") (and (header-matches \"from\" \"hermes@opensuse.org\") (header-starts-with \"subject\" \"submit-Request\"))))) (match-all (> (get-sent-date) (- (get-current-date) 1209600))) )) (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))))",
863         "and ((match-all (system-flag \"Deleted\")) (match-all (system-flag  \"junk\")))",
864         "(and (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\")))))))",
865         "(and (sql-exp \"folder_key = 'ASDGASd' AND folder_key = 'DSFWEA'\") (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\")))))))"
866 #endif
867         "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or  (match-all list-post.*zypp-devel)  ) ))"
868         };
869
870         for (i = 0; i < G_N_ELEMENTS (txt); i++) {
871                 gchar *sql = NULL;
872                 printf ("Q: %s\n\"%c\"\n", txt[i], 40);
873                 sql = camel_sexp_to_sql_sexp (txt[i]);
874                 printf ("A: %s\n\n\n", sql);
875                 g_free (sql);
876         }
877
878 }
879 #endif
880