Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

netmail.cpp 14.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* netmail.cpp */

/* Synchronet network mail-related functions */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "qwk.h"

/****************************************************************************/
/****************************************************************************/
43
bool sbbs_t::inetmail(const char *into, const char *subj, long mode, smb_t* resmb, smbmsg_t* remsg)
44
{
45 46
	char	str[256],str2[256],msgpath[256],ch
			,buf[SDT_BLOCK_LEN],*p;
47
	char 	tmp[512];
48 49 50
	char	title[256] = "";
	char	name[256] = "";
	char	addr[256] = "";
51
	char*	editor=NULL;
52 53 54 55 56 57 58 59
	char	your_addr[128];
	ushort	xlat=XLAT_NONE,net=NET_INTERNET;
	int 	i,j,x,file;
	long	l;
	ulong	length,offset;
	FILE	*instream;
	smbmsg_t msg;

60
	if(useron.etoday>=cfg.level_emailperday[useron.level] && !SYSOP && !(useron.exempt&FLAG('M'))) {
61 62 63 64
		bputs(text[TooManyEmailsToday]);
		return(false); 
	}

65 66 67 68 69 70 71 72 73 74 75 76 77 78
	if(into != NULL) {
		SAFECOPY(name,into);
		SAFECOPY(addr,into);
	}
	if(subj != NULL)
		SAFECOPY(title,subj);
	if(remsg != NULL) {
		if(title[0] == 0 && remsg->subj != NULL)
			SAFECOPY(title, remsg->subj);
		if(name[0] == 0 && remsg->from != NULL)
			SAFECOPY(name, remsg->from);
		if(addr[0] == 0 && remsg->from_net.addr != NULL)
			smb_netaddrstr(&remsg->from_net, addr);
	}
79 80 81

	if((!SYSOP && !(cfg.inetmail_misc&NMAIL_ALLOW)) || useron.rest&FLAG('M')) {
		bputs(text[NoNetMailAllowed]);
rswindell's avatar
rswindell committed
82 83
		return(false); 
	}
84 85 86 87

	if(cfg.inetmail_cost && !(useron.exempt&FLAG('S'))) {
		if(useron.cdt+useron.freecdt<cfg.inetmail_cost) {
			bputs(text[NotEnoughCredits]);
rswindell's avatar
rswindell committed
88 89
			return(false); 
		}
90 91
		sprintf(str,text[NetMailCostContinueQ],cfg.inetmail_cost);
		if(noyes(str))
rswindell's avatar
rswindell committed
92 93
			return(false); 
	}
94 95 96 97 98 99 100

	/* Get destination user name */
	p=strrchr(name,'@');
	if(!p)
		p=strrchr(name,'!');
	if(p) {
		*p=0;
rswindell's avatar
rswindell committed
101 102
		truncsp(name); 
	}
103 104

	/* Get this user's internet mailing address */
rswindell's avatar
rswindell committed
105 106
	usermailaddr(&cfg,your_addr
		,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name);
107 108 109 110 111

	bprintf(text[InternetMailing],addr,your_addr);
	action=NODE_SMAL;
	nodesync();

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

117
	SAFEPRINTF(msgpath,"%snetmail.msg",cfg.node_dir);
118
	if(!writemsg(msgpath,nulstr,title,WM_NETMAIL|mode,INVALID_SUB,into,/* from: */your_addr,&editor)) {
119
		bputs(text[Aborted]);
rswindell's avatar
rswindell committed
120 121
		return(false); 
	}
122 123

	if(mode&WM_FILE) {
124
		sprintf(str2,"%sfile/%04u.out",cfg.data_dir,useron.number);
rswindell's avatar
rswindell committed
125
		MKDIR(str2);
126
		sprintf(str2,"%sfile/%04u.out/%s",cfg.data_dir,useron.number,title);
127
		if(fexistcase(str2)) {
128 129
			bputs(text[FileAlreadyThere]);
			remove(msgpath);
rswindell's avatar
rswindell committed
130 131
			return(false); 
		}
rswindell's avatar
rswindell committed
132
		{ /* Remote */
133
			xfer_prot_menu(XFER_UPLOAD);
134
			mnemonics(text[ProtocolOrQuit]);
135
			sprintf(str,"%c",text[YNQP][2]);
136
			for(x=0;x<cfg.total_prots;x++)
rswindell's avatar
rswindell committed
137
				if(cfg.prot[x]->ulcmd[0] && chk_ar(cfg.prot[x]->ar,&useron,&client)) {
138
					sprintf(tmp,"%c",cfg.prot[x]->mnemonic);
rswindell's avatar
rswindell committed
139
					SAFECAT(str,tmp); 
rswindell's avatar
rswindell committed
140
				}
141
			ch=(char)getkeys(str,0);
142
			if(ch==text[YNQP][2] || sys_status&SS_ABORT) {
143 144
				bputs(text[Aborted]);
				remove(msgpath);
rswindell's avatar
rswindell committed
145 146
				return(false); 
			}
147 148
			for(x=0;x<cfg.total_prots;x++)
				if(cfg.prot[x]->ulcmd[0] && cfg.prot[x]->mnemonic==ch
rswindell's avatar
rswindell committed
149
					&& chk_ar(cfg.prot[x]->ar,&useron,&client))
150 151
					break;
			if(x<cfg.total_prots)	/* This should be always */
152
				protocol(cfg.prot[x],XFER_UPLOAD,str2,nulstr,true); 
153
		}
154 155 156
		sprintf(tmp,"%s%s",cfg.temp_dir,title);
		if(!fexistcase(str2) && fexistcase(tmp))
			mv(tmp,str2,0);
157
		l=(long)flength(str2);
158 159 160 161 162
		if(l>0)
			bprintf(text[FileNBytesReceived],title,ultoac(l,tmp));
		else {
			bprintf(text[FileNotReceived],title);
			remove(msgpath);
rswindell's avatar
rswindell committed
163 164 165
			return(false); 
		} 
	}
166

167
	if((i=smb_stack(&smb,SMB_STACK_PUSH))!=SMB_SUCCESS) {
168
		errormsg(WHERE,ERR_OPEN,"MAIL",i);
rswindell's avatar
rswindell committed
169 170
		return(false); 
	}
171
	sprintf(smb.file,"%smail",cfg.data_dir);
172
	smb.retry_time=cfg.smb_retry_time;
173
	smb.subnum=INVALID_SUB;
174
	if((i=smb_open(&smb))!=SMB_SUCCESS) {
175 176
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
177 178
		return(false); 
	}
179 180 181 182

	if(filelength(fileno(smb.shd_fp))<1) {	 /* Create it if it doesn't exist */
		smb.status.max_crcs=cfg.mail_maxcrcs;
		smb.status.max_age=cfg.mail_maxage;
183
		smb.status.max_msgs=0;
184
		smb.status.attr=SMB_EMAIL;
185
		if((i=smb_create(&smb))!=SMB_SUCCESS) {
186 187
			smb_close(&smb);
			smb_stack(&smb,SMB_STACK_POP);
188
			errormsg(WHERE,ERR_CREATE,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
189 190 191
			return(false); 
		} 
	}
192

193
	if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
194 195
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
196
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
197 198
		return(false); 
	}
199

200
	length=(long)flength(msgpath)+2;	 /* +2 for translation string */
201 202 203 204 205 206

	if(length&0xfff00000UL) {
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_LEN,msgpath,length);
rswindell's avatar
rswindell committed
207 208
		return(false); 
	}
209

210
	if((i=smb_open_da(&smb))!=SMB_SUCCESS) {
211 212 213 214
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
215 216
		return(false); 
	}
217 218 219 220 221 222
	if(cfg.sys_misc&SM_FASTMAIL)
		offset=smb_fallocdat(&smb,length,1);
	else
		offset=smb_allocdat(&smb,length,1);
	smb_close_da(&smb);

223
	if((instream=fnopen(&file,msgpath,O_RDONLY|O_BINARY))==NULL) {
224 225 226 227 228
		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);
rswindell's avatar
rswindell committed
229 230
		return(false); 
	}
231 232 233 234 235 236 237 238 239

	setvbuf(instream,NULL,_IOFBF,2*1024);
	fseek(smb.sdt_fp,offset,SEEK_SET);
	xlat=XLAT_NONE;
	fwrite(&xlat,2,1,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);
240 241 242
		if(j<1)
			break;
		if(j>1 && (j!=x || feof(instream)) && buf[j-1]==LF && buf[j-2]==CR)
243 244
			buf[j-1]=buf[j-2]=0;
		fwrite(buf,j,1,smb.sdt_fp);
rswindell's avatar
rswindell committed
245 246
		x=SDT_BLOCK_LEN; 
	}
247 248 249 250 251 252 253
	fflush(smb.sdt_fp);
	fclose(instream);

	memset(&msg,0,sizeof(smbmsg_t));
	msg.hdr.version=smb_ver();
	if(mode&WM_FILE)
		msg.hdr.auxattr|=MSG_FILEATTACH;
