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

newuser.cpp 15.1 KB
Newer Older
1 2 3 4 5 6
/* Synchronet new user routine */

/****************************************************************************
 * @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 "petdefs.h"
24 25 26 27 28 29 30
#include "cmdshell.h"

/****************************************************************************/
/* This function is invoked when a user enters "NEW" at the NN: prompt		*/
/* Prompts user for personal information and then sends feedback to sysop.  */
/* Called from function waitforcall											*/
/****************************************************************************/
rswindell's avatar
rswindell committed
31
BOOL sbbs_t::newuser()
32
{
33 34
	char	c,str[512];
	char 	tmp[512];
35
	uint	i;
36
	long	kmode;
37
	bool	usa;
38

39
	bputs(text[StartingNewUserRegistration]);
40 41 42
	getnodedat(cfg.node_num,&thisnode,0);
	if(thisnode.misc&NODE_LOCK) {
		bputs(text[NodeLocked]);
43
		logline(LOG_WARNING,"N!","New user locked node logon attempt");
44
		hangup();
rswindell's avatar
rswindell committed
45 46
		return(FALSE); 
	}
47 48 49 50

	if(cfg.sys_misc&SM_CLOSED) {
		bputs(text[NoNewUsers]);
		hangup();
rswindell's avatar
rswindell committed
51 52
		return(FALSE); 
	}
53 54
	getnodedat(cfg.node_num,&thisnode,1);
	thisnode.status=NODE_NEWUSER;
55
	thisnode.connection=node_connection;
56 57 58 59 60 61
	putnodedat(cfg.node_num,&thisnode);
	memset(&useron,0,sizeof(user_t));	  /* Initialize user info to null */
	if(cfg.new_pass[0] && online==ON_REMOTE) {
		c=0;
		while(++c<4) {
			bputs(text[NewUserPasswordPrompt]);
62
			getstr(str,40,K_UPPER|K_TRIM);
63 64
			if(!strcmp(str,cfg.new_pass))
				break;
65
			SAFEPRINTF(tmp,"NUP Attempted: '%s'",str);
66
			logline(LOG_NOTICE,"N!",tmp); 
rswindell's avatar
rswindell committed
67
		}
68
		if(c==4) {
69
			menu("../nupguess", P_NOABORT|P_NOERROR);
70
			hangup();
rswindell's avatar
rswindell committed
71 72 73
			return(FALSE); 
		} 
	}
74 75 76

	/* Sets defaults per sysop config */
	useron.misc|=(cfg.new_misc&~(DELETED|INACTIVE|QUIET|NETMAIL));
77
	useron.qwk=QWK_DEFAULT;
78
	useron.firston=useron.laston=useron.pwmod=time32(NULL);
79 80
	if(cfg.new_expire) {
		now=time(NULL);
81
		useron.expire=(time32_t)(now+((long)cfg.new_expire*24L*60L*60L)); 
rswindell's avatar
rswindell committed
82
	} else
83
		useron.expire=0;
84
	useron.sex=' ';
85
	useron.prot=cfg.new_prot;
86
	SAFECOPY(useron.comp,client_name);	/* hostname or CID name */
deuce's avatar
deuce committed
87 88
	SAFECOPY(useron.ipaddr,cid);			/* IP address or CID number */
	if((i=userdatdupe(0,U_IPADDR,LEN_IPADDR,cid, /* del */true))!=0) {	/* Duplicate IP address */
89
		SAFEPRINTF2(useron.comment,"Warning: same IP address as user #%d %s"
90
			,i,username(&cfg,i,str));
91
		logline(LOG_NOTICE,"N!",useron.comment); 
92 93
	}

94 95
	SAFECOPY(useron.alias,"New");     /* just for status line */
	SAFECOPY(useron.modem,connection);
96
	if(!lastuser(&cfg)) {	/* Automatic sysop access for first user */
97 98 99
		bprintf("Creating sysop account... System password required.\r\n");
		if(!chksyspass())
			return(FALSE); 
100 101 102
		useron.level=99;
		useron.exempt=useron.flags1=useron.flags2=0xffffffffUL;
		useron.flags3=useron.flags4=0xffffffffUL;
rswindell's avatar
rswindell committed
103 104
		useron.rest=0L; 
	} else {
105 106 107 108 109 110
		useron.level=cfg.new_level;
		useron.flags1=cfg.new_flags1;
		useron.flags2=cfg.new_flags2;
		useron.flags3=cfg.new_flags3;
		useron.flags4=cfg.new_flags4;
		useron.rest=cfg.new_rest;
rswindell's avatar
rswindell committed
111 112
		useron.exempt=cfg.new_exempt; 
	}
113 114 115 116 117 118

	useron.cdt=cfg.new_cdt;
	useron.min=cfg.new_min;
	useron.freecdt=cfg.level_freecdtperday[useron.level];

	if(cfg.total_fcomps)
119
		SAFECOPY(useron.tmpext,cfg.fcomp[0]->ext);
120
	else
121
		SAFECOPY(useron.tmpext,"ZIP");
122 123 124 125

	useron.shell=cfg.new_shell;

	useron.alias[0]=0;
126

127
	kmode=(cfg.uq&UQ_NOEXASC)|K_EDIT|K_AUTODEL|K_TRIM;
128 129 130
	if(!(cfg.uq&UQ_NOUPRLWR))
		kmode|=K_UPRLWR;

131
	while(online) {
132

133
		if(autoterm || (text[AutoTerminalQ][0] && yesno(text[AutoTerminalQ]))) {
134 135 136 137 138
			useron.misc|=AUTOTERM;
			useron.misc|=autoterm; 
		} else
			useron.misc&=~AUTOTERM;

139 140
		while(text[HitYourBackspaceKey][0] && !(useron.misc&(PETSCII|SWAP_DELETE)) && online) {
			bputs(text[HitYourBackspaceKey]);
141
			uchar key = getkey(K_CTRLKEYS);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
			bprintf(text[CharacterReceivedFmt], key, key);
			if(key == '\b')
				break;
			if(key == DEL) {
				if(text[SwapDeleteKeyQ][0] == 0 || yesno(text[SwapDeleteKeyQ]))
					useron.misc |= SWAP_DELETE;
			}
			else if(key == PETSCII_DELETE)
				useron.misc |= (PETSCII|COLOR);
			else {
				bprintf(text[InvalidBackspaceKeyFmt], key, key);
				if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
					return FALSE;
			}
		}

		if(!(useron.misc&(AUTOTERM|PETSCII))) {
159
			if(text[AnsiTerminalQ][0] && yesno(text[AnsiTerminalQ]))
160 161 162 163 164 165
				useron.misc|=ANSI; 
			else
				useron.misc&=~ANSI;
		}

		if(useron.misc&ANSI) {
166 167
			useron.rows = TERM_ROWS_AUTO;
			useron.cols = TERM_COLS_AUTO;
168
			if(!(cfg.uq&UQ_COLORTERM) || useron.misc&(RIP|WIP|HTML) || yesno(text[ColorTerminalQ]))
169 170 171
				useron.misc|=COLOR; 
			else
				useron.misc&=~COLOR;
172 173 174 175
			if(text[MouseTerminalQ][0] && yesno(text[MouseTerminalQ]))
				useron.misc |= MOUSE;
			else
				useron.misc &= ~MOUSE;
176 177
		}
		else
178 179
			useron.rows = TERM_ROWS_DEFAULT;

180 181 182
		if(useron.misc&PETSCII) {
			autoterm |= PETSCII;
			outcom(PETSCII_UPPERLOWER);
rswindell's avatar
rswindell committed
183
			bputs(text[PetTerminalDetected]);
184
		} else {
185 186 187 188 189
			if(!yesno(text[ExAsciiTerminalQ]))
				useron.misc|=NO_EXASCII;
			else
				useron.misc&=~NO_EXASCII;
		}
190

191
		if(rlogin_name[0])
192 193
			SAFECOPY(useron.alias,rlogin_name);

194 195 196 197 198
		char* prompt = text[EnterYourRealName];
		if(cfg.uq&UQ_ALIASES)
			prompt = text[EnterYourAlias];
		while(*prompt && online) {
			bputs(prompt);
199 200
			getstr(useron.alias,LEN_ALIAS,kmode);
			truncsp(useron.alias);
201
			if (!check_name(&cfg,useron.alias)
202 203
				|| (!(cfg.uq&UQ_ALIASES) && !strchr(useron.alias,' '))) {
				bputs(text[YouCantUseThatName]);
204
				if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
205
					return(FALSE);
206
				continue;
rswindell's avatar
rswindell committed
207
			}
208
			break; 
209
		}
rswindell's avatar
rswindell committed
210
		if(!online) return(FALSE);
211
		if((cfg.uq&UQ_ALIASES) && (cfg.uq&UQ_REALNAME)) {
212
			while(online && text[EnterYourRealName][0]) {
213
				bputs(text[EnterYourRealName]);
214 215
				getstr(useron.name,LEN_NAME,kmode);
				if (!check_name(&cfg,useron.name)
216
					|| !strchr(useron.name,' ')
217
					|| ((cfg.uq&UQ_DUPREAL)
218
						&& userdatdupe(useron.number,U_NAME,LEN_NAME,useron.name)))
219 220
					bputs(text[YouCantUseThatName]);
				else
rswindell's avatar
rswindell committed
221
					break; 
222
				if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
223
					return(FALSE);
rswindell's avatar
rswindell committed
224 225
			} 
		}
226
		else if(cfg.uq&UQ_COMPANY && text[EnterYourCompany][0]) {
227
				bputs(text[EnterYourCompany]);
228
				getstr(useron.name,LEN_NAME,kmode); 
rswindell's avatar
rswindell committed
229
		}
230 231 232 233
		if(!useron.alias[0]) {
			errormsg(WHERE, ERR_CHK, "alias", 0);
			return FALSE;
		}
234
		if(!useron.name[0])
235
			SAFECOPY(useron.name,useron.alias);
rswindell's avatar
rswindell committed
236
		if(!online) return(FALSE);
237
		if(!useron.handle[0])
238
			SAFECOPY(useron.handle,useron.alias);
239
		while((cfg.uq&UQ_HANDLE) && online && text[EnterYourHandle][0]) {
240 241
			bputs(text[EnterYourHandle]);
			if(!getstr(useron.handle,LEN_HANDLE
242
				,K_LINE|K_EDIT|K_AUTODEL|K_TRIM|(cfg.uq&UQ_NOEXASC))
243
				|| strchr(useron.handle,0xff)
244
				|| ((cfg.uq&UQ_DUPHAND)
245
					&& userdatdupe(0,U_HANDLE,LEN_HANDLE,useron.handle))
246
				|| trashcan(useron.handle,"name"))
247 248
				bputs(text[YouCantUseThatName]);
			else
rswindell's avatar
rswindell committed
249
				break; 
250
			if(text[ContinueQ][0] && !yesno(text[ContinueQ]))
251
				return(FALSE);
rswindell's avatar
rswindell committed
252 253
		}
		if(!online) return(FALSE);
254
		if(cfg.uq&UQ_ADDRESS)
255
			while(online && text[EnterYourAddress][0]) { 	   /* Get address and zip code */
256
				bputs(text[EnterYourAddress]);
257
				if(getstr(useron.address,LEN_ADDRESS,kmode))
rswindell's avatar
rswindell committed
258 259 260
					break; 
			}
		if(!online) return(FALSE);
261
		while((cfg.uq&UQ_LOCATION) && online && text[EnterYourCityState][0]) {
262
			bputs(text[EnterYourCityState]);
263
			if(getstr(useron.location,LEN_LOCATION,kmode)
264
				&& ((cfg.uq&UQ_NOCOMMAS) || strchr(useron.location,',')))
265
				break;
266
			bputs(text[CommaInLocationRequired]);
rswindell's avatar
rswindell committed
267 268
			useron.location[0]=0; 
		}
269
		if(cfg.uq&UQ_ADDRESS)
270
			while(online && text[EnterYourZipCode][0]) {
271 272
				bputs(text[EnterYourZipCode]);
				if(getstr(useron.zipcode,LEN_ZIPCODE
273
					,K_UPPER|(cfg.uq&UQ_NOEXASC)|K_EDIT|K_AUTODEL|K_TRIM))
rswindell's avatar
rswindell committed
274 275 276
					break; 
			}
		if(!online) return(FALSE);
277
		if((cfg.uq&UQ_PHONE) && text[EnterYourPhoneNumber][0]) {
278 279 280 281 282
			if(text[CallingFromNorthAmericaQ][0])
				usa=yesno(text[CallingFromNorthAmericaQ]);
			else
				usa=false;
			while(online && text[EnterYourPhoneNumber][0]) {
283 284 285
				bputs(text[EnterYourPhoneNumber]);
				if(!usa) {
					if(getstr(useron.phone,LEN_PHONE
286
						,K_UPPER|K_LINE|(cfg.uq&UQ_NOEXASC)|K_EDIT|K_AUTODEL|K_TRIM)<5)
rswindell's avatar
rswindell committed
287 288
						continue; 
				}
289 290 291
				else {
					if(gettmplt(useron.phone,cfg.sys_phonefmt
						,K_LINE|(cfg.uq&UQ_NOEXASC)|K_EDIT)<strlen(cfg.sys_phonefmt))
rswindell's avatar
rswindell committed
292 293
						continue; 
				}
294
				if(!trashcan(useron.phone,"phone"))
rswindell's avatar
rswindell committed
295 296 297 298
					break; 
			} 
		}
		if(!online) return(FALSE);
299 300 301 302 303 304 305
		while((cfg.uq&UQ_SEX) && text[EnterYourGender][0] && cfg.new_genders[0] != '\0' && online) {
			bputs(text[EnterYourGender]);
			long gender = getkeys(cfg.new_genders, 0);
			if(gender > 0) {
				useron.sex = (char)gender;
				break;
			}
rswindell's avatar
rswindell committed
306
		}
307
		while((cfg.uq&UQ_BIRTH) && online && text[EnterYourBirthday][0]) {
308 309 310
			bprintf(text[EnterYourBirthday], birthdate_format(&cfg));
			format_birthdate(&cfg, useron.birth, str, sizeof(str));
			if(gettmplt(str, "nn/nn/nnnn", K_EDIT) < 10)
311
				continue;
312 313 314
			int age = getage(&cfg, parse_birthdate(&cfg, str, tmp, sizeof(tmp)));
			if(age >= 0 && age <= 200) { // TODO: Configurable min/max user age
				SAFECOPY(useron.birth, tmp);
315 316
				break;
			}
rswindell's avatar
rswindell committed
317 318
		}
		if(!online) return(FALSE);
319
		while(!(cfg.uq&UQ_NONETMAIL) && online && text[EnterNetMailAddress][0]) {
320
			bputs(text[EnterNetMailAddress]);
321
			if(getstr(useron.netmail,LEN_NETMAIL,K_EDIT|K_AUTODEL|K_LINE|K_TRIM)
322 323 324
				&& !trashcan(useron.netmail,"email"))
				break;
		}
325 326
		useron.misc&=~NETMAIL;
		if((cfg.sys_misc&SM_FWDTONET) && is_supported_netmail_addr(&cfg, useron.netmail) && yesno(text[ForwardMailQ]))
327
			useron.misc|=NETMAIL;
328

329
		if(text[UserInfoCorrectQ][0]==0 || yesno(text[UserInfoCorrectQ]))
rswindell's avatar
rswindell committed
330 331 332
			break; 
	}
	if(!online) return(FALSE);
333
	SAFEPRINTF(str,"New user: %s",useron.alias);
334
	logline("N",str);
rswindell's avatar
rswindell committed
335
	if(!online) return(FALSE);
336 337 338
	menu("../sbbs", P_NOABORT|P_NOERROR);
	menu("../system", P_NOABORT|P_NOERROR);
	menu("../newuser", P_NOABORT|P_NOERROR);
339 340
	answertime=time(NULL);		/* could take 10 minutes to get this far */

341 342
	/* Default editor (moved here, after terminal type setup Jan-2003) */
	for(i=0;i<cfg.total_xedits;i++)
rswindell's avatar
rswindell committed
343
		if(!stricmp(cfg.xedit[i]->code,cfg.new_xedit) && chk_ar(cfg.xedit[i]->ar,&useron,&client))
344 345 346 347
			break;
	if(i<cfg.total_xedits)
		useron.xedit=i+1;

348
	if(cfg.total_xedits && (cfg.uq&UQ_XEDIT) && text[UseExternalEditorQ][0]) {
349
		if(yesno(text[UseExternalEditorQ])) {
350
			for(i=0;i<cfg.total_xedits;i++)
351
				uselect(1,i,text[ExternalEditorHeading],cfg.xedit[i]->name,cfg.xedit[i]->ar);
352 353 354 355
			if((int)(i=uselect(0,useron.xedit ? useron.xedit-1 : 0,0,0,0))>=0)
				useron.xedit=i+1; 
		} else
			useron.xedit=0;
rswindell's avatar
rswindell committed
356
	}
357

358
	if(cfg.total_shells>1 && (cfg.uq&UQ_CMDSHELL) && text[CommandShellHeading][0]) {
359
		for(i=0;i<cfg.total_shells;i++)
360
			uselect(1,i,text[CommandShellHeading],cfg.shell[i]->name,cfg.shell[i]->ar);
361
		if((int)(i=uselect(0,useron.shell,0,0,0))>=0)
rswindell's avatar
rswindell committed
362 363
			useron.shell=i; 
	}
364

deuce's avatar
deuce committed
365
	if(rlogin_pass[0] && chkpass(rlogin_pass,&useron,true)) {
366
		CRLF;
367
		SAFECOPY(useron.pass, rlogin_pass);
368
		strupr(useron.pass);	/* passwords are case insensitive, but assumed (in some places) to be uppercase in the user database */
369 370
	}
	else {
371
		c=0;
372
		while(c < MAX(RAND_PASS_LEN, cfg.min_pwlen)) { 				/* Create random password */
373
			useron.pass[c]=sbbs_random(43)+'0';
374
			if(IS_ALPHANUMERIC(useron.pass[c]))
375 376 377
				c++; 
		}
		useron.pass[c]=0;
378

379 380
		bprintf(text[YourPasswordIs],useron.pass);

381
		if(cfg.sys_misc&SM_PWEDIT && text[NewPasswordQ][0] && yesno(text[NewPasswordQ]))
382
			while(online) {
383
				bprintf(text[NewPasswordPromptFmt], cfg.min_pwlen, LEN_PASS);
384
				getstr(str,LEN_PASS,K_UPPER|K_LINE|K_TRIM);
385 386
				truncsp(str);
				if(chkpass(str,&useron,true)) {
387
					SAFECOPY(useron.pass,str);
388 389
					CRLF;
					bprintf(text[YourPasswordIs],useron.pass);
rswindell's avatar
rswindell committed
390 391 392 393
					break; 
				}
				CRLF; 
			}
394 395

		c=0;
396
		while(online && text[NewUserPasswordVerify][0]) {
397
			bputs(text[NewUserPasswordVerify]);
398 399
			console|=CON_R_ECHOX;
			str[0]=0;
400
			getstr(str,LEN_PASS*2,K_UPPER);
401 402
			console&=~(CON_R_ECHOX|CON_L_ECHOX);
			if(!strcmp(str,useron.pass)) break;
403
			if(cfg.sys_misc&SM_ECHO_PW) 
404
				SAFEPRINTF2(tmp,"FAILED Password verification: '%s' instead of '%s'"
405
					,str
406 407
					,useron.pass);
			else
408
				SAFECOPY(tmp,"FAILED Password verification");
409
			logline(LOG_NOTICE,nulstr,tmp);
410
			if(++c==4) {
411
				logline(LOG_NOTICE,"N!","Couldn't figure out password.");
rswindell's avatar
rswindell committed
412 413
				hangup(); 
			}
414
			bputs(text[IncorrectPassword]);
rswindell's avatar
rswindell committed
415 416
			bprintf(text[YourPasswordIs],useron.pass); 
		}
417
	}
418

rswindell's avatar
rswindell committed
419
	if(!online) return(FALSE);
420
	if(cfg.new_magic[0] && text[MagicWordPrompt][0]) {
421 422
		bputs(text[MagicWordPrompt]);
		str[0]=0;
423
		getstr(str,50,K_UPPER|K_TRIM);
424 425
		if(strcmp(str,cfg.new_magic)) {
			bputs(text[FailedMagicWord]);
426
			SAFEPRINTF(tmp,"failed magic word: '%s'",str);
427
			logline("N!",tmp);
rswindell's avatar
rswindell committed
428 429 430 431
			hangup(); 
		}
		if(!online) return(FALSE); 
	}
432 433 434

	bputs(text[CheckingSlots]);

435
	if((i=newuserdat(&cfg,&useron))!=0) {
436
		SAFEPRINTF(str,"user record #%u",useron.number);
437 438
		errormsg(WHERE,ERR_CREATE,str,i);
		hangup();
rswindell's avatar
rswindell committed
439
		return(FALSE); 
440
	}
441
	SAFEPRINTF2(str,"Created user record #%u: %s",useron.number,useron.alias);
442
	logline(nulstr,str);
443
	if(cfg.new_sif[0]) {
444
		SAFEPRINTF2(str,"%suser/%4.4u.dat",cfg.data_dir,useron.number);
rswindell's avatar
rswindell committed
445 446
		create_sif_dat(cfg.new_sif,str); 
	}
447 448 449
	if(!(cfg.uq&UQ_NODEF))
		maindflts(&useron);

450
	delallmail(useron.number, MAIL_ANY);
451 452

	if(useron.number!=1 && cfg.node_valuser) {
453
		menu("../feedback", P_NOABORT|P_NOERROR);
454
		safe_snprintf(str,sizeof(str),text[NewUserFeedbackHdr]
455 456
			,nulstr,getage(&cfg,useron.birth),useron.sex,useron.birth
			,useron.name,useron.phone,useron.comp,useron.modem);
457
		email(cfg.node_valuser,str,"New User Validation",WM_SUBJ_RO|WM_FORCEFWD);
458 459 460
		if(!useron.fbacks && !useron.emails) {
			if(online) {						/* didn't hang up */
				bprintf(text[NoFeedbackWarning],username(&cfg,cfg.node_valuser,tmp));
461
				email(cfg.node_valuser,str,"New User Validation",WM_SUBJ_RO|WM_FORCEFWD);
462 463 464
				} /* give 'em a 2nd try */
			if(!useron.fbacks && !useron.emails) {
        		bprintf(text[NoFeedbackWarning],username(&cfg,cfg.node_valuser,tmp));
465
				logline(LOG_NOTICE,"N!","Aborted feedback");
466 467 468 469
				hangup();
				putuserrec(&cfg,useron.number,U_COMMENT,60,"Didn't leave feedback");
				putuserrec(&cfg,useron.number,U_MISC,8
					,ultoa(useron.misc|DELETED,tmp,16));
470
				putusername(&cfg,useron.number,nulstr);
rswindell's avatar
rswindell committed
471
				return(FALSE); 
472 473 474
			} 
		} 
	}
475 476

	answertime=starttime=time(NULL);	  /* set answertime to now */
477

478
#ifdef JAVASCRIPT
479
	js_create_user_objects(js_cx, js_glob);
480 481
#endif

482 483 484 485 486 487
	if(cfg.newuser_mod[0])
		exec_bin(cfg.newuser_mod,&main_csi);
	user_event(EVENT_NEWUSER);
	getuserdat(&cfg,&useron);	// In case event(s) modified user data
	logline("N+","Successful new user logon");
	sys_status|=SS_NEWUSER;
rswindell's avatar
rswindell committed
488 489

	return(TRUE);
490
}