#include #include #include "tlmpmail.h" #include "tlmpmail.m" PUBLIC FOLDER_THREAD::FOLDER_THREAD(MAIL_MESSAGE &msg) { folder = msg.getfolder(); folder->incrusage(); title.setfrom (msg.subject.get(),20); if (msg.references.is_filled()){ ref.copyword (msg.references.get()); }else{ ref.setfrom (msg.id); } } PUBLIC FOLDER_THREAD::~FOLDER_THREAD() { folder->decrusage(); } PUBLIC int FOLDER_THREAD::loadindex (MAIL_MESSAGES &idx, int &revision) { int ret = -1; if (folder->loadindex (idx,revision)!=-1){ /* #Specification: folder_view / thread / principles There are two ways to extract the thread relations in a folder. One is to use the message-id and in-reply-to header entry. By following this one to one relation (many to one in fact) we can extract messages which belong to a given thread. Unfortunatly, this involves lots of processing since we only have on level of reference (one message is the answer to another message, which is an answer to this other message and so on). The other way is to use the references header entry. This one is trivial. The "references" contains the message-id's of all mail between one and the original (the thread history). So to find out which messages belong to a given thread we simply have to find one message-id in common, the first. All messages from the same thread have the same message-id at the start of the references field. Now there are two problems: First, some mail agent do not handle/maintain the reference field. So we must be able to compute the thread content from the many to one relation "in-reply-to" pointing to "message-id". The second problem is that our folder may be incomplete. Indeed we are handling a "personnal" folder. Some original message of the thread may have been deleted. A solution would be to keep deleted message around and the FOLDER_THREAD would automatically combine the "deleted" folder and the current folder to create a more complete thread view. This is especially useful for people tracking their mail to the "minute" and wanting to review a thread in light of new information. Now, how to we solve this for now. We combine the two strategies. First we use the many to one relations (in-reply-to -> message-id) to create the missing "references" field (the messages without this entry). Once we have filled the references, we extract any message having one id in its references field matching the "common" id table. This table originally contains the ID of the message selected by the user. While we extract messages, the common table grow. So we iterate over the non-extracted messages until the table stop to grow. This gets us a good list of related messages (same thread). The sorting is done simply using the references field as the key. Unfortunatly, the references field may be incomplete since some messages may have been deleted, so the sorting may be weird. HUm... */ fprintf (stderr,"ref = :%s:\n",ref.get()); if (ref.is_empty()){ // No message-id in the mail, we produce an empty folder idx.remove_all(); }else{ // First step, we build the references field for the messages // missing one. At this point, we have no clue which message // belong to the thread, so we have to do it for every one. int n = idx.getnb(); int parent[n]; memset (parent,-1,sizeof(parent)); for (int i=0; ireferences.is_empty()){ const char *id = m->replyid.get(); if (id[0] != '\0'){ for (int j=0; jid.cmp(id)==0){ parent[i] = j; break; } } } } } // Now, for each message having a parent, we build its reference for (int i=0; ireferences; if (p != -1 && refs->is_empty()){ // We collect all the parents and the message itself // to build the ref. int nbparent = 1; int tbref[n]; tbref[0] = i; while (p != -1){ tbref[nbparent++] = p; p = parent[p]; } const char *ctl = "%s"; for (int j=nbparent-1; j>=0; j--){ refs->appendf (ctl,idx.getitem(tbref[j])->id.get()); ctl = " %s"; } } } // Now we use the references field to know which messages // are part of the thread. for (int i=0; iid.cmp(ref)!=0 && m->references.strstr(ref.get())==NULL){ idx.remove_del (i); i--; n--; } } } (idx); MAIL_MESSAGE *m1 = (MAIL_MESSAGE*)o1; MAIL_MESSAGE *m2 = (MAIL_MESSAGE*)o2; return m1->references.cmp(m2->references); ret = idx.getnb(); if (debug){ for (int i=0; ireferences.get()); } } } return ret; } PUBLIC int FOLDER_THREAD::update (bool clean) { return folder->update(clean); } PUBLIC const char *FOLDER_THREAD::gettitle () { return title.get(); } PUBLIC int FOLDER_THREAD::loadmsg (const MAIL_MESSAGE &msg, MAIL_MESSAGE_FULL &full) { return folder->loadmsg (msg,full); } PUBLIC bool FOLDER_THREAD::is_postponed () { return folder->is_postponed(); } PUBLIC bool FOLDER_THREAD::is_shared () { return folder->is_shared(); } int FOLDER_THREAD::getid (SSTRING &) { return -1; } PUBLIC int FOLDER_THREAD::getrevision () { return folder->getrevision(); } PUBLIC void FOLDER_THREAD::getupdmsgs (PRIVATE_MESSAGES &upds) { folder->getupdmsgs(upds); } PUBLIC void FOLDER_THREAD::checkupdate () { folder->checkupdate(); }