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

getmsg.cpp 18.3 KB
Newer Older
1 2 3
/* Synchronet message retrieval functions */

/* $Id$ */
4
// vi: tabstop=4
5 6 7 8 9

/****************************************************************************
 * @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
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11 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 43 44 45
 *																			*
 * 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.	*
 ****************************************************************************/

/***********************************************************************/
/* Functions that do i/o with messages (posts/mail/auto) or their data */
/***********************************************************************/

#include "sbbs.h"

/****************************************************************************/
/* Loads an SMB message from the open msg base the fastest way possible 	*/
/* first by offset, and if that's the wrong message, then by number.        */
46
/* Returns >=0 if the message was loaded and left locked, otherwise < 0.	*/
47 48 49
/* !WARNING!: If you're going to write the msg index back to disk, you must */
/* Call this function with a msg->idx.offset of 0 (so msg->offset will be	*/
/* initialized correctly)													*/
50 51 52 53 54 55 56 57
/****************************************************************************/
int sbbs_t::loadmsg(smbmsg_t *msg, ulong number)
{
	char str[128];
	int i;

	if(msg->idx.offset) {				/* Load by offset if specified */

58
		if((i=smb_lockmsghdr(&smb,msg)) != SMB_SUCCESS) {
59
			errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
60
			return i;
rswindell's avatar
rswindell committed
61
		}
62 63

		i=smb_getmsghdr(&smb,msg);
64 65
		if(i==SMB_SUCCESS) {
			if(msg->hdr.number==number)
66
				return msg->total_hfields;
67
			/* Wrong offset  */
68
			smb_freemsgmem(msg);
rswindell's avatar
rswindell committed
69
		}
70

71
		smb_unlockmsghdr(&smb,msg);
rswindell's avatar
rswindell committed
72
	}
73 74

	msg->hdr.number=number;
75
	if((i=smb_getmsgidx(&smb,msg))!=SMB_SUCCESS)				 /* Message is deleted */
76
		return i;
77
	if((i=smb_lockmsghdr(&smb,msg))!=SMB_SUCCESS) {
78
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
79
		return i;
rswindell's avatar
rswindell committed
80
	}
81
	if((i=smb_getmsghdr(&smb,msg))!=SMB_SUCCESS) {
82
		SAFEPRINTF4(str,"(%06" PRIX32 ") #%" PRIu32 "/%lu %s",msg->idx.offset,msg->idx.number
83 84 85
			,number,smb.file);
		smb_unlockmsghdr(&smb,msg);
		errormsg(WHERE,ERR_READ,str,i,smb.last_error);
86
		return i;
rswindell's avatar
rswindell committed
87
	}
88
	return msg->total_hfields;
89 90
}

rswindell's avatar
rswindell committed
91
/* Synchronized with atcode()! */
rswindell's avatar
rswindell committed
92
void sbbs_t::show_msgattr(smbmsg_t* msg)
93
{
rswindell's avatar
rswindell committed
94 95 96
	uint16_t attr = msg->hdr.attr;
	uint16_t poll = attr&MSG_POLL_VOTE_MASK;
	uint32_t auxattr = msg->hdr.auxattr;
97 98 99

	bprintf(text[MsgAttr]
		,attr&MSG_PRIVATE	? "Private  "   :nulstr
100
		,attr&MSG_SPAM		? "SPAM  "      :nulstr
101 102 103 104 105 106 107 108 109
		,attr&MSG_READ		? "Read  "      :nulstr
		,attr&MSG_DELETE	? "Deleted  "   :nulstr
		,attr&MSG_KILLREAD	? "Kill  "      :nulstr
		,attr&MSG_ANONYMOUS ? "Anonymous  " :nulstr
		,attr&MSG_LOCKED	? "Locked  "    :nulstr
		,attr&MSG_PERMANENT ? "Permanent  " :nulstr
		,attr&MSG_MODERATED ? "Moderated  " :nulstr
		,attr&MSG_VALIDATED ? "Validated  " :nulstr
		,attr&MSG_REPLIED	? "Replied  "	:nulstr
110
		,attr&MSG_NOREPLY	? "NoReply  "	:nulstr
rswindell's avatar
rswindell committed
111 112
		,poll == MSG_POLL	? "Poll  "		:nulstr
		,poll == MSG_POLL && auxattr&POLL_CLOSED ? "(Closed)  "	:nulstr
113 114 115 116 117 118 119 120 121
		,nulstr
		,nulstr
		,nulstr
		);
}

