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

postmsg.cpp 16.8 KB
Newer Older
1
/* Synchronet user create/post public message routine */
2
// vi: tabstop=4
3 4 5 6 7 8 9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
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
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"

39
int msgbase_open(scfg_t* cfg, smb_t* smb, unsigned int subnum, int* storage, long* dupechk_hashes, uint16_t* xlat)
40
{
41 42
	int i;

43 44 45
	*dupechk_hashes=SMB_HASH_SOURCE_DUPE;
	*xlat=XLAT_NONE;

46 47 48
	if((i=smb_open_sub(cfg, smb, subnum)) != SMB_SUCCESS)
		return i;

49 50 51 52 53 54 55
	if(smb->subnum==INVALID_SUB) {
		/* duplicate message-IDs must be allowed in mail database */
		*dupechk_hashes&=~(1<<SMB_HASH_SOURCE_MSG_ID);
	} else {
		if(cfg->sub[smb->subnum]->misc&SUB_LZH)
			*xlat=XLAT_LZH;
	}
56

57 58 59
	if(smb->status.max_crcs==0)	/* no CRC checking means no body text dupe checking */
		*dupechk_hashes&=~(1<<SMB_HASH_SOURCE_BODY);

60 61 62
	if(filelength(fileno(smb->shd_fp)) < 1) /* MsgBase doesn't exist yet, create it */
		i=smb_create(smb);

63 64
	*storage=smb_storage_mode(cfg, smb);

65
	return i;
66 67 68
}


