getmsg.cpp 21.2 KB
Newer Older
1 2 3 4 5 6
/* Synchronet message retrieval functions */

/****************************************************************************
 * @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
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 24 25 26
 *																			*
 * 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.	*
 ****************************************************************************/

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

#include "sbbs.h"
27
#include "utf8.h"
28 29 30 31

/****************************************************************************/
/* 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.        */
32
/* Returns >=0 if the message was loaded and left locked, otherwise < 0.	*/
33 34 35
/* !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)													*/
36 37 38 39 40 41 42 43
/****************************************************************************/
int sbbs_t::loadmsg(smbmsg_t *msg, ulong number)
{
	char str[128];
	int i;

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

44
		if((i=smb_lockmsghdr(&smb,msg)) != SMB_SUCCESS) {
45
			errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
46
			return i;
rswindell's avatar
rswindell committed
47
		}
48 49

		i=smb_getmsghdr(&smb,msg);
50 51
		if(i==SMB_SUCCESS) {
			if(msg->hdr.number==number)
52
				return msg->total_hfields;
53
			/* Wrong offset  */
54
			smb_freemsgmem(msg);
rswindell's avatar
rswindell committed
55
		}
56

57
		smb_unlockmsghdr(&smb,msg);
rswindell's avatar
rswindell committed
58
	}
59 60

	msg->hdr.number=number;
61
	if((i=smb_getmsgidx(&smb,msg))!=SMB_SUCCESS)				 /* Message is deleted */
62
		return i;
63
	if((i=smb_lockmsghdr(&smb,msg))!=SMB_SUCCESS) {
64
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
65
		return i;
rswindell's avatar
rswindell committed
66
	}
67
	if((i=smb_getmsghdr(&smb,msg))!=SMB_SUCCESS) {
68
		SAFEPRINTF4(str,"(%06" PRIX32 ") #%" PRIu32 "/%lu %s",msg->idx.offset,msg->idx.number
69 70 71
			,number,smb.file);
		smb_unlockmsghdr(&smb,msg);
		errormsg(WHERE,ERR_READ,str,i,smb.last_error);
72
		return i;
rswindell's avatar
rswindell committed
73
	}
74
	return msg->total_hfields;
75 76
}

rswindell's avatar
rswindell committed
77
/* Synchronized with atcode()! */
78
void sbbs_t::show_msgattr(const smbmsg_t* msg)
79
{
rswindell's avatar
rswindell committed
80 81 82
	uint16_t attr = msg->hdr.attr;
	uint16_t poll = attr&MSG_POLL_VOTE_MASK;
	uint32_t auxattr = msg->hdr.auxattr;
83
	uint32_t netattr = msg->hdr.netattr;
84

85 86
	char attr_str[64];
	safe_snprintf(attr_str, sizeof(attr_str), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
87
		,attr&MSG_PRIVATE	? "Private  "   :nulstr
88
		,attr&MSG_SPAM		? "SPAM  "      :nulstr
89 90 91 92 93 94 95 96 97
		,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
98
		,attr&MSG_NOREPLY	? "NoReply  "	:nulstr
rswindell's avatar
rswindell committed
99 100
		,poll == MSG_POLL	? "Poll  "		:nulstr
		,poll == MSG_POLL && auxattr&POLL_CLOSED ? "(Closed)  "	:nulstr
101 102 103 104 105 106 107 108 109 110 111
	);

	char auxattr_str[64];
	safe_snprintf(auxattr_str, sizeof(auxattr_str), "%s%s%s%s%s%s%s"
		,auxattr&MSG_FILEREQUEST? "FileRequest  "   :nulstr
		,auxattr&MSG_FILEATTACH	? "FileAttach  "    :nulstr
		,auxattr&MSG_MIMEATTACH	? "MimeAttach  "	:nulstr
		,auxattr&MSG_KILLFILE	? "KillFile  "      :nulstr
		,auxattr&MSG_RECEIPTREQ	? "ReceiptReq  "	:nulstr
		,auxattr&MSG_CONFIRMREQ	? "ConfirmReq  "    :nulstr
		,auxattr&MSG_NODISP		? "DontDisplay  "	:nulstr
112
		);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

	char netattr_str[64];
	safe_snprintf(netattr_str, sizeof(netattr_str), "%s%s%s%s%s%s%s%s"
		,netattr&MSG_LOCAL		? "Local  "			:nulstr
		,netattr&MSG_INTRANSIT	? "InTransit  "     :nulstr
		,netattr&MSG_SENT		? "Sent  "			:nulstr
		,netattr&MSG_KILLSENT	? "KillSent  "      :nulstr
		,netattr&MSG_HOLD		? "Hold  "			:nulstr
		,netattr&MSG_CRASH		? "Crash  "			:nulstr
		,netattr&MSG_IMMEDIATE	? "Immediate  "		:nulstr
		,netattr&MSG_DIRECT		? "Direct  "		:nulstr
		);

	bprintf(text[MsgAttr], attr_str, auxattr_str, netattr_str
		,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr
		,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr
		,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr);
130 131
}

