xtrn_sec.cpp 48.3 KB
Newer Older
1 2 3 4 5 6
/* Synchronet external program/door section and drop file routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "pcbdefs.hpp"
24
#include "qbbsdefs.hpp"
25 26 27 28 29

/****************************************************************************/
/* This is the external programs (doors) section of the bbs                 */
/* Return 1 if no externals available, 0 otherwise. 						*/
/****************************************************************************/
30
int sbbs_t::xtrn_sec(const char* section)
31
{
32
	char	str[MAX_PATH+1];
33

34 35 36
	if(cfg.xtrnsec_mod[0] == '\0') {
		errormsg(WHERE, ERR_CHK, "xtrnsec_mod", 0);
		return 1;
37
	}
38 39
	SAFEPRINTF2(str, "%s %s", cfg.xtrnsec_mod, section);
	return exec_bin(str, &main_csi);
40 41
}

42
const char *hungupstr="\1n\1h%s\1n hung up on \1h%s\1n %s\r\n";
43 44 45 46

/****************************************************************************/
/* Convert from unix time (seconds since 1/70) to julian (days since 1900)	*/
/****************************************************************************/
47
int unixtojulian(time_t unix_time)
48 49 50
{
	int days[12]={0,31,59,90,120,151,181,212,243,273,304,334};
	long j;
51
	struct tm tm;
52

53
	if(localtime_r(&unix_time,&tm)==NULL)
54
		return(0);
55 56
	j=36525L*(1900+tm.tm_year);
	if(!(j%100) && (tm.tm_mon+1)<3)
57 58
		j--;
	j=(j-(1900*36525))/100;
59
	j+=tm.tm_mday+days[tm.tm_mon];
60 61 62 63 64 65
	return(j);
}

/****************************************************************************/
/* Convert julian date into unix format 									*/
/****************************************************************************/
66 67 68
#ifdef __BORLANDC__
#pragma argsused
#endif
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
time_t juliantounix(ulong j)
{
#if 0 /* julian time */
    int days[2][12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
                       0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
    long temp;
	int leap,counter;
	struct tm tm;

	if(!j) return(0L);

	tm.tm_year=((100L*j)/36525L)-1900;
	temp=(long)date.da_year*36525L;
	date.da_year+=1900;
	j-=temp/100L;

	if (!(temp%100)) {
		j++;
87 88
		leap=1; 
	}
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	else leap=0;

	for(date.da_mon=counter=0;counter<12;counter++)
		if(days[leap][counter]<j)
			date.da_mon=counter;

	date.da_day=j-days[leap][date.da_mon];
	date.da_mon++;	/* go from 0 to 1 based */

	curtime.ti_hour=curtime.ti_min=curtime.ti_sec=0;
	return(dostounix(&date,&curtime));
#else
	return((time_t)-1);
#endif
}

105 106 107 108 109
#ifdef __unix__
static void lfexpand(char *str, ulong misc)
{
	char *p;
	char newstr[1024];
rswindell's avatar
rswindell committed
110
	size_t len=0;
111 112 113 114

	if(misc&XTRN_NATIVE)
		return;

rswindell's avatar
rswindell committed
115
	for(p=str;*p && len < sizeof(newstr)-2;p++) {
116 117 118 119 120 121 122 123 124 125 126 127
		if(*p=='\n')
			newstr[len++]='\r';
		newstr[len++]=*p;
	}

	newstr[len]=0;
	strcpy(str,newstr);
}
#else
	#define lfexpand(str, misc)
#endif

128 129 130
/****************************************************************************/
/* Creates various types of xtrn (Doors, Chains, etc.) data (drop) files.	*/
/****************************************************************************/
131
void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tleft
132 133
					,ulong misc)
{
134
	char	str[1024],tmp2[128],*p;
135
	char 	tmp[512];
deuce's avatar
64-bit  
deuce committed
136 137
	/* TODO: 16-bit i */
	int16_t	i;
138
	FILE*	fp;
deuce's avatar
64-bit  
deuce committed
139
	int32_t	l;
140 141
	struct tm tm;
	struct tm tl;
142
	stats_t stats;
143
	long term = term_supports();
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	char	node_dir[MAX_PATH+1];
	char	ctrl_dir[MAX_PATH+1];
	char	data_dir[MAX_PATH+1];
	char	exec_dir[MAX_PATH+1];
	char	text_dir[MAX_PATH+1];
	char	temp_dir[MAX_PATH+1];

	SAFECOPY(node_dir,cfg.node_dir);
	SAFECOPY(ctrl_dir,cfg.ctrl_dir);
	SAFECOPY(data_dir,cfg.data_dir);
	SAFECOPY(exec_dir,cfg.exec_dir);
	SAFECOPY(text_dir,cfg.text_dir);
	SAFECOPY(temp_dir,cfg.temp_dir);

	if(!(misc&XTRN_NATIVE)) {
160
#ifdef _WIN32
161 162
		/* Put Micros~1 shortened paths in drop files when running 16-bit DOS programs */
		GetShortPathName(cfg.node_dir,node_dir,sizeof(node_dir));
163
		GetShortPathName(cfg.ctrl_dir,ctrl_dir,sizeof(ctrl_dir));
164 165 166 167
		GetShortPathName(cfg.data_dir,data_dir,sizeof(data_dir));
		GetShortPathName(cfg.exec_dir,exec_dir,sizeof(exec_dir));
		GetShortPathName(cfg.text_dir,text_dir,sizeof(text_dir));
		GetShortPathName(cfg.temp_dir,temp_dir,sizeof(temp_dir));
168 169
#elif defined(__linux__) && defined(USE_DOSEMU)
		/* These drive mappings must match the Linux/DOSEMU patch in xtrn.cpp: */
170 171 172 173 174 175
		SAFECOPY(node_dir, DOSEMU_NODE_DIR);
		SAFECOPY(ctrl_dir, DOSEMU_CTRL_DIR);
		SAFECOPY(data_dir, DOSEMU_DATA_DIR);
		SAFECOPY(exec_dir, DOSEMU_EXEC_DIR);
		SAFECOPY(text_dir, DOSEMU_TEXT_DIR);
		SAFECOPY(temp_dir, DOSEMU_TEMP_DIR);
176
#endif
177 178 179
	}


180
	if(type==XTRN_SBBS) {	/* SBBS XTRN.DAT file */
Rob Swindell's avatar
Rob Swindell committed
181
		SAFECOPY(tmp,"XTRN.DAT");
182 183
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
184
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
185
		(void)removecase(str);
186
		if((fp=fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT)) == NULL) {
187
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT);
188 189
			return; 
		}
