Skip to content
Snippets Groups Projects
email.cpp 9.88 KiB
Newer Older
/* Synchronet email function - for sending private e-mail */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * 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.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "cmdshell.h"
Rob Swindell's avatar
Rob Swindell committed
#include "utf8.h"

/****************************************************************************/
/* Mails a message to usernumber. 'top' is a buffer to place at beginning   */
/* of message.                                                              */
/* Called from functions main_sec, newuser, readmail and scanposts			*/
/****************************************************************************/
bool sbbs_t::email(int usernumber, const char *top, const char *subj, int mode, smb_t* resmb, smbmsg_t* remsg)
	char		str[256],str2[256],msgpath[256],ch
	char		title[LEN_TITLE+1] = "";
	const char*	editor=NULL;
	const char*	charset=NULL;
	uint16_t	xlat=XLAT_NONE;
	int 		i,j,x,file;
	uint32_t	crc=0xffffffffUL;
	FILE*		instream;
	node_t		node;
	user_t		user{};
Rob Swindell's avatar
Rob Swindell committed
		SAFECOPY_UTF8(title, subj);
	if(remsg != NULL && title[0] == 0)
Rob Swindell's avatar
Rob Swindell committed
		SAFECOPY_UTF8(title, remsg->subj);
	if(useron.etoday>=cfg.level_emailperday[useron.level] && !SYSOP && !(useron.exempt&FLAG('M'))) {
		bputs(text[TooManyEmailsToday]);
	user.number = usernumber;
	if(getuserdat(&cfg, &user) != 0 || (user.misc & (DELETED | INACTIVE))) {
		bputs(text[UnknownUser]);
		return(false);
	}
	bool to_sysop = user_is_sysop(&user);
	if(to_sysop && useron.rest&FLAG('S')
		&& (cfg.valuser!=usernumber || useron.fbacks || useron.emails)) { /* ! val fback */
		bprintf(text[R_Feedback],cfg.sys_op);
	if(!to_sysop && useron.rest&FLAG('E')
		&& (cfg.valuser!=usernumber || useron.fbacks || useron.emails)) {
		bputs(text[R_Email]);
	if((user.misc&NETMAIL) && (cfg.sys_misc&SM_FWDTONET) && !(mode & WM_NOFWD) && !(useron.rest&FLAG('M'))) {
		if(is_supported_netmail_addr(&cfg, user.netmail)) {
			bprintf(text[UserNetMail], user.netmail);
			if((mode & WM_FORCEFWD) || yesno(text[ForwardMailQ])) /* Forward to netmail address */
				return(netmail(user.netmail, subj, mode, resmb, remsg));
			bprintf(text[InvalidNetMailAddr], user.netmail);
	if(sys_status&SS_ABORT) {
		bputs(text[Aborted]);
		return false;
	}
	bprintf(text[Emailing],username(&cfg,usernumber,tmp),usernumber);
	action=NODE_SMAL;
	nodesync();

	if(cfg.feedback_mod[0] && to_sysop && !SYSOP
		&& (useron.fbacks || usernumber != cfg.valuser)) {
		main_csi.logic = LOGIC_TRUE;
		if(exec_bin(cfg.feedback_mod, &main_csi) != 0 || main_csi.logic != LOGIC_TRUE)
			return(false);

	if(cfg.sys_misc&SM_ANON_EM && useron.exempt&FLAG('A')
		msgattr|=MSG_ANONYMOUS;

	if(cfg.sys_misc&SM_DELREADM)
		msgattr|=MSG_KILLREAD;

	if(remsg != NULL && resmb != NULL && !(mode&WM_QUOTE)) {
		if(quotemsg(resmb, remsg, /* include tails: */true))
			mode |= WM_QUOTE;
	}

	msg_tmp_fname(useron.xedit, msgpath, sizeof(msgpath));
	username(&cfg,usernumber,str2);
	if(!writemsg(msgpath,top, /* subj: */title,WM_EMAIL|mode,INVALID_SUB,/* to: */str2,/* from: */useron.alias, &editor, &charset)) {
		bputs(text[Aborted]);
	if(mode&WM_FILE && !SYSOP && !(cfg.sys_misc&SM_FILE_EM)) {
		bputs(text[EmailFilesNotAllowed]);

	if(mode&WM_FILE) {
			bprintf(text[BadFilename], title);
			(void)remove(msgpath);
		SAFEPRINTF2(str2,"%sfile/%04u.in", cfg.data_dir,usernumber);
		(void)MKDIR(str2);
		SAFEPRINTF3(str2,"%sfile/%04u.in/%s", cfg.data_dir,usernumber,title);
			bprintf(text[FileAlreadyThere], str2);
			(void)remove(msgpath);
		xfer_prot_menu(XFER_UPLOAD, &useron, keys, sizeof keys);
		SAFECAT(keys, quit_key(str));
		if(ch==quit_key() || sys_status&SS_ABORT) {
			(void)remove(msgpath);
		if(x<cfg.total_prots)	/* This should be always */
			protocol(cfg.prot[x],XFER_UPLOAD,str2,nulstr,true); 
		safe_snprintf(tmp,sizeof(tmp),"%s%s",cfg.temp_dir,title);
		if(!fexistcase(str2) && fexistcase(tmp))
			mv(tmp,str2,0);
		off_t l=flength(str2);
			bprintf(text[FileNBytesReceived],title,u64toac(l,tmp));
		else {
			bprintf(text[FileNotReceived],title);
			(void)remove(msgpath);

	bputs(text[WritingIndx]);

	if((i=smb_stack(&smb,SMB_STACK_PUSH))!=0) {
		errormsg(WHERE,ERR_OPEN,"MAIL",i);

	if((i=smb_open_sub(&cfg, &smb, INVALID_SUB))!=0) {
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);

	if((i=smb_locksmbhdr(&smb))!=0) {
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
	length=(int)flength(msgpath)+2;	 /* +2 for translation string */

	if(length&0xfff00000UL) {
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_LEN,msgpath,length);

	if((i=smb_open_da(&smb))!=0) {
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
	if(cfg.sys_misc&SM_FASTMAIL)
		offset=smb_fallocdat(&smb,length,1);
	else
		offset=smb_allocdat(&smb,length,1);
	smb_close_da(&smb);

rswindell's avatar
rswindell committed
	if((instream=fnopen(&file,msgpath,O_RDONLY|O_BINARY))==NULL) {
		smb_freemsgdat(&smb,offset,length,1);
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,msgpath,O_RDONLY|O_BINARY);

	smb_fseek(smb.sdt_fp,offset,SEEK_SET);
	xlat=XLAT_NONE;
	smb_fwrite(&smb,&xlat,2,smb.sdt_fp);
	x=SDT_BLOCK_LEN-2;				/* Don't read/write more than 255 */
	while(!feof(instream)) {
		memset(buf,0,x);
		j=fread(buf,1,x,instream);
		if(j<1)
			break;
		if(j>1 && (j!=x || feof(instream)) && buf[j-1]==LF && buf[j-2]==CR)
			buf[j-1]=buf[j-2]=0;
		if(cfg.mail_maxcrcs) {
			for(i=0;i<j;i++)
		smb_fwrite(&smb,buf,j,smb.sdt_fp);
	smb_fflush(smb.sdt_fp);
	fclose(instream);
	crc=~crc;

	memset(&msg,0,sizeof(smbmsg_t));
	msg.hdr.version=smb_ver();
	if(mode&WM_FILE)
		msg.hdr.auxattr |= (MSG_FILEATTACH | MSG_KILLFILE);
	msg.hdr.when_imported.time = time32(NULL);
	msg.hdr.when_imported.zone = sys_timezone(&cfg);
	msg.hdr.when_written = smb_when(msg.hdr.when_imported.time, msg.hdr.when_imported.zone);

	if(cfg.mail_maxcrcs) {
		i=smb_addcrc(&smb,crc);
		if(i) {
			smb_freemsgdat(&smb,offset,length,1);
			smb_unlocksmbhdr(&smb);
			smb_close(&smb);
			smb_stack(&smb,SMB_STACK_POP);
			bprintf(text[CantPostMsg], i == SMB_DUPE_MSG ? "duplicate" : "other");
	msg.hdr.offset=(uint32_t)offset;

	username(&cfg,usernumber,str);
	smb_hfield_str(&msg,RECIPIENT,str);
	SAFEPRINTF(str,"%u",usernumber);
	smb_hfield_str(&msg,RECIPIENTEXT,str);
	smb_hfield_str(&msg,SENDER,str);
	SAFEPRINTF(str,"%u",useron.number);
	smb_hfield_str(&msg,SENDEREXT,str);
		if(useron.rest&FLAG('G'))
			smb_hfield_str(&msg,REPLYTO,useron.name);
		smb_hfield_netaddr(&msg,REPLYTONETADDR,useron.netmail,NULL);
	/* Security logging */
	smb_hfield_str(&msg,SENDERSERVER,server_host_name());
	normalize_msg_hfield_encoding(charset, title, sizeof title);

	smb_hfield_str(&msg,SUBJECT,title);
	add_msg_ids(&cfg, &smb, &msg, remsg);
	editor_info_to_msg(&msg, editor, charset);
	smb_dfield(&msg,TEXT_BODY,length);

	i=smb_addmsghdr(&smb,&msg,smb_storage_mode(&cfg, &smb)); // calls smb_unlocksmbhdr() 
	if(i == SMB_SUCCESS && remsg != NULL)
		smb_updatethread(&smb, remsg, msg.hdr.number);
	smb_close(&smb);
	smb_stack(&smb,SMB_STACK_POP);

	smb_freemsgmem(&msg);
	if(i!=SMB_SUCCESS) {
		smb_freemsgdat(&smb,offset,length,1);
		errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
		logon_fbacks++;
		logon_emails++;
	user_sent_email(&cfg, &useron, 1, usernumber==1);
	bprintf(text[Emailed],username(&cfg,usernumber,tmp),usernumber);
	safe_snprintf(str,sizeof(str),"sent e-mail to %s #%d"
		,username(&cfg,usernumber,tmp),usernumber);
	logline("E+",str);
	if(mode&WM_FILE && online==ON_REMOTE)
		autohangup();
	if(msgattr&MSG_ANONYMOUS)				/* Don't tell user if anonymous */
		return(true);
	for(i=1;i<=cfg.sys_nodes;i++) { /* Tell user, if online */
		if(node.useron==usernumber && !(node.misc&NODE_POFF)
			&& (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
deuce's avatar
deuce committed
			safe_snprintf(str,sizeof(str),text[EmailNodeMsg],cfg.node_num,useron.alias);
	if(i>cfg.sys_nodes) {	/* User wasn't online, so leave short msg */
		safe_snprintf(str,sizeof(str),text[UserSentYouMail],useron.alias);