132 133 134 135 136 137 138 139 140 141 142 143
/* Returns a CP437 text.dat string converted to UTF-8, when appropriate */
const char* sbbs_t::msghdr_text(const smbmsg_t* msg, uint index)
{
	if(msg == NULL || !(msg->hdr.auxattr & MSG_HFIELDS_UTF8))
		return text[index];

	if(cp437_to_utf8_str(text[index], msghdr_utf8_text, sizeof(msghdr_utf8_text), /* min-char-val: */'\x80') < 1)
		return text[index];

	return msghdr_utf8_text;
}

rswindell's avatar
rswindell committed
144 145 146 147 148 149 150
// Returns a CP437 version of a message header field or UTF-8 if can_utf8 is true
// Doesn't do CP437->UTF-8 conversion
const char* sbbs_t::msghdr_field(const smbmsg_t* msg, const char* str, char* buf, bool can_utf8)
{
	if(msg == NULL || !(msg->hdr.auxattr & MSG_HFIELDS_UTF8))
		return str;

151
	if(can_utf8)
rswindell's avatar
rswindell committed
152 153 154 155 156
		return str;

	if(buf == NULL)
		buf = msgghdr_field_cp437_str;

157
	strncpy(buf, str, sizeof(msgghdr_field_cp437_str) - 1);
158
	utf8_to_cp437_inplace(buf);
rswindell's avatar
rswindell committed
159 160 161 162

	return buf;
}