190

191
		safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%s\n"
192 193 194 195
			,name								/* User name */
			,cfg.sys_name						/* System name */
			,cfg.sys_op 						/* Sysop name */
			,cfg.sys_guru); 					/* Guru name */
196
		lfexpand(str,misc);
197
		fwrite(str,strlen(str),1,fp);
198

199
		safe_snprintf(str, sizeof(str), "%s\n%s\n%u\n%u\n%lu\n%s\n%lu\n%" PRIu64 "\n"
200 201
			,ctrl_dir							/* Ctrl dir */
			,data_dir							/* Data dir */
202 203 204
			,cfg.sys_nodes						/* Total system nodes */
			,cfg.node_num						/* Current node */
			,tleft								/* User Timeleft in seconds */
205 206
			,(term & ANSI)						/* User ANSI ? (Yes/Mono/No) */
				? (term & COLOR)
207 208 209
				? "Yes":"Mono":"No"
			,rows								/* User Screen lines */
			,useron.cdt+useron.freecdt);		/* User Credits */
210
		lfexpand(str,misc);
211
		fwrite(str,strlen(str),1,fp);
212

213
		safe_snprintf(str, sizeof(str), "%u\n%u\n%s\n%c\n%u\n%s\n"
214 215
			,useron.level						/* User main level */
			,useron.level						/* User transfer level */
216
			,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp)) /* User birthday (MM/DD/YY) */
217
			,useron.sex ? useron.sex : '?'		/* User sex (M/F) */
218 219
			,useron.number						/* User number */
			,useron.phone); 					/* User phone number */
220
		lfexpand(str,misc);
221
		fwrite(str,strlen(str),1,fp);
222

223
		safe_snprintf(str, sizeof(str), "%u\n%u\n%x\n%lu\n%s\n%s\n"
224
			"%s\n%s\n%s\n%s\n%s\n%s\n%lu\n"
225
			,misc&(XTRN_STDIO|XTRN_CONIO) ? 0:cfg.com_port		/* Com port or 0 if !FOSSIL */
226 227 228 229 230 231 232 233 234 235 236
			,cfg.com_irq						/* Com IRQ */
			,cfg.com_base						/* Com base in hex */
			,dte_rate							/* Com rate */
			,"Yes"								/* Hardware flow ctrl Y/N */
			,"Yes"								/* Locked DTE rate Y/N */
			,cfg.mdm_init						/* Modem initialization string */
			,cfg.mdm_spec						/* Modem special init string */
			,cfg.mdm_term						/* Modem terminal mode init str */
			,cfg.mdm_dial						/* Modem dial string */
			,cfg.mdm_offh						/* Modem off-hook string */
			,cfg.mdm_answ						/* Modem answer string */
237
			,0L
238
			);
239
		lfexpand(str,misc);
240
		fwrite(str,strlen(str),1,fp);
241

Rob Swindell's avatar
Rob Swindell committed
242
		SAFEPRINTF(str,"%u\n",cfg.total_xtrns);
243
		lfexpand(str,misc);
244
		fwrite(str,strlen(str),1,fp);			/* Total external programs */
