readmail.cpp 22.9 KB
Newer Older
1 2 3 4 5 6
/* Synchronet private mail reading function */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *																			*
 * 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"

24 25 26
static char mail_listing_flag(smbmsg_t* msg)
{
	if(msg->hdr.attr&MSG_DELETE)				return '-';
27
	if(msg->hdr.attr&MSG_SPAM)					return 'S';
28 29 30 31 32 33 34 35 36 37 38
	if(msg->hdr.attr&MSG_REPLIED)				return 'R';
	if(msg->hdr.attr&MSG_READ)					return ' ';
	if(msg->hdr.attr&MSG_PERMANENT)				return 'p';
	if(msg->hdr.attr&MSG_LOCKED)				return 'L';
	if(msg->hdr.attr&MSG_KILLREAD)				return 'K';
	if(msg->hdr.attr&MSG_NOREPLY)				return '#';
	if(msg->from_net.type || msg->to_net.type)	return 'N';
	if(msg->hdr.attr&MSG_ANONYMOUS)				return 'A';
	return '*';
}

39 40 41
/****************************************************************************/
/* Reads mail waiting for usernumber.                                       */
/****************************************************************************/
rswindell's avatar
rswindell committed
42
void sbbs_t::readmail(uint usernumber, int which, long lm_mode)
43
{
44 45
	char	str[256],str2[256],done=0,domsg=1
			,*p;
46
	char 	tmp[512];
47 48
	int		i;
	uint32_t u,v;
49
	int		mismatches=0,act;
50
	uint	unum;
51
    long    l,last_mode;
52 53 54 55
	ulong	last;
	bool	replied;
	mail_t	*mail;
	smbmsg_t msg;
56
	char search_str[128] = "";
57

58 59 60 61 62 63 64 65
	if(which==MAIL_SENT)
		act=NODE_RSML;
	else if(which==MAIL_ALL)
		act=NODE_SYSP;
	else
		act=NODE_RMAL;
	action=act;

66 67 68 69
	if(cfg.readmail_mod[0] && !readmail_inside) {
		char cmdline[256];

		readmail_inside = true;
rswindell's avatar
rswindell committed
70
		safe_snprintf(cmdline, sizeof(cmdline), "%s %d %u %lu", cfg.readmail_mod, which, usernumber, lm_mode);
71 72 73 74 75
		exec_bin(cmdline, &main_csi);
		readmail_inside = false;
		return;
	}

76 77 78 79 80
	if(which==MAIL_SENT && useron.rest&FLAG('K')) {
		bputs(text[R_ReadSentMail]);
		return;
	}

81 82 83 84
	msg.total_hfields=0;			/* init to NULL, cause not allocated yet */

	if((i=smb_stack(&smb,SMB_STACK_PUSH))!=0) {
		errormsg(WHERE,ERR_OPEN,"MAIL",i);
85 86
		return; 
	}
87
	SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
88
	smb.retry_time=cfg.smb_retry_time;
89
	smb.subnum=INVALID_SUB;
90 91 92
	if((i=smb_open(&smb))!=0) {
		smb_stack(&smb,SMB_STACK_POP);
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
93 94
		return; 
	}
95 96

	if(cfg.sys_misc&SM_SYSVDELM && (SYSOP || cfg.sys_misc&SM_USRVDELM))
rswindell's avatar
rswindell committed
97
		lm_mode |= LM_INCDEL;
98
	mail=loadmail(&smb,&smb.msgs,usernumber,which,lm_mode);
99
	last_mode = lm_mode;
100
	if(!smb.msgs) {
101 102 103 104 105
		if(which==MAIL_SENT)
			bputs(text[NoMailSent]);
		else if(which==MAIL_ALL)
			bputs(text[NoMailOnSystem]);
		else
rswindell's avatar
rswindell committed
106
			bprintf(text[NoMailWaiting], lm_mode&LM_UNREAD ? "un-read mail" : "mail");
107 108
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
109 110
		return; 
	}
111 112 113

	last=smb.status.last_msg;

114
	const char* order = (lm_mode&LM_REVERSE) ? "newest" : "oldest";
115
	if(smb.msgs>1 && which!=MAIL_ALL) {
116
		if(which==MAIL_SENT)
117
			bprintf(text[MailSentLstHdr], order);
118
		else
119
			bprintf(text[MailWaitingLstHdr], order);
120

121
		for(smb.curmsg=0;smb.curmsg<smb.msgs && !msgabort();smb.curmsg++) {
122 123 124
			if(msg.total_hfields)
				smb_freemsgmem(&msg);
			msg.total_hfields=0;
125
			msg.idx.offset=mail[smb.curmsg].offset;
126
			if(loadmsg(&msg,mail[smb.curmsg].number) < 1)
127 128
				continue;
			smb_unlockmsghdr(&smb,&msg);
rswindell's avatar
rswindell committed
129 130
			bprintf(P_TRUNCATE|(msg.hdr.auxattr&MSG_HFIELDS_UTF8)
				,msghdr_text(&msg, MailWaitingLstFmt), smb.curmsg+1
131 132 133
				,which==MAIL_SENT ? msg.to
				: (msg.hdr.attr&MSG_ANONYMOUS) && !SYSOP ? text[Anonymous]
				: msg.from
134
				,mail_listing_flag(&msg)
135 136
				,msg.subj);
			smb_freemsgmem(&msg);
137 138
			msg.total_hfields=0; 
		}
139 140

		ASYNC;
141 142 143 144
		if(sys_status&SS_ABORT) {
			domsg=0;
			smb.curmsg=0;
		} else {
145
			bprintf(text[StartWithN],1L);
146 147 148 149
			l=getnum(smb.msgs);
			if(l>0)
				smb.curmsg=l-1;
			else if(l==-1) {
deuce's avatar
deuce committed
150
				free(mail);
151 152
				smb_close(&smb);
				smb_stack(&smb,SMB_STACK_POP);
153
				return;
154
			}
155 156
			else
				smb.curmsg=l;
157 158 159
		}
		sys_status&=~SS_ABORT; 
	}
160
	else {
161
		smb.curmsg=0;
162
		if(which==MAIL_ALL)
163 164
			domsg=0; 
	}
165
	if(which==MAIL_SENT) {
166
		logline("E","read sent mail");
167
	} else if(which==MAIL_ALL) {
168
		logline("S+","read all mail");
169
	} else {
170
		logline("E","read mail");
171
	}
rswindell's avatar
rswindell committed
172 173 174
	const char* menu_file = (which == MAIL_ALL ? "allmail" : which==MAIL_SENT ? "sentmail" : "mailread");
	if(useron.misc&RIP)
		menu(menu_file); 
175
	current_msg=&msg;	/* For MSG_* @-codes and bbs.msg_* property values */
176 177 178 179 180 181 182
	while(online && !done) {
		action=act;

		if(msg.total_hfields)
			smb_freemsgmem(&msg);
		msg.total_hfields=0;

183 184 185 186 187
		msg.idx.offset=mail[smb.curmsg].offset;
		msg.idx.number=mail[smb.curmsg].number;
		msg.idx.to=mail[smb.curmsg].to;
		msg.idx.from=mail[smb.curmsg].from;
		msg.idx.subj=mail[smb.curmsg].subj;
188 189

		if((i=smb_locksmbhdr(&smb))!=0) {
190
			errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
191 192
			break; 
		}
193 194 195

		if((i=smb_getstatus(&smb))!=0) {
			smb_unlocksmbhdr(&smb);
196
			errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
197 198
			break; 
		}
199 200
		smb_unlocksmbhdr(&smb);

201
		if(smb.status.last_msg!=last || lm_mode != last_mode) { 	/* New messages */
202
			last=smb.status.last_msg;
deuce's avatar
deuce committed
203
			free(mail);
204
			order = (lm_mode&LM_REVERSE) ? "newest" : "oldest";
205 206
			mail=loadmail(&smb,&smb.msgs,usernumber,which,lm_mode);   /* So re-load */
			if(!smb.msgs)
207
				break;
208 209 210 211 212 213 214 215 216
			if(lm_mode != last_mode)
				smb.curmsg = 0;
			else {
				for(smb.curmsg=0;smb.curmsg<smb.msgs;smb.curmsg++)
					if(mail[smb.curmsg].number==msg.idx.number)
						break;
				if(smb.curmsg>=smb.msgs)
					smb.curmsg=(smb.msgs-1);
			}
217
			last_mode = lm_mode;
218 219
			continue; 
		}
220

221
		if(loadmsg(&msg,mail[smb.curmsg].number) < 0) {	/* Message header gone */
222
			if(mismatches>5) {	/* We can't do this too many times in a row */
223
				errormsg(WHERE,ERR_CHK,"message number",mail[smb.curmsg].number);
224 225
				break; 
			}
deuce's avatar
deuce committed
226
			free(mail);
227 228
			mail=loadmail(&smb,&smb.msgs,usernumber,which,lm_mode);
			if(!smb.msgs)
229
				break;
230
			last_mode = lm_mode;
231 232
			if(smb.curmsg>(smb.msgs-1))
				smb.curmsg=(smb.msgs-1);
233
			mismatches++;
234 235
			continue; 
		}
236 237 238 239 240 241 242
		smb_unlockmsghdr(&smb,&msg);
		msg.idx.attr=msg.hdr.attr;

		mismatches=0;

		if(domsg && !(sys_status&SS_ABORT)) {

243
			if(!show_msg(&smb, &msg
244
				,msg.from_ext && msg.idx.from==1 && !msg.from_net.type
245 246
					? 0:P_NOATCODES))
				errormsg(WHERE,"showing", "mail message", msg.hdr.number, smb.last_error);
247
			download_msg_attachments(&smb, &msg, which == MAIL_YOUR);
248
			if(which==MAIL_YOUR && !(msg.hdr.attr&MSG_READ)) {
249
				mail[smb.curmsg].attr|=MSG_READ;
250 251 252 253 254 255
				if(thisnode.status==NODE_INUSE)
					telluser(&msg);
				if(msg.total_hfields)
					smb_freemsgmem(&msg);
				msg.total_hfields=0;
				msg.idx.offset=0;						/* Search by number */
256
				if(smb_locksmbhdr(&smb)==SMB_SUCCESS) {	/* Lock the entire base */
257
					if(loadmsg(&msg,msg.idx.number) >= 0) {
258
						msg.hdr.attr|=MSG_READ;
259 260
						if(msg.hdr.attr&MSG_KILLREAD)
							msg.hdr.attr|=MSG_DELETE;
261 262
						msg.idx.attr=msg.hdr.attr;
						if((i=smb_putmsg(&smb,&msg))!=0)
263
							errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
264 265 266 267
						smb_unlockmsghdr(&smb,&msg); 
					}
					smb_unlocksmbhdr(&smb); 
				}
268 269
				if(!msg.total_hfields) {				/* unsuccessful reload */
					domsg=0;
270 271
					continue; 
				}
272
			}
273
		}
274 275
		else domsg=1;

rswindell's avatar
rswindell committed
276 277
		if(useron.misc&WIP)
			menu(menu_file); 
278 279 280

		ASYNC;
		if(which==MAIL_SENT)
281
			bprintf(text[ReadingSentMail],smb.curmsg+1,smb.msgs);
282
		else if(which==MAIL_ALL)
283
			bprintf(text[ReadingAllMail],smb.curmsg+1,smb.msgs);
284
		else
285
			bprintf(text[ReadingMail],smb.curmsg+1,smb.msgs);
286 287 288 289 290
		sprintf(str,"ADFLNQRT?<>[]{}()-+/!%c%c%c%c"
			,TERM_KEY_LEFT
			,TERM_KEY_RIGHT
			,TERM_KEY_HOME
			,TERM_KEY_END);
291
		if(SYSOP)
292
			strcat(str,"CUSPH");
293 294 295 296 297 298
		if(which == MAIL_YOUR)
			strcat(str,"K");	// kill all (visible)
		else
			strcat(str,"E");	// edit msg
		if(which != MAIL_SENT)
			strcat(str,"V");	// View SPAM toggle
299
		l=getkeys(str,smb.msgs);
300 301 302
		if(l&0x80000000L) {
			if(l==-1)	/* ctrl-c */
				break;
303
			smb.curmsg=(l&~0x80000000L)-1;
304 305
			continue; 
		}
306
		switch(l) {
307 308 309 310
			case '!':
				lm_mode ^= LM_REVERSE;
				domsg=0;
				break;
311 312 313 314 315 316 317
			case 'A':   /* Auto-reply to last piece */
			case 'R':
				if(l==(cfg.sys_misc&SM_RA_EMU ? 'A' : 'R'))  /* re-read last message */
					break;

				if(which==MAIL_SENT)
					break;
318 319
				if((msg.hdr.attr&(MSG_NOREPLY|MSG_ANONYMOUS)) && !SYSOP) {
					bputs(text[CantReplyToMsg]);
320 321
					break; 
				}
322

323
				if(msg.from_net.addr==NULL)
324
					SAFECOPY(str,msg.from);
325
				else if(msg.from_net.type==NET_FIDO) 	/* FidoNet type */
326
					SAFEPRINTF2(str,"%s@%s",msg.from
327
						,smb_faddrtoa((faddr_t *)msg.from_net.addr,tmp));
328
				else if(msg.from_net.type==NET_INTERNET || strchr((char*)msg.from_net.addr,'@')!=NULL) {
329 330 331 332 333 334 335 336
					if(msg.replyto_net.type==NET_INTERNET)
						SAFECOPY(str,(char *)msg.replyto_net.addr);
					else
						SAFECOPY(str,(char *)msg.from_net.addr);
				} else
					SAFEPRINTF2(str,"%s@%s",msg.from,(char*)msg.from_net.addr);

				SAFECOPY(str2,str);
337 338 339 340 341 342 343 344

				bputs(text[Email]);
				if(!getstr(str,64,K_EDIT|K_AUTODEL))
					break;
				msg.hdr.number=msg.idx.number;
				smb_getmsgidx(&smb,&msg);

				if(!stricmp(str2,str))		/* Reply to sender */
rswindell's avatar
rswindell committed
345
					SAFEPRINTF(str2,text[Regarding], msghdr_field(&msg, msg.subj));
346
				else						/* Reply to other */
rswindell's avatar
rswindell committed
347
					SAFEPRINTF3(str2,text[RegardingByOn], msghdr_field(&msg, msg.subj), msghdr_field(&msg, msg.from, tmp)
348
						,timestr(msg.hdr.when_written.time));
349 350 351

				p=strrchr(str,'@');
				if(p) { 							/* name @addr */
352
					replied=netmail(str,msg.subj,WM_NONE, &smb, &msg);
rswindell's avatar
rswindell committed
353
					SAFEPRINTF(str2,text[DeleteMailQ],msghdr_field(&msg, msg.from)); 
354
				}
355 356
				else {
					if(!msg.from_net.type && !stricmp(str,msg.from))
357
						replied=email(msg.idx.from,str2,msg.subj,WM_NONE, &smb, &msg);
358
					else if(!stricmp(str,"SYSOP"))
359
						replied=email(1,str2,msg.subj,WM_NONE, &smb, &msg);
360
					else if((i=finduser(str))!=0)
361
						replied=email(i,str2,msg.subj,WM_NONE, &smb, &msg);
362 363
					else
						replied=false;
rswindell's avatar
rswindell committed
364
					SAFEPRINTF(str2,text[DeleteMailQ],msghdr_field(&msg, msg.from)); 
365
				}
366 367 368 369 370 371

				if(replied==true && !(msg.hdr.attr&MSG_REPLIED)) {
					if(msg.total_hfields)
						smb_freemsgmem(&msg);
					msg.total_hfields=0;
					msg.idx.offset=0;
372
					if(smb_locksmbhdr(&smb)==SMB_SUCCESS) {	/* Lock the entire base */
373
						if(loadmsg(&msg,msg.idx.number) >= 0) {
374 375 376
							msg.hdr.attr|=MSG_REPLIED;
							msg.idx.attr=msg.hdr.attr;
							if((i=smb_putmsg(&smb,&msg))!=0)
377
								errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
378 379 380 381
							smb_unlockmsghdr(&smb,&msg); 
						}
						smb_unlocksmbhdr(&smb);
					}
382 383
				}

384
				if(msg.hdr.attr&MSG_DELETE || noyes(str2)) {
385
					if(smb.curmsg<smb.msgs-1) smb.curmsg++;
386 387 388 389 390
					else done=1;
					break;	}
				/* Case 'D': must follow! */
			case 'D':   /* Delete last piece (toggle) */
				if(msg.hdr.attr&MSG_PERMANENT) {
391
					bprintf(text[CantDeleteMsg], smb.curmsg + 1);
392
					domsg=0;
393 394
					break; 
				}
395 396 397 398
				if(msg.total_hfields)
					smb_freemsgmem(&msg);
				msg.total_hfields=0;
				msg.idx.offset=0;
399
				if(smb_locksmbhdr(&smb)==SMB_SUCCESS) {	/* Lock the entire base */
400
					if(loadmsg(&msg,msg.idx.number) >= 0) {
401 402 403 404
						msg.hdr.attr^=MSG_DELETE;
						msg.idx.attr=msg.hdr.attr;
		//				  mail[smb.curmsg].attr=msg.hdr.attr;
						if((i=smb_putmsg(&smb,&msg))!=0)
405
							errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
406 407 408 409
						smb_unlockmsghdr(&smb,&msg); 
					}
					smb_unlocksmbhdr(&smb);
				}
410
				if(smb.curmsg<smb.msgs-1) smb.curmsg++;
411 412
				else done=1;
				break;
413 414 415
			case 'K':	/* Kill All Mail */
				SAFEPRINTF(str,text[DeleteMailQ],"everyone");
				if(!noyes(str))
416
					delallmail(usernumber, which, /* permanent: */false, lm_mode);
417 418
				domsg=false;
				break;
419 420 421
			case 'F':  /* Forward last piece */
				domsg=0;
				bputs(text[ForwardMailTo]);
422
				if(!getstr(str, sizeof(str) - 1, K_TRIM))
423
					break;
424
				smb_getmsgidx(&smb,&msg);
425
				if(!forwardmsg(&smb, &msg, str))
426 427
					break;
				domsg=1;
428
				if(smb.curmsg<smb.msgs-1) smb.curmsg++;
429
				else done=1;
430
				if(msg.hdr.attr&(MSG_PERMANENT | MSG_DELETE))
431
					break;
rswindell's avatar
rswindell committed
432
				SAFEPRINTF(str2,text[DeleteMailQ],msghdr_field(&msg, msg.from));
433 434
				if(!yesno(str2))
					break;
435
				smb_freemsgmem(&msg);
436
				msg.idx.offset=0;
437
				if(smb_locksmbhdr(&smb)==SMB_SUCCESS) {	/* Lock the entire base */
438
					if(loadmsg(&msg,msg.idx.number) >= 0) {
439 440 441 442
						msg.hdr.attr|=MSG_DELETE;
						msg.idx.attr=msg.hdr.attr;
		//				  mail[smb.curmsg].attr=msg.hdr.attr;
						if((i=smb_putmsg(&smb,&msg))!=0)
443
							errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
444 445 446 447
						smb_unlockmsghdr(&smb,&msg); 
					}
					smb_unlocksmbhdr(&smb);
				}
448 449
				break;
			case 'H':
450
				domsg=0;
451
				dump_msghdr(&msg);
452 453 454
				break;
			case 'L':     /* List mail */
				domsg=0;
455 456 457 458 459 460 461 462
				if(cfg.listmsgs_mod[0]) {
					char cmdline[256];

					safe_snprintf(cmdline, sizeof(cmdline), "%s %s %d %u %lu", cfg.listmsgs_mod, "mail", which, usernumber, lm_mode);
					exec_bin(cmdline, &main_csi);
					break;
				}

463 464
				bprintf(text[StartWithN],(long)smb.curmsg+1);
				if((i=getnum(smb.msgs))>0)
465 466 467 468
					i--;
				else if(i==-1)
					break;
				else
469
					i=smb.curmsg;
470
				if(which==MAIL_SENT)
471
					bprintf(text[MailSentLstHdr], order);
472
				else if(which==MAIL_ALL)
473
					bprintf(text[MailOnSystemLstHdr], order);
474
				else
475
					bprintf(text[MailWaitingLstHdr], order);
deuce's avatar
deuce committed
476
				for(u=i;u<smb.msgs && !msgabort();u++) {
477 478 479
					if(msg.total_hfields)
						smb_freemsgmem(&msg);
					msg.total_hfields=0;
480
					msg.idx.offset=mail[u].offset;
481
					if(loadmsg(&msg,mail[u].number) < 0)
482 483 484
						continue;
					smb_unlockmsghdr(&smb,&msg);
					if(which==MAIL_ALL)
rswindell's avatar
rswindell committed
485 486
						bprintf(P_TRUNCATE|(msg.hdr.auxattr&MSG_HFIELDS_UTF8)
							,msghdr_text(&msg, MailOnSystemLstFmt)
487
							,u+1,msg.from,msg.to
488
							,mail_listing_flag(&msg)
489 490
							,msg.subj);
					else
rswindell's avatar
rswindell committed
491 492
						bprintf(P_TRUNCATE|(msg.hdr.auxattr&MSG_HFIELDS_UTF8)
							,msghdr_text(&msg, MailWaitingLstFmt),u+1
493 494 495
							,which==MAIL_SENT ? msg.to
							: (msg.hdr.attr&MSG_ANONYMOUS) && !SYSOP
							? text[Anonymous] : msg.from
496
							,mail_listing_flag(&msg)
497 498
							,msg.subj);
					smb_freemsgmem(&msg);
499 500
					msg.total_hfields=0; 
				}
501 502 503 504 505
				break;
			case 'Q':
				done=1;
				break;
			case 'C':   /* Change attributes of last piece */
506
				i=chmsgattr(&msg);
507 508 509 510 511 512
				if(msg.hdr.attr==i)
					break;
				if(msg.total_hfields)
					smb_freemsgmem(&msg);
				msg.total_hfields=0;
				msg.idx.offset=0;
513
				if(smb_locksmbhdr(&smb)==SMB_SUCCESS) {	/* Lock the entire base */
514
					if(loadmsg(&msg,msg.idx.number) >= 0) {
515 516
						msg.hdr.attr=msg.idx.attr=(ushort)i;
						if((i=smb_putmsg(&smb,&msg))!=0)
517
							errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
518 519 520 521
						smb_unlockmsghdr(&smb,&msg); 
					}
					smb_unlocksmbhdr(&smb);
				}
522 523
				break;
			case '>':
524 525
				for(u=smb.curmsg+1;u<smb.msgs;u++)
					if(mail[u].subj==msg.idx.subj)
526
						break;
527 528
				if(u<smb.msgs)
					smb.curmsg=u;
529
				else {
530
					domsg=0;
531 532
					bputs(text[NoMessagesFound]);
				}
533 534
				break;
			case '<':   /* Search Title backward */
535
				for(i=smb.curmsg-1;i>-1;i--)
536 537 538
					if(mail[i].subj==msg.idx.subj)
						break;
				if(i>-1)
539
					smb.curmsg=i;
540
				else {
541
					domsg=0;
542 543
					bputs(text[NoMessagesFound]);
				}
544
				break;
545
			case ')':
546 547
			case '}':   /* Search Author forward */
				strcpy(str,msg.from);
548 549
				for(u=smb.curmsg+1;u<smb.msgs;u++)
					if(mail[u].from==msg.idx.from)
550
						break;
551 552
				if(u<smb.msgs)
					smb.curmsg=u;
553
				else {
554
					domsg=0;
555 556
					bputs(text[NoMessagesFound]);
				}
557 558
				break;
			case 'N':   /* Got to next un-read message */
559 560
				for(u=smb.curmsg+1;u<smb.msgs;u++)
					if(!(mail[u].attr&MSG_READ))
561
						break;
562 563
				if(u<smb.msgs)
					smb.curmsg=u;
564
				else {
565
					domsg=0;
566 567
					bputs(text[NoMessagesFound]);
				}
568
				break;
569
			case '(':
570 571
			case '{':   /* Search Author backward */
				strcpy(str,msg.from);
572 573 574 575 576 577 578 579
				if(smb.curmsg > 0) {
					for(u=smb.curmsg-1;;u--) {
						if(mail[u].from==msg.idx.from) {
							smb.curmsg=u;
							break;
						}
						if(u==0) {
							domsg=0;
580
							bputs(text[NoMessagesFound]);
581 582 583 584
							break;
						}
					}
				}
585 586 587
				break;
			case ']':   /* Search To User forward */
				strcpy(str,msg.to);
588 589
				for(u=smb.curmsg+1;u<smb.msgs;u++)
					if(mail[u].to==msg.idx.to)
590
						break;
591 592
				if(u<smb.msgs)
					smb.curmsg=u;
593
				else {
594
					domsg=0;
595 596
					bputs(text[NoMessagesFound]);
				}
597 598 599
				break;
			case '[':   /* Search To User backward */
				strcpy(str,msg.to);
600 601 602 603 604 605 606 607
				if(smb.curmsg > 0) {
					for(u=smb.curmsg-1;;u--) {
						if(mail[u].to==msg.idx.to) {
							smb.curmsg=u;
							break;
						}
						if(u==0) {
							domsg=0;
608
							bputs(text[NoMessagesFound]);
609 610 611 612
							break;
						}
					}
				}
613
				break;
614 615 616 617 618 619 620 621 622 623
			case TERM_KEY_HOME:
				smb.curmsg = 0;
				newline();
				break;
			case TERM_KEY_END:
				smb.curmsg = smb.msgs - 1;
				newline();
				break;
			case TERM_KEY_RIGHT:
				newline();
624
				// fall-through
625 626
			case 0:
			case '+':
627
				if(smb.curmsg<smb.msgs-1) smb.curmsg++;
628 629
				else done=1;
				break;
630 631
			case TERM_KEY_LEFT:
				newline();
632
				// fall-through
633
			case '-':
634
				if(smb.curmsg>0) smb.curmsg--;
635 636 637 638 639 640 641 642
				break;
			case 'S':
				domsg=0;
	/*
				if(!yesno(text[SaveMsgToFile]))
					break;
	*/
				bputs(text[FileToWriteTo]);
643
				if(getstr(str,50,K_LINE))
rswindell's avatar
rswindell committed
644
					msgtotxt(&smb, &msg, str, /* header: */true, /* mode: */GETMSGTXT_ALL);
645 646
				break;
			case 'E':
647
				editmsg(&smb, &msg);
648 649 650
				break;
			case 'T':
				domsg=0;
651 652 653 654 655
				u=smb.curmsg;
				if(u) u++;
				v=u+10;
				if(v>smb.msgs)
					v=smb.msgs;
656 657

				if(which==MAIL_SENT)
658
					bprintf(text[MailSentLstHdr], order);
659
				else if(which==MAIL_ALL)
660
					bprintf(text[MailOnSystemLstHdr], order);
661
				else
662
					bprintf(text[MailWaitingLstHdr], order);
663
				for(;u<v;u++) {
664 665 666
					if(msg.total_hfields)
						smb_freemsgmem(&msg);
					msg.total_hfields=0;
667
					msg.idx.offset=mail[u].offset;
668
					if(loadmsg(&msg,mail[u].number) < 0)
669 670 671
						continue;
					smb_unlockmsghdr(&smb,&msg);
					if(which==MAIL_ALL)
rswindell's avatar
rswindell committed
672 673
						bprintf(P_TRUNCATE|(msg.hdr.auxattr&MSG_HFIELDS_UTF8)
							,msghdr_text(&msg, MailOnSystemLstFmt)
674
							,u+1,msg.from,msg.to
675
							,mail_listing_flag(&msg)
676 677
							,msg.subj);
					else
rswindell's avatar
rswindell committed
678 679
						bprintf(P_TRUNCATE|(msg.hdr.auxattr&MSG_HFIELDS_UTF8)
							,msghdr_text(&msg, MailWaitingLstFmt),u+1
680 681 682
							,which==MAIL_SENT ? msg.to
							: (msg.hdr.attr&MSG_ANONYMOUS) && !SYSOP
							? text[Anonymous] : msg.from
683
							,mail_listing_flag(&msg)
684 685
							,msg.subj);
					smb_freemsgmem(&msg);
686 687
					msg.total_hfields=0; 
				}
688
				smb.curmsg=(u-1);
689
				break;
690
			case 'U':   /* user edit */
691 692
				msg.hdr.number=msg.idx.number;
				smb_getmsgidx(&smb,&msg);
693 694 695 696 697
				unum = msg.idx.from;
				if(unum == 0)
					unum = matchuser(&cfg, msg.from, /*sysop_alias: */FALSE);
				if(unum == 0 && which != MAIL_YOUR)
					unum = msg.idx.to;
698
				if(unum == 0 || unum > lastuser(&cfg)) {
699 700 701
					bputs(text[UnknownUser]);
					domsg=false;
				} else
702
					useredit(unum);
703
				break;
704
#if 0
705 706 707 708
			case 'U':	/* View Unread-Only (toggle) */
			{
				domsg = false;
				if(!(lm_mode&LM_UNREAD)) {
709 710
					if(getmail(&cfg, usernumber, /* Sent: */FALSE, /* attr: */0)
						== getmail(&cfg, usernumber, /* Sent: */FALSE, /* attr: */MSG_READ)) {
711
						bprintf(text[NoMailWaiting], "un-read mail");
712 713 714 715
						break;
					}
				}
				lm_mode ^= LM_UNREAD;
716 717 718
				bprintf("%s: %s"
					,text[DisplayUnreadMessagesOnlyQ]
					,(lm_mode&LM_UNREAD) ? text[On] : text[Off]);
719 720 721
				CRLF;
				break;
			}
722
#endif
723 724 725
			case 'V':	/* View SPAM (toggle) */
			{
				domsg = false;
726
				int spam = getmail(&cfg, usernumber, /* Sent: */FALSE, /* attr: */MSG_SPAM);
727 728 729 730
				if(!spam) {
					bprintf(text[NoMailWaiting], "SPAM");
					break;
				}
731
				if(spam >= getmail(&cfg, usernumber, /* Sent: */FALSE, /* attr: */0)) {
732 733 734
					bprintf(text[NoMailWaiting], "HAM");
					break;
				}
735
				bputs(text[SPAMVisibilityIsNow]);
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
				switch(lm_mode&(LM_SPAMONLY | LM_NOSPAM)) {
					case 0:
						lm_mode |= LM_NOSPAM;
						bputs(text[Off]);
						break;
					case LM_NOSPAM:
						lm_mode ^= (LM_SPAMONLY | LM_NOSPAM);
						bputs(text[Only]);
						break;
					case LM_SPAMONLY:
						lm_mode &= ~LM_SPAMONLY;
						bputs(text[On]);
						break;
				}
				CRLF;
				break;
			}
753
			case 'P':   /* Purge author and all mail to/from */
754
				if(noyes(text[UeditDeleteQ]))
755 756 757
					break;
				msg.hdr.number=msg.idx.number;
				smb_getmsgidx(&smb,&msg);
758 759 760 761 762 763 764
				if((which==MAIL_SENT ? msg.idx.to : msg.idx.from) == 0) {
					bputs(text[UnknownUser]);
					domsg=false;
				} else {
					purgeuser(msg.idx.from);
					if(smb.curmsg<smb.msgs-1) smb.curmsg++;
				}
765
				break;
766 767 768 769 770 771 772 773
			case '/':
				domsg = false;
				int64_t i64;
				if((i64=get_start_msgnum(&smb))<0)
					break;
				bputs(text[SearchStringPrompt]);
				if(!getstr(search_str,40,K_LINE|K_UPPER|K_EDIT|K_AUTODEL))
					break;
774
				searchmail(mail, (long)i64, smb.msgs, which, search_str, order);
775
				break;
776
			case '?':
rswindell's avatar
rswindell committed
777
				menu(menu_file);
778
				if(SYSOP && which==MAIL_SENT)
779
					menu("syssmail");
780
				else if(SYSOP && which==MAIL_YOUR)
781
					menu("sysmailr");   /* Sysop Mail Read */
782 783
				domsg=0;
				break;
784 785 786
				
		} 
	}
787 788 789 790

	if(msg.total_hfields)
		smb_freemsgmem(&msg);

791
	if(smb.msgs)
deuce's avatar
deuce committed
792
		free(mail);
793 794 795 796 797 798 799

	/***************************************/
	/* Delete messages marked for deletion */
	/***************************************/

	if(cfg.sys_misc&SM_DELEMAIL) {
		if((i=smb_locksmbhdr(&smb))!=0) 			/* Lock the base, so nobody */
800
			errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);	/* messes with the index */
801
		else
802 803
			delmail(usernumber,which); 
	}
804 805 806

	smb_close(&smb);
	smb_stack(&smb,SMB_STACK_POP);
807
	current_msg=NULL;
808 809
}

810
long sbbs_t::searchmail(mail_t *mail, long start, long msgs, int which, const char *search, const char* order)