254
	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
255
	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
256 257 258 259

	msg.hdr.offset=offset;

	net=NET_INTERNET;
260
	smb_hfield_str(&msg,RECIPIENT,name);
261
	smb_hfield(&msg,RECIPIENTNETTYPE,sizeof(net),&net);
262
	smb_hfield_str(&msg,RECIPIENTNETADDR,addr);
263

rswindell's avatar
rswindell committed
264
	smb_hfield_str(&msg,SENDER,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name);
265 266

	sprintf(str,"%u",useron.number);
267
	smb_hfield_str(&msg,SENDEREXT,str);
268 269 270 271 272 273

	/*
	smb_hfield(&msg,SENDERNETTYPE,sizeof(net),&net);
	smb_hfield(&msg,SENDERNETADDR,strlen(sys_inetaddr),sys_inetaddr);
	*/

274
	/* Security logging */
275
	msg_client_hfields(&msg,&client);
276
	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
277

278
	smb_hfield_str(&msg,SUBJECT,title);
279

280
	add_msg_ids(&cfg, &smb, &msg, remsg);
281 282 283

	if(editor!=NULL)
		smb_hfield_str(&msg,SMB_EDITOR,editor);
284
	smb_hfield_bin(&msg, SMB_COLUMNS, cols);
285 286 287

	smb_dfield(&msg,TEXT_BODY,length);

288
	i=smb_addmsghdr(&smb,&msg,smb_storage_mode(&cfg, &smb));	// calls smb_unlocksmbhdr() 
289 290 291 292
	smb_close(&smb);
	smb_stack(&smb,SMB_STACK_POP);

	smb_freemsgmem(&msg);
293
	if(i!=SMB_SUCCESS) {
294
		errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
295 296 297
		smb_freemsgdat(&smb,offset,length,1);
		return(false); 
	}
298 299 300 301 302

	if(mode&WM_FILE && online==ON_REMOTE)
		autohangup();

	if(cfg.inetmail_sem[0]) 	 /* update semaphore file */
303
		ftouch(cmdstr(cfg.inetmail_sem,nulstr,nulstr,NULL));
304 305 306 307 308
	if(!(useron.exempt&FLAG('S')))
		subtract_cdt(&cfg,&useron,cfg.inetmail_cost);

	useron.emails++;
	logon_emails++;
309
	putuserrec(&cfg,useron.number,U_EMAILS,5,ultoa(useron.emails,tmp,10)); 
310
	useron.etoday++;
311
	putuserrec(&cfg,useron.number,U_ETODAY,5,ultoa(useron.etoday,tmp,10));
312

313
	sprintf(str,"sent Internet Mail to %s (%s)"
314
		,name,addr);