245 246

		for(i=0;i<cfg.total_xtrns;i++) {		/* Each program's name */
Rob Swindell's avatar
Rob Swindell committed
247 248 249
			if(SYSOP || chk_ar(cfg.xtrn[i]->ar,&useron,&client)) {
				SAFECOPY(str,cfg.xtrn[i]->name);
			} else
250
				str[0]=0;						/* Blank if no access */
rswindell's avatar
rswindell committed
251
			SAFECAT(str,"\n");
252
			lfexpand(str,misc);
253
			fwrite(str,strlen(str),1,fp);
254
		}
255

Rob Swindell's avatar
Rob Swindell committed
256
		SAFEPRINTF2(str,"%s\n%s\n"
257 258 259
			,ltoaf(useron.flags1,tmp)			/* Main flags */
			,ltoaf(useron.flags2,tmp2)			/* Transfer flags */
			);
260
		lfexpand(str,misc);
261
		fwrite(str,strlen(str),1,fp);
262

Rob Swindell's avatar
Rob Swindell committed
263
		safe_snprintf(str, sizeof(str), "%s\n%s\n%lx\n%s\n%s\n%s\n"
264 265
			,ltoaf(useron.exempt,tmp)			/* Exemptions */
			,ltoaf(useron.rest,tmp2)			/* Restrictions */
266
			,(long)useron.expire				/* Expiration date in unix form */
267 268 269 270
			,useron.address 					/* Address */
			,useron.location					/* City/State */
			,useron.zipcode 					/* Zip/Postal code */
			);
271
		lfexpand(str,misc);
272
		fwrite(str,strlen(str),1,fp);
273

274
		safe_snprintf(str, sizeof(str), "%s\n%s\n%d\n%s\n%lu\n%s\n%s\n%s\n%s\n"
275
			"%" PRIx32 "\n%d\n"
276 277
			,ltoaf(useron.flags3,tmp)			/* Flag set #3 */
			,ltoaf(useron.flags4,tmp2)			/* Flag set #4 */
278
			,0									/* Time-slice type */
279 280
			,useron.name						/* Real name/company */
			,cur_rate							/* DCE rate */
281 282 283
			,exec_dir
			,text_dir
			,temp_dir
284 285
			,cfg.sys_id
			,cfg.node_misc
286
			,misc&(XTRN_STDIO|XTRN_CONIO) ? INVALID_SOCKET : client_socket_dup
287
			);
288
		lfexpand(str,misc);
289
		fwrite(str,strlen(str),1,fp);
290

291
		fclose(fp); 
292
	}
293 294

	else if(type==XTRN_WWIV) {	/*	WWIV CHAIN.TXT File */
Rob Swindell's avatar
Rob Swindell committed
295
		SAFECOPY(tmp,"CHAIN.TXT");
296 297
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
298
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
299
		(void)removecase(str);
300
		if((fp=fnopen(NULL, str, O_WRONLY|O_CREAT|O_TRUNC|O_TEXT)) == NULL) {
301
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT);
302 303
			return; 
		}
304

305
		safe_snprintf(str, sizeof(str), "%u\n%s\n%s\n%s\n%u\n%c\n"
306 307 308 309 310
			,useron.number						/* User number */
			,name								/* User name */
			,useron.name						/* User real name */
			,nulstr 							/* User call sign */
			,getage(&cfg,useron.birth)			/* User age */
311
			,useron.sex ? useron.sex : '?');	/* User sex (M/F) */
312
		strupr(str);
313
		lfexpand(str,misc);
314
		fwrite(str,strlen(str),1,fp);
315

316
		safe_snprintf(str, sizeof(str), "%" PRIu64 "\n%s\n%lu\n%ld\n%u\n%u\n%u\n%d\n%u\n"
317 318
			,useron.cdt+useron.freecdt			/* Gold */
			,unixtodstr(&cfg,useron.laston,tmp)	/* User last on date */
319
			,cols 								/* User screen width */
320 321 322 323
			,rows								/* User screen length */
			,useron.level						/* User SL */
			,0									/* Cosysop? */
			,SYSOP								/* Sysop? (1/0) */
324
			,INT_TO_BOOL(term & ANSI)			/* ANSI ? (1/0) */
325
			,online==ON_REMOTE);				/* Remote (1/0) */
326
		lfexpand(str,misc);
327
		fwrite(str,strlen(str),1,fp);
328

329
		safe_snprintf(str, sizeof(str), "%lu\n%s\n%s\n%s\n%lu\n%d\n%s\n%s\n"
330
			"%u\n%u\n%" PRIu64 "\n%u\n%" PRIu64 "\n%u\n%s\n"
331
			,MIN(tleft, INT16_MAX)				/* Time left in seconds */
332 333
			,node_dir							/* Gfiles dir (log dir) */
			,data_dir							/* Data dir */
334
			,"node.log"                         /* Name of log file */
335 336 337 338 339 340 341 342 343 344 345
			,dte_rate							/* DTE rate */
			,cfg.com_port						/* COM port number */
			,cfg.sys_name						/* System name */
			,cfg.sys_op 						/* Sysop name */
			,0									/* Logon time (sec past 12am) */
			,0									/* Current time (sec past 12am) */
			,useron.ulb/1024UL					/* User uploaded kbytes */
			,useron.uls 						/* User uploaded files */
			,useron.dlb/1024UL					/* User downloaded kbytes */
			,useron.dls 						/* User downloaded files */
			,"8N1");                            /* Data, parity, stop bits */
