/*************************************************************************** FILE: cl_mail.cc LVU : 1.3.9 DESC: This code runs the mail object that every local user has while logged in which keeps a list of their mail and can write and delete mails also. A temporary instance of this class is also created when someone mails a user but they are not logged in at the time. Copyright (C) Neil Robertson 2003-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ***************************************************************************/ #include "globals.h" /*** Constructor ***/ cl_mail::cl_mail(cl_user *own, uint16_t id) { cl_msginfo *minfo; cl_splitline *sl; char path[MAXPATHLEN]; char line[ARR_SIZE]; struct stat fs; struct tm *tms; FILE *fp; int linenum,err; owner = own; uid = id; error = OK; msgcnt = 0; todays_msgcnt = 0; unread_msgcnt = 0; first_msg = NULL; last_msg = NULL; // Check user exists first sprintf(path,"%s/%04X/%s",USER_DIR,uid,USER_CONFIG_FILE); if (stat(path,&fs)) { error = ERR_NO_SUCH_USER; return; } // Open mail file. If its not there its a new user who has no mail. sprintf(path,"%s/%04X/%s",USER_DIR,uid,USER_MAILINFO_FILE); if (!(fp=fopen(path,"r"))) return; err = 0; minfo = NULL; linenum = 1; fgets(line,ARR_SIZE-1,fp); // Loop through file while(!feof(fp) && !(err = ferror(fp))) { // Create new msginfo if we don't have one spare from last iteration if (!minfo) minfo = new cl_msginfo; sl = new cl_splitline(1); // Currently only parsing lines of the "msg = " format and if theres // an error on the line or its blank just ignore it. if (sl->parse(line) != OK || sl->wcnt < 8 || strcasecmp(sl->word[0],"msg")) { if (sl->wcnt && owner) owner->warnprintf("Invalid configuration on mailinfo file line %d.\n",linenum); delete sl; } else { minfo->set(msgcnt+1,sl); unread_msgcnt += !minfo->read; msgcnt++; /* Check if message was posted today. Since this means since midnight, not just in last 24 hours can't do simple subtraction on server_time */ tms = localtime(&minfo->create_time); todays_msgcnt += (tms->tm_year == server_time_tms.tm_year && tms->tm_yday == server_time_tms.tm_yday); add_list_item(first_msg,last_msg,minfo); minfo = NULL; } fgets(line,ARR_SIZE-1,fp); ++linenum; } fclose(fp); if (err) { error = ERR_CONFIG; return; } // If last line errored we could have an unused minfo object left over. if (minfo) delete minfo; } /*** Destructor ***/ cl_mail::~cl_mail() { cl_msginfo *minfo,*next; save(); // Destroy all msginfo structures in list for(minfo = first_msg;minfo;minfo = next) { next = minfo->next; delete minfo; } } /*** List all the users mails ***/ void cl_mail::list() { cl_msginfo *minfo; struct tm *tms; char sft[15],namestr[40]; char *str; int flag,lcnt,mcnt; if (!owner) return; if (!msgcnt) { owner->uprintf("You have no mail.\n"); return; } if (owner->com_page_line == -1) owner->uprintf("\n~BB*** Your mails ***\n\n"); lcnt = 0; mcnt = 0; flag = 0; FOR_ALL_MSGS(minfo) { if (!flag && owner->com_page_line == -1) { owner->uprintf(" ~OL~ULMsg From Recvd Bytes Subject\n"); flag = 1; } ++mcnt; // If we're not at paging start point skip if (owner->com_page_line != -1 && mcnt <= owner->com_page_line) continue; // Print message info str = (char *)(minfo->read ? " " : "~OL~FTU~RS "); tms = localtime(&minfo->create_time); strftime(sft,sizeof(sft),"%b %d %H:%M",tms); sprintf(namestr,"%s, %s",minfo->id,minfo->name); if (strlen(namestr) > 30) { namestr[27]='.'; namestr[28]='.'; namestr[29]='.'; namestr[30]='\0'; } owner->uprintf("%s~FY%2d~RS ~FT%-30s~RS %s %5d %s\n", str,minfo->mnum,namestr,sft,minfo->size,minfo->subject); if (O_FLAGISSET(owner,USER_FLAG_PAGING) && ++lcnt == owner->term_rows - 2) { owner->com_page_line = mcnt; return; } } if (O_FLAGISSET(owner,USER_FLAG_PAGING) && lcnt && lcnt >= owner->term_rows - 4) { owner->com_page_line = mcnt; return; } owner->uprintf("\nTotal of %d mails, %d are unread.\n\n",msgcnt,unread_msgcnt); owner->com_page_line = -1; } /*** Read the given mail ***/ int cl_mail::mread(cl_msginfo *minfo) { char path[MAXPATHLEN]; struct stat ss; int ret; if (!owner) return OK; if (owner->stage == USER_STAGE_MAILER_READ_FROM) { owner->uprintf("\n~FYMesg num:~RS %d\n",minfo->mnum); owner->page_header_lines = 5; } else { owner->uprintf("\n"); owner->page_header_lines = 4; } owner->uprintf("~FYFrom :~RS %s, %s\n",minfo->id,minfo->name); owner->uprintf("~FYSubject :~RS %s\n",minfo->subject); owner->uprintf("~FYReceived:~RS %s",ctime(&minfo->create_time)); owner->uprintf("~FYBytes :~RS %d",minfo->size); // Check if expected and actual file sizes match sprintf(path,"%s/%04X/%s",USER_DIR,uid,minfo->filename); ss.st_size = 0; stat(path,&ss); if (ss.st_size != minfo->size) owner->uprintf(" (~OL~FRFile corrupted!~RS Actual file size = %d bytes)\n\n",ss.st_size); else owner->uprintf("\n\n"); if (!minfo->read) { minfo->read = 1; unread_msgcnt--; } ret = owner->page_file(path,1); if (!owner->page_pos) owner->uprintf("\n"); return ret; } /*** Write new mail ***/ int cl_mail::mwrite( uint16_t uid_from, char *name_from, cl_server *svr, char *subj, char *msg) { cl_msginfo *minfo; char filename[MAXPATHLEN]; char path[MAXPATHLEN]; char fromstr[100]; int cnt,nl,ret; FILE *fp; if (!name_from || !msg) return ERR_MISSING_VALUE; if ((int)strlen(msg) > max_mail_chars) return ERR_MSG_TOO_LONG; // Pick a filename based on mail count. If it already exists pick the // next one along. cnt = msgcnt+1; do { sprintf(filename,"mail_%d",cnt); FOR_ALL_MSGS(minfo) if (!strcmp(minfo->filename,filename)) break; cnt++; } while(minfo); // Write file sprintf(path,"%s/%04X/%s",USER_DIR,uid,filename); if (!(fp=fopen(path,"w"))) return ERR_CANT_OPEN_FILE; fputs(msg,fp); if (msg[strlen(msg)-1] != '\n') { nl=1; fputc('\n',fp); } else nl=0; fclose(fp); // Add entry to list. if (svr) sprintf(fromstr,"%04X@%s",uid_from,svr->name); else sprintf(fromstr,"%04X",uid_from); minfo = new cl_msginfo(); if ((ret=minfo->set( last_msg ? last_msg->mnum+1 : 1, fromstr, name_from, subj ? subj : (char *)"<No subject>", filename, strlen(msg) + nl)) != OK) { if (owner) owner->errprintf("Unable to set message!\n"); delete minfo; return ret; } msgcnt++; todays_msgcnt++; unread_msgcnt++; add_list_item(first_msg,last_msg,minfo); // Save to mailinfo file return save(); } /*** Delete an entry from the list ***/ int cl_mail::mdelete(cl_msginfo *minfo) { struct tm *tms; char path[MAXPATHLEN]; sprintf(path,"%s/%04X/%s",USER_DIR,uid,minfo->filename); unlink(path); remove_list_item(first_msg,last_msg,minfo); tms = localtime(&minfo->create_time); todays_msgcnt -= (tms->tm_year == server_time_tms.tm_year && tms->tm_yday == server_time_tms.tm_yday); if (!minfo->read) unread_msgcnt--; msgcnt--; delete minfo; return save(); } /*** Save the info to the mailinfo file ***/ int cl_mail::save() { char path[MAXPATHLEN]; cl_msginfo *minfo; FILE *fp; sprintf(path,"%s/%04X/%s",USER_DIR,uid,USER_MAILINFO_FILE); // If no messages then just unlink if (!first_msg) { unlink(path); return OK; } if (!(fp=fopen(path,"w"))) return ERR_CANT_OPEN_FILE; FOR_ALL_MSGS(minfo) { fprintf(fp,"msg = %s, \"%s\", \"%s\", %s, %u, %u, %s\n", minfo->id, minfo->name, minfo->subject, minfo->filename, (uint)minfo->create_time, minfo->size, noyes[minfo->read]); } fclose(fp); return OK; } /*** Renumbers all the mail messages back to an ordered sequence from 1 ***/ void cl_mail::renumber() { cl_msginfo *minfo; int i=1; FOR_ALL_MSGS(minfo) minfo->mnum = i++; }