/****************************************************************************/
/* Displays a message header to the screen                                  */
/****************************************************************************/
rswindell's avatar
rswindell committed
122
void sbbs_t::show_msghdr(smb_t* smb, smbmsg_t* msg)
123
{
124
	char	str[MAX_PATH+1];
125
	char	age[64];
126 127
	char	*sender=NULL;
	int 	i;
rswindell's avatar
rswindell committed
128
	smb_t	saved_smb = this->smb;
129

rswindell's avatar
rswindell committed
130 131
	this->smb = *smb;	// Needed for @-codes and JS bbs.smb_* properties
	current_msg = msg;	// Needed for @-codes and JS bbs.msg_* properties
132

rswindell's avatar
rswindell committed
133 134 135 136 137 138 139
	attr(LIGHTGRAY);
	if(!tos) {
		if(useron.misc&CLRSCRN)
			outchar(FF);
		else
			CRLF;
	}
140
	msghdr_tos = tos;
141
	if(!menu("msghdr", P_NOERROR)) {
142
		bprintf(text[MsgSubj],msg->subj);
143 144
		if(msg->tags && *msg->tags)
			bprintf(text[MsgTags], msg->tags);
145 146 147 148 149 150 151 152 153 154 155 156 157 158
		if(msg->hdr.attr)
			show_msgattr(msg);
		if(msg->to && *msg->to) {
			bprintf(text[MsgTo],msg->to);
			if(msg->to_net.addr!=NULL)
				bprintf(text[MsgToNet],smb_netaddrstr(&msg->to_net,str));
			if(msg->to_ext)
				bprintf(text[MsgToExt],msg->to_ext);
		}
		if(!(msg->hdr.attr&MSG_ANONYMOUS) || SYSOP) {
			bprintf(text[MsgFrom],msg->from);
			if(msg->from_ext)
				bprintf(text[MsgFromExt],msg->from_ext);
			if(msg->from_net.addr!=NULL && strchr(msg->from,'@')==NULL)
159
				bprintf(text[MsgFromNet],smb_netaddrstr(&msg->from_net,str));
160 161 162 163 164 165 166 167 168 169
		}
		if(!(msg->hdr.attr&MSG_POLL) && (msg->upvotes || msg->downvotes))
			bprintf(text[MsgVotes]
				,msg->upvotes, msg->user_voted==1 ? text[PollAnswerChecked] : nulstr
				,msg->downvotes, msg->user_voted==2 ? text[PollAnswerChecked] : nulstr
				,msg->upvotes - msg->downvotes);
		bprintf(text[MsgDate]
			,timestr(msg->hdr.when_written.time)
			,smb_zonestr(msg->hdr.when_written.zone,NULL)
			,age_of_posted_item(age, sizeof(age), msg->hdr.when_written.time - (smb_tzutc(msg->hdr.when_written.zone) * 60)));
rswindell's avatar
rswindell committed
170
		bputs(text[MsgHdrBodySeparator]);
171
	}
172 173 174 175 176
	for(i=0;i<msg->total_hfields;i++) {
		if(msg->hfield[i].type==SENDER)
			sender=(char *)msg->hfield_dat[i];
		if(msg->hfield[i].type==FORWARDED && sender)
			bprintf(text[ForwardedFrom],sender
177
				,timestr(*(time32_t *)msg->hfield_dat[i]));
178
	}
rswindell's avatar
rswindell committed
179
	this->smb = saved_smb;
180 181 182 183 184
}