163 164 165
/****************************************************************************/
/* Displays a message header to the screen                                  */
/****************************************************************************/
166
void sbbs_t::show_msghdr(smb_t* smb, const smbmsg_t* msg, const char* subject, const char* from, const char* to)
167
{
168
	char	str[MAX_PATH+1];
169
	char	age[64];
170 171
	char	*sender=NULL;
	int 	i;
rswindell's avatar
rswindell committed
172
	smb_t	saved_smb = this->smb;
173
	long	pmode = 0;
174

175 176
	if(smb != NULL)
		this->smb = *smb;	// Needed for @-codes and JS bbs.smb_* properties
Rob Swindell's avatar
Rob Swindell committed
177 178 179 180 181 182 183
	current_msg = msg;		// Needed for @-codes and JS bbs.msg_* properties
	current_msg_subj = msg->subj;
	current_msg_from = msg->from;
	current_msg_to = msg->to;
	if(msg->hdr.auxattr & MSG_HFIELDS_UTF8)
		pmode |= P_UTF8;

rswindell's avatar
rswindell committed
184 185 186 187 188 189
	if(subject != NULL)
		current_msg_subj = subject;
	if(from != NULL)
		current_msg_from = from;
	if(to != NULL)
		current_msg_to = to;
190

rswindell's avatar
rswindell committed
191
	attr(LIGHTGRAY);
rswindell's avatar
rswindell committed
192
	if(row != 0) {
rswindell's avatar
rswindell committed
193 194 195 196 197
		if(useron.misc&CLRSCRN)
			outchar(FF);
		else
			CRLF;
	}
rswindell's avatar
rswindell committed
198
	msghdr_tos = (row == 0);
199
	if(!menu("msghdr", P_NOERROR)) {
200
		bprintf(pmode, msghdr_text(msg, MsgSubj), current_msg_subj);
201 202
		if(msg->tags && *msg->tags)
			bprintf(text[MsgTags], msg->tags);
203
		if(msg->hdr.attr || msg->hdr.netattr || (msg->hdr.auxattr & ~MSG_HFIELDS_UTF8))
204
			show_msgattr(msg);
rswindell's avatar
rswindell committed
205
		if(current_msg_to != NULL && *current_msg_to != 0) {
206
			bprintf(pmode, msghdr_text(msg, MsgTo), current_msg_to);
207 208 209 210 211
			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);
		}
212 213
		if(msg->cc_list != NULL)
			bprintf(text[MsgCarbonCopyList], msg->cc_list);
rswindell's avatar
rswindell committed
214
		if(current_msg_from != NULL && (!(msg->hdr.attr&MSG_ANONYMOUS) || SYSOP)) {
215
			bprintf(pmode, msghdr_text(msg, MsgFrom), current_msg_from);
216 217
			if(msg->from_ext)
				bprintf(text[MsgFromExt],msg->from_ext);
218
			if(msg->from_net.addr!=NULL)
219
				bprintf(text[MsgFromNet],smb_netaddrstr(&msg->from_net,str));
220 221 222 223 224 225 226 227 228 229
		}
		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
230
		bputs(text[MsgHdrBodySeparator]);
231
	}
232 233 234 235 236
	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
237
				,timestr(*(time32_t *)msg->hfield_dat[i]));
238
	}
rswindell's avatar
rswindell committed
239
	this->smb = saved_smb;
rswindell's avatar
rswindell committed
240 241 242
	current_msg_subj = NULL;
	current_msg_from = NULL;
	current_msg_to = NULL;
243 244 245 246 247
}

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

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

rswindell's avatar
rswindell committed
257
	show_msghdr(smb, msg);
258

259 260 261 262 263 264 265 266 267
	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
268
	if(msg->hdr.type == SMB_MSG_TYPE_POLL && post != NULL && smb->subnum < cfg.total_subs) {
rswindell's avatar
rswindell committed
269 270
		char* answer;
		int longest_answer = 0;
rswindell's avatar
rswindell committed
271

rswindell's avatar
rswindell committed
272 273 274 275 276 277 278 279 280 281 282 283 284
		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];
285
			float pct = post->total_votes ? ((float)post->votes[answers] / post->total_votes)*100.0F : 0.0F;
rswindell's avatar
rswindell committed
286 287 288 289 290 291
			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
292 293 294
			bool results_visible = false;
			if((msg->hdr.auxattr&POLL_RESULTS_MASK) == POLL_RESULTS_OPEN)
				results_visible = true;
295
			else if((msg->from_net.type == NET_NONE && sub_op(smb->subnum))
rswindell's avatar
rswindell committed
296
				|| smb_msg_is_from(msg, cfg.sub[smb->subnum]->misc&SUB_NAME ? useron.name : useron.alias, NET_NONE, NULL))
rswindell's avatar
rswindell committed
297 298 299 300
				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)
301
				results_visible = msg->user_voted ? true : false;
rswindell's avatar
rswindell committed
302
			if(results_visible) {
303 304
				safe_snprintf(str, sizeof(str), text[PollAnswerFmt]
					,width, width, answer, post->votes[answers], pct);
305
				backfill(str, pct, cfg.color[clr_votes_full], cfg.color[clr_votes_empty]);
306
				if(msg->user_voted&(1<<answers))
307 308
					bputs(text[PollAnswerChecked]);
			} else {
309
				attr(cfg.color[clr_votes_empty]);
310 311
				bputs(answer);
			}