346
		lfexpand(str,misc);
347
		fwrite(str,strlen(str),1,fp);
348

349
		fclose(fp); 
350
	}
351 352

	else if(type==XTRN_GAP) {	/* Gap DOOR.SYS File */
Rob Swindell's avatar
Rob Swindell committed
353
		SAFECOPY(tmp,"DOOR.SYS");
354 355
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
356
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
357
		(void)removecase(str);
358
		if((fp=fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT)) == NULL) {
359
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT);
360 361
			return; 
		}
362

Rob Swindell's avatar
Rob Swindell committed
363
		SAFEPRINTF(str,"COM%d:\n"
364
			,online==ON_REMOTE ? cfg.com_port:0);	/* 01: COM port - 0 if Local */
Rob Swindell's avatar
Rob Swindell committed
365

366
		lfexpand(str,misc);
367
		fwrite(str,strlen(str),1,fp);
368 369 370 371 372 373 374 375
		/* Note about door.sys, line 2 (April-24-2005):
		   It *should* be the DCE rate (any number, including the popular modem
		   DCE rates of 14400, 28800, and 33600).  However, according to Deuce,
		   doors created with the DMlib door kit choke with "Error -25: Illegal
		   baud rate" if this isn't a valid FOSSIL baud (DTE) rate, so we're
		   changing this value to the DTE rate until/unless some other doors
		   have an issue with that. <sigh>
		*/
Rob Swindell's avatar
Rob Swindell committed
376
		safe_snprintf(str, sizeof(str), "%lu\n%u\n%u\n%lu\n%c\n%c\n%c\n%c\n"
377
			,dte_rate /* was cur_rate */		/* 02: DCE rate, see note above */
378 379 380
			,8									/* 03: Data bits */
			,cfg.node_num						/* 04: Node number */
			,dte_rate							/* 05: DTE rate */
381
			,'Y'								/* 06: Screen display */
382 383 384
			,'Y'                                /* 07: Printer toggle */
			,'Y'                                /* 08: Page bell */
			,'Y');                              /* 09: Caller alarm */
385
		lfexpand(str,misc);
386
		fwrite(str,strlen(str),1,fp);
387

Rob Swindell's avatar
Rob Swindell committed
388
		safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%s\n%s\n"
389 390 391 392 393
			,name								/* 10: User name */
			,useron.location					/* 11: User location */
			,useron.phone						/* 12: User home phone */
			,useron.phone						/* 13: User work/data phone */
			,useron.pass);						/* 14: User password */
394
		lfexpand(str,misc);
395
		fwrite(str,strlen(str),1,fp);
396

Rob Swindell's avatar
Rob Swindell committed
397
		safe_snprintf(str, sizeof(str), "%u\n%u\n%s\n%lu\n%lu\n%s\n"
398
			,useron.level						/* 15: User security level */
399
			,MIN(useron.logons, INT16_MAX)		/* 16: User total logons */
400
			,unixtodstr(&cfg,useron.laston,tmp)	/* 17: User last on date */
401 402
			,MIN(tleft, INT16_MAX)				/* 18: User time left in sec */
			,MIN((tleft/60), INT16_MAX)			/* 19: User time left in min */
403 404
			,(term & NO_EXASCII)				/* 20: GR if COLOR ANSI */
				? "7E" : (term & (ANSI|COLOR)) == (ANSI|COLOR) ? "GR" : "NG");
405
		lfexpand(str,misc);
406
		fwrite(str,strlen(str),1,fp);
407

Rob Swindell's avatar
Rob Swindell committed
408
		safe_snprintf(str, sizeof(str), "%lu\n%c\n%s\n%u\n%s\n%u\n%c\n%u\n%u\n"
409
			,rows								/* 21: User screen length */
410
			,(useron.misc&EXPERT) ? 'Y':'N'     /* 22: Expert? (Y/N) */
411 412 413 414
			,ltoaf(useron.flags1,tmp2)			/* 23: Registered conferences */
			,0									/* 24: Conference came from */
			,unixtodstr(&cfg,useron.expire,tmp)	/* 25: User expiration date */
			,useron.number						/* 26: User number */
415
			,useron.prot                        /* 27: Default protocol */
416 417
			,useron.uls 						/* 28: User total uploads */
			,useron.dls);						/* 29: User total downloads */
418
		lfexpand(str,misc);
419
		fwrite(str,strlen(str),1,fp);
420

