#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tlmpmail.h" #include "tlmpmail.m" #include "keys.h" bool debug = false; void n_debug (const char *ctl, ...) { if (debug){ va_list list; va_start (list,ctl); vfprintf (stderr,ctl,list); va_end (list); } } void *malloc_err (int size) { void *ret = malloc (size); if (ret == NULL){ tlmp_error (MSG_U(E_NOMEM,"Out of memory")); exit (-1); } return ret; } void _F_maillock::fail(const char *path) { xconf_error (MSG_U(E_NOLOCK,"Can't lock folder file %s"),path); } int _F_maillock::create(const char *path) { return open (path,O_RDWR|O_EXCL|O_CREAT,0600); } /* Set a lock on a mail folder */ int maillock (_F_maillock &c, const char *path) { int ret = -1; { // Create the directory as needed char tmp[strlen(path)+1]; strcpy (tmp,path); char *pt = strrchr(tmp,'/'); if (pt != NULL){ *pt = '\0'; file_mkdirp (tmp,-1,-1,-1); } } int fd = open (path,O_RDONLY,0); if (fd == -1 && errno == ENOENT){ fd = c.create(path); } if (fd == -1){ c.fail (path); }else{ int i; for (i=0; i<5; i++){ if (flock(fd,LOCK_EX)!=-1){ c.ok(path); flock(fd,LOCK_UN); ret = 0; break; }else{ sleep(1); } } close (fd); if (i==5){ c.fail(path); } } return ret; } /* Copy a message to another folder, selected by the user. The message is appended to the folder. Return -1 if the message is not copied properly. */ int mail_copy (MAIL_MESSAGE &msg) { MAIL_MESSAGE *msg; int ret; SSTRING relpath; glocal.msg = &msg; glocal.ret = -1; if (folder_pick(relpath)!=-1){ SSTRING path; // Make sure the target folder is updated on disk // since it may be in another view. folders_update(relpath.get(),false); folder_2abs (relpath.get(),path); bool is_deleted = msg.deleted; bool is_tagged = msg.tagged; msg.set_deleted (false); msg.set_tagged (false); msg.update_folder(); MAIL_MESSAGE_FULL full; if (glocal.msg->loadmsg(full)!=-1){ FOLDER_VIEW *f = folders_alloc (path.get()); if (f != NULL){ f->appendmsg (full); } msg.set_deleted (is_deleted); msg.set_tagged (is_tagged); } } return glocal.ret; } /* Find the path of the personal mail folder (HOME/mail) */ void folder_getmaildir (SSTRING &s) { const char *home = getenv ("HOME"); if (home != NULL) s.setfromf ("%s/mail",home); } /* Convert a folder path to absolute path */ void folder_2abs (const char *_path, SSTRING &path) { if (_path[0] != '/'){ SSTRING dir; folder_getmaildir(dir); path.setfromf ("%s/%s",dir.get(),_path); }else{ path.setfrom (_path); } } /* Return the title of a folder according to its path */ const char *folder_gettitle(const char *path) { const char *ret = path; if (strcmp(path,prefs_getinbox())==0){ ret = MSG_R(I_INBOX); }else{ SSTRING maildir; folder_getmaildir(maildir); maildir.append ("/"); int len = maildir.getlen(); if (maildir.ncmp(path,len)==0){ ret = path + len; } } return ret; } static const char *tbmon[]={ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep" ,"Oct","Nov","Dec" }; /* Convert a mail date into time_t the format is DAY, day_of_month month year hh:mm:ss +/-offset */ time_t miscmail_parsedate (const char *line) { time_t ret = 0; line = str_skip(line); while (!isdigit(*line) && *line > ' ') line++; line = str_skip(line); if (isdigit(*line)){ int mday = atoi(line); line = str_skipdig(line); line = str_skip(line); int mon = 0; for (int i=0; i<12; i++){ if (strncasecmp(line,tbmon[i],3)==0){ mon = i; break; } } line += 3; line = str_skip(line); int year = atoi(line); if (year == 0){ year = 2000; }else if (year < 50){ year += 2000; }else if (year < 1000){ year += 1900; } line = str_skipdig(line); line = str_skip(line); int hour = atoi(line); int minu = atoi(line+3); int sec = atoi(line+6); while (*line > ' ') line++; struct tm t; t.tm_isdst = -1; t.tm_year = year - 1900; t.tm_mon = mon; t.tm_mday = mday; t.tm_hour = hour; t.tm_min = minu; t.tm_sec = sec; ret = mktime (&t); } return ret; } /* Convert a date in yyyy/mm/dd format to unix time_t format */ time_t miscmail_date2sec (const char *line) { struct tm t; t.tm_isdst = -1; t.tm_year = atoi(line) - 1900; t.tm_mon = atoi(line+5)-1; t.tm_mday = atoi(line+8); t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; return mktime (&t); } /* Format a date (time_t) in mail format */ void miscmail_formatdate (time_t date, SSTRING &s) { struct tm *t = localtime (&date); static const char *tbday[]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; char diffstr[10]; if (timezone == 0){ diffstr[0] = '\0'; }else{ int tmz = timezone; if (daylight) tmz -= 3600; const char *neg = tmz > 0 ? "-" : ""; long tm = abs(tmz); sprintf (diffstr," %s%04ld",neg,tm/36); } s.setfromf ("%s, %d %s %d %02d:%02d:%02d%s" ,tbday[t->tm_wday],t->tm_mday,tbmon[t->tm_mon],t->tm_year+1900 ,t->tm_hour,t->tm_min,t->tm_sec,diffstr); } void miscmail_editsignature() { const char *home = getenv ("HOME"); if (home != NULL){ char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/.signature",home); SSTRING text; (path,false); glocal.text.append (line); return 0; DIALOG dia; dia.setcontext (NULL); dia.newf_textarea (NULL,glocal.text,80,25); int nof = 0; if (dia.edit(MSG_U(T_SIGNATURE,"Signature"),"",help_nil,nof) == MENU_ACCEPT){ (path,false); fputs (glocal.text.get(),fout); return 0; } } } /* Create a small dialog to ask the user if we must overwrite or append to a given file. */ bool miscmail_filemode (const char *path, bool &mode) { bool ret = false; if (!file_exist(path)){ ret = true; mode = true; }else{ DIALOG dia; dia.settype (DIATYPE_POPUP); char oper=1; dia.newf_radio ("",oper,0,MSG_U(I_OVERWRITE,"Overwrite file")); dia.newf_radio ("",oper,1,MSG_U(I_APPEND,"Append")); SSTRING intro; intro.setfromf (MSG_U(I_FILEEXIST ,"File %s exist\n" "We may either overwrite it (and loose the old content)\n" "or append at the end"),path); if (dia.edit (MSG_U(T_FILEEXIST,"File exist"),intro.get(),help_nil) == MENU_ACCEPT){ mode = oper == 1; ret = true; } } return ret; } /* Dialog to save a message */ void miscmail_save (MAIL_MESSAGE_FULL &msg) { char header,body; MAIL_MESSAGE_FULL *msg; glocal.header = glocal.body = 1; glocal.msg = &msg; DIALOG dia; dia.settype (DIATYPE_POPUP); dia.newf_chk (MSG_U(F_INCLUDE,"Include"),glocal.header,MSG_U(I_INCLHEADER,"message header")); dia.newf_chk ("",glocal.body,MSG_U(I_INCLBODY,"message body")); SSTRING file; dia.newf_str (MSG_U(F_FILENAME,"Save in file"),file); dia.last_noempty(); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_SAVE,"Saving to file") ,"",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ bool mode = false; const char *f = file.get(); if (miscmail_filemode (f,mode)){ (f,mode); if (glocal.header){ // When we save the header and the body // We save it as is. // When we save the body only, we do // the mime translation fputs (glocal.msg->header.get(),fout); fputs ("\n",fout); if (glocal.body){ fputs (glocal.msg->text.get(),fout); } }else if (glocal.body){ FILE *fout; glocal.fout = fout; (*glocal.msg,true,false); fprintf (glocal.fout,"\n%s\n",info.body); } return 0; break; } } } } /* Dialog to save a message */ int miscmail_print (MAIL_MESSAGE_FULL &msg) { int ret = -1; DIALOG dia; dia.settype (DIATYPE_POPUP); SSTRINGS tb; linuxconf_getall (K_PRINTCMD,K_INDEX,tb,true); SSTRING cmd; if (tb.getnb()>0) cmd.setfrom (tb.getitem(0)->get()); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_PRINTCMD,"Print command"),cmd,50); for (int i=0; iaddopt(tb.getitem(i)->get()); dia.last_noempty(); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_PRINTING,"Printing a message") ,"",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ // Make the the current comment is at the top of the list int pos = tb.lookup(cmd.get()); tb.remove_del(pos); tb.insert (0,new SSTRING(cmd)); linuxconf_replace (K_PRINTCMD,K_INDEX,tb); linuxconf_save(); MAIL_MESSAGE_FULL *msg; SSTRING output; bool some_errors; glocal.some_errors = false; glocal.msg = &msg; (cmd.get(),true); FILE *fout; glocal.fout = fout; fprintf (fout,"%s: %s <%s>\n",MSG_R(F_FROM) ,glocal.msg->get_from() ,glocal.msg->getemail_from()); fprintf (fout,"%s: %s\n",MSG_R(F_SUBJECT) ,glocal.msg->getsubject()); (*glocal.msg,true,true); fprintf (glocal.fout,"\n%s\n",info.body); close_input(); glocal.output.appendf (" %s\n",line); return 0; glocal.output.appendf ("*** %s\n",line); glocal.some_errors = true; return 0; if (glocal.output.is_filled()){ xconf_notice (MSG_U(N_PRINTCMD ,"The command \"%s\" produced\n" "the following output:\n\n%s") ,cmd.get() ,glocal.output.get()); } if (!glocal.some_errors){ ret = 0; break; } } } return ret; } /* Format text with automatic paragraph generation for long line and support for carriage return */ void miscmail_formattext (const char *s, SSTRING &dst) { const char *start = s; const char *startline = s; while (*s != '\0'){ if (*s == '\n' || *s == '\r'){ // dst.append (start,(int)(s-start)); // dst.append ("\n"); s++; startline = s; }else if (isspace(*s) && (int)(s-startline) > 80){ dst.append (start,(int)(s-start)); dst.append ("\n"); s++; start = s; startline = s; }else{ s++; } } dst.append (start,(int)(s-start)); } /* Pick and classify the addresses used to reply to a mail */ int miscmail_pickaddrs ( MAIL_MESSAGE &msg, EMAILADDRS &to, EMAILADDRS &cc, EMAILADDRS &bcc) { int ret = -1; DIALOG dia; dia.settype (DIATYPE_POPUP); EMAILADDRS tmp; tmp.append (msg.reply); int pos_from = tmp.getnb(); tmp.append_nodup (msg.from); int pos_to = tmp.getnb(); tmp.append_nodup (msg.to); int pos_cc = tmp.getnb(); tmp.append_nodup (msg.cc); SSTRINGS aliases; prefs_getmyaddrs(aliases); dia.gui_label (""); dia.gui_label (MSG_U(I_NAME,"Name")); dia.gui_label (MSG_U(I_ADDRESS,"Address")); dia.gui_label ("\"%s\"",MSG_U(I_IN_TO,"in To")); dia.gui_label ("\"%s\"",MSG_U(I_IN_CC,"in Cc")); dia.gui_label ("\"%s\"",MSG_U(I_IN_BCC,"in Bcc")); dia.gui_label ("\"%s\"",MSG_U(I_IGNORE,"ignore")); dia.newline(); char tbsel[tmp.getnb()+1]; memset (tbsel,0,sizeof(tbsel)); tbsel[0] = 1; for (int i=0; iget(),name); mime_translate (tmp.addrs.getitem(i)->get(),addr); if (aliases.lookup(addr.get())!=-1){ tmp.names.remove_del(i); tmp.addrs.remove_del(i); if (pos_from >= i) pos_from--; if (pos_to >= i) pos_to--; if (pos_cc >= i) pos_cc--; i--; }else{ if (i == pos_cc){ dia.gui_label ("\"%s\"",MSG_R(F_CC)); }else if (i==pos_to){ dia.gui_label ("\"%s\"",MSG_R(F_TO)); }else if (i==pos_from){ dia.gui_label ("\"%s\"",MSG_R(F_FROM)); }else if (i==0){ dia.gui_label ("\"%s\"",MSG_U(F_REPLY,"Reply")); }else{ dia.gui_label (""); } dia.newf_info (name.get(),addr.get()); dia.newf_radio(NULL,tbsel[i],1,""); dia.gui_dispolast (GUI_H_CENTER,1,GUI_V_CENTER,1); dia.newf_radio(NULL,tbsel[i],2,""); dia.gui_dispolast (GUI_H_CENTER,1,GUI_V_CENTER,1); dia.newf_radio(NULL,tbsel[i],3,""); dia.gui_dispolast (GUI_H_CENTER,1,GUI_V_CENTER,1); dia.newf_radio(NULL,tbsel[i],0,""); dia.gui_dispolast (GUI_H_CENTER,1,GUI_V_CENTER,1); dia.newline(); } } dia.setbutinfo (MENU_USR1,MSG_U(B_ALL,"All"),MSG_R(B_ALL)); dia.setbutinfo (MENU_USR2,MSG_R(B_CLEAR),MSG_R(B_CLEAR)); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_PICKADDRS,"Pick email addresses") ,MSG_U(I_PICKADDRS ,"Pick the addresses you want to use in the reply\n" "You can select if the address will be included in the\n" "to, cc or bcc field") ,help_nil,nof ,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_USR1 || code == MENU_USR2){ memset (tbsel,code == MENU_USR1 ? 2 : 0,sizeof(tbsel)); tbsel[0] = 1; dia.reload(); }else{ to.remove_all(); cc.remove_all(); bcc.remove_all(); for (int i=0; iget(); const char *addr = tmp.addrs.getitem(i)->get(); int sel = tbsel[i]; if (sel == 1){ to.set (name,addr); }else if (sel == 2){ cc.set (name,addr); }else if (sel == 3){ bcc.set (name,addr); } } if (to.getnb()==0){ xconf_error (MSG_U(E_NOADDRESS,"No address selected")); }else{ ret = 0; break; } } } return ret; } void miscmail_cnvhtml(const char *text, SSTRING &buf) { SSTRING *buf; const char *text; glocal.buf = &buf; glocal.text = text; SSTRING cmd; cmd.setfromf ("lynx -stdin -dump"); (cmd.get(),true); fputs (glocal.text,fout); close_input(); glocal.buf->appendf ("%s\n",line); return 0; }