315 316 317 318
	logline("EN",str);
	return(true);
}

319
bool sbbs_t::qnetmail(const char *into, const char *subj, long mode, smb_t* resmb, smbmsg_t* remsg)
320
{
321
	char	str[256],msgpath[128],fulladdr[128]
322
			,buf[SDT_BLOCK_LEN],*addr;
323
	char 	tmp[512];
324 325
	char	title[128] = "";
	char	to[128] = "";
326
	char*	editor=NULL;
327 328 329 330 331 332
	ushort	xlat=XLAT_NONE,net=NET_QWK,touser;
	int 	i,j,x,file;
	ulong	length,offset;
	FILE	*instream;
	smbmsg_t msg;

333
	if(useron.etoday>=cfg.level_emailperday[useron.level] && !SYSOP && !(useron.exempt&FLAG('M'))) {
334 335 336 337
		bputs(text[TooManyEmailsToday]);
		return(false); 
	}

338 339 340 341
	if(into != NULL)
		SAFECOPY(to,into);
	if(subj != NULL)
		SAFECOPY(title,subj);
342 343 344

	if(useron.rest&FLAG('M')) {
		bputs(text[NoNetMailAllowed]);
rswindell's avatar
rswindell committed
345 346
		return(false); 
	}
347 348 349

	addr=strrchr(to,'@');
	if(!addr) {
350 351 352
		bputs(text[InvalidNetMailAddr]);
		return(false); 
	}
353 354 355 356
	*addr=0;
	addr++;
	strupr(addr);
	truncsp(addr);
357
	touser=qwk_route(&cfg,addr,fulladdr,sizeof(fulladdr)-1);
358
	if(!fulladdr[0]) {
359 360 361
		bputs(text[InvalidNetMailAddr]);
		return(false); 
	}
362 363 364

	truncsp(to);
	if(!stricmp(to,"SBBS") && !SYSOP) {
365 366 367
		bputs(text[InvalidNetMailAddr]);
		return(false); 
	}
368 369 370 371 372
	bprintf(text[NetMailing],to,fulladdr
		,useron.alias,cfg.sys_id);
	action=NODE_SMAL;
	nodesync();

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

378
	SAFEPRINTF(msgpath,"%snetmail.msg",cfg.node_dir);
379
	if(!writemsg(msgpath,nulstr,title, (mode|WM_QWKNET|WM_NETMAIL) ,INVALID_SUB,to,/* from: */useron.alias,&editor)) {
380
		bputs(text[Aborted]);
rswindell's avatar
rswindell committed
381 382
		return(false); 
	}
383

384
	if((i=smb_stack(&smb,SMB_STACK_PUSH))!=SMB_SUCCESS) {
385
		errormsg(WHERE,ERR_OPEN,"MAIL",i);
rswindell's avatar
rswindell committed
386 387
		return(false); 
	}
388
	sprintf(smb.file,"%smail",cfg.data_dir);
389
	smb.retry_time=cfg.smb_retry_time;
390
	smb.subnum=INVALID_SUB;
391
	if((i=smb_open(&smb))!=SMB_SUCCESS) {
392 393
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
394 395
		return(false); 
	}
396 397 398

	if(filelength(fileno(smb.shd_fp))<1) {	 /* Create it if it doesn't exist */
		smb.status.max_crcs=cfg.mail_maxcrcs;
399
		smb.status.max_msgs=0;
400 401
		smb.status.max_age=cfg.mail_maxage;
		smb.status.attr=SMB_EMAIL;
402
		if((i=smb_create(&smb))!=SMB_SUCCESS) {
403 404 405
			smb_close(&smb);
			smb_stack(&smb,SMB_STACK_POP);
			errormsg(WHERE,ERR_CREATE,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
406 407 408
			return(false); 
		} 
	}
409

410
	if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
411 412 413
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
414 415
		return(false); 
	}
416

417
	length=(long)flength(msgpath)+2;	 /* +2 for translation string */
418 419 420 421 422 423

	if(length&0xfff00000UL) {
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_LEN,msgpath,length);
rswindell's avatar
rswindell committed
424 425
		return(false); 
	}