421
		safe_snprintf(str, sizeof(str), "%u\n%" PRIu64 "\n%s\n%s\n%s\n%s"
422
			"\n%s\n%02d:%02d\n%c\n"
423 424
			,0									/* 30: Kbytes downloaded today */
			,(useron.cdt+useron.freecdt)/1024UL /* 31: Max Kbytes to download today */
425
			,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp))	/* 32: User birthday (MM/DD/YY) */
426 427
			,node_dir							/* 33: Path to MAIN directory */
			,data_dir							/* 34: Path to GEN directory */
428
			,cfg.sys_op 						/* 35: Sysop name */
429
			,useron.handle						/* 36: Alias name */
430 431 432
			,0 // sys_eventtime/60				/* 37: Event time HH:MM */
			,0 // sys_eventtime%60
			,'Y');                              /* 38: Error correcting connection */
433
		lfexpand(str,misc);
434
		fwrite(str,strlen(str),1,fp);
435

436
		localtime_r(&ns_time,&tm);
437
		safe_snprintf(str, sizeof(str), "%c\n%c\n%u\n%" PRIu32 "\n%02d/%02d/%02d\n"
438
			,(term & (NO_EXASCII|ANSI|COLOR)) == ANSI
439 440
				? 'Y':'N'                       /* 39: ANSI supported but NG mode */
			,'Y'                                /* 40: Use record locking */
441
			,cfg.color[clr_external]			/* 41: BBS default color */
442
			,MIN(useron.min, INT16_MAX)			/* 42: Time credits in minutes */
443 444 445
			,tm.tm_mon+1						/* 43: File new-scan date */
			,tm.tm_mday
			,TM_YEAR(tm.tm_year));
446
		lfexpand(str,misc);
447
		fwrite(str,strlen(str),1,fp);
448

deuce's avatar
deuce committed
449
		localtime_r(&logontime,&tm);
450
		localtime32(&useron.laston,&tl);
451 452
		safe_snprintf(str, sizeof(str), "%02d:%02d\n%02d:%02d\n%u\n%u\n%" PRIu64 "\n"
			"%"  PRIu64 "\n%s\n%u\n%u\n"
453
			,tm.tm_hour							/* 44: Time of this call */
454
			,tm.tm_min
455
			,tl.tm_hour							/* 45: Time of last call */
456
			,tl.tm_min
457 458 459 460 461 462
			,999								/* 46: Max daily files available */
			,0									/* 47: Files downloaded so far today */
			,useron.ulb/1024UL					/* 48: Total Kbytes uploaded */
			,useron.dlb/1024UL					/* 49: Total Kbytes downloaded */
			,useron.comment 					/* 50: User comment */
			,0									/* 51: Total doors opened */
463
			,MIN(useron.posts, INT16_MAX));		/* 52: User message left */
464
		lfexpand(str,misc);
465
		fwrite(str,strlen(str),1,fp);
466

467
		fclose(fp);
468
	}
469 470

	else if(type==XTRN_RBBS || type==XTRN_RBBS1) {
Rob Swindell's avatar
Rob Swindell committed
471 472 473 474 475
		if(type==XTRN_RBBS) {
			SAFEPRINTF(tmp,"DORINFO%X.DEF",cfg.node_num);   /* support 1-F */
		} else {
			SAFECOPY(tmp,"DORINFO1.DEF");
		}
476 477
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
478
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
479
		(void)removecase(str);
480
		if((fp = fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT)) == NULL) {
481
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT);
482 483
			return; 
		}
484

Rob Swindell's avatar
Rob Swindell committed
485
		SAFECOPY(tmp,cfg.sys_op);
486
		p=strchr(tmp,' ');
487 488 489
		if(p)
			*(p++)=0;
		else
490
			p=(char*)nulstr;
491

Rob Swindell's avatar
Rob Swindell committed
492
		safe_snprintf(str, sizeof(str), "%s\n%s\n%s\nCOM%d\n%lu BAUD,N,8,1\n%u\n"
493 494 495 496 497 498 499
			,cfg.sys_name						/* Name of BBS */
			,tmp								/* Sysop's firstname */
			,p									/* Sysop's lastname */
			,online==ON_REMOTE ? cfg.com_port:0 /* COM port number, 0 if local */
			,dte_rate							/* DTE rate */
			,0);								/* Network type */
		strupr(str);
500
		lfexpand(str,misc);
501
		fwrite(str,strlen(str),1,fp);
502

Rob Swindell's avatar
Rob Swindell committed
503
		SAFECOPY(tmp,name);
504
		p=strchr(tmp,' ');
505 506 507
		if(p)
			*(p++)=0;
		else
508
			p=(char*)nulstr;
509
		safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%d\n%u\n%lu\n"
510 511 512
			,tmp								/* User's firstname */
			,p									/* User's lastname */
			,useron.location					/* User's city */
513
			,INT_TO_BOOL(term & ANSI)			/* 1=ANSI 0=ASCII */