rswindell's avatar
rswindell committed
312
			CRLF;
rswindell's avatar
rswindell committed
313 314
			answers++;
		}
315
		if(!msg->user_voted && !(useron.misc&EXPERT) && !(msg->hdr.auxattr&POLL_CLOSED) && !(useron.rest&FLAG('V')))
rswindell's avatar
rswindell committed
316
			mnemonics(text[VoteInThisPollNow]);
rswindell's avatar
rswindell committed
317
		return true;
rswindell's avatar
rswindell committed
318
	}
319
	if((txt=smb_getmsgtxt(smb, msg, GETMSGTXT_BODY_ONLY)) == NULL)
rswindell's avatar
rswindell committed
320 321 322
		return false;
	char* p = txt;
	if(!(console&CON_RAW_IN)) {
323 324
		if(strstr(txt, "\x1b[") == NULL)	// Don't word-wrap raw ANSI text
			p_mode|=P_WORDWRAP;
rswindell's avatar
rswindell committed
325 326 327
		p = smb_getplaintext(msg, txt);
		if(p == NULL)
			p = txt;
328
		else if(*p != '\0')
329 330
			bprintf(text[MIMEDecodedPlainTextFmt]
				, msg->text_charset == NULL ? "unspecified (US-ASCII)" : msg->text_charset
331
				, msg->text_subtype == NULL ? "plain" : msg->text_subtype);
332
	}
rswindell's avatar
rswindell committed
333 334
	truncsp(p);
	SKIP_CRLF(p);
335 336 337 338 339
	if(smb_msg_is_utf8(msg)) {
		if(!term_supports(UTF8))
			utf8_normalize_str(txt);
		p_mode |= P_UTF8;
	}
340
	if(smb->subnum < cfg.total_subs) {
341
		p_mode |= cfg.sub[smb->subnum]->pmode;
342 343
		p_mode &= ~cfg.sub[smb->subnum]->n_pmode;
	}
344 345
	if(console & CON_RAW_IN)
		p_mode = P_NOATCODES;
rswindell's avatar
rswindell committed
346 347 348 349 350 351 352 353 354 355
	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;
356 357
}

358 359 360 361 362 363 364
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;
365
	while(found && (txt=smb_getmsgtxt(smb, msg, 0)) != NULL) {
366 367 368
		char filename[MAX_PATH+1] = {0};
		uint32_t filelen = 0;
		uint8_t* filedata;
369
		if((filedata = smb_getattachment(msg, txt, filename, sizeof(filename), &filelen, attachment_index++)) != NULL
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
			&& 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 */
393
		char subj[FIDO_SUBJ_LEN];
Rob Swindell's avatar
Rob Swindell committed
394 395 396 397 398
		int result = smb_getmsgidx(smb, msg);
		if(result != SMB_SUCCESS) {
			errormsg(WHERE, ERR_READ, "index", result, smb->last_error);
			return;
		}
399
		SAFECOPY(subj, msg->subj);					/* filenames (multiple?) in title */
rswindell's avatar
rswindell committed
400
		char *p,*tp,ch;
401
		tp=subj;
402 403 404
		while(online) {
			p=strchr(tp,' ');
			if(p) *p=0;
405
			tp=getfname(tp);
406 407 408 409 410 411
			if(strcspn(tp, ILLEGAL_FILENAME_CHARS) == strlen(tp)) {
				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);
412
				off_t length=flength(fpath);
413 414 415
				if(length<1)
					bprintf(text[FileDoesNotExist], tp);
				else if(!(useron.exempt&FLAG('T')) && cur_cps && !SYSOP
416
					&& (ulong)(length/cur_cps)>timeleft)
417 418 419 420 421
					bputs(text[NotEnoughTimeToDl]);
				else {
					char 	tmp[512];
					int		i;
					SAFEPRINTF2(str, text[DownloadAttachedFileQ]
422
						,getfname(fpath),u64toac(length,tmp));
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
					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);
									SAFECAT(str,tmp);
								}
							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);
441
								if(checkprotresult(cfg.prot[i],error,fpath)) {
442 443 444 445 446
									if(del)
										(void)remove(fpath);
									logon_dlb+=length;	/* Update stats */
									logon_dls++;
									useron.dls=(ushort)adjustuserrec(&cfg,useron.number
447
										,U_DLS,1);
448
									useron.dlb=adjustuserrec(&cfg,useron.number
449
										,U_DLB,length);
450
									bprintf(text[FileNBytesSent]
451
										,getfname(fpath),u64toac(length,tmp));
452 453
									SAFEPRINTF(str
										,"downloaded attached file: %s"
454
										,getfname(fpath));
455 456 457
									logline("D-",str);
								}
								autohangup();
458
							}
