#include #include #include #include #include #include #include #include #include #include #include #include #include "tlmpmail.h" #include "tlmpmail.m" #include "keys.h" PUBLIC MAIL_MESSAGE::MAIL_MESSAGE() { filepos = 0; size = 0; deleted = false; replied = false; viewed = false; tagged = false; folder = NULL; rstmodified(); usage = 0; date = 0; fid = -1; } static bool delete_full; // To know if we are deleting a full or normal // MAIL_MESSAGE PUBLIC MAIL_MESSAGE::~MAIL_MESSAGE() { if (delete_full){ delete_full = false; }else{ n_debug ("delete msg %s\n",getauthor()); } } PUBLIC MAIL_MESSAGE_FULL::~MAIL_MESSAGE_FULL() { delete_full=true; } /* Assign a message to a folder manger */ PUBLIC void MAIL_MESSAGE::assign (FOLDER_VIEW *_folder) { folder = _folder; } /* Make sure all the flags and comments of a message are stored on the folder. */ PUBLIC void MAIL_MESSAGE::update_folder() { if (was_modified() && folder != NULL){ folder->update (false); } } /* Return the folder holding this message (generally a FOLDER_FILE) */ PUBLIC FOLDER_VIEW *MAIL_MESSAGE::getfolder() const { return folder; } /* Return one key flag of the message. Return "" if there is no value assigned to this key. */ PUBLIC const char *MAIL_MESSAGE::getflag (const char *key) { const char *ret = flags.get_str(key); if (ret == NULL) ret = ""; return ret; } /* Increment the usage count of a message. This is mostly used to tell if a folder is still needed. */ PUBLIC void MAIL_MESSAGE::incrusage() { usage++; // if (folder != NULL) folder->incrusage(); } /* Decrement the usage count of a message. This is mostly used to tell if a folder is still needed. */ PUBLIC void MAIL_MESSAGE::decrusage() { usage--; // if (folder != NULL) folder->decrusage(); } /* Return the usage count of the message */ PUBLIC int MAIL_MESSAGE::getusage() { return usage; } /* Return the main source of a message (from) */ PUBLIC const char *MAIL_MESSAGE::getemail_from() const { return from.getfirstaddr(); } /* Return the main source of a message (from) */ PUBLIC const char *MAIL_MESSAGE::get_from() const { return from.getfirstname(); } /* Return the main recipient of a message (to) */ PUBLIC const char *MAIL_MESSAGE::getemail_to() const { return to.getfirstaddr(); } /* Return the main recipient of a message (to) */ PUBLIC const char *MAIL_MESSAGE::get_to() const { return to.getfirstname(); } /* Return the number of recipient of a message (to) */ PUBLIC int MAIL_MESSAGE::getnb_to() const { return to.size(); } /* Return the main reply address of a message (reply-to) */ PUBLIC const char *MAIL_MESSAGE::getemail_reply() const { return reply.getfirstaddr(); } /* Return the main reply name of a message (reply-to) */ PUBLIC const char *MAIL_MESSAGE::get_reply() const { return reply.getfirstname(); } PUBLIC const char *MAIL_MESSAGE::getsubject() const { return subject.get(); } PUBLIC void MAIL_MESSAGE::formatdate (char datestr[20], bool nohour) { struct tm *t = localtime (&date); if (nohour){ sprintf (datestr,"%4d/%02d/%02d" ,t->tm_year+1900,t->tm_mon+1,t->tm_mday); }else{ sprintf (datestr,"%4d/%02d/%02d %02d:%02d:%02d" ,t->tm_year+1900,t->tm_mon+1,t->tm_mday ,t->tm_hour,t->tm_min,t->tm_sec); } } /* Return the author of a mail, either his name or email address is the name is missing. */ PUBLIC const char *MAIL_MESSAGE::getauthor() const { const char *ret = get_from(); if (ret[0] == '\0') ret = getemail_from(); return ret; } /* Return the recipient of a mail, either his name or email address is the name is missing. */ PUBLIC const char *MAIL_MESSAGE::getdest() const { const char *ret = get_to(); if (ret[0] == '\0') ret = getemail_to(); return ret; } /* Load the complete message. Return -1 if it can't be done. */ PUBLIC int MAIL_MESSAGE::loadmsg (MAIL_MESSAGE_FULL &full) const { int ret = -1; if (folder != NULL){ ret = folder->loadmsg (*this,full); }else{ xconf_error ("Odd: Message disconnected from folder"); } return ret; } /* Wake the various browser managing the folder holding this message */ PUBLIC void MAIL_MESSAGE::folder_wakeup() { if (folder != NULL) folder->wakeup(); } /* Update the message in the folder Some folder types do nothing and wait for the update to process all modified messages. So by default, we do nothing. */ PUBLIC VIRTUAL void FOLDER_VIEW::updatemsg (MAIL_MESSAGE &) { } /* Return true if a message already exist in the folder. Generally the msgid is compared since it must be uniq. */ PUBLIC VIRTUAL bool FOLDER_VIEW::msgexist (MAIL_MESSAGE &) { return false; } PUBLIC void MAIL_MESSAGE::setmodified() { ARRAY_OBJ::setmodified(); folder_wakeup(); if (folder != NULL) folder->updatemsg (*this); } /* Change the status of a message */ PUBLIC void MAIL_MESSAGE::set_deleted (bool val) { if (deleted != val){ deleted = val; setmodified(); } } PUBLIC void MAIL_MESSAGE::set_viewed (bool val) { if (viewed != val){ viewed = val; setmodified(); } } PUBLIC void MAIL_MESSAGE::set_replied (bool val) { if (replied != val){ replied = val; if (val) set_viewed(true); setmodified(); } } PUBLIC void MAIL_MESSAGE::set_tagged (bool val) { if (tagged != val){ tagged = val; setmodified(); } } /* Parse the content type field of the message to tell how to interpret its content. */ PUBLIC CONTENT_TYPE MAIL_MESSAGE_FULL::parsetype (SSTRING &boundary) const { return mime_parsetype (content_type.get(),boundary); } PUBLIC CONTENT_TYPE MAIL_MESSAGE_FULL::gettype () const { SSTRING boundary; return parsetype (boundary); } PUBLIC MAIL_MESSAGE *MAIL_MESSAGES::getitem(int no) const { return (MAIL_MESSAGE*)ARRAY::getitem(no); } /* Increment the usage count of all messages */ PUBLIC void MAIL_MESSAGES::incrusage () { int n = getnb(); for (int i=0; iincrusage(); } /* Decrement the usage count of all messages */ PUBLIC void MAIL_MESSAGES::decrusage () { int n = getnb(); for (int i=0; idecrusage(); } PUBLIC HEAD_STATE::HEAD_STATE() { lastline = NULL; addrs = NULL; } PUBLIC void MAIL_MESSAGE::parse_head( HEAD_STATE &st, const char *line) { if (line[0] == '\0'){ // fprintf (stderr,"Inhead\n"); if (st.addrs != NULL){ st.addrs->parse (st.lastline->get()); st.addrs = NULL; } st.lastline = NULL; }else{ if (isspace (line[0])){ if (st.lastline != NULL){ if (st.lastline == &comment){ st.lastline->appendf ("%s\n",line+1); }else{ st.lastline->append (line+1); } } }else{ if (st.addrs != NULL){ st.addrs->parse (st.lastline->get()); st.addrs = NULL; } st.lastline = NULL; if (strncasecmp(line,"Subject:",8)==0){ st.lastline = &subject; st.lastline->setfrom (str_skip(line+8)); }else if (strncasecmp(line,"From:",5)==0){ st.lastline = &st.lastlinebuf; st.addrs = &from; st.lastline->setfrom (str_skip(line+5)); }else if (strncasecmp(line,"Reply-To:",9)==0){ st.lastline = &st.lastlinebuf; st.addrs = &reply; st.lastline->setfrom (str_skip(line+9)); }else if (strncasecmp(line,"Message-Id:",11)==0){ id.setfrom (str_skip(line+11)); }else if (strncasecmp(line,"To:",3)==0){ st.lastline = &st.lastlinebuf; st.addrs = &to; st.lastline->setfrom (str_skip(line+3)); }else if (strncasecmp(line,"Cc:",3)==0){ st.lastline = &st.lastlinebuf; st.addrs = &cc; st.lastline->setfrom (str_skip(line+3)); }else if (strncasecmp(line,"In-Reply-To:",12)==0){ replyid.setfrom (str_skip(line+12)); }else if (strncasecmp(line,"Date:",5)==0){ date = miscmail_parsedate(line+5); }else if (strncasecmp(line,"Status:",7)==0){ // Parse Pine status tags const char *pt = str_skip(line+7); while (*pt != '\0'){ char carac = *pt++; if (carac == 'R'){ viewed = true; }else if (carac == 'O'){ // glocal.msg->viewed = true; }else if (carac == 'D'){ deleted = true; }else if (carac == 'A'){ replied = true; }else if (carac == 'T'){ tagged = true; } } }else if (strncasecmp(line,"X-Status:",9)==0){ // Parse Pine status tags const char *pt = str_skip(line+9); while (*pt != '\0'){ char carac = *pt++; if (carac == 'A'){ replied = true; } } }else if (strncmp(line,"X-tlmpmail:",11)==0){ // Parse TLMPMAIL flags (xxx=val yyy=val ...) int len = strlen(line)+1; const char *pt = line+11; while (*pt != '\0'){ pt = str_skip(pt); char parm[len]; pt = str_copyword (parm,pt); if (parm[0] == '\0') break; char *eq = strchr(parm,'='); if (eq != NULL){ *eq++ = '\0'; flags.set_str(parm,eq); } } }else if (strncmp(line,"X-comment:",10)==0){ st.lastline = &comment; st.lastline->setfromf("%s\n",str_skip(line+10)); }else if (strncasecmp(line,"References:",11)==0){ st.lastline = &references; st.lastline->setfrom(str_skip(line+11)); } } } } int file_folder (const char *fname, MAIL_MESSAGES &tb) { MAIL_MESSAGES *tb; MAIL_MESSAGE *msg; SSTRING *lastline; SSTRING lastlinebuf; EMAILADDRS *addrs; bool inhead; glocal.tb = &tb; glocal.msg = NULL; glocal.inhead = false; glocal.lastline = NULL; glocal.addrs = NULL; (fname,true); // fprintf (stderr,"oneline %d %d %p %p %s\n",noline,glocal.inhead,glocal.lastline,glocal.addrs,line); if (strncmp(line,"From ",5)==0){ glocal.inhead = true; glocal.lastline = NULL; if (glocal.msg != NULL){ glocal.msg->size = filepos - glocal.msg->filepos; } glocal.msg = new MAIL_MESSAGE; glocal.msg->filepos = filepos; glocal.tb->add (glocal.msg); }else if (line[0] == '\0'){ // fprintf (stderr,"Inhead\n"); if (glocal.addrs != NULL){ glocal.addrs->parse (glocal.lastline->get()); glocal.addrs = NULL; } glocal.inhead = false; glocal.lastline = NULL; }else if (glocal.inhead){ if (isspace (line[0])){ if (glocal.lastline != NULL){ if (glocal.lastline == &glocal.msg->comment){ glocal.lastline->appendf ("%s\n",line+1); }else{ glocal.lastline->append (line+1); } } }else{ if (glocal.addrs != NULL){ glocal.addrs->parse (glocal.lastline->get()); glocal.addrs = NULL; } glocal.lastline = NULL; if (strncasecmp(line,"Subject:",8)==0){ glocal.lastline = &glocal.msg->subject; glocal.lastline->setfrom (str_skip(line+8)); }else if (strncasecmp(line,"From:",5)==0){ glocal.lastline = &glocal.lastlinebuf; glocal.addrs = &glocal.msg->from; glocal.lastline->setfrom (str_skip(line+5)); }else if (strncasecmp(line,"Reply-To:",9)==0){ glocal.lastline = &glocal.lastlinebuf; glocal.addrs = &glocal.msg->reply; glocal.lastline->setfrom (str_skip(line+9)); }else if (strncasecmp(line,"Message-Id:",11)==0){ glocal.msg->id.setfrom (str_skip(line+11)); }else if (strncasecmp(line,"To:",3)==0){ glocal.lastline = &glocal.lastlinebuf; glocal.addrs = &glocal.msg->to; glocal.lastline->setfrom (str_skip(line+3)); }else if (strncasecmp(line,"Cc:",3)==0){ glocal.lastline = &glocal.lastlinebuf; glocal.addrs = &glocal.msg->cc; glocal.lastline->setfrom (str_skip(line+3)); }else if (strncasecmp(line,"In-Reply-To:",12)==0){ glocal.msg->replyid.setfrom (str_skip(line+12)); }else if (strncasecmp(line,"Date:",5)==0){ glocal.msg->date = miscmail_parsedate(line+5); }else if (strncasecmp(line,"Status:",7)==0){ // Parse Pine status tags const char *pt = str_skip(line+7); while (*pt != '\0'){ char carac = *pt++; if (carac == 'R'){ glocal.msg->viewed = true; }else if (carac == 'O'){ // glocal.msg->viewed = true; }else if (carac == 'D'){ glocal.msg->deleted = true; }else if (carac == 'A'){ glocal.msg->replied = true; }else if (carac == 'T'){ glocal.msg->tagged = true; } } }else if (strncasecmp(line,"X-Status:",9)==0){ // Parse Pine status tags const char *pt = str_skip(line+9); while (*pt != '\0'){ char carac = *pt++; if (carac == 'A'){ glocal.msg->replied = true; } } }else if (strncmp(line,"X-tlmpmail:",11)==0){ // Parse TLMPMAIL flags (xxx=val yyy=val ...) int len = strlen(line)+1; const char *pt = line+11; while (*pt != '\0'){ pt = str_skip(pt); char parm[len]; pt = str_copyword (parm,pt); if (parm[0] == '\0') break; char *eq = strchr(parm,'='); if (eq != NULL){ *eq++ = '\0'; glocal.msg->flags.set_str(parm,eq); } } }else if (strncmp(line,"X-comment:",10)==0){ glocal.lastline = &glocal.msg->comment; glocal.lastline->setfromf("%s\n",str_skip(line+10)); }else if (strncasecmp(line,"References:",11)==0){ glocal.lastline = &glocal.msg->references; glocal.lastline->setfrom(str_skip(line+11)); } } } return 0; if (glocal.msg != NULL){ glocal.msg->size = filepos - glocal.msg->filepos; } // fprintf (stderr,"end %p %p\n",glocal.lastline,glocal.addrs); if (glocal.addrs != NULL){ glocal.addrs->parse (glocal.lastline->get()); } return tb.getnb(); } class FOLDER_VIEW_PRIVATE{ public: PRIVATE_MESSAGE msg; int usage; FOLDER_VIEW_PRIVATE(){ usage = 0; } }; PUBLIC FOLDER_VIEW::FOLDER_VIEW() { priv = new FOLDER_VIEW_PRIVATE; } PUBLIC VIRTUAL FOLDER_VIEW::~FOLDER_VIEW() { delete priv; } /* Get the PRIVATE_MESSAGE used to wakeup folder browser threads */ PUBLIC VIRTUAL void FOLDER_VIEW::getmsgs(PRIVATE_MESSAGES &msgs) { msgs.neverdelete(); msgs.add (&priv->msg); } /* Send a message to all threads listening on this folder or sub-folder */ PUBLIC VIRTUAL void FOLDER_VIEW::wakeup() { PRIVATE_MESSAGES msgs; getmsgs(msgs); dialog_sendmessages(msgs); } /* Set all messages to deleted or not */ PUBLIC void FOLDER_VIEW::set_deleted(bool val) { MAIL_MESSAGES tbs; int rev = -1; loadindex (tbs,rev); for (int i=0; iset_deleted(val); } } /* Set all messages to tagged or not */ PUBLIC void FOLDER_VIEW::set_tagged(bool val) { MAIL_MESSAGES tbs; int rev = -1; loadindex (tbs,rev); for (int i=0; iset_tagged(val); } } /* Get the usage count of the folder */ PUBLIC VIRTUAL int FOLDER_VIEW::getusage() { return priv->usage; } /* Increment the usage count of the folder */ PUBLIC VIRTUAL void FOLDER_VIEW::incrusage() { priv->usage++; // fprintf (stderr,"incr folder %s %d\n",gettitle(),priv->usage); } /* Decrement the usage count of the folder */ PUBLIC VIRTUAL void FOLDER_VIEW::decrusage() { if (priv->usage == 0){ fprintf (stderr,"FOLDER_VIEW: usage count going below 0, not good\n"); }else{ priv->usage--; } // fprintf (stderr,"decr folder %s %d\n",gettitle(),priv->usage); } /* Append a message at the end of a folder Return -1 if error or not applicable. */ PUBLIC VIRTUAL int FOLDER_VIEW::appendmsg (MAIL_MESSAGE_FULL &msg) { SSTRING id; getid(id); xconf_error (MSG_U(E_NOAPPEND ,"Can't append message to folder %s.\n" "Not applicable"),id.get()); return -1; } PUBLIC VIRTUAL int FOLDER_FILE::appendmsg (MAIL_MESSAGE_FULL &full) { glocal int ret = -1; glocal MAIL_MESSAGE_FULL *full = &full; (path.get()); FILE *fout = fopen (path,"a"); if (fout != NULL){ fputs (glocal.full->header.get(),fout); fputs ("\n",fout); fputs (glocal.full->text.get(),fout); if (fclose(fout)!=-1){ glocal.ret = 0; } }else{ xconf_error (MSG_U(E_CANTOPENAPPEND ,"Can't open folder %s in append mode (%s)\n") ,path,strerror(errno)); } xconf_error(MSG_U(E_CANTLOCK,"Can't lock folder %s") ,path); return glocal.ret; } PUBLIC FOLDER_FILE::FOLDER_FILE (const char *_path) { folder_2abs (_path,path); last = 0; size = 0; revision = 0; lastupdate = time(NULL); } PUBLIC void FOLDER_FILE::getupdmsgs (PRIVATE_MESSAGES &upds) { upds.add (&updmsg); } PUBLIC void FOLDER_FILE::checkupdate () { struct stat st; if (stat(path.get(),&st)!=-1 && (st.st_mtime != last || st.st_size != size)){ dialog_sendmessage (updmsg); } } /* Return true if the folder contained postponed messages */ PUBLIC bool FOLDER_FILE::is_postponed() { return path.strstr (K_POSTPONED)!=NULL; } /* Return true if this is a shared folder */ PUBLIC bool FOLDER_FILE::is_shared() { return false; } /* Delete one message. Keep it alive if it is still in use */ PUBLIC void FOLDER_FILE::forget (int no) { MAIL_MESSAGE *m = tb.getitem(no); if (m->getusage()>0){ m->assign (NULL); folders_register(m); tb.remove (m); }else{ tb.remove_del (no); } } /* Return an int telling how much time the folder was reloaded */ PUBLIC int FOLDER_FILE::getrevision() { return revision; } /* Remember the state of the folder to know if it has changed quickly. */ PUBLIC void FOLDER_FILE::setstate () { struct stat st; if (stat(path.get(),&st)!=-1){ last = st.st_mtime; size = st.st_size; } } /* Indicate that the folder content has changed, so loadindex knows to update sub-views */ PUBLIC void FOLDER_FILE::incrrevision() { revision++; } /* Load the index of the messages in idx. Return -1 if the folder did not changed since the last time (and idx is not empty). Return the number of message placed in idx otherwise. */ PUBLIC int FOLDER_FILE::loadindex (MAIL_MESSAGES &idx, int &callerrev) { int ret = -1; idx.neverdelete(); struct stat st; if (stat(path.get(),&st)!=-1 && (st.st_mtime != last || st.st_size != size)){ { // Make sure that the our changes are commited to the foler // before reloading it for (int i=0; iwas_modified()){ update (false); break; } } } setstate (); // Delete all message, keep the one in use for (int i=tb.getnb()-1; i>=0; i--) forget(i); n_debug ("Reading folder %s\n",path.get()); file_folder (path.get(),tb); incrrevision(); for (int i=0; iassign(this); } if (idx.getnb() == 0 || revision != callerrev){ callerrev = revision; idx.remove_all(); for (int i=0; isubject.strstr("FOLDER INTERNAL DATA")!=NULL) continue; idx.add (m); } ret = idx.getnb(); } return ret; } /* Return the usage count of the folder + the usage count of any mail it contains */ PUBLIC int FOLDER_FILE::getusage() { int ret = priv->usage; if (ret == 0){ for (int i=0; igetusage(); } return ret; } /* Format the Status: line written in the folder */ static int folder_formatstatus (MAIL_MESSAGES *tb, int nomsg, char *newline) { int ret = -1; MAIL_MESSAGE *m = tb->getitem(nomsg); if (m != NULL){ strcpy (newline,"Status: "); if (m->replied) strcat (newline,"A"); if (m->deleted) strcat (newline,"D"); if (m->viewed) strcat (newline,"R"); if (m->tagged) strcat (newline,"T"); strcat (newline,"\n"); ret = 0; } return ret; } /* Format the X-tlmpmail: line written in the folder */ static void folder_formatflags (MAIL_MESSAGE *m, char *newline) { newline[0] = '\0'; if (m != NULL && m->flags.getnb()>0){ strcpy (newline,"X-tlmpmail:"); for (int i=0; iflags.getnb(); i++){ const char *var = m->flags.get_var(i); const char *val = m->flags.get_val(i); if (val[0] != '\0'){ strcat (newline," "); strcat (newline,var); strcat (newline,"="); strcat (newline,val); } } strcat (newline,"\n"); } } /* Format the X-tlmpmail: line written in the folder */ static void folder_formatflags (MAIL_MESSAGES *tb, int nomsg, char *newline) { newline[0] = '\0'; MAIL_MESSAGE *m = tb->getitem(nomsg); folder_formatflags (m,newline); } /* Update the header with the flags */ PUBLIC void MAIL_MESSAGE_FULL::updateheader() { SSTRINGS tbl; header.cnv2lines (tbl); int n = tbl.getnb(); bool skipcont = false; // Skip continuation line header.setempty(); for (int i=0; iget(); if (isspace(*s)){ if (!skipcont) header.appendf ("%s\n",s); }else{ skipcont = false; if (strncmp(s,"X-tlmpmail:",11)==0){ skipcont = true; }else{ header.appendf ("%s\n",s); } } } char newline[10000]; folder_formatflags (this,newline); if (newline[0] != '\0') header.append (newline); } /* Format the X-comment: line written in the folder. It generates several lines in fact. */ static void folder_formatcomment (MAIL_MESSAGES *tb, int nomsg, char *newline) { newline[0] = '\0'; MAIL_MESSAGE *m = tb->getitem(nomsg); if (m != NULL && m->comment.is_filled()){ // Fold the comment in several line. The next lines must start // with a space. This way the X-comment: tag may span multiple lines // in the folder. strcpy (newline,"X-comment:"); char *dst = newline + 10; *dst++ = ' '; const char *pt = m->comment.get(); while (*pt != '\0'){ char carac = *pt++; if (carac == '\n'){ *dst++ = '\n'; if (*pt == '\0'){ break; }else{ *dst++ = ' '; } }else{ *dst++ = carac; if (*pt == '\0'){ *dst++ = '\n'; } } } *dst = '\0'; } } PUBLIC int FOLDER_FILE::update (bool clean) { FOLDER_FILE *folder; MAIL_MESSAGES *tb; bool clean; bool outputon; // Are we skipping a deleted message const char *path; int ret; /* The update function is called every 3 seconds. This is too demanding for a flat file folder. Unless clean is true, we wait 30 seconds between updates. */ time_t now = time(NULL); if (!clean && now - lastupdate < 30) return 0; glocal.clean = clean; glocal.outputon = true; glocal.path = path.get(); glocal.folder = this; glocal.tb = &tb; bool update_needed = false; for (int i=0; ideleted){ update_needed = true; break; } }else if (m->was_modified()){ update_needed = true; break; } } glocal.ret = -1; if (!update_needed){ glocal.ret = 0; }else{ (path.get()); FILE *tmpout; int nomsg; bool inhead; bool status_seen; bool tlmpmail_seen; bool comment_seen; bool in_comment; MAIL_MESSAGE *lastmsg; char tmppath[]="/tmp/tlmpmail.XXXXXX"; { int fd = mkstemp (tmppath); glocal.tmpout = fd == -1 ? NULL : fdopen (fd,"r+"); } if (glocal.tmpout == NULL){ xconf_error (MSG_U(E_OPENTMP ,"Can't open temporary file (%s)") ,strerror(errno)); }else{ glocal.nomsg = -1; glocal.inhead = false; glocal.lastmsg = NULL; n_debug ("Updating folder %s\n",glocal.path); (glocal.path,false); char newline[10000]; if (strncmp(line,"From ",5)==0){ glocal.nomsg++; glocal.inhead = true; glocal.status_seen = false; glocal.tlmpmail_seen = false; glocal.comment_seen = false; glocal.in_comment = false; long filepos = ftell(glocal.tmpout); if (glocal.lastmsg != NULL){ glocal.lastmsg->size = filepos - glocal.lastmsg->filepos; } MAIL_MESSAGE *m = glocal.tb->getitem(glocal.nomsg); if (m != NULL) m->filepos = filepos; glocal.lastmsg = m; if (glocal.clean){ glocal.outputon = m == NULL || !m->deleted; if (glocal.outputon){ fputs (line,glocal.tmpout); } }else{ fputs (line,glocal.tmpout); } }else if (glocal.outputon){ if (glocal.inhead){ if (line[0] == '\n' && line[1] == '\0'){ glocal.inhead = false; if (!glocal.status_seen){ if (folder_formatstatus(glocal.tb,glocal.nomsg,newline)!=-1){ fputs (newline,glocal.tmpout); } } if (!glocal.tlmpmail_seen){ folder_formatflags (glocal.tb,glocal.nomsg,newline); fputs (newline,glocal.tmpout); } if (!glocal.comment_seen){ folder_formatcomment (glocal.tb,glocal.nomsg,newline); fputs (newline,glocal.tmpout); } }else if (glocal.in_comment && isspace(line[0])){ newline[0] = '\0'; line = newline; }else{ glocal.in_comment = false; if (strncmp(line,"Status:",7)==0){ glocal.status_seen = true; if (folder_formatstatus(glocal.tb,glocal.nomsg,newline)!=-1){ line = newline; } }else if (strncmp(line,"X-tlmpmail:",11)==0){ glocal.tlmpmail_seen = true; folder_formatflags (glocal.tb,glocal.nomsg,newline); line = newline; }else if (strncmp(line,"X-comment:",10)==0){ // A X-comment may span several lines // glocal.in_comment is there to wipe the continuation line // since we generate all the comment lines at once. glocal.comment_seen = true; folder_formatcomment (glocal.tb,glocal.nomsg,newline); line = newline; glocal.in_comment = true; } } } fputs (line,glocal.tmpout); } return 0; if (glocal.lastmsg != NULL){ long filepos = ftell(glocal.tmpout); glocal.lastmsg->size = filepos - glocal.lastmsg->filepos; } { FILE *fout = fopen (glocal.path,"w"); char buf[4096]; fseek (glocal.tmpout,0,SEEK_SET); int nb; while ((nb=fread(buf,1,sizeof(buf),glocal.tmpout))>0){ fwrite (buf,1,nb,fout); } glocal.ret = fclose (fout); unlink (tmppath); glocal.folder->setstate (); fclose (glocal.tmpout); for (int i=0; igetnb(); i++){ MAIL_MESSAGE *m = glocal.tb->getitem(i); if (glocal.clean && m->deleted){ glocal.folder->forget (i); i--; }else{ m->rstmodified(); } } if (glocal.clean){ // Other views must update glocal.folder->incrrevision(); glocal.folder->wakeup(); } } } } lastupdate = time(NULL); return glocal.ret; } PUBLIC void MAIL_MESSAGE::copy (MAIL_MESSAGE &full) const { full.fid = fid; full.uuid = uuid; full.deleted = deleted; full.replied = replied; full.viewed = viewed; full.tagged = tagged; full.attrib.remove_all(); full.attrib.append (attrib); full.from = from; full.to = to; full.cc = cc; full.reply = reply; //full.name = name; full.subject = subject; full.date = date; full.id = id; full.references = references; full.replyid = replyid; full.filepos = filepos; full.size = size; full.comment.setfrom (comment); full.flags = flags; } PUBLIC void MAIL_MESSAGE_FULL::parse_header() { SSTRINGS lines; received.setempty(); content_type.setempty(); content_encoding.setempty(); content_description.setempty(); SSTRING *lastline = NULL; int n = str_cnv2lines (header.get(),lines); for (int i=0; iget(); if (isspace (line[0])){ if (lastline != NULL){ lastline->append (line+1); } }else if (strncasecmp(line,"Received:",9)==0){ // We accumulate only the first received if (received.is_empty()){ lastline = &received; lastline->setfrom (str_skip(line+9)); } }else if (strncasecmp(line,"Content-Type:",13)==0){ lastline = &content_type; lastline->setfrom(str_skip(line+13)); }else if (strncasecmp(line,"Content-Transfer-encoding:",26)==0){ lastline = &content_encoding; lastline->setfrom(str_skip(line+26)); }else if (strncasecmp(line,"Content-Description:",20)==0){ lastline = &content_description; lastline->setfrom(str_skip(line+20)); }else{ lastline = NULL; } } SSTRING tmp; mime_splittype (content_type.get(),tmp,charset); } PUBLIC int FOLDER_FILE::loadmsg ( const MAIL_MESSAGE &msg, MAIL_MESSAGE_FULL &full) { msg.copy (full); FILE *fin = fopen (path.get(),"r"); if (fin != NULL){ if (fseek (fin,full.filepos,SEEK_SET)!=-1){ char *buf = (char*)malloc_err(msg.size+1); if (fread (buf,1,msg.size,fin)==(size_t)msg.size){ buf[msg.size] = '\0'; char *pt = buf; while (*pt != '\0'){ if (*pt == '\n' && pt[1] == '\n'){ pt[1] = '\0'; pt+=2; break; } pt++; } full.text.setfrom (pt); full.header.setfrom (buf); } free (buf); } fclose (fin); full.parse_header(); } return 0; } PUBLIC const char *FOLDER_FILE::gettitle () { return folder_gettitle(path.get()); } PUBLIC int FOLDER_FILE::getid (SSTRING &id) { id.setfrom (path); return 0; } PRIVATE void FOLDER_FILTER::init (FOLDER_VIEW *_folder) { folder = _folder; folder->incrusage(); filter.init(); negative = false; } PRIVATE void FOLDER_FILTER::setfilter( FILTER_ON cmd, FILTER_SCOPE scope, const char *_key) { static const char *tbt[]={ MSG_U(I_FROM,"From"), MSG_U(I_TO,"To"), MSG_U(I_CC,"Cc"), MSG_U(I_SUBJECT,"Subject"), MSG_U(I_BODY,"Body"), MSG_U(I_HEADER,"Header"), MSG_U(I_ON_VIEWED,"Viewed"), MSG_U(I_ON_REPLIED,"Replied"), MSG_U(I_ON_DELETED,"Deleted"), MSG_U(I_ON_TAGGED,"Tagged"), MSG_U(I_ON_TODAY,"Today"), MSG_U(I_ON_THISWEEK,"This week"), MSG_U(I_ON_THISMONTH,"This month"), MSG_U(I_ON_MSGTYPE,"Qualified"), MSG_U(I_ON_PROJECT,"Assigned"), MSG_U(I_ON_COMMENT,"Commented"), }; title.setfromf ("%s:%s",folder->gettitle(),tbt[cmd]); filter.set (cmd,scope,_key); } PUBLIC FOLDER_FILTER::FOLDER_FILTER ( FOLDER_VIEW *_folder, FILTER_ON cmd, FILTER_SCOPE scope, const char *_key) { init (_folder); setfilter (cmd,scope,_key); } PUBLIC FOLDER_FILTER::FOLDER_FILTER ( FOLDER_VIEW *_folder, FILTER_ON cmd, const char *_key) { init (_folder); setfilter(cmd,FILTER_SCOPE_FULL,_key); } /* Set the title of the folder */ PUBLIC void FOLDER_FILTER::settitle (const char *s) { title.setfrom (s); } PUBLIC FOLDER_FILTER::FOLDER_FILTER (FOLDER_VIEW *_folder) { init (_folder); format_title(); } PUBLIC FOLDER_FILTER::~FOLDER_FILTER () { folder->decrusage(); } PUBLIC int FOLDER_FILTER::getrevision() { return folder->getrevision(); } PUBLIC int FOLDER_FILTER::loadindex (MAIL_MESSAGES &idx, int &callerrev) { if (filter.status_sensitive()) callerrev = -1; n_debug ("FILTER::loadindex %d %d\n",callerrev,folder->getrevision()); if (folder->loadindex(idx,callerrev) != -1){ n_debug ("FILTER::loadindex scanning\n"); filter.setup(); for (int i=0; iupdate(clean); } PUBLIC int FOLDER_FILTER::loadmsg ( const MAIL_MESSAGE &msg, MAIL_MESSAGE_FULL &full) { return folder->loadmsg (msg,full); } PUBLIC const char *FOLDER_FILTER::gettitle () { return title.get(); } /* A filter built from a folder with an id and a named filter has an id itself. We are able to reload this folder using the virtual_eval() function. */ PUBLIC int FOLDER_FILTER::getid (SSTRING &_id) { int ret = -1; if (id.is_filled()){ _id.setfrom (id); ret = 0; } return ret; } PUBLIC bool FOLDER_FILTER::is_postponed() { return folder->is_postponed(); } /* Return true if this is a shared folder */ PUBLIC bool FOLDER_FILTER::is_shared() { return folder->is_shared(); } /* Get the PRIVATE_MESSAGE used to wakeup folder browser threads All views of a folder are sharing the same message, this way they are all updated if one message change. */ PUBLIC void FOLDER_FILTER::getmsgs(PRIVATE_MESSAGES &msgs) { msgs.neverdelete(); folder->getmsgs(msgs); msgs.add (&priv->msg); } /* Increment the usage count of the folder */ PUBLIC void FOLDER_FILTER::incrusage() { priv->usage++; } /* Decrement the usage count of the folder */ PUBLIC void FOLDER_FILTER::decrusage() { if (priv->usage == 0){ fprintf (stderr,"FOLDER_VIEW: usage count going below 0, not good\n"); }else{ priv->usage--; } } /* Set the "from" field of the filter */ PUBLIC void FOLDER_FILTER::setfrom( const char *_from, // String to match FILTER_SCOPE scope) { filter.setfrom (_from,scope); } /* Set the "to" field of the filter */ PUBLIC void FOLDER_FILTER::setto( const char *_to, // String to match FILTER_SCOPE scope) { filter.setto (_to,scope); } /* Set the "subject" field of the filter */ PUBLIC void FOLDER_FILTER::setsubject( const char *_subject, // String to match FILTER_SCOPE scope) { filter.setsubject (_subject,scope); } /* Setup the input field with a help list */ static void folder_view_combo( DIALOG &dia, SSTRING &s, const char *key, const char *ftitle) { SSTRINGS values; linuxconf_getall (key,K_INDEX,values,false); values.sort(); FIELD_COMBO *comb = dia.newf_combo (ftitle,s); for (int i=0; iaddopt (values.getitem(i)->get()); } } /* Setup the input field for a message type */ void folder_view_fieldtype( DIALOG &dia, SSTRING &type) { folder_view_combo (dia,type,K_MSGTYPE,MSG_U(F_MSGTYPE,"Message type")); } /* Setup the input field for a message project */ void folder_view_fieldproject( DIALOG &dia, SSTRING &project) { folder_view_combo (dia,project,K_PROJECT,MSG_U(F_ASSIGNPROJECT,"Assigned project")); } /* Add a string in the resource file if missing */ static void folder_view_updatetb(const char *s, const char *key) { if (s != NULL && s[0] != '\0'){ SSTRINGS tb; linuxconf_getall (key,K_INDEX,tb,true); if (tb.lookup(s)==-1){ tb.add (new SSTRING(s)); linuxconf_replace (key,K_INDEX,tb); linuxconf_save(); } } } /* Add a message type in the resource file if missing */ void folder_view_updatetype(const char *type) { folder_view_updatetb(type,K_MSGTYPE); } /* Add a project in the resource file if missing */ void folder_view_updateproject(const char *prj) { folder_view_updatetb(prj,K_PROJECT); } PUBLIC int FOLDER_FILTER::editfilter() { int ret = filter.edit(); if (ret != -1){ title.setfromf ("%s:%s",folder->gettitle(),filter.getname()); } return ret; } PUBLIC int FOLDER_FILTER::editqfilter() { int ret = filter.quickedit(); if (ret != -1){ title.setfromf ("%s:%s",folder->gettitle(),filter.getname()); } return ret; } PRIVATE void FOLDER_FILTER::format_title() { title.setfromf ("%s:%s",folder->gettitle(),filter.getname()); } /* Load a preset filter definition */ PUBLIC void FOLDER_FILTER::loaddef(const char *filtername) { filter.loaddef(filtername); format_title(); SSTRING tmp; if (folder->getid(tmp)!=-1){ id.setfromf ("%s:%s%s",tmp.get() ,negative ? "!" : "" ,filtername); } } /* Set the filter mode. Negative means that non matching messages will be presented. */ PUBLIC void FOLDER_FILTER::setmode (bool _negative) { negative = _negative; } PUBLIC void FOLDER_FILTER::getupdmsgs (PRIVATE_MESSAGES &upds) { folder->getupdmsgs(upds); } PUBLIC void FOLDER_FILTER::checkupdate () { folder->checkupdate(); }