69
/****************************************************************************/
70 71
/* Posts a message on sub-board number 'subnum'								*/
/* Returns true if posted, false if not.                                    */
72
/****************************************************************************/
73
bool sbbs_t::postmsg(uint subnum, long wm_mode, smb_t* resmb, smbmsg_t* remsg)
74
{
75 76 77 78
	char	str[256];
	char	title[LEN_TITLE+1] = "";
	char	top[256] = "";
	char	touser[64] = "";
79
	char	from[64];
80
	char	tags[64] = "";
81 82
	const char*	editor=NULL;
	const char*	charset=NULL;
83
	char*	msgbuf=NULL;
deuce's avatar
64-bit  
deuce committed
84
	uint16_t xlat;
85
	ushort	msgattr = 0;
86 87 88 89
	int 	i,storage;
	long	dupechk_hashes;
	long	length;
	FILE*	fp;
90
	smbmsg_t msg;
91
	uint	reason;
92 93

	if(remsg) {
94
		SAFECOPY(title, remsg->subj);
95
		if(remsg->hdr.attr&MSG_ANONYMOUS)
96 97 98 99
			SAFECOPY(from,text[Anonymous]);
		else
			SAFECOPY(from,remsg->from);
		// If user posted this message, reply to the original recipient again
100 101 102
		if(remsg->to != NULL
			&& ((remsg->from_ext != NULL && atoi(remsg->from_ext)==useron.number)
				|| stricmp(useron.alias,remsg->from) == 0 || stricmp(useron.name,remsg->from) == 0))
103
			SAFECOPY(touser,remsg->to);
104
		else
105
			SAFECOPY(touser,from);
106
		msgattr=(ushort)(remsg->hdr.attr&MSG_PRIVATE);
107
		sprintf(top,text[RegardingByToOn],title,from,remsg->to
108
			,timestr(remsg->hdr.when_written.time)
109 110 111
			,smb_zonestr(remsg->hdr.when_written.zone,NULL));
		if(remsg->tags != NULL)
			SAFECOPY(tags, remsg->tags);
112
	}
113

114
	/* Security checks */
rswindell's avatar
rswindell committed
115
	if(!can_user_post(&cfg,subnum,&useron,&client,&reason)) {
116 117
		bputs(text[reason]);
		return false;
118
	}
119 120 121 122 123 124 125 126 127 128 129 130

	bprintf(text[Posting],cfg.grp[cfg.sub[subnum]->grp]->sname,cfg.sub[subnum]->lname);
	action=NODE_PMSG;
	nodesync();

	if(!(msgattr&MSG_PRIVATE) && (cfg.sub[subnum]->misc&SUB_PONLY
		|| (cfg.sub[subnum]->misc&SUB_PRIV && !noyes(text[PrivatePostQ]))))
		msgattr|=MSG_PRIVATE;

	if(sys_status&SS_ABORT)
		return(false);

131 132 133 134 135
	if(
#if 0	/* we *do* support internet posts to specific people July-11-2002 */
		!(cfg.sub[subnum]->misc&SUB_INET) &&	// Prompt for TO: user
#endif
		(cfg.sub[subnum]->misc&SUB_TOUSER || msgattr&MSG_PRIVATE || touser[0])) {
136
		if(!touser[0] && !(msgattr&MSG_PRIVATE))
137
			SAFECOPY(touser,"All");
138 139 140 141
		bputs(text[PostTo]);
		i=LEN_ALIAS;
		if(cfg.sub[subnum]->misc&SUB_QNET)
			i=25;
142
		if(cfg.sub[subnum]->misc&SUB_FIDO)
143
			i=FIDO_NAME_LEN-1;
144 145
		if(cfg.sub[subnum]->misc&(SUB_PNET|SUB_INET))
			i=60;
146
		getstr(touser,i,K_LINE|K_EDIT|K_AUTODEL|K_TRIM);
147 148 149
		if(stricmp(touser,"ALL")
		&& !(cfg.sub[subnum]->misc&(SUB_PNET|SUB_FIDO|SUB_QNET|SUB_INET|SUB_ANON))) {
			if(cfg.sub[subnum]->misc&SUB_NAME) {
150
				if(!userdatdupe(useron.number,U_NAME,LEN_NAME,touser)) {
151
					bputs(text[UnknownUser]);
152 153 154
					return(false); 
				} 
			}
155 156 157
			else {
				if((i=finduser(touser))==0)
					return(false);
158 159 160
				username(&cfg,i,touser); 
			} 
		}
161
		if(sys_status&SS_ABORT)
162 163
			return(false); 
	}
164 165

	if(!touser[0])
166
		SAFECOPY(touser,"All");       // Default to ALL
167 168 169 170 171 172

	if(!stricmp(touser,"SYSOP") && !SYSOP)  // Change SYSOP to user #1
		username(&cfg,1,touser);

	if(msgattr&MSG_PRIVATE && !stricmp(touser,"ALL")) {
		bputs(text[NoToUser]);
173 174
		return(false); 
	}
175 176 177 178 179
	if(msgattr&MSG_PRIVATE)
		wm_mode|=WM_PRIVATE;

	if(cfg.sub[subnum]->misc&SUB_AONLY
		|| (cfg.sub[subnum]->misc&SUB_ANON && useron.exempt&FLAG('A')
180
			&& !noyes(text[AnonymousQ]))) {
181
		msgattr|=MSG_ANONYMOUS;
182 183
		wm_mode|=WM_ANON;
	}
184

185
	if(cfg.sub[subnum]->mod_ar!=NULL && cfg.sub[subnum]->mod_ar[0] && chk_ar(cfg.sub[subnum]->mod_ar,&useron,&client))
186 187 188 189 190 191 192 193 194 195
		msgattr|=MSG_MODERATED;

	if(cfg.sub[subnum]->misc&SUB_SYSPERM && sub_op(subnum))
		msgattr|=MSG_PERMANENT;

	if(msgattr&MSG_PRIVATE)
		bputs(text[PostingPrivately]);

	if(msgattr&MSG_ANONYMOUS)
		bputs(text[PostingAnonymously]);
196
	else if(cfg.sub[subnum]->misc&SUB_NAME)
197 198
		bputs(text[UsingRealName]);

199
	msg_tmp_fname(useron.xedit, str, sizeof(str));
200

201
	if((i=smb_stack(&smb,SMB_STACK_PUSH))!=SMB_SUCCESS) {
202
		errormsg(WHERE,ERR_OPEN,cfg.sub[subnum]->code,i,smb.last_error);
203 204
		return(false); 
	}
205

206
	if((i=msgbase_open(&cfg,&smb,subnum,&storage,&dupechk_hashes,&xlat))!=SMB_SUCCESS) {
207
		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
208
		smb_stack(&smb,SMB_STACK_POP);
209 210
		return(false); 
	}
211

212 213 214
	if(remsg != NULL && resmb != NULL && !(wm_mode&WM_QUOTE)) {
		if(quotemsg(resmb, remsg))
			wm_mode |= WM_QUOTE;
215 216 217 218
	}

	if(!writemsg(str,top,title,wm_mode,subnum,touser
		,/* from: */cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias
219
		,&editor, &charset)
220 221 222 223 224 225 226 227 228
		|| (length=(long)flength(str))<1) {	/* Bugfix Aug-20-2003: Reject negative length */
		bputs(text[Aborted]);
		smb_close(&smb);
		smb_stack(&smb,SMB_STACK_POP);
		return(false); 
	}

	bputs(text[WritingIndx]);

229
	if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
230 231
		smb_close(&smb);
		errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
232
		smb_stack(&smb,SMB_STACK_POP);
233 234
		return(false); 
	}
235

236
	if((i=smb_getstatus(&smb))!=SMB_SUCCESS) {
237 238
		smb_close(&smb);
		errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
239
		smb_stack(&smb,SMB_STACK_POP);
240 241
		return(false); 
	}
242

243
	if((msgbuf=(char*)calloc(length+1,sizeof(char))) == NULL) {
244
		smb_close(&smb);
245
		errormsg(WHERE,ERR_ALLOC,"msgbuf",length+1);
246
		smb_stack(&smb,SMB_STACK_POP);
247
		return(false);
248
	}
249

250 251
	if((fp=fopen(str,"rb"))==NULL) {
		free(msgbuf);
252 253
		smb_close(&smb);
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY|O_BINARY);
254
		smb_stack(&smb,SMB_STACK_POP);
255 256
		return(false); 
	}
257

258 259 260 261 262 263 264 265
	i=fread(msgbuf,1,length,fp);
	fclose(fp);
	if(i != length) {
		free(msgbuf);
		smb_close(&smb);
		errormsg(WHERE,ERR_READ,str,length);
		smb_stack(&smb,SMB_STACK_POP);
		return(false);
266
	}
267
	truncsp(msgbuf);
268

269 270 271
	/* ToDo: split body/tail */

	memset(&msg,0,sizeof(msg));
272
	msg.hdr.attr=msgattr;
273
	msg.hdr.when_written.time=msg.hdr.when_imported.time=time32(NULL);
274
	msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
275

276
	msg.hdr.number=smb.status.last_msg+1; /* this *should* be the new message number */
277

278
	smb_hfield_str(&msg,RECIPIENT,touser);
279

280
	SAFECOPY(str,cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias);
281
	smb_hfield_str(&msg,SENDER,str);
282 283

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

286
	/* Security logging */
287
	msg_client_hfields(&msg,&client);
288
	smb_hfield_str(&msg,SENDERSERVER,startup->host_name);
289

290
	smb_hfield_str(&msg,SUBJECT,title);
291

292
	add_msg_ids(&cfg, &smb, &msg, remsg);
293

294
	editor_info_to_msg(&msg, editor, charset);
295 296 297
	
	if((cfg.sub[subnum]->misc&SUB_MSGTAGS)
		&& (tags[0] || text[TagMessageQ][0] == 0 || !noyes(text[TagMessageQ]))) {
298
		bputs(text[TagMessagePrompt]);
299
		getstr(tags, sizeof(tags)-1, K_EDIT|K_LINE|K_TRIM);
300
	}
301 302
	if(tags[0])
		smb_hfield_str(&msg, SMB_TAGS, tags);
303

304 305 306 307 308 309 310 311
	i=smb_addmsg(&smb,&msg,storage,dupechk_hashes,xlat,(uchar*)msgbuf,NULL);
	free(msgbuf);

	if(i==SMB_DUPE_MSG) {
		attr(cfg.color[clr_err]);
		bprintf(text[CantPostMsg], smb.last_error);
	} else if(i!=SMB_SUCCESS)
		errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
312 313 314 315

	smb_close(&smb);
	smb_stack(&smb,SMB_STACK_POP);
	smb_freemsgmem(&msg);
316
	if(i!=SMB_SUCCESS)
317
		return(false); 
