Back to index

courier  0.68.2
search.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2009 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 
00010 #include      <stdio.h>
00011 #include      <string.h>
00012 #include      <stdlib.h>
00013 #include      <ctype.h>
00014 #include      <time.h>
00015 #if    HAVE_UNISTD_H
00016 #include      <unistd.h>
00017 #endif
00018 #include      <errno.h>
00019 #include      <sys/types.h>
00020 #include      <sys/stat.h>
00021 #include      "rfc822/rfc822.h"
00022 #include      "rfc822/rfc822hdr.h"
00023 #include      "rfc822/rfc2047.h"
00024 #include      "rfc2045/rfc2045.h"
00025 #include      "unicode/unicode.h"
00026 #include      "numlib/numlib.h"
00027 #include      "searchinfo.h"
00028 #include      "imapwrite.h"
00029 #include      "imaptoken.h"
00030 #include      "imapscanclient.h"
00031 
00032 
00033 extern time_t rfc822_parsedt(const char *);
00034 extern struct imapscaninfo current_maildir_info;
00035 extern char *current_mailbox;
00036 
00037 extern int get_flagname(const char *, struct imapflags *);
00038 extern void get_message_flags( struct imapscanmessageinfo *,
00039        char *, struct imapflags *);
00040 extern int valid_keyword(const char *kw);
00041 
00042 static void fill_search_preparse(struct searchinfo *);
00043 
00044 static void fill_search_veryquick(struct searchinfo *,
00045        unsigned long, struct imapflags *);
00046 
00047 static void fill_search_quick(struct searchinfo *,
00048        unsigned long, struct stat *);
00049 
00050 static void fill_search_header(struct searchinfo *,
00051                             const char *,
00052                             struct rfc2045 *, FILE *,
00053                             struct imapscanmessageinfo *);
00054 
00055 static void fill_search_body(struct searchinfo *,
00056                           struct rfc2045 *, FILE *,
00057                           struct imapscanmessageinfo *);
00058 
00059 static int search_evaluate(struct searchinfo *);
00060 
00061 static void search_callback(struct searchinfo *, struct searchinfo *, int,
00062        unsigned long, void *);
00063 
00064 /*
00065 **     search_internal() does the main heavylifting of searching the
00066 **     maildir for qualifying messages.  It calls a callback function
00067 **     when a matching message is found.
00068 **
00069 **     For a plain SEARCH, the callback function merely prints the message
00070 **     number.
00071 */
00072 
00073 void dosearch(struct searchinfo *si, struct searchinfo *sihead,
00074              const char *charset, int isuid)
00075 {
00076        search_internal(si, sihead, charset, isuid, search_callback, 0);
00077 }
00078 
00079 static void search_callback(struct searchinfo *si, struct searchinfo *sihead,
00080        int isuid, unsigned long i, void *dummy)
00081 {
00082        writes(" ");
00083        writen(isuid ? current_maildir_info.msgs[i].uid:i+1);
00084 }
00085 
00086 static void search_oneatatime(struct searchinfo *si,
00087                            unsigned long i,
00088                            struct searchinfo *sihead,
00089                            const char *charset, int isuid,
00090                            void (*callback_func)(struct searchinfo *,
00091                                               struct searchinfo *, int,
00092                                               unsigned long, void *),
00093                            void *voidarg);
00094 
00095 static void search_byKeyword(struct searchinfo *tree,
00096                         struct searchinfo *keyword,
00097                         struct searchinfo *sihead,
00098                         const char *charset, int isuid,
00099                         void (*callback_func)(struct searchinfo *,
00100                                            struct searchinfo *, int,
00101                                            unsigned long, void *),
00102                         void *voidarg);
00103 
00104 void search_internal(struct searchinfo *si, struct searchinfo *sihead,
00105                    const char *charset, int isuid,
00106                    void (*callback_func)(struct searchinfo *,
00107                                       struct searchinfo *, int,
00108                                       unsigned long, void *),
00109                    void *voidarg)
00110 {
00111        unsigned long i;
00112        struct searchinfo *p;
00113 
00114        for (p=sihead; p; p=p->next)
00115               fill_search_preparse(p);
00116 
00117        /* Shortcuts for keyword-based searches */
00118 
00119        if (si->type == search_msgkeyword && si->bs == NULL && si->ke)
00120               search_byKeyword(NULL, si, sihead, charset, isuid,
00121                             callback_func, voidarg);
00122        else if (si->type == search_and &&
00123                si->a->type == search_msgkeyword && si->a->bs == NULL
00124                && si->a->ke)
00125               search_byKeyword(si->b, si->a, sihead, charset, isuid,
00126                             callback_func, voidarg);
00127        else for (i=0; i<current_maildir_info.nmessages; i++)
00128               search_oneatatime(si, i, sihead, charset, isuid,
00129                               callback_func, voidarg);
00130 }
00131 
00132 static void search_byKeyword(struct searchinfo *tree,
00133                           struct searchinfo *keyword,
00134                           struct searchinfo *sihead,
00135                           const char *charset, int isuid,
00136                           void (*callback_func)(struct searchinfo *,
00137                                              struct searchinfo *, int,
00138                                              unsigned long, void *),
00139                           void *voidarg)
00140 {
00141        struct libmail_kwMessageEntry *kme;
00142 
00143        for (kme=keyword->ke->firstMsg; kme; kme=kme->keywordNext)
00144        {
00145               unsigned long n=kme->libmail_kwMessagePtr->u.userNum;
00146               if (!tree)
00147               {
00148                      (*callback_func)(keyword, keyword, isuid, n, voidarg);
00149                      continue;
00150               }
00151 
00152               search_oneatatime(tree, n, sihead, charset, isuid,
00153                               callback_func, voidarg);
00154        }
00155 }
00156 
00157 /*
00158 ** Evaluate the search tree for a given message.
00159 */
00160 
00161 static void search_oneatatime(struct searchinfo *si,
00162                            unsigned long i,
00163                            struct searchinfo *sihead,
00164                            const char *charset, int isuid,
00165                            void (*callback_func)(struct searchinfo *,
00166                                               struct searchinfo *, int,
00167                                               unsigned long, void *),
00168                            void *voidarg)
00169 {
00170 struct searchinfo *p;
00171 struct imapflags     flags;
00172 int    fd;
00173 FILE   *fp;
00174 struct stat   stat_buf;
00175 int    rc;
00176 
00177        {
00178               for (p=sihead; p; p=p->next)
00179                      p->value= -1; /* Search result unknown */
00180 
00181               /* First, see if non-content search will be sufficient */
00182 
00183               get_message_flags(current_maildir_info.msgs+i, 0, &flags);
00184 
00185               for (p=sihead; p; p=p->next)
00186                      fill_search_veryquick(p, i, &flags);
00187 
00188               if ((rc=search_evaluate(si)) >= 0)
00189               {
00190                      if (rc > 0)
00191                             (*callback_func)(si, sihead, isuid, i,
00192                                            voidarg);
00193                      return;
00194               }
00195 
00196               fd=imapscan_openfile(current_mailbox,
00197                      &current_maildir_info, i);
00198               if (fd < 0)   return;
00199 
00200               if ((fp=fdopen(fd, "r")) == 0)
00201                      write_error_exit(0);
00202 
00203               if (fstat(fileno(fp), &stat_buf))
00204               {
00205                      fclose(fp);
00206                      return;
00207               }
00208 
00209               /* First, see if non-content search will be sufficient */
00210 
00211               for (p=sihead; p; p=p->next)
00212                      fill_search_quick(p, i, &stat_buf);
00213 
00214               if ((rc=search_evaluate(si)) < 0)
00215               {
00216                      /* No, search the headers then */
00217                         /* struct        rfc2045 *rfcp=rfc2045_fromfp(fp); */
00218                         struct        rfc2045 *rfcp=rfc2045header_fromfp(fp); 
00219 
00220                      fill_search_header(sihead, charset, rfcp, fp,
00221                                       current_maildir_info.msgs+i);
00222                      rc=search_evaluate(si);
00223                         rfc2045_free(rfcp); 
00224 
00225                      if (rc < 0)
00226                      {
00227                             /* Ok, search message contents */
00228                                 struct        rfc2045 *rfcp=rfc2045_fromfp(fp);
00229 
00230                             fill_search_body(sihead, rfcp, fp,
00231                                            current_maildir_info.msgs+i);
00232 
00233                             /*
00234                             ** If there are still UNKNOWN nodes, change
00235                             ** them to fail.
00236                             */
00237 
00238                             for (p=sihead; p; p=p->next)
00239                                    if (p->value < 0)
00240                                           p->value=0;
00241 
00242                             rc=search_evaluate(si);
00243                                 rfc2045_free(rfcp);
00244                      }
00245                         /* rfc2045_free(rfcp); */
00246               }
00247 
00248               if (rc > 0)
00249               {
00250                      (*callback_func)(si, sihead, isuid, i, voidarg);
00251               }
00252               fclose(fp);
00253               close(fd);
00254        }
00255 }
00256 
00257 /* Check if the given index is included in the specified message set */
00258 
00259 static int is_in_set(const char *msgset, unsigned long n)
00260 {
00261 unsigned long i, j;
00262 
00263        while (isdigit((int)(unsigned char)*msgset))
00264        {
00265               i=0;
00266               while (isdigit((int)(unsigned char)*msgset))
00267               {
00268                      i=i*10 + (*msgset++-'0');
00269               }
00270               if (*msgset != ':')
00271                      j=i;
00272               else
00273               {
00274                      j=0;
00275                      ++msgset;
00276                      if (*msgset == '*')
00277                      {
00278                             ++msgset;
00279                             /*
00280                             ** Ok, we don't really need to know the upper
00281                             ** end, just hack it.
00282                             */
00283                             j=i;
00284                             if (j < n)
00285                                    j=n;
00286                      }
00287                      else
00288                             while (isdigit((int)(unsigned char)*msgset))
00289                             {
00290                                    j=j*10 + (*msgset++-'0');
00291                             }
00292               }
00293               if (n >= i && n <= j)       return (1);
00294               if (*msgset == 0 || *msgset++ != ',')     break;
00295        }
00296        return (0);
00297 }
00298 
00299 /*
00300 ** Search date comparisons compare the dates only, not the time.
00301 ** We convert all timestamps to midnight GMT on their respective dates.
00302 ** Use convenient RFC822 functions for that purpose.
00303 */
00304 
00305 static time_t decode_date(char *p)
00306 {
00307 char   *s=malloc(strlen(p)+sizeof(" 00:00:00"));
00308 unsigned        i;
00309 time_t t;
00310 
00311        if (!s)       write_error_exit(0);
00312 
00313         /* Convert to format rfc822_parsedt likes */
00314  
00315         for (i=1; p[i] != ' '; i++)
00316         {
00317                 if (!p[i])  break;
00318         }
00319        memcpy(s, p, i);
00320        strcpy(s+i, " 00:00:00");
00321        while (i)
00322        {
00323               if (s[--i] == '-')
00324                      s[i]=' ';
00325        }
00326 
00327        t=rfc822_parsedt(s);
00328        free(s);
00329        return (t);
00330 }
00331 
00332 /* Given a time_t that falls on, say, 3-Aug-1999 9:50:43 local time,
00333 ** calculate the time_t for midnight 3-Aug-1999 UTC.  Search date comparisons
00334 ** are done against midnight UTCs */
00335 
00336 static time_t timestamp_to_day(time_t t)
00337 {
00338 char   buf1[60], buf2[80];
00339 
00340        rfc822_mkdate_buf(t, buf1); /* Converts to local time */
00341        (void)strtok(buf1, " ");    /* Skip weekday */
00342        strcpy(buf2, strtok(0, " "));
00343        strcat(buf2, " ");
00344        strcat(buf2, strtok(0, " "));
00345        strcat(buf2, " ");
00346        strcat(buf2, strtok(0, " "));
00347        strcat(buf2, " 00:00:00");
00348        return (rfc822_parsedt(buf2));
00349 }
00350 
00351 static char *timestamp_for_sorting(time_t t)
00352 {
00353 struct tm *tm=localtime(&t);
00354 char   buf[200];
00355 
00356        buf[0]=0;
00357        if ( strftime(buf, sizeof(buf), "%Y.%m.%d.%H.%M.%S", tm) == 0)
00358               buf[0]=0;
00359        return (my_strdup(buf));
00360 }
00361 
00362 static void fill_search_preparse(struct searchinfo *p)
00363 {
00364        switch (p->type) {
00365        case search_msgflag:
00366               {
00367                      struct imapflags flags;
00368 
00369                      memset(&flags, 0, sizeof(flags));
00370                      p->ke=NULL;
00371 
00372                      if (get_flagname(p->as, &flags) == 0)
00373                      {
00374                             p->bs=malloc(sizeof(flags));
00375 
00376                             if (!p->bs)
00377                                    write_error_exit(0);
00378 
00379                             memcpy(p->bs, &flags, sizeof(flags));
00380                      }
00381               }
00382               break;
00383 
00384        case search_msgkeyword:
00385               p->ke=NULL;
00386               if (valid_keyword(p->as))
00387                      p->ke=libmail_kweFind(current_maildir_info
00388                                           .keywordList,
00389                                           p->as, 0);
00390               break;
00391        default:
00392               break;
00393        }
00394 }
00395 
00396 /* Evaluate non-content search nodes */
00397 
00398 static void fill_search_veryquick(struct searchinfo *p,
00399        unsigned long msgnum, struct imapflags *flags)
00400 {
00401        switch (p->type) {
00402        case search_msgflag:
00403               {
00404                      struct imapflags *f=(struct imapflags *)p->bs;
00405 
00406                      p->value=0;
00407                      if (strcmp(p->as, "\\RECENT") == 0 &&
00408                             current_maildir_info.msgs[msgnum].recentflag)
00409                             p->value=1;
00410 
00411                      if (f)
00412                      {
00413                             if (f->seen && flags->seen)
00414                                    p->value=1;
00415                             if (f->answered && flags->answered)
00416                                    p->value=1;
00417                             if (f->deleted && flags->deleted)
00418                                    p->value=1;
00419                             if (f->flagged && flags->flagged)
00420                                    p->value=1;
00421                             if (f->drafts && flags->drafts)
00422                                    p->value=1;
00423                      }
00424                      break;
00425               }
00426 
00427        case search_msgkeyword:
00428               p->value=0;
00429               if (p->ke)
00430               {
00431                      struct libmail_kwMessage *km=
00432                             current_maildir_info.msgs[msgnum]
00433                             .keywordMsg;
00434                      struct libmail_kwMessageEntry *kme;
00435 
00436                      for (kme=km ? km->firstEntry:NULL;
00437                           kme; kme=kme->next)
00438                             if (strcasecmp(keywordName(kme->
00439                                                     libmail_keywordEntryPtr),
00440                                           keywordName(p->ke))==0)
00441                             {
00442                                    p->value=1;
00443                                    break;
00444                             }
00445               }
00446               break;
00447        case search_messageset:
00448               if (is_in_set(p->as, msgnum+1))
00449                      p->value=1;
00450               else
00451                      p->value=0;
00452               break;
00453        case search_all:
00454               p->value=1;
00455               break;
00456        case search_uid:
00457               if (is_in_set(p->as, current_maildir_info.msgs[msgnum].uid))
00458                      p->value=1;
00459               else
00460                      p->value=0;
00461               break;
00462        case search_reverse:
00463               p->value=1;
00464               break;
00465        default:
00466               break;
00467        }
00468 }
00469 
00470 static void fill_search_quick(struct searchinfo *p,
00471        unsigned long msgnum, struct stat *stat_buf)
00472 {
00473        switch (p->type)     {
00474        case search_before:
00475               p->value=0;
00476               {
00477               time_t t=decode_date(p->as);
00478 
00479                      if (t && timestamp_to_day(stat_buf->st_mtime) < t)
00480                             p->value=1;
00481               }
00482               break;
00483        case search_since:
00484               p->value=0;
00485               {
00486               time_t t=decode_date(p->as);
00487 
00488                      if (t && timestamp_to_day(stat_buf->st_mtime) >= t)
00489                             p->value=1;
00490               }
00491               break;
00492        case search_on:
00493               p->value=0;
00494               {
00495               time_t t=decode_date(p->as);
00496 
00497                      if (t && timestamp_to_day(stat_buf->st_mtime) == t)
00498                             p->value=1;
00499               }
00500               break;
00501        case search_smaller:
00502               p->value=0;
00503               {
00504               unsigned long n;
00505 
00506                      if (sscanf(p->as, "%lu", &n) > 0 &&
00507                             stat_buf->st_size < n)
00508                             p->value=1;
00509               }
00510               break;
00511        case search_larger:
00512               p->value=0;
00513               {
00514               unsigned long n;
00515 
00516                      if (sscanf(p->as, "%lu", &n) > 0 &&
00517                             stat_buf->st_size > n)
00518                             p->value=1;
00519               }
00520               break;
00521        case search_orderedsubj:
00522        case search_references1:
00523        case search_references2:
00524        case search_references3:
00525        case search_references4:
00526        case search_arrival:
00527        case search_cc:
00528        case search_date:
00529        case search_from:
00530        case search_reverse:
00531        case search_size:
00532        case search_to:
00533 
00534               /* DUMMY nodes for SORT/THREAD.  Make sure that the
00535               ** dummy node is CLEARed */
00536 
00537               if (p->as)
00538               {
00539                      free(p->as);
00540                      p->as=0;
00541               }
00542 
00543               if (p->bs)
00544               {
00545                      free(p->bs);
00546                      p->bs=0;
00547               }
00548 
00549               switch (p->type)     {
00550               case search_arrival:
00551                      p->as=timestamp_for_sorting(stat_buf->st_mtime);
00552                      p->value=1;
00553                      break;
00554               case search_size:
00555                      {
00556                      char   buf[NUMBUFSIZE], buf2[NUMBUFSIZE];
00557                      char *q;
00558 
00559                             libmail_str_size_t(stat_buf->st_size, buf);
00560                             sprintf(buf2, "%*s", (int)(sizeof(buf2)-1), buf);
00561                             for (q=buf2; *q == ' '; *q++='0')
00562                                    ;
00563                             p->as=my_strdup(buf2);
00564                             p->value=1;
00565                      }
00566                      break;
00567               default:
00568                      break;
00569               }
00570               break;
00571        default:
00572               break;
00573        }
00574 }
00575 
00576 /* Evaluate search results.  Returns: 0 - false, 1 - true, -1 - unknown
00577 ** (partial search on message metadata, like size or flags, in hopes of
00578 ** preventing a search
00579 ** of message contents).
00580 */
00581 
00582 static int search_evaluate(struct searchinfo *si)
00583 {
00584 int    rc, rc2;
00585 
00586        switch (si->type)    {
00587        case search_orderedsubj:    /* DUMMIES for THREAD and SORT */
00588        case search_references1:
00589        case search_references2:
00590        case search_references3:
00591        case search_references4:
00592         case search_arrival:
00593         case search_cc:
00594         case search_date:
00595         case search_from:
00596         case search_reverse:
00597         case search_size:
00598         case search_to:
00599               rc = search_evaluate(si->a);
00600               if (rc == 0) return 0;
00601               if (si->value < 0)  return (-1);
00602               break;
00603        case search_not:
00604               rc=search_evaluate(si->a);
00605               if (rc >= 0)  rc= 1-rc;
00606               break;
00607        case search_and:
00608               rc=search_evaluate(si->a);
00609               rc2=search_evaluate(si->b);
00610 
00611               rc=  rc > 0 && rc2 > 0 ? 1:
00612                      rc == 0 || rc2 == 0 ? 0:-1;
00613               break;
00614        case search_or:
00615               rc=search_evaluate(si->a);
00616               rc2=search_evaluate(si->b);
00617 
00618               rc=  rc > 0 || rc2 > 0 ? 1:
00619                      rc == 0 && rc2 == 0 ? 0:-1;
00620               break;
00621        default:
00622               rc=si->value;
00623               break;
00624        }
00625        return (rc);
00626 }
00627 
00628 /* ------- header search -------- */
00629 
00630 struct fill_search_header_info {
00631 
00632        struct searchinfo *si;
00633 
00634        char *utf8buf;
00635        size_t utf8buflen;
00636        size_t utf8bufsize;
00637 };
00638 
00639 static int headerfilter_func(const char *name, const char *value, void *arg);
00640 static int fill_search_header_utf8(const char *, size_t, void *);
00641 static int fill_search_header_done(const char *, void *);
00642 
00643 static void fill_search_header(struct searchinfo *si,
00644                             const char *charset,
00645                             struct rfc2045 *rfcp, FILE *fp,
00646                             struct imapscanmessageinfo *mi)
00647 {
00648        struct searchinfo *sip;
00649        struct rfc2045src *src;
00650        struct rfc2045_decodemsgtoutf8_cb decodecb;
00651        struct fill_search_header_info decodeinfo;
00652 
00653        /* Consider the following dummy nodes as evaluated */
00654 
00655        for (sip=si; sip; sip=sip->next)
00656               switch (sip->type) {
00657               case search_orderedsubj:
00658               case search_references1:
00659               case search_references2:
00660               case search_references3:
00661               case search_references4:
00662               case search_cc:
00663               case search_date:
00664               case search_from:
00665               case search_to:
00666                      sip->value=1;
00667                      break;
00668               default:
00669                      break;
00670               }
00671 
00672        search_set_charset_conv(si, charset);
00673 
00674        src=rfc2045src_init_fd(fileno(fp));
00675 
00676        if (!src)
00677               return;
00678 
00679        memset(&decodecb, 0, sizeof(decodecb));
00680        memset(&decodeinfo, 0, sizeof(decodeinfo));
00681 
00682        decodeinfo.si=si;
00683 
00684        decodecb.flags=RFC2045_DECODEMSG_NOBODY
00685               | RFC2045_DECODEMSG_NOHEADERNAME;
00686        decodecb.headerfilter_func=headerfilter_func;
00687        decodecb.output_func=fill_search_header_utf8;
00688        decodecb.headerdone_func=fill_search_header_done;
00689        decodecb.arg=&decodeinfo;
00690 
00691        rfc2045_decodemsgtoutf8(src, rfcp, &decodecb);
00692        rfc2045src_deinit(src);
00693        if (decodeinfo.utf8buf)
00694               free(decodeinfo.utf8buf);
00695 }
00696 
00697 static int headerfilter_func(const char *name, const char *value, void *arg)
00698 {
00699        struct fill_search_header_info *decodeinfo=
00700               (struct fill_search_header_info *)arg;
00701        struct searchinfo *sip;
00702        const char *p;
00703        int isto=rfc822hdr_namecmp(name, "to");
00704        int iscc=rfc822hdr_namecmp(name, "cc");
00705        int isfrom=rfc822hdr_namecmp(name, "from");
00706        int isinreplyto=rfc822hdr_namecmp(name, "in-reply-to");
00707        int isdate=rfc822hdr_namecmp(name, "date");
00708 
00709        int isreferences=rfc822hdr_namecmp(name, "references");
00710        int ismessageid=rfc822hdr_namecmp(name, "message-id");
00711 
00712        for (sip=decodeinfo->si; sip; sip=sip->next)
00713        {
00714               if (sip->type == search_text && sip->value <= 0)
00715               {
00716                      /*
00717                      ** Full message search. Reset the search engine,
00718                      ** feed it "Headername: "
00719                      */
00720 
00721                      maildir_search_reset(&sip->sei);
00722 
00723                      for (p=name; *p; p++)
00724                      {
00725                             maildir_search_step_unicode_lc(&sip->sei,
00726                                                         (unsigned char)
00727                                                         *p);
00728                             if (maildir_search_found(&sip->sei))
00729                                    sip->value=1;
00730                      }
00731                      for (p=": "; *p; p++)
00732                      {
00733                             maildir_search_step_unicode_lc(&sip->sei,
00734                                                         (unsigned char)
00735                                                         *p);
00736                             if (maildir_search_found(&sip->sei))
00737                                    sip->value=1;
00738                      }
00739               }
00740 
00741               if ( (sip->type == search_cc && iscc == 0 && sip->as == 0)
00742                    ||
00743                    (sip->type == search_from && isfrom == 0 && sip->as == 0)
00744                    ||
00745                    (sip->type == search_to && isto == 0 && sip->as == 0)
00746                    ||
00747                    (sip->type == search_references1 && isinreplyto == 0
00748                     && sip->bs == 0))
00749               {
00750                      struct rfc822t *t;
00751                      struct rfc822a *a;
00752                      char *s;
00753 
00754                      t=rfc822t_alloc_new(value, NULL, NULL);
00755                      if (!t) write_error_exit(0);
00756                      a=rfc822a_alloc(t);
00757                      if (!a) write_error_exit(0);
00758                      s=a->naddrs > 0 ? rfc822_getaddr(a, 0):strdup("");
00759                      rfc822a_free(a);
00760                      rfc822t_free(t);
00761                      if (!s) write_error_exit(0);
00762 
00763                      if (sip->type == search_references1)
00764                      {
00765                             sip->bs=malloc(strlen(s)+3);
00766                             if (!sip->bs)
00767                                    write_error_exit(0);
00768                             strcat(strcat(strcpy(sip->bs, "<"), s), ">");
00769                             free(s);
00770                      }
00771                      else
00772                             sip->as=s;
00773               }
00774 
00775               switch (sip->type) {
00776               case search_orderedsubj:
00777 
00778                      if (isdate == 0 && sip->bs == 0)
00779                      {
00780                             sip->bs=strdup(value);
00781                             if (!sip->bs)
00782                                    write_error_exit(0);
00783                      }
00784                      break;
00785 
00786               case search_date:
00787 
00788                      if (isdate == 0 && sip->as == 0)
00789                      {
00790                             time_t msg_time=rfc822_parsedt(value);
00791 
00792                             sip->as=timestamp_for_sorting(msg_time);
00793                      }
00794                      break;
00795 
00796               case search_sentbefore:
00797               case search_sentsince:
00798               case search_senton:
00799 
00800                      if (sip->value > 0)
00801                             break;
00802 
00803                      if (isdate == 0)
00804                      {
00805                             time_t given_time=decode_date(sip->as);
00806                             time_t msg_time=rfc822_parsedt(value);
00807 
00808                             if (given_time == 0 || msg_time == 0)
00809                                    break;
00810 
00811                             msg_time=timestamp_to_day(msg_time);
00812                             sip->value=0;
00813                             if ((sip->type == search_sentbefore &&
00814                                    msg_time < given_time) ||
00815                                    (sip->type == search_sentsince&&
00816                                           msg_time>=given_time)||
00817                                    (sip->type == search_senton &&
00818                                           msg_time == given_time))
00819                                    sip->value=1;
00820                      }
00821                      break;
00822 
00823               case search_references1:
00824                      if (isreferences == 0 && sip->as == 0)
00825                      {
00826                             sip->as=strdup(value);
00827                             if (!sip->as)
00828                                    write_error_exit(0);
00829                      }
00830                      break;
00831               case search_references2:
00832                      if (isdate == 0 && sip->as == 0)
00833                      {
00834                             sip->as=strdup(value);
00835                             if (!sip->as)
00836                                    write_error_exit(0);
00837                      }
00838                      break;
00839               case search_references4:
00840                      if (ismessageid == 0 && sip->as == 0)
00841                      {
00842                             sip->as=strdup(value);
00843                             if (!sip->as)
00844                                    write_error_exit(0);
00845                      }
00846                      break;
00847               default:
00848                      break;
00849               }
00850        }
00851        decodeinfo->utf8buflen=0;
00852        return 1;
00853 }
00854 
00855 static int fill_search_header_utf8(const char *str, size_t cnt, void *arg)
00856 {
00857        struct fill_search_header_info *decodeinfo=
00858               (struct fill_search_header_info *)arg;
00859 
00860        if (decodeinfo->utf8bufsize - decodeinfo->utf8buflen < cnt)
00861        {
00862               size_t newsize=decodeinfo->utf8buflen + cnt*2;
00863               char *p=decodeinfo->utf8buf
00864                      ? realloc(decodeinfo->utf8buf, newsize):
00865                      malloc(newsize);
00866 
00867               if (!p)
00868                      write_error_exit(0);
00869               decodeinfo->utf8buf=p;
00870               decodeinfo->utf8bufsize=newsize;
00871        }
00872 
00873        if (cnt)
00874               memcpy(decodeinfo->utf8buf+decodeinfo->utf8buflen, str, cnt);
00875        decodeinfo->utf8buflen += cnt;
00876        return 0;
00877 }
00878 
00879 static int fill_search_header_done(const char *name, void *arg)
00880 {
00881        struct fill_search_header_info *decodeinfo=
00882               (struct fill_search_header_info *)arg;
00883        struct searchinfo *sip;
00884        int issubject=rfc822hdr_namecmp(name, "subject");
00885        size_t j;
00886        libmail_u_convert_handle_t conv;
00887        unicode_char *ucptr;
00888        size_t ucsize;
00889        int rc;
00890 
00891        if (decodeinfo->utf8buflen &&
00892            decodeinfo->utf8buf[decodeinfo->utf8buflen-1] == '\n')
00893               --decodeinfo->utf8buflen;
00894 
00895        fill_search_header_utf8("", 1, arg);
00896 
00897        for (sip=decodeinfo->si; sip; sip=sip->next)
00898               switch (sip->type) {
00899               case search_references3:
00900                      if (issubject == 0 && sip->as == 0)
00901                      {
00902                             sip->as=strdup(decodeinfo->utf8buf);
00903                             if (!sip->as)
00904                                    write_error_exit(0);
00905                      }
00906                      break;
00907               case search_orderedsubj:
00908 
00909                      if (issubject == 0 && sip->as == 0)
00910                      {
00911                             int dummy;
00912 
00913                             sip->as=rfc822_coresubj(decodeinfo->utf8buf,
00914                                                  &dummy);
00915                             if (!sip->as)
00916                                    write_error_exit(0);
00917                      }
00918                      break;
00919               case search_header:
00920 
00921                      if (sip->cs == NULL || rfc822hdr_namecmp(sip->cs, name))
00922                             break;
00923 
00924                      /* FALLTHRU */
00925 
00926               case search_text:
00927                      if (sip->value > 0)
00928                             break;
00929 
00930                      maildir_search_reset(&sip->sei);
00931 
00932                      conv=libmail_u_convert_tou_init("utf-8", &ucptr,
00933                                                  &ucsize, 0);
00934 
00935                      if (!conv)
00936                             break;
00937 
00938                      rc=libmail_u_convert(conv, decodeinfo->utf8buf,
00939                                         decodeinfo->utf8buflen-1);
00940 
00941                      if (libmail_u_convert_deinit(conv, NULL))
00942                             break;
00943 
00944                      if (rc)
00945                      {
00946                             free(ucptr);
00947                             break;
00948                      }
00949 
00950                      for (j=0; j<=ucsize; ++j)
00951                      {
00952                             maildir_search_step_unicode_lc(&sip->sei,
00953                                                         j == ucsize
00954                                                         ? ' ':
00955                                                         ucptr[j]);
00956                             if (maildir_search_found(&sip->sei))
00957                             {
00958                                    sip->value=1;
00959                                    break;
00960                             }
00961                      }
00962                      free(ucptr);
00963                      break;
00964               default:
00965                      break;
00966               }
00967 
00968 
00969        return 0;
00970 }
00971 
00972 struct fill_search_body_info {
00973 
00974        struct searchinfo *si;
00975        libmail_u_convert_handle_t toucs4_handle;
00976 
00977 };
00978 
00979 static int fill_search_body_utf8(const char *str, size_t n, void *arg);
00980 static int fill_search_body_ucs4(const char *str, size_t n, void *arg);
00981 
00982 static void fill_search_body(struct searchinfo *si,
00983                           struct rfc2045 *rfcp, FILE *fp,
00984                           struct imapscanmessageinfo *mi)
00985 {
00986        struct rfc2045src *src;
00987        struct rfc2045_decodemsgtoutf8_cb decodecb;
00988        struct fill_search_body_info decodeinfo;
00989        struct searchinfo *sip;
00990 
00991        src=rfc2045src_init_fd(fileno(fp));
00992 
00993        if (!src)
00994               return;
00995 
00996        memset(&decodecb, 0, sizeof(decodecb));
00997        memset(&decodeinfo, 0, sizeof(decodeinfo));
00998 
00999        decodecb.flags=RFC2045_DECODEMSG_NOHEADERS;
01000        decodecb.output_func=fill_search_body_utf8;
01001        decodecb.arg=&decodeinfo;
01002 
01003        decodeinfo.si=si;
01004 
01005        if ((decodeinfo.toucs4_handle=
01006             libmail_u_convert_init("utf-8",
01007                                 libmail_u_ucs4_native,
01008                                 fill_search_body_ucs4,
01009                                 &decodeinfo)) == NULL)
01010        {
01011               write_error_exit("libmail_u_convert_init");
01012        }
01013 
01014        for (sip=decodeinfo.si; sip; sip=sip->next)
01015               if ((sip->type == search_text || sip->type == search_body)
01016                   && sip->value <= 0)
01017               {
01018                      rfc2045_decodemsgtoutf8(src, rfcp, &decodecb);
01019                      break;
01020               }
01021 
01022        libmail_u_convert_deinit(decodeinfo.toucs4_handle, NULL);
01023 
01024        rfc2045src_deinit(src);
01025 }
01026 
01027 static int fill_search_body_utf8(const char *str, size_t n, void *arg)
01028 {
01029        struct fill_search_body_info *decodeinfo=
01030               (struct fill_search_body_info *)arg;
01031 
01032        return libmail_u_convert(decodeinfo->toucs4_handle, str, n);
01033 }
01034 
01035 static int fill_search_body_ucs4(const char *str, size_t n, void *arg)
01036 {
01037        struct fill_search_body_info *decodeinfo=
01038               (struct fill_search_body_info *)arg;
01039        struct searchinfo *sip;
01040        const unicode_char *u=(const unicode_char *)str;
01041        int notfound=1;
01042 
01043        n /= 4;
01044 
01045        for (sip=decodeinfo->si; sip; sip=sip->next)
01046               if ((sip->type == search_text || sip->type == search_body)
01047                   && sip->value <= 0)
01048               {
01049                      size_t i;
01050 
01051                      notfound=0;
01052 
01053                      for (i=0; i<n; i++)
01054                      {
01055                             maildir_search_step_unicode_lc(&sip->sei, u[i]);
01056 
01057                             if (maildir_search_found(&sip->sei))
01058                             {
01059                                    sip->value=1;
01060                                    break;
01061                             }
01062                      }
01063               }
01064 
01065        return notfound;
01066 }