459 460 461
						}
					}
				}
462 463 464 465
			}
			if(!p)
				break;
			tp=p+1;
466
			while(*tp==' ') tp++;
467 468 469
		}
		// Remove the *.in directory, only if its empty
		SAFEPRINTF2(fpath, "%sfile/%04u.in", cfg.data_dir, msg->idx.to);
470
		rmdir(fpath);
471 472 473
	}
}

474 475 476
/****************************************************************************/
/* Writes message header and text data to a text file						*/
/****************************************************************************/
rswindell's avatar
rswindell committed
477
bool sbbs_t::msgtotxt(smb_t* smb, smbmsg_t* msg, const char *fname, bool header, ulong gettxt_mode)
478
{
deuce's avatar
deuce committed
479
	char	*buf;
480
	char	tmp[128];
481 482 483
	int 	i;
	FILE	*out;

rswindell's avatar
rswindell committed
484 485
	if((out=fnopen(&i,fname,O_WRONLY|O_CREAT|O_APPEND))==NULL) {
		errormsg(WHERE,ERR_OPEN,fname,0);
486
		return false;
487
	}
488 489 490 491 492
	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)
493
			fprintf(out," (%s)",smb_netaddrstr(&msg->to_net,tmp));
494 495
		if(msg->to_ext)
			fprintf(out," #%s",msg->to_ext);
496 497 498 499
		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)
500
			fprintf(out," (%s)",smb_netaddrstr(&msg->from_net,tmp));
501
		fprintf(out,"\r\nDate : %.24s %s"
502
			,timestr(msg->hdr.when_written.time)
503
			,smb_zonestr(msg->hdr.when_written.zone,NULL));
504
		fprintf(out,"\r\n\r\n");
505
	}
506

rswindell's avatar
rswindell committed
507 508
	bool result = false;
	buf=smb_getmsgtxt(smb, msg, gettxt_mode);
509
	if(buf!=NULL) {
510
		strip_invalid_attr(buf);
511
		fputs(buf,out);
512
		smb_freemsgtxt(buf);
rswindell's avatar
rswindell committed
513
		result = true;
514
	} else if(smb_getmsgdatlen(msg)>2)
rswindell's avatar
rswindell committed
515
		errormsg(WHERE,ERR_READ,smb->file,smb_getmsgdatlen(msg));
516
	fclose(out);
rswindell's avatar
rswindell committed
517
	return result;
518 519 520 521 522 523 524
}

/****************************************************************************/
/* Returns message number posted at or after time							*/
/****************************************************************************/
ulong sbbs_t::getmsgnum(uint subnum, time_t t)
{
rswindell's avatar
rswindell committed
525 526 527
    int			i;
	smb_t		smb;
	idxrec_t	idx;
528 529 530 531

	if(!t)
		return(0);

rswindell's avatar
rswindell committed
532 533
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
534
	smb.retry_time=cfg.smb_retry_time;
535
	smb.subnum=subnum;
536
	if((i=smb_open_index(&smb)) != SMB_SUCCESS) {
537
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
538
		return 0;
539
	}
540
	int result = smb_getmsgidx_by_time(&smb, &idx, t);
541
	smb_close(&smb);
542 543 544
	if(result >= SMB_SUCCESS)
		return idx.number - 1;
	return ~0;
545 546 547 548 549 550 551
}

