struct imap_refmsg *rfc822_threadgetroot(struct imap_refmsgtable *mt) { struct imap_refmsg *root, *m; if (mt->rootptr) return (mt->rootptr); root=rfc822_threadallocmsg(mt, "(root)"); if (!root) return (0); root->parent=root; /* Temporary */ root->isdummy=1; for (m=mt->firstmsg; m; m=m->next) if (!m->parent) { if (m->isdummy && m->firstchild == 0) continue; /* Can happen in reference creation */ linkparent(m, root); } root->parent=NULL; return (mt->rootptr=root); }
static void test1() { struct imap_refmsgtable *mt=rfc822_threadalloc(); char buf[20]; struct imap_refmsg *p; strcpy(buf, "a@b"); p=rfc822_threadallocmsg(mt, buf); strcpy(buf, "c@d"); p=rfc822_threadallocmsg(mt, buf); printf("%s\n", (rfc822_threadsearchmsg(mt, "a@b") ? "found":"not found")); printf("%s\n", (rfc822_threadsearchmsg(mt, "c@d") ? "found":"not found")); printf("%s\n", (rfc822_threadsearchmsg(mt, "e@f") ? "found":"not found")); rfc822_threadfree(mt); }
int rfc822_threadmergesubj(struct imap_refmsgtable *mt, struct imap_refmsg *root) { struct imap_refmsg *toproot, *p, *q, *nextroot; char *str; for (toproot=root->firstchild; toproot; toproot=nextroot) { const char *subj; struct imap_subjlookup *subjtop; int isrefwd; nextroot=toproot->nextsib; /* ** (i) Find the subject of this thread as in step 4.B.i ** above. */ p=toproot; if (p->isdummy) p=p->firstchild; subj=p->subj ? p->subj:""; /* ** (ii) If the extracted subject is empty, skip this ** message. */ if (*subj == 0) continue; /* ** (iii) Lookup the message associated with this extracted ** subject in the table. */ if (findsubj(mt, subj, &isrefwd, 0, &subjtop) || subjtop == 0) return (-1); /* ** (iv) If the message in the table is the current message, ** skip it. */ /* NOTE - ptr comparison IS NOT LEGAL */ subjtop->msg->flag2=1; if (toproot->flag2) { toproot->flag2=0; continue; } subjtop->msg->flag2=0; /* ** Otherwise, merge the current message with the one in the ** table using the following rules: ** ** If both messages are dummies, append the current ** message's children to the children of the message in ** the table (the children of both messages become ** siblings), and then delete the current message. */ if (subjtop->msg->isdummy && toproot->isdummy) { while ((p=toproot->firstchild) != 0) { breakparent(p); linkparent(p, subjtop->msg); } breakparent(toproot); continue; } /* ** If the message in the table is a dummy and the current ** message is not, make the current message a child of ** the message in the table (a sibling of it's children). */ if (subjtop->msg->isdummy) { breakparent(toproot); linkparent(toproot, subjtop->msg); continue; } /* ** If the current message is a reply or forward and the ** message in the table is not, make the current message ** a child of the message in the table (a sibling of it's ** children). */ if (isrefwd) { p=subjtop->msg; if (p->isdummy) p=p->firstchild; subj=p->subj ? p->subj:""; str=rfc822_coresubj(subj, &isrefwd); if (!str) return (-1); free(str); /* Don't really care */ if (!isrefwd) { breakparent(toproot); linkparent(toproot, subjtop->msg); continue; } } /* ** Otherwise, create a new dummy container and make both ** messages children of the dummy, and replace the ** message in the table with the dummy message. */ /* What we do is create a new message, then move the ** contents of subjtop->msg (including its children) ** to the new message, then make the new message a child ** of subjtop->msg, and mark subjtop->msg as a dummy msg. */ q=rfc822_threadallocmsg(mt, "(dummy)"); if (!q) return (-1); q->isdummy=1; swapmsgdata(q, subjtop->msg); while ((p=subjtop->msg->firstchild) != 0) { breakparent(p); linkparent(p, q); } linkparent(q, subjtop->msg); breakparent(toproot); linkparent(toproot, subjtop->msg); } return (0); }
static struct imap_refmsg *dorefcreate(struct imap_refmsgtable *mt, const char *newmsgid, struct rfc822a *a) /* a - references header */ { struct imap_refmsg *lastmsg=0, *m; struct imap_refmsg *msg; int n; /* (A) Using the Message-IDs in the message's references, link the corresponding messages together as parent/child. Make the first reference the parent of the second (and the second a child of the first), the second the parent of the third (and the third a child of the second), etc. The following rules govern the creation of these links: If no reference message can be found with a given Message-ID, create a dummy message with this ID. Use this dummy message for all subsequent references to this ID. */ for (n=0; n<a->naddrs; n++) { char *msgid=rfc822_getaddr(a, n); msg=*msgid ? rfc822_threadsearchmsg(mt, msgid ? msgid:""):0; if (!msg) { msg=rfc822_threadallocmsg(mt, msgid ? msgid:""); if (!msg) { if (msgid) free(msgid); return (0); } msg->isdummy=1; } if (msgid) free(msgid); /* If a reference message already has a parent, don't change the existing link. */ if (lastmsg == 0 || msg->parent) { lastmsg=msg; continue; } /* Do not create a parent/child link if creating that link would introduce a loop. For example, before making message A the parent of B, make sure that A is not a descendent of B. */ for (m=lastmsg; m; m=m->parent) if (strcmp(m->msgid, msg->msgid) == 0) break; if (m) { lastmsg=msg; continue; } linkparent(msg, lastmsg); lastmsg=msg; } /* (B) Create a parent/child link between the last reference (or NIL if there are no references) and the current message. If the current message has a parent already, break the current parent/child link before creating the new one. Note that if this message has no references, that it will now have no parent. NOTE: The parent/child links MUST be kept consistent with one another at ALL times. */ msg=*newmsgid ? rfc822_threadsearchmsg(mt, newmsgid):0; /* If a message does not contain a Message-ID header line, or the Message-ID header line does not contain a valid Message ID, then assign a unique Message ID to this message. Implementation note: empty msgid, plus dupe check below, implements that. */ if (msg && msg->isdummy) { msg->isdummy=0; if (msg->parent) breakparent(msg); } else { #if 1 /* ** If two or more messages have the same Message ID, assign ** a unique Message ID to each of the duplicates. ** ** Implementation note: just unlink the existing message from ** it's parents/children. */ if (msg) { while (msg->firstchild) breakparent(msg->firstchild); breakparent(msg); newmsgid=""; /* Create new entry with an empty msgid, if any more ** msgids come, they'll hit the dupe check again. */ } #endif msg=rfc822_threadallocmsg(mt, newmsgid); if (!msg) return (0); } if (lastmsg) { for (m=lastmsg; m; m=m->parent) if (strcmp(m->msgid, msg->msgid) == 0) break; if (!m) linkparent(msg, lastmsg); } return (msg); }