514
			,useron.level						/* Security level */
515
			,MIN((tleft/60), INT16_MAX)); 		/* Time left in minutes */
516
		strupr(str);
517
		lfexpand(str,misc);
518
		fwrite(str,strlen(str),1,fp);
519

520
		fclose(fp);
521

Rob Swindell's avatar
Rob Swindell committed
522
		SAFECOPY(tmp,"EXITINFO.BBS");
523 524
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
525
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
526
		(void)removecase(str);
527
		if((fp=fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC)) == NULL) {
528
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
529 530
			return; 
		}
531
		getstats(&cfg,0,&stats);
532 533 534 535 536 537 538
		QBBS::exitinfo exitinfo{};
		exitinfo.BaudRate = (uint16_t)dte_rate;
		exitinfo.SysInfo.CallCount = stats.logons;
		exitinfo.UserInfo.Name = name;
		exitinfo.UserInfo.Location = useron.location;
		exitinfo.UserInfo.DataPhone = useron.phone;
		exitinfo.UserInfo.HomePhone = useron.phone;
539
		localtime32(&useron.laston,&tm);
Rob Swindell's avatar
Rob Swindell committed
540
		SAFEPRINTF2(tmp,"%02d:%02d",tm.tm_hour,tm.tm_min);
541
		exitinfo.UserInfo.LastTime = tmp;
542
		unixtodstr(&cfg,useron.laston,tmp);
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
		exitinfo.UserInfo.LastDate = tmp;
		if(useron.misc&DELETED) exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_DELETED;
		if(useron.misc&CLRSCRN) exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_CLRSCRN;
		if(useron.misc&UPAUSE)	exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_MORE;
		if(term & ANSI)			exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_ANSI;
		if(useron.sex=='F')     exitinfo.UserInfo.Attrib |= QBBS::USER_ATTRIB_FEMALE;
		exitinfo.UserInfo.Flags = useron.flags1;
		exitinfo.UserInfo.TimesPosted = useron.posts;
		exitinfo.UserInfo.SecLvl = useron.level;
		exitinfo.UserInfo.Ups = useron.uls;
		exitinfo.UserInfo.Downs = useron.dls;
		exitinfo.UserInfo.UpK = (uint16_t)(useron.ulb/1024UL);
		exitinfo.UserInfo.DownK = (uint16_t)(useron.dlb/1024UL);
		exitinfo.UserInfo.TodayK = (uint16_t)(logon_dlb/1024UL);
		exitinfo.UserInfo.ScreenLength = (int16_t)rows;
deuce's avatar
deuce committed
558
		localtime_r(&logontime,&tm);
Rob Swindell's avatar
Rob Swindell committed
559
		SAFEPRINTF2(tmp,"%02d:%02d",tm.tm_hour,tm.tm_min);
560
		exitinfo.LoginTime = tmp;
561
		unixtodstr(&cfg,(time32_t)logontime,tmp);
562 563
		exitinfo.LoginDate = tmp;
		exitinfo.TimeLimit = cfg.level_timepercall[useron.level];
564
		exitinfo.Credit = (uint32_t)MIN(useron.cdt, UINT32_MAX);
565 566 567 568 569 570 571 572 573 574 575
		exitinfo.UserRecNum = useron.number;
		exitinfo.WantChat = (sys_status & SS_SYSPAGE);
		exitinfo.ScreenClear = (useron.misc & CLRSCRN);
		exitinfo.MorePrompts = (useron.misc & UPAUSE);
		exitinfo.GraphicsMode = !(term & NO_EXASCII);
		exitinfo.ExternEdit = (useron.xedit);
		exitinfo.ScreenLength = (int16_t)rows;
		exitinfo.MNP_Connect = true;
		exitinfo.ANSI_Capable = (term & ANSI);
		exitinfo.RIP_Active = (term & RIP);

576 577
		fwrite(&exitinfo, sizeof(exitinfo), 1, fp);
		fclose(fp);
578
	}
579 580

	else if(type==XTRN_WILDCAT) { /* WildCat CALLINFO.BBS File */
Rob Swindell's avatar
Rob Swindell committed
581
		SAFECOPY(tmp,"CALLINFO.BBS");
582 583
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
584
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
585
		(void)removecase(str);
586
		if((fp=fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT)) == NULL) {
587
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC|O_TEXT);
588 589
			return; 
		}
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613

		if(online==ON_LOCAL) i=5;
		else
			switch(dte_rate) {
				case 300:
					i=1;
					break;
				case 1200:
					i=2;
					break;
				case 2400:
					i=0;
					break;
				case 9600:
					i=3;
					break;
				case 19200:
					i=4;
					break;
				case 38400:
					i=6;
					break;
				default:
					i=7;
614 615
					break; 
		}
