static char rcsid[] = "@(#)$Id: canceled_mail.c,v 1.16 2006/07/23 08:47:23 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.16 $ $State: Exp $ * * Author: Kari Hurtta *****************************************************************************/ #include "def_messages.h" #include "s_elm.h" #include "s_me.h" DEBUG_VAR(Debug,__FILE__,"messages"); #if ANSI_C #define S_(x) static x; #else #define S_(x) #endif #include #ifndef ANSI_C extern int errno; #endif static struct MailboxView *canceled_mail_view = NULL; struct mv_canceldir { char * dirname; char * indexname; FILE * index_F; charset_t cs; time_t last_scan; time_t last_mtime; struct message_list { char * filename; struct header_rec REC; } * message_list; int message_list_len; int last_open_index; FILE * last_open_file; }; S_(mt_init_mailbox mt_init_canceldir) static void mt_init_canceldir P_((struct MailboxView *mbx)); static void mt_init_canceldir(mbx) struct MailboxView *mbx; { mbx->u.canceldir = safe_malloc(sizeof (* (mbx->u.canceldir))); /* bzero is defined hdrs/defs.h */ bzero((void *)mbx->u.canceldir, sizeof (* (mbx->u.canceldir))); mbx->u.canceldir->dirname = NULL; mbx->u.canceldir->indexname = NULL; mbx->u.canceldir->index_F = NULL; mbx->u.canceldir->cs = NULL; mbx->u.canceldir->last_scan = 0; mbx->u.canceldir->last_mtime = 0; mbx->u.canceldir->message_list = NULL; mbx->u.canceldir->message_list_len = 0; mbx->u.canceldir->last_open_index = -1; mbx->u.canceldir->last_open_file = NULL; } S_(mt_free_mailbox mt_free_canceldir) static void mt_free_canceldir P_((struct MailboxView *mbx)); static void mt_free_canceldir(mbx) struct MailboxView *mbx; { if (mbx->u.canceldir->dirname) { free(mbx->u.canceldir->dirname); mbx->u.canceldir->dirname = NULL; } if (mbx->u.canceldir->indexname) { free(mbx->u.canceldir->indexname); mbx->u.canceldir->indexname = NULL; } if (mbx->u.canceldir->index_F) { fflush(mbx->u.canceldir->index_F); Release_the_file(fileno(mbx->u.canceldir->index_F)); fclose(mbx->u.canceldir->index_F); mbx->u.canceldir->index_F = NULL; } mbx->u.canceldir->cs = NULL; if (mbx->u.canceldir->message_list) { int i; for (i = 0; i < mbx->u.canceldir->message_list_len; i++) { if (mbx->u.canceldir->message_list[i].filename) { free (mbx->u.canceldir->message_list[i].filename); mbx->u.canceldir->message_list[i].filename = NULL; } header_clear ( & ( mbx->u.canceldir->message_list[i].REC) ); } free(mbx->u.canceldir->message_list); mbx->u.canceldir->message_list = NULL; } mbx->u.canceldir->message_list_len = 0; mbx->u.canceldir->last_open_index = -1; if (mbx->u.canceldir->last_open_file) { fclose(mbx->u.canceldir->last_open_file); mbx->u.canceldir->last_open_file = NULL; } free(mbx->u.canceldir); mbx->u.canceldir = NULL; /* Reset canceled_mail_view -pointer if we are going erase it */ if (canceled_mail_view == mbx) canceled_mail_view = NULL; } static void mt_make_canceldir_view P_((struct MailboxView *mailbox)); S_(mt_add_mailbox_storage mt_add_canceldir_storage) static void mt_add_canceldir_storage P_((struct MailboxView *mailbox, struct current_storage *storage)); static void mt_add_canceldir_storage(mailbox,storage) struct MailboxView *mailbox; struct current_storage *storage; { panic("MBX VIEW PANIC",__FILE__,__LINE__,"mt_add_canceldir_storage", "mt_add_canceldir_storage called",0); } static int process_index P_((struct MailboxView *view)); static void unlock_index P_((struct MailboxView *view)); static void unlock_index(view) struct MailboxView *view; { if (!view->u.canceldir->index_F) return; fflush(view->u.canceldir->index_F); Release_the_file(fileno(view->u.canceldir->index_F)); fclose(view->u.canceldir->index_F); view->u.canceldir->index_F = NULL; /* Mark -- no locked */ } static void update_index P_((struct MailboxView *v)); static void add_canceled_mail1 P_((char *filename, struct mailing_headers * headers, struct MailboxView *view, time_t X, int lines, header_list_ptr h, long body_offset)); static void move_olds P_((struct MailboxView *v)); #ifdef DIROPS #if DIROPS == USE_DIRENT #include #endif /* DIROPS == USE_DIRENT */ #if DIROPS == USE_SYSDIR #include #endif /* DIROPS == USE_SYSDIR */ static void update_index(v) struct MailboxView *v; { DIR * handle; struct stat dstat; time_t now = time(NULL); int unlock_it = 0; #if DIROPS == USE_DIRENT struct dirent * Xptr; #endif /* DIROPS == USE_DIRENT */ #if DIROPS == USE_SYSDIR struct direct * Xptr; #endif /* DIROPS == USE_SYSDIR */ if (0 != stat(v->u.canceldir->dirname,&dstat)) { DPRINT(Debug,1,(&Debug,"stat %s failed!\n", v->u.canceldir->dirname)); dstat.st_mtime = now; } if (dstat.st_mtime == v->u.canceldir->last_mtime && now < v->u.canceldir->last_scan + 60) { DPRINT(Debug,7,(&Debug,"scan %s skipped\n", v->u.canceldir->dirname)); return; } if (!v->u.canceldir->index_F) { if (! process_index(v)) return; unlock_it++; } v->u.canceldir->last_mtime = dstat.st_mtime; v->u.canceldir->last_scan = now; handle = opendir(v->u.canceldir->dirname); if (!handle) goto fail2; while (NULL != (Xptr = readdir(handle))) { if ('.' != Xptr->d_name[0]) { struct stat X; char *fullname; int fd; FILE *f1 = NULL; header_list_ptr h = NULL; long b; int lines = 0; int i,c; #if DIROPS == USE_DIRENT char * entryname = safe_strdup(Xptr->d_name); #endif /* DIROPS == USE_DIRENT */ #if DIROPS == USE_SYSDIR char * entryname = safe_malloc(Xptr->d_namlen+1); strncpy(entryname,Xptr->d_name,Xptr->d_namlen); entryname[Xptr->d_namlen] = '\0'; #endif /* DIROPS == USE_SYSDIR */ fullname = safe_strdup(v->u.canceldir->dirname); fullname = strmcat(fullname,"/"); fullname = strmcat(fullname,entryname); for (i = 0; i < v->u.canceldir->message_list_len; i++) if ( 0 == strcmp(entryname, v->u.canceldir->message_list[i].filename)) goto found; fd = open(fullname,O_RDONLY); if (fd < 0) goto fail1; if (-1 == fstat(fd,&X)) { close(fd); goto fail1; } if (X.st_uid != userid) { /* Wrong owner */ close(fd); goto fail1; } f1 = fdopen(fd,"r"); if (!f1) { close(fd); goto fail1; } h = file_read_headers(f1,RHL_CHECK_HEADER); b = ftell(f1); while (EOF != (c = getc(f1))) { if ('\n' == c) lines++; } add_canceled_mail1(entryname, NULL,v,X.st_mtime,lines,h,b); fail1: if (h) delete_headers(&h); if (f1) fclose(f1);; found: free(entryname); free(fullname); } } closedir(handle); if (v == canceled_mail_view) move_olds(v); fail2: if (unlock_it) unlock_index(v); } #else static void update_index(v) struct MailboxView *v; { /* EMPTY */ } #endif /* Return 1 if redraw required */ S_(mt_update_view_mailbox mt_update_view_canceldir) /* Return 1 if redraw required */ static int mt_update_view_canceldir P_((struct MailboxView *mailbox)); static int mt_update_view_canceldir(mailbox) struct MailboxView *mailbox; { int count; int i,x; update_index(mailbox); count = mailbox->u.canceldir->message_list_len; if (count != mailbox->view_len) { mt_make_canceldir_view(mailbox); DPRINT(Debug,7,(&Debug, "mt_update_view_canceldir=1 (%d messages)\n", mailbox->view_len)); return 1; } DPRINT(Debug,7,(&Debug, "mt_update_view_canceldir=0\n")); return 0; } S_(mt_get_main_mailbox_folder mt_get_main_canceldir_folder) static struct folder_info * mt_get_main_canceldir_folder P_((struct MailboxView *mailbox)); static struct folder_info * mt_get_main_canceldir_folder(mailbox) struct MailboxView *mailbox; { return NULL; } /* Can be called from signal handler */ S_(mt_get_mailbox_storage mt_get_canceldir_storage) /* Can be called from signal handler */ static struct current_storage * mt_get_canceldir_storage P_((struct MailboxView *mailbox, int i)); /* Can be called from signal handler */ static struct current_storage * mt_get_canceldir_storage(mailbox,i) struct MailboxView *mailbox; int i; { return NULL; } /* Can be called from signal handler */ S_(mt_get_mailbox_storage_count mt_get_canceldir_storage_count) /* Can be called from signal handler */ static int mt_get_canceldir_storage_count P_((struct MailboxView *mailbox)); /* Can be called from signal handler */ static int mt_get_canceldir_storage_count(mailbox) struct MailboxView *mailbox; { return 1; } static int update_open P_((struct mv_canceldir *d, int idx)); static int update_open(d,idx) struct mv_canceldir *d; int idx; { if (d->last_open_index != idx && d->last_open_file) { fclose(d->last_open_file); d->last_open_file = NULL; } if (!d->last_open_file) { char * filename = elm_message(FRM("%s/%s"), d->dirname, d->message_list[idx]. filename); int err = can_open(filename,"r"); if (err) { lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile, "Can't open %s!"), filename); free(filename); return 0; } d->last_open_file = fopen(filename,"r"); if (!d->last_open_file) { lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile, "Can't open %s!"), filename); free(filename); return 0; } d->last_open_index = idx; free(filename); } else rewind(d->last_open_file); return 1; } static int update_header P_((struct mv_canceldir *d, int idx)); static int update_header(d,idx) struct mv_canceldir *d; int idx; { struct header_rec *hdr = & (d->message_list[idx].REC); if (!update_open(d,idx)) { DPRINT(Debug,10,(&Debug,"update_header: %d -- failed\n", idx)); return 0; } if (! hdr->body_parsed) { header_list_ptr parsed_headers = file_read_headers(d->last_open_file,0); long content_start; if (!parsed_headers) { DPRINT(Debug,3,(&Debug, "update_header: No headers parsed! \n")); } read_folder_headers_helper(hdr,parsed_headers); header_parse_helper(hdr,parsed_headers); content_start = ftell(d->last_open_file); start_body_helper(hdr, content_start,parsed_headers); delete_headers(&parsed_headers); /* start_body_helper is called */ hdr->body_parsed = 1; } DPRINT(Debug,10,(&Debug,"update_header: %d -- OK\n", idx)); return 1; } S_(mt_give_header_mailbox mt_give_header_canceldir) static struct header_rec * mt_give_header_canceldir P_((struct MailboxView *mailbox, int index, struct folder_view *v)); static struct header_rec * mt_give_header_canceldir(mailbox,index,v) struct MailboxView *mailbox; int index; struct folder_view *v; { int mbx,idx; mbx = v->mailbox_number; if (mbx != 0) panic("MBX VIEW PANIC",__FILE__,__LINE__, "mt_give_header_canceldir", "Bad mailbox number",0); idx = v->index; if (idx < 0 || idx >= mailbox->u.canceldir->message_list_len) panic("MBX VIEW PANIC",__FILE__,__LINE__, "mt_give_header_canceldir", "Bad internal index",0); if (-1 == mailbox->u.canceldir->message_list[idx]. REC.mime_rec.begin_offset) update_header(mailbox->u.canceldir,idx); return & (mailbox->u.canceldir->message_list[idx].REC); } S_(sdt_give_header_s sdt_give_header_cm) static struct header_rec * sdt_give_header_cm P_((struct sort_data *s, struct folder_view *v)); static struct header_rec * sdt_give_header_cm(s,v) struct sort_data *s; struct folder_view *v; { return & (s->u.canceldir->message_list[v->index].REC); } static struct sort_data_type cm_sort = { SORTDATATYPE_magic, sdt_give_header_cm }; S_(mt_sort_mailbox_view mt_sort_canceldir_view) static void mt_sort_canceldir_view P_((struct MailboxView *mailbox, hdr_compare_func *func)); static void mt_sort_canceldir_view(mailbox,func) struct MailboxView *mailbox; hdr_compare_func *func; { int i; struct sort_data * array; /* Little dirty ... */ typedef int (*compar) P_((const void *, const void *)); compar X = (compar) func; array = safe_malloc(mailbox->view_len * sizeof (array[0])); for (i = 0; i < mailbox->view_len; i++) { int mbx = mailbox->view[i].mailbox_number; array[i].w = mailbox->view[i]; array[i].t = mailbox->thread_view; /* For thread sorting */ array[i].sort_data_type = &cm_sort; array[i].u.canceldir = mailbox->u.canceldir; } qsort(array,mailbox->view_len,sizeof (array[0]), X); for (i = 0; i < mailbox->view_len; i++) { mailbox->view[i] = array[i].w; } free(array); } /* XXX not good .... */ S_(mt_give_message_data_mailbox mt_give_message_data_canceldir) static int mt_give_message_data_canceldir P_((struct MailboxView *mailbox, int index, struct header_rec **ret_header, FILE **ret_F, struct counter_data *counter, parse_mime_callback *parse_mime, struct folder_view *v)); static int mt_give_message_data_canceldir(mailbox,index,ret_header,ret_F, counter,parse_mime,v) struct MailboxView *mailbox; int index; struct header_rec **ret_header; FILE **ret_F; struct counter_data *counter; parse_mime_callback *parse_mime; struct folder_view *v; { struct header_rec *hdr; int mbx,idx; mbx = v->mailbox_number; if (mbx != 0) { panic("MBX VIEW PANIC",__FILE__,__LINE__, "mt_give_message_data_canceldir", "Bad mailbox number",0); return 0; } idx = v->index; if (idx < 0 || idx >= mailbox->u.canceldir->message_list_len) { panic("MBX VIEW PANIC",__FILE__,__LINE__, "mt_give_message_data_canceldir", "Bad internal index",0); return 0; } hdr = & (mailbox->u.canceldir->message_list[idx].REC); /* hdr is also needed on error messages ... */ if (ret_header) { if (-1 == hdr->mime_rec.begin_offset) update_header(mailbox->u.canceldir,idx); *ret_header = hdr; DPRINT(Debug,10,(&Debug,"mt_give_message_data_canceldir: %d -- giving header\n", idx)); } if (ret_F) { if (!update_header(mailbox->u.canceldir,idx)) { DPRINT(Debug,6,(&Debug,"mt_give_message_data_canceldir: %d failed\n", idx)); return 0; } /* Simulate prepare_message_access */ if (! hdr->mime_parsed && parse_mime == NO_mime_parse) { DPRINT(Debug,10,(&Debug, "mt_give_message_data_canceldir: mime structure not needed\n")); } else if (! hdr->mime_parsed) { int status; DPRINT(Debug,10,(&Debug, "mt_give_message_data_canceldir: Need parsing of mime structure (offset %ld)\n", hdr->offset )); if (0 != fseek(mailbox->u.canceldir->last_open_file, hdr->offset, SEEK_SET)) { DPRINT(Debug,10,(&Debug, "mt_give_message_data_canceldir: seek to %ld failed\n", hdr->offset)); return 0; } status = parse_mime(NULL,hdr, mailbox->u.canceldir->last_open_file); if (status <= 0) { DPRINT(Debug,10,(&Debug, "mt_give_message_data_canceldir: parse_mime callback failed (%d)\n", status)); } } rewind(mailbox->u.canceldir->last_open_file); *ret_F = mailbox->u.canceldir->last_open_file; DPRINT(Debug,10,(&Debug,"mt_give_message_data_canceldir: %d -- giving file\n", idx)); } DPRINT(Debug,10,(&Debug,"mt_give_message_data_canceldir: %d -- succeed\n", idx)); return 1; } S_(mt_write_mailbox_info mt_write_canceldir_info) static void mt_write_canceldir_info P_((FILE *fp, struct MailboxView *mailbox, int s, int cur_idx)); static void mt_write_canceldir_info(fp,mailbox,s, cur_idx) FILE *fp; struct MailboxView *mailbox; int s; int cur_idx; { /* EMPTY */ } S_(mt_mailbox_title mt_canceldir_title) static struct string * mt_canceldir_title P_((struct MailboxView *mailbox)); static struct string * mt_canceldir_title(mailbox) struct MailboxView *mailbox; { if (canceled_mail_view == mailbox) { if (*hostname && menu_display_host) return format_string(CATGETS(elm_msg_cat, ElmSet, ElmCanceledMailsOn, "Canceled mails on %s"), hostname); return format_string(CATGETS(elm_msg_cat, ElmSet, ElmCanceledMails, "Canceled mails")); } /* These two should not really be used ... */ if (*hostname && menu_display_host) return format_string(CATGETS(elm_msg_cat, ElmSet, ElmMDirectoryOn, "Message directory %s on %s"), mailbox->u.canceldir->dirname, hostname); return format_string(CATGETS(elm_msg_cat, ElmSet, ElmMDirectory, "Message directory %s"), mailbox->u.canceldir->dirname); } S_(mt_make_mailbox_view mt_make_canceldir_view) static void mt_make_canceldir_view (mailbox) struct MailboxView *mailbox; { int count = mailbox->u.canceldir->message_list_len; int i,x; if (count < 1) { if (mailbox->view) free(mailbox->view); mailbox->view = NULL; mailbox->view_len = 0; return; } mailbox->view = safe_realloc(mailbox->view, count * (sizeof ( mailbox->view[0]))); for (x = 0; x < count; x++) { zero_folder_view(& (mailbox->view[x])); mailbox->view[x].mailbox_number = 0; mailbox->view[x].index = x; } mailbox->view_len = count; } S_(mt_add_mailbox_digest mt_add_canceldir_digest) static void mt_add_canceldir_digest P_((struct MailboxView *mailbox, mime_t *list, time_t received_time, char *env_from, FILE *F, charset_t defcharset )); static void mt_add_canceldir_digest(mailbox, list, received_time, env_from, F, defcharset ) struct MailboxView *mailbox; mime_t *list; time_t received_time; char *env_from; FILE *F; charset_t defcharset; { panic("MBX VIEW PANIC",__FILE__,__LINE__,"mt_add_canceldir_digest", "mt_add_canceldir_digest called",0); } static struct mailbox_type mt_canceldir = { MAILBOXTYPE_magic, mt_init_canceldir, mt_free_canceldir, mt_add_canceldir_storage, mt_update_view_canceldir, mt_get_main_canceldir_folder, mt_get_canceldir_storage, mt_get_canceldir_storage_count, mt_give_header_canceldir, mt_sort_canceldir_view, mt_give_message_data_canceldir, mt_write_canceldir_info, mt_canceldir_title, mt_make_canceldir_view, mt_add_canceldir_digest }; /* ------------------------------------------------------------------------ */ static void index_initialize P_((struct MailboxView *view)); /* 1 == readed and locked, 0 = failure */ static int process_index(view) struct MailboxView *view; { int count = 0; int err = can_open(view->u.canceldir->indexname, "a"); int ret; char buffer[20]; if (err) { lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile, "Can't open %s!"), view->u.canceldir->indexname); return 0; } view->u.canceldir->index_F = open_or_create(view->u.canceldir->indexname); if (!view->u.canceldir->index_F) { lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile, "Can't open %s!"), view->u.canceldir->indexname); return 0; } ret = Grab_the_file(fileno(view->u.canceldir->index_F)); if (FLOCKING_RETRY == ret) { lib_transient(CATGETS(elm_msg_cat, MeSet, MeLockingFile, "Locking %s..."), view->u.canceldir->indexname); while (FLOCKING_RETRY == ret && count++ < 3) { #if POLL_METHOD wait_for_timeout(1); #else sleep(1); #endif ret = Grab_the_file(fileno(view->u.canceldir->index_F)); } if (FLOCKING_OK == ret) lib_transient(CATGETS(elm_msg_cat, MeSet, MeLockingFileOK, "Locking %s... OK."), view->u.canceldir->indexname); if (FLOCKING_RETRY == ret) { fclose(view->u.canceldir->index_F); view->u.canceldir->index_F = NULL; return 0; } } if (FLOCKING_FAIL == ret) { lib_error(CATGETS(elm_msg_cat, MeSet, MeLockingFileFAIL, "Locking %s... Failed."), view->u.canceldir->indexname); fclose(view->u.canceldir->index_F); view->u.canceldir->index_F = NULL; return 0; } if ((ret = mail_gets(buffer, sizeof buffer, view->u.canceldir->index_F)) > 0) { struct message_list * current = NULL; /* current index */ if (ret != 12 || 0 != memcmp(buffer,"ELMME+ MIDX\n",12)) { DPRINT(Debug,7,(&Debug, "process_index: Bad bagic (len %d): %.*s\n", ret,ret,buffer)); lib_error(CATGETS(elm_msg_cat, MeSet, MeFileCorrupted, "File %s is corrupted."), view->u.canceldir->indexname); Release_the_file(fileno(view->u.canceldir->index_F)); fclose(view->u.canceldir->index_F); view->u.canceldir->index_F = NULL; return 0; } /* malloc_gets reallocates buffer1 and do not return \n. It returns -1 if line is longer than given limit (32000) and -2 is returned on error (buffer may still be alloced) */ while (!feof(view->u.canceldir->index_F) && !ferror(view->u.canceldir->index_F) && ret >= 0) { char * buffer1 = NULL; ret = malloc_gets(&buffer1, 32000, view->u.canceldir->index_F); if (ret < 0 || !buffer1) goto fail; if (ret >= 2) { if (strcmp("--END",buffer1) == 0) { /* Temporary terminator */ ret = -2; goto fail; } if (0 == strncmp(buffer1,"C ",2)) { view->u.canceldir->cs = MIME_name_to_charset(buffer1+2,0); goto next_line; } if (!view->u.canceldir->cs || ' ' != buffer1[1]) { DPRINT(Debug,7,(&Debug, "process_index: Bad line (len %d): %.*s\n", ret,ret,buffer1)); lib_error(CATGETS(elm_msg_cat, MeSet, MeFileCorrupted, "File %s is corrupted."), view->u.canceldir->indexname); ret = -1; goto fail; } switch (buffer1[0]) { case 'F': { char * fname = buffer1+2; int i; current = NULL; for (i = 0; i < view->u.canceldir->message_list_len; i++) if (0 == strcmp(view->u.canceldir->message_list[i]. filename,fname)) { current = & (view->u.canceldir-> message_list[i]); DPRINT(Debug,7,(&Debug, "process_index: File %s already on index (%d)\n", fname,i)); } if (!current) { DPRINT(Debug,7,(&Debug, "process_index: Adding file %s to index\n", fname)); view->u.canceldir->message_list = safe_realloc(view->u.canceldir->message_list, (view->u.canceldir->message_list_len+1) * sizeof (view->u.canceldir->message_list[0])); current = & (view->u.canceldir-> message_list[view->u.canceldir->message_list_len++]); header_zero( & (current->REC)); current->filename = safe_strdup(buffer1+2); /* Charset used by index writing code... */ current->REC.header_charset = view->u.canceldir->cs; current->REC.offset = 0; /* Default envelope sender */ if (view == canceled_mail_view) strfcpy(current->REC.env_from,username, sizeof (current->REC.env_from)); } } break; case 'L': if (!current) goto bad0; current->REC.lines = atoi(buffer1+2); break; case 'T': if (!current) goto bad0; current->REC.received_time = atol(buffer1+2); break; case 'E': if (!current) goto bad0; strfcpy(current->REC.env_from,buffer1+2, sizeof (current->REC.env_from)); break; case 'f': if (!current) goto bad0; if (current->REC.from) break; current->REC.from = break_down_address(buffer1+2,0,view->u.canceldir->cs); break; case 't': if (!current) goto bad0; if (current->REC.to) break; current->REC.to = break_down_address(buffer1+2,0,view->u.canceldir->cs); break; case 'c': if (!current) goto bad0; if (current->REC.cc) break; current->REC.cc = break_down_address(buffer1+2,0,view->u.canceldir->cs); break; case 'M': if (!current) goto bad0; strfcpy(current->REC.messageid,buffer1+2, sizeof (current->REC.messageid)); break; case 'd': if (!current) goto bad0; current->REC.time_sent = atol(buffer1+2); break; case 'S': if (!current) goto bad0; if (current->REC.subject) break; current->REC.subject = hdr_to_string(HDR_TEXT, buffer1+2, view->u.canceldir->cs, 0); break; default: bad0: DPRINT(Debug,7,(&Debug, "process_index: Bad line or token (len %d): %.*s\n", ret,ret,buffer1)); lib_error(CATGETS(elm_msg_cat, MeSet, MeFileCorrupted, "File %s is corrupted."), view->u.canceldir->indexname); ret = -1; goto fail; } } else if (0 != ret) { DPRINT(Debug,7,(&Debug, "process_index: short line (len %d): %.*s\n", ret,ret,buffer1)); lib_error(CATGETS(elm_msg_cat, MeSet, MeFileCorrupted, "File %s is corrupted."), view->u.canceldir->indexname); ret = -1; } else current = NULL; next_line: fail: if (buffer1) { free(buffer1); buffer1 = NULL; } } } else { /* Initialize file */ index_initialize(view); } return 1; } static void setup_canceldir P_((struct MailboxView *view, char *dir)); static void setup_canceldir(view,dir) struct MailboxView *view; char *dir; { if (&mt_canceldir!= view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__,"setup_canceldir", "Bad type for canceldir view",0); if (view->u.canceldir->dirname || view->u.canceldir->indexname || view->u.canceldir->index_F) panic("MBX VIEW PANIC",__FILE__,__LINE__,"setup_canceldir", "canceldir view already set up",0); DPRINT(Debug,8,(&Debug, "setup_canceldir(view=%p,dir=%s)\n", view,dir)); if (dir[0]) { int err,ret; char buffer[30]; view->u.canceldir->dirname = safe_strdup(dir); view->u.canceldir->indexname = elm_message(FRM("%s/.index"),dir); /* Is directory created yet ? */ if (access(dir, WRITE_ACCESS) < 0) { int err = errno; if (ENOENT == err) return; lib_error(CATGETS(elm_msg_cat, ElmSet,ElmErrorAccess, "%.45s: %.33s"), dir,error_description(err)); return; } process_index(view); } } static struct addr_item *dup_addr_list P_((struct addr_item *list)); static struct addr_item *dup_addr_list(list) struct addr_item *list; { int L = 0; struct addr_item *ptr; int i; for (ptr=list; ptr->addr && ptr->fullname; ptr++) L++; ptr = safe_malloc((L+1) * sizeof (ptr[0])); for (i = 0; i < L; i++) { ptr[i].addr = safe_strdup(list[i].addr); ptr[i].fullname = dup_string(list[i].fullname); ptr[i].comment = NULL; if (list[i].comment) ptr[i].comment = dup_string(list[i].comment); } ptr[i].addr = NULL; ptr[i].fullname = NULL; ptr[i].comment = NULL; return ptr; } static void write_addr_list P_((FILE *F, struct addr_item *list, charset_t cs, int letter)); static void write_addr_list(F,list,cs,letter) FILE *F; struct addr_item *list; charset_t cs; int letter; { struct addr_item *ptr; putc(letter,F); putc(' ',F); for (ptr=list; ptr->addr && ptr->fullname; ptr++) { char * buffer; if (ptr != list) { putc(',',F); putc(' ',F); } buffer = string_to_hdr(HDR_PHRASE, ptr->fullname, cs, 0, NULL); if (buffer) { fputs(buffer,F); putc(' ',F); free(buffer); } putc('<',F); fputs(ptr->addr,F); putc('>',F); if (ptr->comment) { buffer = string_to_hdr(HDR_COMMENT, ptr->comment, cs, 0, NULL); if (buffer) { putc(' ',F); putc('(',F); fputs(buffer,F); putc(')',F); free(buffer); } } } putc('\n',F); } static void write_index_rec P_((struct MailboxView *view, struct message_list * current)); static void write_index_rec(view,current) struct MailboxView *view; struct message_list * current; { fprintf(view->u.canceldir->index_F, "F %s\n", current->filename); if (current->REC.lines >= 0) { fprintf(view->u.canceldir->index_F,"L %d\n", current->REC.lines); } fprintf(view->u.canceldir->index_F, "T %ld\n", (long int) current->REC.received_time); fprintf(view->u.canceldir->index_F, "d %ld\n", (long int) current->REC.time_sent); if (current->REC.env_from[0]) { fprintf(view->u.canceldir->index_F, "E %s\n", current->REC.env_from); } if (current->REC.from) { write_addr_list(view->u.canceldir->index_F, current->REC.from, view->u.canceldir->cs,'f'); } if (current->REC.to) { write_addr_list(view->u.canceldir->index_F, current->REC.to, view->u.canceldir->cs,'t'); } if (current->REC.cc) { write_addr_list(view->u.canceldir->index_F, current->REC.cc, view->u.canceldir->cs,'c'); } if (current->REC.messageid[0]) { fprintf(view->u.canceldir->index_F, "M %s\n", current->REC.messageid); } if (current->REC.subject) { char * s = string_to_hdr(HDR_TEXT,current->REC.subject, view->u.canceldir->cs,0, NULL); fprintf(view->u.canceldir->index_F, "S %s\n", s); free(s); } } static void add_canceled_mail1(filename,headers,view,X,lines,h, body_offset) char *filename; struct mailing_headers * headers; struct MailboxView *view; time_t X; int lines; header_list_ptr h; long body_offset; { struct message_list * current = NULL; /* current index */ view->u.canceldir->message_list = safe_realloc(view->u.canceldir->message_list, (view->u.canceldir->message_list_len+1) * sizeof (view->u.canceldir->message_list[0])); current = & (view->u.canceldir-> message_list[view->u.canceldir->message_list_len++]); current->filename = safe_strdup(filename); header_zero( & (current->REC)); /* Dummy default ... */ current->REC.header_charset = view->u.canceldir->cs; /* read_folder_headers_helper() will re-set header_charset if it sees X-ELM-OSV header with parameter hdr-charset */ /* Start of headers */ current->REC.offset = 0; current->REC.mime_rec.begin_offset = 0; /* Start of body */ if (body_offset >= 0L) current->REC.mime_rec.begin_offset = body_offset; if (lines >= 0) current->REC.lines = lines; current->REC.received_time = X; current->REC.time_sent = X; if (h) { /* Parses Return-Path, Message-Id, Content-Length, Date, MIME-Version, X-ELM-OSV */ read_folder_headers_helper(& (current->REC), h); /* Parses Sensitivity, Importance, Priority, Action, Content-Type, Status, Subject, From, To, Cc And X-IMAP is used on message-hide-hack */ header_parse_helper(& (current->REC), h); } if (headers && headers->env_from && ! current->REC.env_from[0]) { int x; CONST char * t = mailer_env_from_value(headers->env_from,&x); /* Assurance */ if (t) strfcpy(current->REC.env_from,t, sizeof (current->REC.env_from)); } /* Adds env_from, if not set */ if (view == canceled_mail_view && !current->REC.env_from[0]) { strfcpy(current->REC.env_from,username, sizeof (current->REC.env_from)); } if (headers && headers->from.addrs && ! current->REC.from) { current->REC.from = dup_addr_list(headers->from.addrs); } if (headers && headers->to.addrs && ! current->REC.to) { current->REC.to = dup_addr_list(headers->to.addrs); } if (headers && headers->cc.addrs && ! current->REC.cc) { current->REC.cc = dup_addr_list(headers->cc.addrs); } if (headers && headers->subject && ! current->REC.subject) current->REC.subject = dup_string(headers->subject); write_index_rec(view,current); } void delete_marked_canceled_mails(view) struct MailboxView *view; { int i; struct mv_canceldir *d; if (&mt_canceldir!= view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__, "delete_marked_canceled_mails", "Bad type for canceldir view",0); d = view->u.canceldir; /* DO not remove data from index on here! because it confuses canceled mail selection */ if (! d->dirname) return; for (i = 0; i < d->message_list_len; i++) { if (ison(d->message_list[i].REC.status, DELETED)) { char * filename = elm_message(FRM("%s/%s"), d->dirname, d->message_list[i]. filename); if (0 == unlink(filename)) { DPRINT(Debug,7,(&Debug, "Removed canceled mail [%d]: %s\n", i,filename)); } free(filename); } } } static void index_initialize(view) struct MailboxView *view; { /* Initialize file */ rewind(view->u.canceldir->index_F); #ifdef FTRUNCATE ftruncate(fileno(view->u.canceldir->index_F),0); #endif fprintf(view->u.canceldir->index_F, "ELMME+ MIDX\n"); view->u.canceldir->cs = MIME_name_to_charset("UTF-8",0); if (view->u.canceldir->cs && view->u.canceldir->cs->MIME_name) fprintf(view->u.canceldir->index_F, "C %s\n", view->u.canceldir->cs->MIME_name); fflush(view->u.canceldir->index_F); } void sync_canceled_mails(view) struct MailboxView *view; { /* Rewrites index if needed */ int i,X; int unlock_it = 0; struct mv_canceldir *d; if (&mt_canceldir!= view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__, "sync_canceled_mails", "Bad type for canceldir view",0); d = view->u.canceldir; if (! d->dirname || ! d->indexname) { return; } if (!view->u.canceldir->index_F) { if (! process_index(view)) return; unlock_it++; } for (i = 0, X=0; i < d->message_list_len; i++) { char * filename = elm_message(FRM("%s/%s"), d->dirname, d->message_list[i]. filename); if (0 != access(filename,ACCESS_EXISTS)) { DPRINT(Debug,7,(&Debug, "[%d] not exists, removing from index: %s\n", i,filename)); header_clear(& (d->message_list[i].REC)); free(d->message_list[i].filename); d->message_list[i].filename = NULL; } else { if (X < i) { d->message_list[X] = d->message_list[i]; /* Avoid double pintters */ header_zero(& (d->message_list[i].REC)); d->message_list[i].filename = NULL; } X++; } free(filename); } if (X < d->message_list_len) { long pos; DPRINT(Debug,7,(&Debug, "Canceled mails reduced %d => %d\n", d->message_list_len,X)); d->message_list_len = X; index_initialize(view); /* Write records */ for (i = 0; i < d->message_list_len; i++) { write_index_rec(view,& (d->message_list[i])); } mt_make_canceldir_view(view); pos = ftell(view->u.canceldir->index_F); /* Temporary terminator */ fprintf(view->u.canceldir->index_F,"--END\n"); /* Set position so that next appended index overwrites this */ fseek(view->u.canceldir->index_F,pos,SEEK_SET); } if (unlock_it) unlock_index(view); } FILE * add_canceled_mail P_((char **outfile, struct mailing_headers * headers, struct MailboxView *view, time_t X, int lines, header_list_ptr h, long body_offset)); FILE * add_canceled_mail(outfile,headers,view, X, lines,h,body_offset) char **outfile; struct mailing_headers * headers; struct MailboxView *view; time_t X; int lines; long body_offset; header_list_ptr h; { int i; FILE *ret = NULL; int unlock_it = 0; if (&mt_canceldir!= view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__,"add_canceled_mail", "Bad type for canceldir view",0); if (!view->u.canceldir->dirname || !view->u.canceldir->indexname || !view->u.canceldir->cs){ DPRINT(Debug,7,(&Debug, "add_canceled_mail=NULL -- not setup\n")); return NULL; } if (!view->u.canceldir->index_F) { if (! process_index(view)) return NULL; unlock_it++; } for (i = view->u.canceldir->message_list_len; i < view->u.canceldir->message_list_len+100; i++) { int fd; if (*outfile) free(*outfile); *outfile = elm_message(FRM("%s/%05d-%02d.%s"), view->u.canceldir->dirname, (int)(X/(24*60*60)), i, dead_letter);; fd = open(*outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) { DPRINT(Debug,7,(&Debug, "add_canceled_mail: %d -- %s creation failed\n", i,*outfile)); continue; } ret = fdopen(fd, "w"); if (!ret) { close(fd); goto fail; } DPRINT(Debug,7,(&Debug, "add_canceled_mail: Created %s\n", *outfile)); break; } if (ret) { char *x = strrchr(*outfile,'/'); if (!x) panic("MBX VIEW PANIC",__FILE__,__LINE__,"add_canceled_mail", "NO / on generated name",0); add_canceled_mail1(x+1,headers,view,X,lines,h,body_offset); } else { DPRINT(Debug,7,(&Debug, "add_canceled_mail: failed to create mail\n")); } fail: if (!ret) { if (*outfile) free(*outfile); *outfile = NULL; } if (unlock_it) unlock_index(view); DPRINT(Debug,7,(&Debug, "add_canceled_mail=%p\n",ret)); return ret; } static void move_olds(view) struct MailboxView *view; { int i; for (i = 0; i < 100; i++) { char *lbuf1; long b; int fd; struct stat stat_buf; FILE *f1,*f2; header_list_ptr h = NULL; int lines = 0,c; char *newname = NULL; if (0 == i) lbuf1 = elm_message(FRM("%s/%s"), home,dead_letter); else lbuf1 = elm_message(FRM("%s/%02d.%s"), home,i,dead_letter); fd = open(lbuf1,O_RDONLY); if (fd < 0) { DPRINT(Debug,7,(&Debug, "move_olds - copy %d: no file %s\n", i,lbuf1)); free(lbuf1); if (i > 1) break; continue; } if (-1 == fstat(fd,&stat_buf)) { close(fd); free(lbuf1); continue; } if (stat_buf.st_uid != userid) { /* Wrong owner */ close(fd); free(lbuf1); continue; } if ( #ifdef S_ISREG ! S_ISREG(stat_buf.st_mode) #else S_IFREG != (stat_buf.st_mode & S_IFMT) #endif ) { /* Wrong type */ close(fd); free(lbuf1); continue; } f1 = fdopen(fd,"r"); if (!f1) { close(fd); free(lbuf1); continue; } h = file_read_headers(f1,RHL_CHECK_HEADER); b = ftell(f1); while (EOF != (c = getc(f1))) { if ('\n' == c) lines++; } f2 = add_canceled_mail(&newname,NULL, view, stat_buf.st_mtime,lines,h,b); if (f2) { rewind(f1); if (0 != rename(lbuf1,newname)) { while (EOF != (c = getc(f1))) { putc(c,f2); } if (0 == fflush(f2) && !ferror(f1) && !ferror(f1)) { DPRINT(Debug,7,(&Debug, "give_canceled_mail - copy %d: copied %s to %s\n", i,lbuf1,newname)); unlink(lbuf1); } } else { DPRINT(Debug,7,(&Debug, "give_canceled_mail - copy %d: renamed %s to %s\n", i,lbuf1,newname)); } fclose(f2); } if (h) delete_headers(&h); if (newname) free(newname); fclose(f1); free(lbuf1); } } struct MailboxView *give_canceled_mail() { char *t = give_dt_estr_as_str(&dead_letter_dir_e,"dead-letter-dir"); if (!canceled_mail_view) { if (!t) { DPRINT(Debug,7,(&Debug, "give_canceled_mail=NULL: no dead-letter-dir\n")); return NULL; } canceled_mail_view = give_canceldir(); setup_canceldir(canceled_mail_view,t); } if (&mt_canceldir!= canceled_mail_view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_canceled_mail", "Bad type for canceled_mail_view",0); if (t && 0 == mkdir(t,0700)) { int i; lib_error(CATGETS(elm_msg_cat, ElmSet, ElmDeadLetterDirCreated, "\"dead-letter-dir\" %s created."), t); /* Try again create index file .... */ if (canceled_mail_view->u.canceldir->indexname && ! canceled_mail_view->u.canceldir->index_F) { if (!process_index(canceled_mail_view)) goto XX; } move_olds(canceled_mail_view); } else if (EEXIST == errno) { DPRINT(Debug,7,(&Debug, "give_canceled_mail: %s exists already\n", t)); move_olds(canceled_mail_view); } update_index(canceled_mail_view); XX: mt_make_canceldir_view(canceled_mail_view); unlock_index(canceled_mail_view); DPRINT(Debug,7,(&Debug, "give_canceled_mail=%p\n", canceled_mail_view)); return canceled_mail_view; } void close_canceled_mail() { if (canceled_mail_view) { /* NOTE: Can not call free_mailbox(& (canceled_mail_view))); because mt_free_canceldir will reset canceled_mail_view */ struct MailboxView *a = canceled_mail_view; sync_canceled_mails(a); free_mailbox (&a); if (canceled_mail_view) panic("MBX VIEW PANIC",__FILE__,__LINE__, "close_canceled_mail", "canceled_mail_view not resetted",0); } } /* HACK -- return 1 on succeed, 0 on failure */ int cancel_set_current(cancel_view,last_canceled_mail) struct MailboxView * cancel_view; char *last_canceled_mail; { int x; char * D = last_canceled_mail; if (&mt_canceldir!= cancel_view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__,"cancel_set_current", "Bad type for cancel_view",0); if (cancel_view->u.canceldir->dirname) { int L = strlen(cancel_view->u.canceldir->dirname); if (0 == strncmp(D,cancel_view->u.canceldir->dirname,L) && '/' == D[L]) { DPRINT(Debug,7,(&Debug, "cancel_set_current: Filename %s have dirname as prefix, good ", D)); D += L+1; DPRINT(Debug,7,(&Debug,"=> %s\n", D)); } } for (x = 0; x < cancel_view->view_len; x++) { int n; if (0 != cancel_view->view[x].mailbox_number) panic("MBX VIEW PANIC",__FILE__,__LINE__,"cancel_set_current", "Bad internal mailbox number",0); n = cancel_view->view[x].index; if (n < 0 || n >= cancel_view->u.canceldir->message_list_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"cancel_set_current", "Bad internal index",0); if (0 == strcmp(cancel_view->u.canceldir->message_list[n].filename, D)) { DPRINT(Debug,7,(&Debug," Found filename %s internal index %d view index %d\n", D,n,x)); /* NOTE: Caller uses current == view_index +1 */ cancel_view->current = x+1; return 1; } } DPRINT(Debug,7,(&Debug,"cancel_set_current: Filename %s not found\n", D)); return 0; } /* HACK -- delete curently open file */ void delete_current_cancel(cancel_view,ref_file) struct MailboxView * cancel_view; FILE *ref_file; { int idx; char *filename = NULL; if (&mt_canceldir!= cancel_view->mailbox_type) panic("MBX VIEW PANIC",__FILE__,__LINE__,"cancel_set_current", "delete_current_cancel",0); if (! cancel_view->u.canceldir->last_open_file || ref_file != cancel_view->u.canceldir->last_open_file) { DPRINT(Debug,3,(&Debug, "delete_current_cancel: referenced file not currently open\n")); return; } idx = cancel_view->u.canceldir->last_open_index; if (idx < 0 || idx >= cancel_view->u.canceldir->message_list_len) panic("MBX VIEW PANIC",__FILE__,__LINE__, "delete_current_cancel", "Bad internal index",0); filename = elm_message(FRM("%s/%s"), cancel_view->u.canceldir->dirname, cancel_view->u.canceldir->message_list[idx]. filename); if (0 == unlink(filename)) { DPRINT(Debug,3,(&Debug, "delete_current_cancel: [%d] %s deleted\n", idx,filename)); } free(filename); } struct MailboxView *give_canceldir() { struct MailboxView *ret = malloc_view(&mt_canceldir); return ret; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */