Back to index

courier  0.68.2
thread.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2000-2009 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 
00007 #include      "config.h"
00008 #include      "imapd.h"
00009 #include      "thread.h"
00010 #include      "searchinfo.h"
00011 #include      <stdio.h>
00012 #include      <string.h>
00013 #include      <stdlib.h>
00014 #include      "imapwrite.h"
00015 #include      "imaptoken.h"
00016 #include      "imapscanclient.h"
00017 #include      "rfc822/rfc822.h"
00018 #include      "rfc822/rfc2047.h"
00019 #include      "rfc822/imaprefs.h"
00020 #include      "unicode/unicode.h"
00021 
00022 static void thread_os_callback(struct searchinfo *, struct searchinfo *, int,
00023        unsigned long, void *);
00024 static void thread_ref_callback(struct searchinfo *, struct searchinfo *, int,
00025        unsigned long, void *);
00026 
00027 extern struct imapscaninfo current_maildir_info;
00028 
00029 
00030 struct os_threadinfo {
00031        struct os_threadinfo *next;
00032        char *subj;
00033        time_t sent_date;
00034        unsigned long n;
00035        } ;
00036 
00037 struct os_threadinfo_list {
00038        struct os_threadinfo_list *next;
00039        size_t thread_start;
00040 } ;
00041 
00042 struct os_struct {
00043        struct os_threadinfo *list;
00044        unsigned nmsgs;
00045        struct os_threadinfo **msgs;
00046        } ;
00047 
00048 static void os_init(struct os_struct *os)
00049 {
00050        memset(os, 0, sizeof(*os));
00051 }
00052 
00053 static void os_add(struct os_struct *os, unsigned long n, const char *s,
00054                  time_t sent_date)
00055 {
00056 struct os_threadinfo *osi=(struct os_threadinfo *)
00057        malloc(sizeof(struct os_threadinfo));
00058 
00059        if (!osi)     write_error_exit(0);
00060        osi->subj=strdup(s);
00061                      /* This decodes the MIME encoding */
00062        if (!osi->subj)      write_error_exit(0);
00063        osi->sent_date=sent_date;
00064        osi->n=n;
00065        osi->next=os->list;
00066        os->list=osi;
00067        ++os->nmsgs;
00068 }
00069 
00070 static void os_free(struct os_struct *os)
00071 {
00072 struct os_threadinfo *p;
00073 
00074        while ((p=os->list) != 0)
00075        {
00076               os->list=p->next;
00077               free(p->subj);
00078               free(p);
00079        }
00080        if (os->msgs) free(os->msgs);
00081 }
00082 
00083 static int cmpsubjs(const void *a, const void *b)
00084 {
00085        const struct os_threadinfo *ap=*(const struct os_threadinfo **)a;
00086        const struct os_threadinfo *bp=*(const struct os_threadinfo **)b;
00087        int rc=strcmp( ap->subj, bp->subj);
00088 
00089        if (rc)       return (rc);
00090 
00091        return (ap->sent_date < bp->sent_date ? -1:
00092               ap->sent_date > bp->sent_date ? 1:0);
00093 }
00094 
00095 /* Print the meat of the THREAD ORDEREDSUBJECT response */
00096 
00097 static void printos(struct os_threadinfo **array, size_t cnt)
00098 {
00099        size_t i;
00100        struct os_threadinfo_list *thread_list=NULL, *threadptr, **tptr;
00101 
00102        /*
00103        ** thread_list - indexes to start of each thread, sort indexes by
00104        ** sent_date
00105        */
00106 
00107        for (i=0; i<cnt; i++)
00108        {
00109               /* Find start of next thread */
00110 
00111               if (i > 0 && strcmp(array[i-1]->subj, array[i]->subj) == 0)
00112                      continue;
00113 
00114               threadptr=malloc(sizeof(struct os_threadinfo_list));
00115               if (!threadptr)
00116                      write_error_exit(0);
00117               threadptr->thread_start=i;
00118 
00119               /* Insert into the list, sorted by sent date */
00120 
00121               for (tptr= &thread_list; *tptr; tptr=&(*tptr)->next)
00122                      if ( array[(*tptr)->thread_start]->sent_date
00123                           > array[i]->sent_date)
00124                             break;
00125 
00126               threadptr->next= *tptr;
00127               *tptr=threadptr;
00128        }
00129 
00130        while ( (threadptr=thread_list) != NULL)
00131        {
00132               size_t i, j;
00133               const char *p;
00134 
00135               thread_list=threadptr->next;
00136 
00137               i=threadptr->thread_start;
00138               free(threadptr);
00139 
00140               for (j=i+1; j<cnt; j++)
00141               {
00142                      if (strcmp(array[i]->subj, array[j]->subj))
00143                             break;
00144               }
00145 
00146               p="(";
00147               while (i < j)
00148               {
00149                      writes(p);
00150                      p=" ";
00151                      writen(array[i]->n);
00152                      ++i;
00153               }
00154               writes(")");
00155        }
00156 }
00157 
00158 void dothreadorderedsubj(struct searchinfo *si, struct searchinfo *sihead,
00159                       const char *charset, int isuid)
00160 {
00161 struct os_struct     os;
00162 
00163        os_init(&os);
00164        search_internal(si, sihead, charset, isuid, thread_os_callback, &os);
00165 
00166        if (os.nmsgs > 0)    /* Found some messages */
00167        {
00168        size_t i;
00169        struct os_threadinfo *o;
00170 
00171               /* Convert it to an array */
00172 
00173               os.msgs= (struct os_threadinfo **)
00174                      malloc(os.nmsgs * sizeof(struct os_threadinfo *));
00175               if (!os.msgs) write_error_exit(0);
00176               for (o=os.list, i=0; o; o=o->next, i++)
00177                      os.msgs[i]=o;
00178 
00179               /* Sort the array */
00180 
00181               qsort(os.msgs, os.nmsgs, sizeof(*os.msgs), cmpsubjs);
00182 
00183               /* Print the array */
00184 
00185               printos(os.msgs, os.nmsgs);
00186        }
00187        os_free(&os);
00188 }
00189 
00190 /*
00191 This callback function is called once search finds a qualifying message.
00192 We save its message number and subject in a link list.
00193 */
00194 
00195 static void thread_os_callback(struct searchinfo *si,
00196                             struct searchinfo *sihead,
00197                             int isuid, unsigned long i,
00198                             void *voidarg)
00199 {
00200        if (sihead->type == search_orderedsubj)
00201               /* SHOULD BE ALWAYS TRUE */
00202               os_add( (struct os_struct *)voidarg,
00203                      isuid ? current_maildir_info.msgs[i].uid:i+1,
00204                      sihead->as ? sihead->as:"",
00205                      sihead->bs ? rfc822_parsedt(sihead->bs):0);
00206 }
00207 
00208 static void printthread(struct imap_refmsg *, int);
00209 
00210 void dothreadreferences(struct searchinfo *si, struct searchinfo *sihead,
00211                      const char *charset,
00212                      int isuid)
00213 {
00214        struct imap_refmsgtable *reftable;
00215        struct imap_refmsg *root;
00216 
00217        if (!(reftable=rfc822_threadalloc()))
00218        {
00219               write_error_exit(0);
00220               return;
00221        }
00222 
00223        search_internal(si, sihead, charset, 0,
00224                      thread_ref_callback, reftable);
00225 
00226        root=rfc822_thread(reftable);
00227        printthread(root, isuid);
00228        rfc822_threadfree(reftable);
00229 }
00230 
00231 static void thread_ref_callback(struct searchinfo *si,
00232                             struct searchinfo *sihead,
00233                             int isuid, unsigned long i,
00234                             void *voidarg)
00235 {
00236        if (sihead->type == search_references1 && sihead->a &&
00237            sihead->a->type == search_references2 && sihead->a->a &&
00238            sihead->a->a->type == search_references3 && sihead->a->a->a &&
00239            sihead->a->a->a->type == search_references4)
00240        {
00241               const char *ref, *inreplyto, *subject, *date, *msgid;
00242 
00243               ref=sihead->as;
00244               inreplyto=sihead->bs;
00245               date=sihead->a->as;
00246               subject=sihead->a->a->as;
00247               msgid=sihead->a->a->a->as;
00248 
00249 #if 0
00250               fprintf(stderr, "REFERENCES: ref=%s, inreplyto=%s, subject=%s, date=%s, msgid=%s\n",
00251                      ref ? ref:"",
00252                      inreplyto ? inreplyto:"",
00253                      subject ? subject:"",
00254                      date ? date:"",
00255                      msgid ? msgid:"");
00256 #endif
00257 
00258               if (!rfc822_threadmsg( (struct imap_refmsgtable *)voidarg,
00259                                    msgid, ref && *ref ? ref:inreplyto,
00260                                    subject, date, 0, i))
00261                      write_error_exit(0);
00262        }
00263 }
00264 
00265 static void printthread(struct imap_refmsg *msg, int isuid)
00266 {
00267        const char *pfix="";
00268 
00269        while (msg)
00270        {
00271               if (!msg->isdummy)
00272               {
00273                      writes(pfix);
00274                      writen(isuid ?
00275                             current_maildir_info.msgs[msg->seqnum].uid:
00276                             msg->seqnum+1);
00277                      pfix=" ";
00278               }
00279 
00280               if (msg->firstchild && (msg->firstchild->nextsib
00281                                    || msg->firstchild->isdummy
00282                                    || msg->parent == NULL))
00283               {
00284                      writes(pfix);
00285                      for (msg=msg->firstchild; msg; msg=msg->nextsib)
00286                      {
00287                             struct imap_refmsg *msg2;
00288 
00289                             msg2=msg;
00290 
00291                             if (msg2->isdummy)
00292                                    msg2=msg2->firstchild;
00293 
00294                             for (; msg2; msg2=msg2->firstchild)
00295                             {
00296                                    if (!msg2->isdummy ||
00297                                        msg2->nextsib)
00298                                           break;
00299                             }
00300 
00301                             if (msg2)
00302                             {
00303                                    writes("(");
00304                                    printthread(msg, isuid);
00305                                    writes(")");
00306                             }
00307                      }
00308                      break;
00309               }
00310               msg=msg->firstchild;
00311        }
00312 }
00313 
00314 void free_temp_sort_stack(struct temp_sort_stack *t)
00315 {
00316        while (t)
00317        {
00318        struct temp_sort_stack *u=t->next;
00319 
00320               free(t);
00321               t=u;
00322        }
00323 }
00324 
00325 /* ---------------------------------- SORT ---------------------------- */
00326 
00327 /* sortmsginfo holds the sorting information for a message. */
00328 
00329 struct sortmsginfo {
00330        struct sortmsginfo *next;   /* next msg */
00331        unsigned long n;            /* msg number/uid */
00332        char **sortfields;          /* array of sorting fields */
00333        char *sortorder;            /* [x]=0 - normal, [x]=1 - reversed */
00334        size_t nfields;
00335        } ;
00336 
00337 struct sortmsgs {
00338        struct sortmsginfo *list;   /* The actual list */
00339        struct sortmsginfo **array; /* In array form */
00340        size_t nmsgs;               /* Array size/count of msgs */
00341        size_t nfields;             /* From the SORT() arg */
00342        } ;
00343 
00344 static void free_sortmsgs(struct sortmsgs *p)
00345 {
00346 struct sortmsginfo *q;
00347 
00348        if (p->array) free(p->array);
00349        while ((q=p->list) != 0)
00350        {
00351        size_t i;
00352 
00353               p->list=q->next;
00354               for (i=0; i<p->nfields; i++)
00355                      if (q->sortfields[i])
00356                             free(q->sortfields[i]);
00357               if (q->sortfields)   free(q->sortfields);
00358               if (q->sortorder)    free(q->sortorder);
00359               free(q);
00360        }
00361 }
00362 
00363 static void sort_callback(struct searchinfo *, struct searchinfo *, int,
00364        unsigned long, void *);
00365 
00366 static int cmpsort(const void *a, const void *b)
00367 {
00368 const struct sortmsginfo *ap=*(const struct sortmsginfo **)a;
00369 const struct sortmsginfo *bp=*(const struct sortmsginfo **)b;
00370 size_t i;
00371 
00372        for (i=0; i<ap->nfields; i++)
00373        {
00374        int    n=strcmp(ap->sortfields[i], bp->sortfields[i]);
00375 
00376               if (n < 0)
00377                      return (ap->sortorder[i] ? 1:-1);
00378               if (n > 0)
00379                      return (ap->sortorder[i] ? -1:1);
00380        }
00381        return (0);
00382 }
00383 
00384 void dosortmsgs(struct searchinfo *si, struct searchinfo *sihead,
00385               const char *charset, int isuid)
00386 {
00387 struct sortmsgs sm;
00388 struct searchinfo *p;
00389 
00390        memset(&sm, 0, sizeof(sm));
00391        for (p=sihead; p; p=p->a)
00392               switch (p->type)     {
00393               case search_orderedsubj:
00394               case search_arrival:
00395               case search_cc:
00396               case search_date:
00397               case search_from:
00398               case search_size:
00399               case search_to:
00400                      ++sm.nfields;
00401                      break;
00402               default:
00403                      break;
00404               }
00405        search_internal(si, sihead, charset, isuid, sort_callback, &sm);
00406        if (sm.nmsgs > 0)
00407        {
00408        size_t i;
00409        struct sortmsginfo *o;
00410 
00411               /* Convert it to an array */
00412 
00413               sm.array= (struct sortmsginfo **)
00414                      malloc(sm.nmsgs * sizeof(struct sortmsginfo *));
00415               if (!sm.array)       write_error_exit(0);
00416               for (o=sm.list, i=0; o; o=o->next, i++)
00417                      sm.array[i]=o;
00418 
00419               /* Sort the array */
00420 
00421               qsort(sm.array, sm.nmsgs, sizeof(*sm.array), cmpsort);
00422 
00423               /* Print the array */
00424 
00425               for (i=0; i<sm.nmsgs; i++)
00426               {
00427                      writes(" ");
00428                      writen(sm.array[i]->n);
00429               }
00430        }
00431        free_sortmsgs(&sm);
00432 }
00433 
00434 static void sort_callback(struct searchinfo *si, struct searchinfo *sihead,
00435        int isuid, unsigned long n, void *voidarg)
00436 {
00437 struct sortmsgs *sm=(struct sortmsgs *)voidarg;
00438 struct sortmsginfo *msg=(struct sortmsginfo *)
00439               malloc(sizeof(struct sortmsginfo));
00440 struct searchinfo *ss;
00441 int rev;
00442 size_t i;
00443 
00444        if (msg)      memset(msg, 0, sizeof(*msg));
00445        if (!msg || (sm->nfields && ((msg->sortfields=(char **)
00446                             malloc(sizeof(char *)*sm->nfields)) == 0 ||
00447                                    (msg->sortorder=(char *)
00448                             malloc(sm->nfields)) == 0)))
00449               write_error_exit(0);
00450 
00451        if (sm->nfields)
00452        {
00453               memset(msg->sortfields, 0, sizeof(char *)*sm->nfields);
00454               memset(msg->sortorder, 0, sm->nfields);
00455        }
00456 
00457        rev=0;
00458        i=0;
00459 
00460 /* fprintf(stderr, "--\n"); */
00461 
00462        for (ss=sihead; ss; ss=ss->a)
00463        {
00464        char   *p;
00465 
00466               if (i >= sm->nfields)
00467                      break; /* Something's fucked up, better handle it
00468                             ** gracefully, instead of dumping core.
00469                             */
00470               switch (ss->type)    {
00471               case search_reverse:
00472                      rev=1-rev;
00473                      continue;
00474               case search_orderedsubj:
00475               case search_arrival:
00476               case search_cc:
00477               case search_date:
00478               case search_from:
00479               case search_size:
00480               case search_to:
00481                      p=ss->as;
00482                      if (!p)       p="";
00483                      msg->sortfields[i]=my_strdup(p);
00484                      msg->sortorder[i]=rev;
00485                      /* fprintf(stderr, "%d %s\n", msg->sortorder[i], msg->sortfields[i]); */
00486                      ++i;
00487                      rev=0;
00488                      continue;
00489               default:
00490                      break;
00491               }
00492               break;
00493        }
00494 
00495        msg->nfields=sm->nfields;
00496        msg->n=isuid ? current_maildir_info.msgs[n].uid:n+1;
00497        msg->next=sm->list;
00498        sm->list=msg;
00499        ++sm->nmsgs;
00500 }