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
	BOOL	is_sub = is_valid_subnum(&cfg, smb->subnum);
252

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

261 262 263 264 265 266 267 268 269
	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;

270
	if(msg->hdr.type == SMB_MSG_TYPE_POLL && post != NULL && is_sub) {
rswindell's avatar
rswindell committed
271 272
		char* answer;
		int longest_answer = 0;
rswindell's avatar
rswindell committed
273

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

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

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

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

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

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

	if(!t)
		return(0);

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

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

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

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

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

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

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

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

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

	if(!filelength(fileno(smb.sid_fp))) {			/* Empty base */
		smb_close(&smb);
652
		return(0);
653
	}
654 655 656
	if((i=smb_locksmbhdr(&smb))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
657
		return(0);
658
	}
659 660 661
	if((i=smb_getlastidx(&smb,&idx))!=0) {
		smb_close(&smb);
		errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
662
		return(0);
663
	}
664 665 666 667
	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));
668 669 670 671 672 673 674 675 676
	smb_unlocksmbhdr(&smb);
	smb_close(&smb);
	if(ptr)
		(*ptr)=idx.number;
	if(t)
		(*t)=idx.time;
	return(total);
}