Rob Swindell's avatar
Rob Swindell committed
616
		safe_snprintf(str, sizeof(str), "%s\n%u\n%s\n%u\n%lu\n%s\n%s\n%u\n"
617 618 619 620
			,name								/* User name */
			,i									/* DTE rate */
			,useron.location					/* User location */
			,useron.level						/* Security level */
621
			,MIN((tleft/60), INT16_MAX)			/* Time left in min */
622
			,(term & ANSI) ? "COLOR":"MONO"		/* ANSI ??? */
623 624
			,useron.pass						/* Password */
			,useron.number);					/* User number */
625
		lfexpand(str,misc);
626
		fwrite(str,strlen(str),1,fp);
627

628
		localtime_r(&now,&tm);
Rob Swindell's avatar
Rob Swindell committed
629
		safe_snprintf(str, sizeof(str), "%lu\n%02d:%02d\n%02d:%02d %02d/%02d/%02d\n%s\n"
630
			,MIN(tleft, INT16_MAX)			/* Time left in seconds */
631 632 633 634
			,tm.tm_hour,tm.tm_min 			/* Current time HH:MM */
			,tm.tm_hour,tm.tm_min 			/* Current time and date HH:MM */
			,tm.tm_mon+1,tm.tm_mday			/* MM/DD/YY */
			,TM_YEAR(tm.tm_year)
635
			,nulstr);							/* Conferences with access */
636
		lfexpand(str,misc);
637
		fwrite(str,strlen(str),1,fp);
638

639
		localtime32(&useron.laston,&tm);
Rob Swindell's avatar
Rob Swindell committed
640
		safe_snprintf(str, sizeof(str), "%u\n%u\n%u\n%u\n%s\n%s %02u:%02u\n"
641 642 643 644 645 646
			,0									/* Daily download total */
			,0									/* Max download files */
			,0									/* Daily download k total */
			,0									/* Max download k total */
			,useron.phone						/* User phone number */
			,unixtodstr(&cfg,useron.laston,tmp)	/* Last on date and time */
647 648
			,tm.tm_hour						/* MM/DD/YY  HH:MM */
			,tm.tm_min);
649
		lfexpand(str,misc);
650
		fwrite(str,strlen(str),1,fp);
651

652
		localtime_r(&ns_time,&tm);
Rob Swindell's avatar
Rob Swindell committed
653
		safe_snprintf(str, sizeof(str), "%s\n%s\n%02d/%02d/%02d\n%u\n%lu\n%u"
654
			"\n%u\n%u\n"
655 656 657
			,useron.misc&EXPERT 				/* Expert or Novice mode */
				? "EXPERT":"NOVICE"
			,"All"                              /* Transfer Protocol */
658 659
			,tm.tm_mon+1,tm.tm_mday			/* File new-scan date */
			,TM_YEAR(tm.tm_year)				/* in MM/DD/YY */
660 661 662 663 664
			,useron.logons						/* Total logons */
			,rows								/* Screen length */
			,0									/* Highest message read */
			,useron.uls 						/* Total files uploaded */
			,useron.dls);						/* Total files downloaded */
665
		lfexpand(str,misc);
666
		fwrite(str,strlen(str),1,fp);
667

Rob Swindell's avatar
Rob Swindell committed
668
		safe_snprintf(str, sizeof(str), "%u\n%s\nCOM%u\n%s\n%lu\n%s\n%s\n"
669 670 671
			,8									/* Data bits */
			,online==ON_LOCAL?"LOCAL":"REMOTE"  /* Online local or remote */
			,cfg.com_port						/* COMx port */
672
			,getbirthmmddyy(&cfg, useron.birth, tmp, sizeof(tmp))	/* User birthday (MM/DD/YY) */
673 674 675
			,dte_rate							/* DTE rate */
			,"FALSE"                            /* Already connected? */
			,"Normal Connection");              /* Normal or ARQ connect */
676
		lfexpand(str,misc);
677
		fwrite(str,strlen(str),1,fp);
678

679
		localtime_r(&now,&tm);
Rob Swindell's avatar
Rob Swindell committed
680
		safe_snprintf(str, sizeof(str), "%02d/%02d/%02d %02d:%02d\n%u\n%u\n"
681 682 683
			,tm.tm_mon+1,tm.tm_mday			/* Current date MM/DD/YY */
			,TM_YEAR(tm.tm_year)
			,tm.tm_hour,tm.tm_min 			/* Current time HH:MM */
684 685
			,cfg.node_num						/* Node number */
			,0);								/* Door number */
686
		lfexpand(str,misc);
687
		fwrite(str,strlen(str),1,fp);
688

689
		fclose(fp);
690
	}