426

427
	if((i=smb_open_da(&smb))!=SMB_SUCCESS) {
428 429 430 431
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
rswindell's avatar
rswindell committed
432 433
		return(false); 
	}
434 435 436 437 438 439
	if(cfg.sys_misc&SM_FASTMAIL)
		offset=smb_fallocdat(&smb,length,1);
	else
		offset=smb_allocdat(&smb,length,1);
	smb_close_da(&smb);

440
	if((instream=fnopen(&file,msgpath,O_RDONLY|O_BINARY))==NULL) {
441 442 443 444 445
		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);
rswindell's avatar
rswindell committed
446 447
		return(false); 
	}
448 449 450 451 452 453 454 455 456

	setvbuf(instream,NULL,_IOFBF,2*1024);
	fseek(smb.sdt_fp,offset,SEEK_SET);
	xlat=XLAT_NONE;
	fwrite(&xlat,2,1,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);
457 458 459
		if(j<1)
			break;
		if(j>1 && (j!=x || feof(instream)) && buf[j-1]==LF && buf[j-2]==CR)
460 461
			buf[j-1]=buf[j-2]=0;
		fwrite(buf,j,1,smb.sdt_fp);
rswindell's avatar
rswindell committed
462 463
		x=SDT_BLOCK_LEN; 
	}
464 465 466 467 468 469 470
	fflush(smb.sdt_fp);
	fclose(instream);

	memset(&msg,0,sizeof(smbmsg_t));
	msg.hdr.version=smb_ver();
	if(mode&WM_FILE)
		msg.hdr.auxattr|=MSG_FILEATTACH;
471
	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
472
	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
473 474 475 476

	msg.hdr.offset=offset;

	net=NET_QWK;
477
	smb_hfield_str(&msg,RECIPIENT,to);
478 479
	sprintf(str,"%u",touser);
	smb_hfield_str(&msg,RECIPIENTEXT,str);
480
	smb_hfield(&msg,RECIPIENTNETTYPE,sizeof(net),&net);
481
	smb_hfield_str(&msg,RECIPIENTNETADDR,fulladdr);
482

483
	smb_hfield_str(&msg,SENDER,useron.alias);
484 485

	sprintf(str,"%u",useron.number);
486
	smb_hfield_str(&msg,SENDEREXT,str);
487

488
	/* Security logging */
489
	msg_client_hfields(&msg,&client);
490
	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
491

492
	smb_hfield_str(&msg,SUBJECT,title);
493

494
	add_msg_ids(&cfg, &smb, &msg, /* remsg: */NULL);
495 496 497

	if(editor!=NULL)
		smb_hfield_str(&msg,SMB_EDITOR,editor);
498
	smb_hfield_bin(&msg, SMB_COLUMNS, cols);
499

500 501
	smb_dfield(&msg,TEXT_BODY,length);

502
	i=smb_addmsghdr(&smb,&msg,smb_storage_mode(&cfg, &smb)); // calls smb_unlocksmbhdr() 
503 504 505 506
	smb_close(&smb);
	smb_stack(&smb,SMB_STACK_POP);

	smb_freemsgmem(&msg);
507
	if(i!=SMB_SUCCESS) {
508
		errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
509 510 511
		smb_freemsgdat(&smb,offset,length,1);
		return(false); 
	}
512 513 514

	useron.emails++;
	logon_emails++;
515
	putuserrec(&cfg,useron.number,U_EMAILS,5,ultoa(useron.emails,tmp,10)); 
516
	useron.etoday++;
517
	putuserrec(&cfg,useron.number,U_ETODAY,5,ultoa(useron.etoday,tmp,10));
518

519
	sprintf(str,"sent QWK NetMail to %s (%s)"
520
		,to,fulladdr);
521 522 523
	logline("EN",str);
	return(true);
}