318 319

	logon_posts++;
320
	user_posted_msg(&cfg, &useron, 1);
321 322
	bprintf(text[Posted],cfg.grp[cfg.sub[subnum]->grp]->sname
		,cfg.sub[subnum]->lname);
323 324
	sprintf(str,"posted on %s %s"
		,cfg.grp[cfg.sub[subnum]->grp]->sname,cfg.sub[subnum]->lname);
325
	logline("P+",str);
326

327
	signal_sub_sem(&cfg,subnum);
328

329 330
	user_event(EVENT_POST);

331 332 333
	return(true);
}

334
extern "C" void DLLCALL signal_sub_sem(scfg_t* cfg, uint subnum)
335
{
336
	char str[MAX_PATH+1];
337

338
	if(subnum==INVALID_SUB || subnum>=cfg->total_subs)	/* e-mail? */
339 340 341
		return;

	/* signal semaphore files */
342
	if(cfg->sub[subnum]->misc&SUB_FIDO && cfg->echomail_sem[0])		
343
		ftouch(cmdstr(cfg,NULL,cfg->echomail_sem,nulstr,nulstr,str));
344
	if(cfg->sub[subnum]->post_sem[0]) 
345
		ftouch(cmdstr(cfg,NULL,cfg->sub[subnum]->post_sem,nulstr,nulstr,str));
346
}
347

348 349
extern "C" int DLLCALL msg_client_hfields(smbmsg_t* msg, client_t* client)
{
350 351
	int		i;
	char	port[16];
352
	char	date[64];
353

354 355 356
	if(client==NULL)
		return(-1);

357 358 359 360 361 362
	if(client->user!=NULL && (i=smb_hfield_str(msg,SENDERUSERID,client->user))!=SMB_SUCCESS)
		return(i);
	if((i=smb_hfield_str(msg,SENDERTIME,xpDateTime_to_isoDateTimeStr(gmtime_to_xpDateTime(client->time)
		,/* separators: */"","","", /* precision: */0
		,date,sizeof(date))))!=SMB_SUCCESS)
		return(i);
363
	if((i=smb_hfield_str(msg,SENDERIPADDR,client->addr))!=SMB_SUCCESS)
364
		return(i);
365
	if((i=smb_hfield_str(msg,SENDERHOSTNAME,client->host))!=SMB_SUCCESS)
366
		return(i);
367
	if(client->protocol!=NULL && (i=smb_hfield_str(msg,SENDERPROTOCOL,client->protocol))!=SMB_SUCCESS)
368
		return(i);
369 370
	SAFEPRINTF(port,"%u",client->port);
	return smb_hfield_str(msg,SENDERPORT,port);
371 372
}