/****************************************************************************/
/* 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
552 553 554 555
	int 		i;
	smb_t		smb;
	smbmsg_t	msg;
	idxrec_t	lastidx;
556

rswindell's avatar
rswindell committed
557 558
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
559
	smb.retry_time=cfg.smb_retry_time;
560
	smb.subnum=subnum;
561 562
	if((i=smb_open(&smb))!=0) {
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
563
		return(0);
564
	}
565 566
	if(!filelength(fileno(smb.sid_fp))) {			/* Empty base */
		smb_close(&smb);
567
		return(0);
568
	}
569
	msg.idx_offset=0;
570 571 572
	msg.hdr.number=0;
	if(smb_getmsgidx(&smb,&msg)) {				/* Get first message index */
		smb_close(&smb);
573
		return(0);
574
	}
575 576
	if(!ptr || msg.idx.number>=ptr) {			/* ptr is before first message */
		smb_close(&smb);
577 578
		return(msg.idx.time);   				/* so return time of first msg */
	}
579 580 581

	if(smb_getlastidx(&smb,&lastidx)) { 			 /* Get last message index */
		smb_close(&smb);
582
		return(0);
583
	}
584 585
	if(lastidx.number<ptr) {					/* ptr is after last message */
		smb_close(&smb);
586 587
		return(lastidx.time);	 				/* so return time of last msg */
	}
588 589 590 591 592

	msg.idx.time=0;
	msg.hdr.number=ptr;
	if(!smb_getmsgidx(&smb,&msg)) {
		smb_close(&smb);
593
		return(msg.idx.time);
594
	}
595 596

	if(ptr-msg.idx.number < lastidx.number-ptr) {
597
		msg.idx_offset=0;
598 599 600 601 602
		msg.idx.number=0;
		while(msg.idx.number<ptr) {
			msg.hdr.number=0;
			if(smb_getmsgidx(&smb,&msg) || msg.idx.number>=ptr)
				break;
603
			msg.idx_offset++;
604
		}
605
		smb_close(&smb);
606
		return(msg.idx.time);
607
	}
608 609 610 611 612 613

	ptr--;
	while(ptr) {
		msg.hdr.number=ptr;
		if(!smb_getmsgidx(&smb,&msg))
			break;
614
		ptr--;
615
	}
616 617 618 619 620 621 622 623 624
	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
625
ulong sbbs_t::getlastmsg(uint subnum, uint32_t *ptr, time_t *t)
626 627 628
{
	int 		i;
	ulong		total;
rswindell's avatar
rswindell committed
629
	smb_t		smb;
630 631 632 633 634 635 636 637 638
	idxrec_t	idx;

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

rswindell's avatar
rswindell committed
639 640
	ZERO_VAR(smb);
	SAFEPRINTF2(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
641
	smb.retry_time=cfg.smb_retry_time;
642
	smb.subnum=subnum;
643 644
	if((i=smb_open(&smb))!=0) {
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
645
		return(0);
646
	}
647 648 649

	if(!filelength(fileno(smb.sid_fp))) {			/* Empty base */
		smb_close(&smb);
650
		return(0);
651
	}
652 653 654
	if((i=smb_locksmbhdr(&smb))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
655
		return(0);
656
	}
657 658 659
	if((i=smb_getlastidx(&smb,&idx))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
660
		return(0);
661
	}
662 663 664 665
	if(cfg.sub[subnum]->misc & SUB_NOVOTING)
		total = (long)filelength(fileno(smb.sid_fp))/sizeof(idxrec_t);
	else
		total = smb_msg_count(&smb, (1 << SMB_MSG_TYPE_NORMAL) | (1 << SMB_MSG_TYPE_POLL));
666 667 668 669 670 671 672 673 674
	smb_unlocksmbhdr(&smb);
	smb_close(&smb);
	if(ptr)
		(*ptr)=idx.number;
	if(t)
		(*t)=idx.time;
	return(total);
}