nuts4_141/
nuts4_141/n4/boards/0002/
nuts4_141/n4/boards/0003/
nuts4_141/n4/boards/0004/
nuts4_141/n4/boards/0006/
nuts4_141/n4/boards/0FFF/
nuts4_141/n4/etc/
nuts4_141/n4/pubgroups/0004/
nuts4_141/n4/pubgroups/0006/
nuts4_141/n4/users/0FFF/
nuts4_141/n4b/boards/0001/
nuts4_141/n4b/boards/0002/
nuts4_141/n4b/boards/0FFF/
nuts4_141/n4b/etc/
nuts4_141/n4b/pubgroups/0004/
nuts4_141/n4b/pubgroups/0005/
nuts4_141/n4b/src/
nuts4_141/n4b/users/0FFF/
/***************************************************************************
 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++;
}