373
/* Note: support MSG_BODY only, no tails or other data fields (dfields) */
374
/* Adds/generates Message-IDs when needed */
375
extern "C" int DLLCALL savemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, client_t* client, const char* server, char* msgbuf, smbmsg_t* remsg)
376
{
377
	ushort	xlat=XLAT_NONE;
378
	int 	i;
379
	long	dupechk_hashes=SMB_HASH_SOURCE_DUPE;
380

381 382 383
	if(msg==NULL)
		return(SMB_FAILURE);

384
	if(!SMB_IS_OPEN(smb)) {
385
		if(smb->subnum==INVALID_SUB)
386 387
			sprintf(smb->file,"%smail",cfg->data_dir);
		else
388
			sprintf(smb->file,"%s%s",cfg->sub[smb->subnum]->data_dir,cfg->sub[smb->subnum]->code);
389
		smb->retry_time=cfg->smb_retry_time;
390
		if((i=smb_open(smb))!=SMB_SUCCESS)
391 392 393
			return(i);
	}

394 395 396 397 398 399 400 401 402 403
	/* Lock the msgbase early to preserve our message number (used in MSG-IDs) */
	if(!smb->locked && smb_locksmbhdr(smb)!=SMB_SUCCESS)
		return(SMB_ERR_LOCK);

	if(filelength(fileno(smb->shd_fp))>0 && (i=smb_getstatus(smb))!=SMB_SUCCESS) {
		if(smb->locked)
			smb_unlocksmbhdr(smb);
		return(i);
	}

404
	if(smb->subnum==INVALID_SUB) {	/* e-mail */
405

406 407 408 409
		smb->status.max_crcs=cfg->mail_maxcrcs;
		smb->status.max_age=cfg->mail_maxage;
		smb->status.max_msgs=0;	/* unlimited */
		smb->status.attr=SMB_EMAIL;
410

411 412 413
		/* duplicate message-IDs must be allowed in mail database */
		dupechk_hashes&=~(1<<SMB_HASH_SOURCE_MSG_ID);

414
	} else {	/* sub-board */
415

416 417 418 419
		smb->status.max_crcs=cfg->sub[smb->subnum]->maxcrcs;
		smb->status.max_msgs=cfg->sub[smb->subnum]->maxmsgs;
		smb->status.max_age=cfg->sub[smb->subnum]->maxage;
		smb->status.attr=0;
420

421
		if(cfg->sub[smb->subnum]->misc&SUB_LZH)
422
			xlat=XLAT_LZH;
423

424 425 426 427 428
		/* enforce anonymous/private posts only */
 		if(cfg->sub[smb->subnum]->misc&SUB_PONLY)
 			msg->hdr.attr|=MSG_PRIVATE;
 		if(cfg->sub[smb->subnum]->misc&SUB_AONLY)
 			msg->hdr.attr|=MSG_ANONYMOUS;
429 430
	}

431
	if(msg->hdr.when_imported.time==0) {
432
		msg->hdr.when_imported.time=time32(NULL);
433 434
		msg->hdr.when_imported.zone=sys_timezone(cfg);
	}
435 436
	if(msg->hdr.when_written.time==0)	/* Uninitialized */
		msg->hdr.when_written = msg->hdr.when_imported;
437

438
	msg->hdr.number=smb->status.last_msg+1;		/* needed for MSG-ID generation */
439

440 441 442 443 444
	if(smb->status.max_crcs==0)	/* no CRC checking means no body text dupe checking */
		dupechk_hashes&=~(1<<SMB_HASH_SOURCE_BODY);

	if(client!=NULL)
		msg_client_hfields(msg,client);
445 446
	if(server!=NULL)
		smb_hfield_str(msg,SENDERSERVER,server);
447
 
448
	add_msg_ids(cfg, smb, msg, remsg);
449

450
	if((i=smb_addmsg(smb,msg,smb_storage_mode(cfg, smb),dupechk_hashes,xlat,(uchar*)msgbuf, /* tail: */NULL))==SMB_SUCCESS
rswindell's avatar
rswindell committed
451 452 453 454 455 456 457
		&& msg->to!=NULL	/* no recipient means no header created at this stage */) {
		if(smb->subnum == INVALID_SUB) {
			if(msg->to_net.type == NET_FIDO)
				ftouch(cmdstr(cfg,NULL,cfg->netmail_sem,nulstr,nulstr,NULL));
		} else
			signal_sub_sem(cfg,smb->subnum);
	}
458 459
	return(i);
}
460