691 692

	else if(type==XTRN_PCBOARD) { /* PCBoard Files */
Rob Swindell's avatar
Rob Swindell committed
693
		SAFECOPY(tmp,"PCBOARD.SYS");
694 695
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
Rob Swindell's avatar
Rob Swindell committed
696
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
697
		(void)removecase(str);
698
		if((fp = fnopen(NULL, str,O_WRONLY|O_CREAT|O_TRUNC)) == NULL) {
699
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
700 701
			return; 
		}
702 703 704 705 706 707 708
		PCBoard::sys sys{};
		sys.Screen = true;
		sys.PageBell = sys_status & SS_SYSPAGE;
		sys.Alarm = startup->sound.answer[0] && !sound_muted(&cfg);
		sys.ErrorCorrected = true;
		sys.GraphicsMode = (term & NO_EXASCII) ? 'N' : 'Y';
		sys.UserNetStatus = (thisnode.misc & NODE_POFF) ? 'U' : 'A'; /* Node chat status ([A]vailable or [U]navailable) */
Rob Swindell's avatar
Rob Swindell committed
709
		SAFEPRINTF(tmp, "%lu", dte_rate);
710 711 712
		sys.ModemSpeed = tmp;
		sys.CarrierSpeed = connection;
		sys.UserRecNo = useron.number;
Rob Swindell's avatar
Rob Swindell committed
713
		SAFECOPY(tmp,name);
714
		p=strchr(tmp,' ');
715
		if(p) *p=0;
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
		sys.FirstName = tmp;
		sys.Password = useron.pass;
		if(localtime_r(&logontime,&tm) != NULL)
			sys.LogonMinute = (tm.tm_hour*60) + tm.tm_min;
		SAFEPRINTF2(tmp, "%02d:%02d", tm.tm_hour, tm.tm_min);
		sys.LogonTime = tmp;
		now = time(NULL);
		sys.TimeUsed = -(int16_t)(((now-starttime)/60)+(time_t)useron.ttoday);/* Negative minutes used */
		sys.PwrdTimeAllowed = cfg.level_timepercall[useron.level];
		sys.Name = name;
		sys.MinutesLeft = (int16_t)(tleft/60);
		sys.NodeNum = (uint8_t)cfg.node_num;
		sys.EventTime = "00:00";
		sys.UseAnsi = INT_TO_BOOL(term & ANSI);
		sys.YesChar = yes_key();
		sys.NoChar = no_key();
		sys.Conference2 = cursubnum;

		fwrite(&sys, sizeof(sys), 1, fp);
		fclose(fp);
736

Rob Swindell's avatar
Rob Swindell committed
737
		SAFECOPY(tmp,"USERS.SYS");
738 739
		if(misc&XTRN_LWRCASE)
			strlwr(tmp);
740
		SAFEPRINTF2(str,"%s%s",dropdir,tmp);
741
		(void)removecase(str);
742
		if((fp = fnopen(NULL, str,O_WRONLY|O_CREAT|O_TRUNC)) == NULL) {
743
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
744 745
			return; 
		}
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
		PCBoard::usersys user{};
		user.hdr.Version = PCBoard::Version;
		user.hdr.SizeOfRec = sizeof(user.fixed);
		SAFECOPY(user.fixed.Name, name);
		SAFECOPY(user.fixed.City, useron.location);
		SAFECOPY(user.fixed.Password, useron.pass);
		SAFECOPY(user.fixed.BusDataPhone, useron.phone);
		SAFECOPY(user.fixed.HomeVoicePhone, useron.phone);
		user.fixed.ExpertMode = INT_TO_BOOL(useron.misc & EXPERT);
		user.fixed.Protocol = useron.prot;
		user.fixed.SecurityLevel = useron.level;
		user.fixed.NumTimesOn = useron.logons;
		user.fixed.PageLen = (uint8_t)rows;
		user.fixed.NumUploads = useron.uls;
		user.fixed.NumDownloads = useron.dls;
		user.fixed.DailyDnldBytes = (uint32_t)logon_dlb;
		SAFECOPY(user.fixed.UserComment, useron.note);
		SAFECOPY(user.fixed.SysopComment, useron.comment);
		user.fixed.ElapsedTimeOn = (int16_t)(now-starttime)/60;
		user.fixed.RegExpDate = unixtojulian(useron.expire);
		user.fixed.ExpSecurityLevel = cfg.expired_level;
		user.fixed.LastConference = cursubnum;
768 769
		user.fixed.ulTotDnldBytes = (uint32_t)MIN(useron.dlb, UINT32_MAX);
		user.fixed.ulTotUpldBytes = (uint32_t)MIN(useron.ulb, UINT32_MAX);
770 771 772 773 774 775 776 777
		user.fixed.DeleteFlag = INT_TO_BOOL(useron.misc & DELETED);
		user.fixed.RecNum = useron.number;
		user.fixed.MsgsLeft = useron.posts + useron.emails + useron.fbacks;
		if(useron.misc & CLRSCRN)
			user.fixed.PackedFlags |= PCBoard::USER_FLAG_MSGCLEAR;

		fwrite(&user, sizeof(user), 1, fp);
		fclose(fp);
778
	}
779 780

	else if(type==XTRN_SPITFIRE) {	 /* SpitFire SFDOORS.DAT File */
Rob Swindell's avatar
Rob Swindell committed
781