/****************************************************************************/
/* Displays message header and text (if not deleted)                        */
/****************************************************************************/
rswindell's avatar
rswindell committed
185
bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, long p_mode, post_t* post)
186
{
rswindell's avatar
rswindell committed
187
	char*	txt;
188

189
	if((msg->hdr.type == SMB_MSG_TYPE_NORMAL && post != NULL && (post->upvotes || post->downvotes))
190
		|| msg->hdr.type == SMB_MSG_TYPE_POLL)
rswindell's avatar
rswindell committed
191 192
		msg->user_voted = smb_voted_already(smb, msg->hdr.number
					,cfg.sub[smb->subnum]->misc&SUB_NAME ? useron.name : useron.alias, NET_NONE, NULL);
193

rswindell's avatar
rswindell committed
194
	show_msghdr(smb, msg);
195

rswindell's avatar
rswindell committed
196
	if(msg->hdr.type == SMB_MSG_TYPE_POLL && post != NULL && smb->subnum < cfg.total_subs) {
rswindell's avatar
rswindell committed
197 198
		char* answer;
		int longest_answer = 0;
rswindell's avatar
rswindell committed
199

200 201 202 203 204 205 206 207 208
		int comments=0;
		for(int i = 0; i < msg->total_hfields; i++)
			if(msg->hfield[i].type == SMB_COMMENT) {
				bprintf("%s\r\n", (char*)msg->hfield_dat[i]);
				comments++;
			}
		if(comments)
			CRLF;

rswindell's avatar
rswindell committed
209 210 211 212 213 214 215 216 217 218 219 220 221
		for(int i = 0; i < msg->total_hfields; i++) {
			if(msg->hfield[i].type != SMB_POLL_ANSWER)
				continue;
			answer = (char*)msg->hfield_dat[i];
			int len = strlen(answer);
			if(len > longest_answer)
				longest_answer = len;
		}
		unsigned answers = 0;
		for(int i = 0; i < msg->total_hfields; i++) {
			if(msg->hfield[i].type != SMB_POLL_ANSWER)
				continue;
			answer = (char*)msg->hfield_dat[i];
222
			float pct = post->total_votes ? ((float)post->votes[answers] / post->total_votes)*100.0F : 0.0F;
rswindell's avatar
rswindell committed
223 224 225 226 227 228
			char str[128];
			int width = longest_answer;
			if(width < cols/3) width = cols/3;
			else if(width > cols-20)
				width = cols-20;
			bprintf(text[PollAnswerNumber], answers+1);
rswindell's avatar
rswindell committed
229 230 231
			bool results_visible = false;
			if((msg->hdr.auxattr&POLL_RESULTS_MASK) == POLL_RESULTS_OPEN)
				results_visible = true;
232
			else if((msg->from_net.type == NET_NONE && sub_op(smb->subnum))
rswindell's avatar
rswindell committed
233
				|| smb_msg_is_from(msg, cfg.sub[smb->subnum]->misc&SUB_NAME ? useron.name : useron.alias, NET_NONE, NULL))
rswindell's avatar
rswindell committed
234 235 236 237
				results_visible = true;
			else if((msg->hdr.auxattr&POLL_RESULTS_MASK) == POLL_RESULTS_CLOSED)
				results_visible = (msg->hdr.auxattr&POLL_CLOSED) ? true : false;
			else if((msg->hdr.auxattr&POLL_RESULTS_MASK) != POLL_RESULTS_SECRET)
238
				results_visible = msg->user_voted ? true : false;
rswindell's avatar
rswindell committed
239
			if(results_visible) {
240 241
				safe_snprintf(str, sizeof(str), text[PollAnswerFmt]
					,width, width, answer, post->votes[answers], pct);
242
				backfill(str, pct, cfg.color[clr_votes_full], cfg.color[clr_votes_empty]);
243
				if(msg->user_voted&(1<<answers))
244 245
					bputs(text[PollAnswerChecked]);
			} else {
246
				attr(cfg.color[clr_votes_empty]);
247 248
				bputs(answer);
			}
rswindell's avatar
rswindell committed
249
			CRLF;
rswindell's avatar
rswindell committed
250 251
			answers++;
		}
252
		if(!msg->user_voted && !(useron.misc&EXPERT) && !(msg->hdr.auxattr&POLL_CLOSED) && !(useron.rest&FLAG('V')))
rswindell's avatar
rswindell committed
253
			mnemonics(text[VoteInThisPollNow]);
rswindell's avatar
rswindell committed
254
		return true;
rswindell's avatar
rswindell committed
255
	}
rswindell's avatar
rswindell committed
256 257 258 259 260 261 262 263 264 265
	if((txt=smb_getmsgtxt(smb, msg, 0)) == NULL)
		return false;
	char* p = txt;
	if(!(console&CON_RAW_IN)) {
		p_mode|=P_WORDWRAP;
		p = smb_getplaintext(msg, txt);
		if(p == NULL)
			p = txt;
		else
			bputs(text[MIMEDecodedPlainText]);
266
	}
rswindell's avatar
rswindell committed
267 268 269 270 271 272 273 274 275 276 277 278
	truncsp(p);
	SKIP_CRLF(p);
	putmsg(p, p_mode, msg->columns);
	smb_freemsgtxt(txt);
	if(column)
		CRLF;
	if((txt=smb_getmsgtxt(smb,msg,GETMSGTXT_TAIL_ONLY))==NULL)
		return false;

	putmsg(txt, p_mode&(~P_WORDWRAP));
	smb_freemsgtxt(txt);
	return true;
279 280
}