461
extern "C" int DLLCALL votemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, const char* smsgfmt, const char* votefmt)
462 463 464 465 466 467
{
	int result;
	smbmsg_t remsg;

	ZERO_VAR(remsg);

468 469 470 471 472 473 474
	if(msg->hdr.when_imported.time == 0) {
		msg->hdr.when_imported.time = time32(NULL);
		msg->hdr.when_imported.zone = sys_timezone(cfg);
	}
	if(msg->hdr.when_written.time == 0)	/* Uninitialized */
		msg->hdr.when_written = msg->hdr.when_imported;

475
	add_msg_ids(cfg, smb, msg, /* remsg: */NULL);
476

477 478 479
	/* Look-up thread_back if RFC822 Reply-ID was specified */
	if(msg->hdr.thread_back == 0 && msg->reply_id != NULL) {
		if(smb_getmsgidx_by_msgid(smb, &remsg, msg->reply_id) == SMB_SUCCESS)
rswindell's avatar
rswindell committed
480
			msg->hdr.thread_back = remsg.idx.number;	/* poll or message being voted on */
481 482 483
	}
	if(smb_voted_already(smb, msg->hdr.thread_back, msg->from, (enum smb_net_type)msg->from_net.type, msg->from_net.addr))
		return SMB_DUPE_MSG;
rswindell's avatar
rswindell committed
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
	remsg.hdr.number = msg->hdr.thread_back;
	if((result = smb_getmsgidx(smb, &remsg)) != SMB_SUCCESS)
		return result;
	if((result = smb_getmsghdr(smb, &remsg)) != SMB_SUCCESS)
		return result;
	if(remsg.hdr.auxattr&POLL_CLOSED)
		result = SMB_CLOSED;
	else
		result = smb_addvote(smb, msg, smb_storage_mode(cfg, smb));
	if(result == SMB_SUCCESS && smsgfmt != NULL && remsg.from_ext != NULL) {
		user_t user;
		ZERO_VAR(user);
		user.number = atoi(remsg.from_ext);
		if(getuserdat(cfg, &user) == 0 && 
			(stricmp(remsg.from, user.alias) == 0 || stricmp(remsg.from, user.name) == 0)) {
			char from[256];
			char tstr[128];
501 502
			char smsg[4000];
			char votes[3000] = "";
rswindell's avatar
rswindell committed
503 504 505 506
			if(msg->from_net.type)
				safe_snprintf(from, sizeof(from), "%s (%s)", msg->from, smb_netaddr(&msg->from_net));
			else
				SAFECOPY(from, msg->from);
507
			if(remsg.hdr.type == SMB_MSG_TYPE_POLL && votefmt != NULL) {
508 509 510 511 512
				int answers = 0;
				for(int i=0; i<remsg.total_hfields; i++) {
					if(remsg.hfield[i].type == SMB_POLL_ANSWER) {
						if(msg->hdr.votes&(1<<answers)) {
							char vote[128];
513
							SAFEPRINTF(vote, votefmt, (char*)remsg.hfield_dat[i]);
514 515 516 517 518 519
							SAFECAT(votes, vote);
						}
						answers++;
					}
				}
			}
rswindell's avatar
rswindell committed
520 521 522 523 524
			safe_snprintf(smsg, sizeof(smsg), smsgfmt
				,timestr(cfg, msg->hdr.when_written.time, tstr)
				,cfg->grp[cfg->sub[smb->subnum]->grp]->sname
				,cfg->sub[smb->subnum]->sname
				,from
525 526
				,remsg.subj);
			SAFECAT(smsg, votes);
rswindell's avatar
rswindell committed
527
			putsmsg(cfg, user.number, smsg);
528 529
		}
	}
rswindell's avatar
rswindell committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
	smb_freemsgmem(&remsg);
	return result;
}

extern "C" int DLLCALL closepoll(scfg_t* cfg, smb_t* smb, uint32_t msgnum, const char* username)
{
	int result;
	smbmsg_t msg;

	ZERO_VAR(msg);

	msg.hdr.when_imported.time = time32(NULL);
	msg.hdr.when_imported.zone = sys_timezone(cfg);
	msg.hdr.when_written = msg.hdr.when_imported;
	msg.hdr.thread_back = msgnum;
	smb_hfield_str(&msg, SENDER, username);
546

547
	add_msg_ids(cfg, smb, &msg, /* remsg: */NULL);
548

rswindell's avatar
rswindell committed
549 550 551
	result = smb_addpollclosure(smb, &msg, smb_storage_mode(cfg, smb));

	smb_freemsgmem(&msg);
552
	return result;
rswindell's avatar
rswindell committed
553
}
554 555 556 557 558 559 560 561 562 563

extern "C" int DLLCALL postpoll(scfg_t* cfg, smb_t* smb, smbmsg_t* msg)
{
	if(msg->hdr.when_imported.time == 0) {
		msg->hdr.when_imported.time = time32(NULL);
		msg->hdr.when_imported.zone = sys_timezone(cfg);
	}
	if(msg->hdr.when_written.time == 0)
		msg->hdr.when_written = msg->hdr.when_imported;

564 565
	add_msg_ids(cfg, smb, msg, /* remsg: */NULL);

566 567
	return smb_addpoll(smb, msg, smb_storage_mode(cfg, smb));
}