281 282 283 284 285 286 287 288 289 290 291
void sbbs_t::download_msg_attachments(smb_t* smb, smbmsg_t* msg, bool del)
{
	char str[256];
	char fpath[MAX_PATH+1];
	char* txt;
	int attachment_index = 0;
	bool found = true;
	while((txt=smb_getmsgtxt(smb, msg, 0)) != NULL && found) {
		char filename[MAX_PATH+1] = {0};
		uint32_t filelen = 0;
		uint8_t* filedata;
292
		if((filedata = smb_getattachment(msg, txt, filename, sizeof(filename), &filelen, attachment_index++)) != NULL
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
			&& filename[0] != 0 && filelen > 0) {
			char tmp[32];
			SAFEPRINTF2(str, text[DownloadAttachedFileQ], filename, ultoac(filelen,tmp));
			if(!noyes(str)) {
				SAFEPRINTF2(fpath, "%s%s", cfg.temp_dir, filename);
				FILE* fp = fopen(fpath, "wb");
				if(fp == NULL)
					errormsg(WHERE, ERR_OPEN, fpath, 0);
				else {
					int result = fwrite(filedata, filelen, 1, fp);
					fclose(fp);
					if(!result)
						errormsg(WHERE, ERR_WRITE, fpath, filelen);
					else
						sendfile(fpath, useron.prot, "attachment");
				}
			}
		} else
			found = false;
		smb_freemsgtxt(txt);
	}

	if(msg->hdr.auxattr&MSG_FILEATTACH) {  /* Attached file */
		smb_getmsgidx(smb, msg);
		SAFECOPY(str, msg->subj);					/* filenames (multiple?) in title */
		char *p,*tp,*sp,ch;
		tp=str;
		while(online) {
			p=strchr(tp,' ');
			if(p) *p=0;
			sp=strrchr(tp,'/');              /* sp is slash pointer */
			if(!sp) sp=strrchr(tp,'\\');
			if(sp) tp=sp+1;
			file_t	fd;
rswindell's avatar
rswindell committed
327
			fd.dir=cfg.total_dirs+1;			/* temp dir for file attachments */
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
			padfname(tp,fd.name);
			SAFEPRINTF3(fpath,"%sfile/%04u.in/%s"  /* path is path/fname */
				,cfg.data_dir, msg->idx.to, tp);
			if(!fexistcase(fpath) && msg->idx.from)
				SAFEPRINTF3(fpath,"%sfile/%04u.out/%s"  /* path is path/fname */
					,cfg.data_dir, msg->idx.from,tp);
			long length=(long)flength(fpath);
			if(length<1)
				bprintf(text[FileDoesNotExist], tp);
			else if(!(useron.exempt&FLAG('T')) && cur_cps && !SYSOP
				&& length/(long)cur_cps>(time_t)timeleft)
				bputs(text[NotEnoughTimeToDl]);
			else {
				char 	tmp[512];
				int		i;
				SAFEPRINTF2(str, text[DownloadAttachedFileQ]
					,tp,ultoac(length,tmp));
				if(length>0L && text[DownloadAttachedFileQ][0] && yesno(str)) {
					{	/* Remote User */
						xfer_prot_menu(XFER_DOWNLOAD);
						mnemonics(text[ProtocolOrQuit]);
						strcpy(str,"Q");
						for(i=0;i<cfg.total_prots;i++)
							if(cfg.prot[i]->dlcmd[0]
								&& chk_ar(cfg.prot[i]->ar,&useron,&client)) {
								sprintf(tmp,"%c",cfg.prot[i]->mnemonic);
354
								strcat(str,tmp);
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
							}
						ch=(char)getkeys(str,0);
						for(i=0;i<cfg.total_prots;i++)
							if(cfg.prot[i]->dlcmd[0] && ch==cfg.prot[i]->mnemonic
								&& chk_ar(cfg.prot[i]->ar,&useron,&client))
								break;
						if(i<cfg.total_prots) {
							int error = protocol(cfg.prot[i], XFER_DOWNLOAD, fpath, nulstr, false);
							if(checkprotresult(cfg.prot[i],error,&fd)) {
								if(del)
									remove(fpath);
								logon_dlb+=length;	/* Update stats */
								logon_dls++;
								useron.dls=(ushort)adjustuserrec(&cfg,useron.number
									,U_DLS,5,1);
								useron.dlb=adjustuserrec(&cfg,useron.number
									,U_DLB,10,length);
								bprintf(text[FileNBytesSent]
									,fd.name,ultoac(length,tmp));
								SAFEPRINTF(str
									,"downloaded attached file: %s"
									,fd.name);
377
								logline("D-",str);
378
							}
379 380 381 382
							autohangup();
						}
					}
				}
383 384 385 386
			}
			if(!p)
				break;
			tp=p+1;
387
			while(*tp==' ') tp++;
388 389 390
		}
		// Remove the *.in directory, only if its empty
		SAFEPRINTF2(fpath, "%sfile/%04u.in", cfg.data_dir, msg->idx.to);
391
		rmdir(fpath);
392 393 394
	}
}

395 396 397
/****************************************************************************/
/* Writes message header and text data to a text file						*/
/****************************************************************************/
rswindell's avatar
rswindell committed
398
bool sbbs_t::msgtotxt(smb_t* smb, smbmsg_t* msg, const char *fname, bool header, ulong gettxt_mode)
399
{
deuce's avatar
deuce committed
400
	char	*buf;
401
	char	tmp[128];
402 403 404
	int 	i;
	FILE	*out;

rswindell's avatar
rswindell committed
405 406
	if((out=fnopen(&i,fname,O_WRONLY|O_CREAT|O_APPEND))==NULL) {
		errormsg(WHERE,ERR_OPEN,fname,0);
407
		return false;
408
	}
409 410 411 412 413
	if(header) {
		fprintf(out,"\r\n");
		fprintf(out,"Subj : %s\r\n",msg->subj);
		fprintf(out,"To   : %s",msg->to);
		if(msg->to_net.addr)
414
			fprintf(out," (%s)",smb_netaddrstr(&msg->to_net,tmp));
415 416
		if(msg->to_ext)
			fprintf(out," #%s",msg->to_ext);
417 418 419 420
		fprintf(out,"\r\nFrom : %s",msg->from);
		if(msg->from_ext && !(msg->hdr.attr&MSG_ANONYMOUS))
			fprintf(out," #%s",msg->from_ext);
		if(msg->from_net.addr)
421
			fprintf(out," (%s)",smb_netaddrstr(&msg->from_net,tmp));
422
		fprintf(out,"\r\nDate : %.24s %s"
423
			,timestr(msg->hdr.when_written.time)
424
			,smb_zonestr(msg->hdr.when_written.zone,NULL));
425
		fprintf(out,"\r\n\r\n");
426
	}
427

rswindell's avatar
rswindell committed
428 429
	bool result = false;
	buf=smb_getmsgtxt(smb, msg, gettxt_mode);
430
	if(buf!=NULL) {
431
		strip_invalid_attr(buf);
432
		fputs(buf,out);
433
		smb_freemsgtxt(buf);
rswindell's avatar
rswindell committed
434
		result = true;
435
	} else if(smb_getmsgdatlen(msg)>2)
rswindell's avatar
rswindell committed
436
		errormsg(WHERE,ERR_READ,smb->file,smb_getmsgdatlen(msg));
437
	fclose(out);
rswindell's avatar
rswindell committed
438
	return result;
439 440 441 442 443 444 445
}

/****************************************************************************/
/* Returns message number posted at or after time							*/
/****************************************************************************/
ulong sbbs_t::getmsgnum(uint subnum, time_t t)
{
rswindell's avatar
rswindell committed
446 447 448
    int			i;
	smb_t		smb;
	idxrec_t	idx;
449 450 451 452

	if(!t)
		return(0);

rswindell's avatar
rswindell committed
453 454
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
455
	smb.retry_time=cfg.smb_retry_time;
456
	smb.subnum=subnum;
457
	if((i=smb_open_index(&smb)) != SMB_SUCCESS) {
458
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
459
		return 0;
460
	}
461
	int result = smb_getmsgidx_by_time(&smb, &idx, t);
462
	smb_close(&smb);
463 464 465
	if(result >= SMB_SUCCESS)
		return idx.number - 1;
	return ~0;
466 467 468 469 470 471 472
}

/****************************************************************************/
/* Returns the time of the message number pointed to by 'ptr'               */
/****************************************************************************/
time_t sbbs_t::getmsgtime(uint subnum, ulong ptr)
{
rswindell's avatar
rswindell committed
473 474 475 476
	int 		i;
	smb_t		smb;
	smbmsg_t	msg;
	idxrec_t	lastidx;
477

rswindell's avatar
rswindell committed
478 479
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
480
	smb.retry_time=cfg.smb_retry_time;
481
	smb.subnum=subnum;
482 483
	if((i=smb_open(&smb))!=0) {
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
484
		return(0);
485
	}
486 487
	if(!filelength(fileno(smb.sid_fp))) {			/* Empty base */
		smb_close(&smb);
488
		return(0);
489
	}
490 491 492 493
	msg.offset=0;
	msg.hdr.number=0;
	if(smb_getmsgidx(&smb,&msg)) {				/* Get first message index */
		smb_close(&smb);
494
		return(0);
495
	}
496 497
	if(!ptr || msg.idx.number>=ptr) {			/* ptr is before first message */
		smb_close(&smb);
498 499
		return(msg.idx.time);   				/* so return time of first msg */
	}
500 501 502

	if(smb_getlastidx(&smb,&lastidx)) { 			 /* Get last message index */
		smb_close(&smb);
503
		return(0);
504
	}
505 506
	if(lastidx.number<ptr) {					/* ptr is after last message */
		smb_close(&smb);
507 508
		return(lastidx.time);	 				/* so return time of last msg */
	}
509 510 511 512 513

	msg.idx.time=0;
	msg.hdr.number=ptr;
	if(!smb_getmsgidx(&smb,&msg)) {
		smb_close(&smb);
514
		return(msg.idx.time);
515
	}
516 517 518 519 520 521 522 523

	if(ptr-msg.idx.number < lastidx.number-ptr) {
		msg.offset=0;
		msg.idx.number=0;
		while(msg.idx.number<ptr) {
			msg.hdr.number=0;
			if(smb_getmsgidx(&smb,&msg) || msg.idx.number>=ptr)
				break;
524
			msg.offset++;
525
		}
526
		smb_close(&smb);
527
		return(msg.idx.time);
528
	}
529 530 531 532 533 534

	ptr--;
	while(ptr) {
		msg.hdr.number=ptr;
		if(!smb_getmsgidx(&smb,&msg))
			break;
535
		ptr--;
536
	}
537 538 539 540 541 542 543 544 545
	smb_close(&smb);
	return(msg.idx.time);
}


/****************************************************************************/
/* Returns the total number of msgs in the sub-board and sets 'ptr' to the  */
/* number of the last message in the sub (0) if no messages.				*/
/****************************************************************************/
deuce's avatar
deuce committed
546
ulong sbbs_t::getlastmsg(uint subnum, uint32_t *ptr, time_t *t)
547 548 549
{
	int 		i;
	ulong		total;
rswindell's avatar
rswindell committed
550
	smb_t		smb;
551 552 553 554 555 556 557 558 559
	idxrec_t	idx;

	if(ptr)
		(*ptr)=0;
	if(t)
		(*t)=0;
	if(subnum>=cfg.total_subs)
		return(0);

rswindell's avatar
rswindell committed
560 561
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
562
	smb.retry_time=cfg.smb_retry_time;
563
	smb.subnum=subnum;
564 565
	if((i=smb_open(&smb))!=0) {
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
566
		return(0);
567
	}
568 569 570

	if(!filelength(fileno(smb.sid_fp))) {			/* Empty base */
		smb_close(&smb);
571
		return(0);
572
	}
573 574 575
	if((i=smb_locksmbhdr(&smb))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
576
		return(0);
577
	}
578 579 580
	if((i=smb_getlastidx(&smb,&idx))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
581
		return(0);
582
	}
583
	total=(long)filelength(fileno(smb.sid_fp))/sizeof(idxrec_t);
584 585 586 587 588 589 590 591 592
	smb_unlocksmbhdr(&smb);
	smb_close(&smb);
	if(ptr)
		(*ptr)=idx.number;
	if(t)
		(*t)=idx.time;
	return(total);
}