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

useredit.cpp 34.2 KB
Newer Older
1 2 3 4 5 6 7 8
/* Synchronet online sysop user editor */

/* $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
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10 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 39 40 41 42 43 44 45 46 47 48 49
 *																			*
 * 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.	*
 ****************************************************************************/

/*******************************************************************/
/* The function useredit(), and functions that are closely related */
/*******************************************************************/

#include "sbbs.h"

#define SEARCH_TXT 0
#define SEARCH_ARS 1

/****************************************************************************/
/* Edits user data. Can only edit users with a Main Security Level less 	*/
/* than or equal to the current user's Main Security Level					*/
/* Called from functions waitforcall, main_sec, xfer_sec and inkey			*/
/****************************************************************************/
50
void sbbs_t::useredit(int usernumber)
51 52
{
	char	str[256],tmp2[256],tmp3[256],c,stype=SEARCH_TXT;
53
	char 	tmp[512];
54 55 56
	char	search[256]={""},artxt[128]={""};
	uchar	*ar=NULL;
	uint	i,j,k;
57
	float	f;
58 59
	long	l;
	user_t	user;
60
	struct	tm tm;
61

62
	if(online==ON_REMOTE && console&(CON_R_ECHO|CON_R_INPUT) && !chksyspass())
63
		return;
64
#if 0	/* no local logins in v3 */
65 66 67
	if(online==ON_LOCAL) {
		if(!(cfg.sys_misc&SM_L_SYSOP))
			return;
68
		if(cfg.node_misc&NM_SYSPW && !chksyspass())
rswindell's avatar
rswindell committed
69 70
			return; 
	}
71
#endif
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	if(usernumber)
		user.number=usernumber;
	else
		user.number=useron.number;
	action=NODE_SYSP;
	if(sys_status&SS_INUEDIT)
		return;
	sys_status|=SS_INUEDIT;
	while(online) {
		CLS;
		attr(LIGHTGRAY);
		getuserdat(&cfg,&user);
		if(!user.number) {
			user.number=1;
			getuserdat(&cfg,&user);
			if(!user.number) {
				bputs(text[NoUserData]);
				getkey(0);
				sys_status&=~SS_INUEDIT;
rswindell's avatar
rswindell committed
91 92 93
				return; 
			} 
		}
94
		unixtodstr(&cfg,time32(NULL),str);
95 96 97 98
		unixtodstr(&cfg,user.laston,tmp);
		if(strcmp(str,tmp) && user.ltoday) {
			user.ltoday=user.ttoday=user.ptoday=user.etoday=user.textra=0;
			user.freecdt=cfg.level_freecdtperday[user.level];
rswindell's avatar
rswindell committed
99 100
			putuserdat(&cfg,&user); 	/* Leave alone */
		}
101 102 103 104 105 106 107 108 109 110 111 112 113 114
		bprintf(text[UeditAliasPassword]
			,user.alias, (user.level>useron.level && console&CON_R_ECHO)
			|| !(cfg.sys_misc&SM_ECHO_PW) ? "XXXXXXXX" : user.pass
			, unixtodstr(&cfg,user.pwmod,tmp));
		bprintf(text[UeditRealNamePhone]
			,user.level>useron.level && console&CON_R_ECHO
			? "XXXXXXXX" : user.name
			,user.level>useron.level && console&CON_R_ECHO
			? "XXX-XXX-XXXX" : user.phone);
		bprintf(text[UeditAddressBirthday]
			,user.address,getage(&cfg,user.birth),user.sex,user.birth);
		bprintf(text[UeditLocationZipcode],user.location,user.zipcode);
		bprintf(text[UeditNoteHandle],user.note,user.handle);
		bprintf(text[UeditComputerModem],user.comp,user.modem);
rswindell's avatar
rswindell committed
115
		bprintf(text[UserIpAddr],user.ipaddr);
116 117 118
		if(user.netmail[0])
			bprintf(text[UserNetMail],user.netmail);

rswindell's avatar
rswindell committed
119
		SAFEPRINTF2(str,"%suser/%4.4u.msg", cfg.data_dir,user.number);
120 121
		i=fexist(str);
		if(user.comment[0] || i)
122
			bprintf(text[UeditCommentLine],i ? '+' : ' '
123 124 125
				,user.comment);
		else
			CRLF;
126
		if(localtime32(&user.laston,&tm)==NULL)
127 128 129
			return;
		bprintf(text[UserDates]
			,unixtodstr(&cfg,user.firston,str),unixtodstr(&cfg,user.expire,tmp)
130
			,unixtodstr(&cfg,user.laston,tmp2),tm.tm_hour, tm.tm_min);
131 132 133 134 135

		bprintf(text[UserTimes]
			,user.timeon,user.ttoday,cfg.level_timeperday[user.level]
			,user.tlast,cfg.level_timepercall[user.level],user.textra);
		if(user.posts)
136
			f=(float)user.logons/user.posts;
137
		else
138
			f=0;
139 140
		bprintf(text[UserLogons]
			,user.logons,user.ltoday,cfg.level_callsperday[user.level],user.posts
141
			,f ? (uint)(100/f) : user.posts>user.logons ? 100 : 0
142 143
			,user.ptoday);
		bprintf(text[UserEmails]
144
			,user.emails,user.fbacks,getmail(&cfg,user.number,/* Sent: */FALSE, /* SPAM: */FALSE),user.etoday);
145 146 147

		bprintf(text[UserUploads],ultoac(user.ulb,tmp),user.uls);
		if(user.leech)
rswindell's avatar
rswindell committed
148
			SAFEPRINTF(str,text[UserLeech],user.leech);
149 150 151 152 153 154 155 156 157 158 159 160
		else
			str[0]=0;
		bprintf(text[UserDownloads],ultoac(user.dlb,tmp),user.dls,str);
		bprintf(text[UserCredits],ultoac(user.cdt,tmp)
			,ultoac(user.freecdt,tmp2),ultoac(cfg.level_freecdtperday[user.level],str));
		bprintf(text[UserMinutes],ultoac(user.min,tmp));
		bprintf(text[UeditSecLevel],user.level);
		bprintf(text[UeditFlags],ltoaf(user.flags1,tmp),ltoaf(user.flags3,tmp2)
			,ltoaf(user.flags2,tmp3),ltoaf(user.flags4,str));
		bprintf(text[UeditExempts],ltoaf(user.exempt,tmp),ltoaf(user.rest,tmp2));
		if(lncntr>=rows-2)
			lncntr=0;
rswindell's avatar
rswindell committed
161 162 163 164 165 166 167 168
		if(user.misc&DELETED)
			center(text[Deleted]);
		else if(user.misc&INACTIVE)
			center(text[Inactive]);
		else
			CRLF;
		l=lastuser(&cfg);
		ASYNC;
169 170
		bprintf(text[UeditPrompt],user.number,l);
		if(user.level>useron.level && console&CON_R_INPUT)
rswindell's avatar
rswindell committed
171
			SAFECOPY(str,"QG[]?/{},");
172
		else
rswindell's avatar
rswindell committed
173
			SAFECOPY(str,"ABCDEFGHIJKLMNOPQRSTUVWXYZ+[]?/{}~*$#");
174 175 176
		l=getkeys(str,l);
		if(l&0x80000000L) {
			user.number=(ushort)(l&~0x80000000L);
rswindell's avatar
rswindell committed
177 178
			continue; 
		}
179 180 181 182
		switch(l) {
			case 'A':
				bputs(text[EnterYourAlias]);
				getstr(user.alias,LEN_ALIAS,K_LINE|K_EDIT|K_AUTODEL);
183 184
				if(sys_status&SS_ABORT)
					break;
185 186
				putuserrec(&cfg,user.number,U_ALIAS,LEN_ALIAS,user.alias);
				if(!(user.misc&DELETED))
187
					putusername(&cfg,user.number,user.alias);
188 189
				bputs(text[EnterYourHandle]);
				getstr(user.handle,LEN_HANDLE,K_LINE|K_EDIT|K_AUTODEL);
190 191
				if(sys_status&SS_ABORT)
					break;
192 193 194
				putuserrec(&cfg,user.number,U_HANDLE,LEN_HANDLE,user.handle);
				break;
			case 'B':
195 196
				bprintf(text[EnterYourBirthday]
					,cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY");
197
				gettmplt(user.birth,"nn/nn/nn",K_LINE|K_EDIT|K_AUTODEL);
198 199
				if(sys_status&SS_ABORT)
					break;
200 201 202 203 204
				putuserrec(&cfg,user.number,U_BIRTH,LEN_BIRTH,user.birth);
				break;
			case 'C':
				bputs(text[EnterYourComputer]);
				getstr(user.comp,LEN_COMP,K_LINE|K_EDIT|K_AUTODEL);
205 206
				if(sys_status&SS_ABORT)
					break;
207 208 209 210 211 212 213
				putuserrec(&cfg,user.number,U_COMP,LEN_COMP,user.comp);
				break;
			case 'D':
				if(user.misc&DELETED) {
					if(!noyes(text[UeditRestoreQ])) {
						putuserrec(&cfg,user.number,U_MISC,8
							,ultoa(user.misc&~DELETED,str,16));
rswindell's avatar
rswindell committed
214 215 216 217
						putusername(&cfg,user.number,user.alias); 
					}
					break; 
				}
218 219 220 221
				if(user.misc&INACTIVE) {
					if(!noyes(text[UeditActivateQ]))
						putuserrec(&cfg,user.number,U_MISC,8
							,ultoa(user.misc&~INACTIVE,str,16));
rswindell's avatar
rswindell committed
222 223
					break; 
				}
224 225
				if(!noyes(text[UeditDeleteQ])) {
					getsmsg(user.number);
226
					if(getmail(&cfg,user.number, /* Sent: */FALSE, /* SPAM: */FALSE)) {
227
						if(yesno(text[UeditReadUserMailWQ]))
rswindell's avatar
rswindell committed
228 229
							readmail(user.number,MAIL_YOUR); 
					}
230
					if(getmail(&cfg,user.number, /* Sent: */TRUE, /* SPAM: */FALSE)) {
231
						if(yesno(text[UeditReadUserMailSQ]))
rswindell's avatar
rswindell committed
232 233
							readmail(user.number,MAIL_SENT); 
					}
234 235
					putuserrec(&cfg,user.number,U_MISC,8
						,ultoa(user.misc|DELETED,str,16));
236
					putusername(&cfg,user.number,nulstr);
rswindell's avatar
rswindell committed
237 238
					break; 
				}
239
				if(!noyes(text[UeditDeactivateUserQ])) {
240
					if(getmail(&cfg,user.number, /* Sent: */FALSE, /* SPAM: */FALSE)) {
241
						if(yesno(text[UeditReadUserMailWQ]))
rswindell's avatar
rswindell committed
242 243
							readmail(user.number,MAIL_YOUR); 
					}
244
					if(getmail(&cfg,user.number, /* Sent: */TRUE, /* SPAM: */FALSE)) {
245
						if(yesno(text[UeditReadUserMailSQ]))
rswindell's avatar
rswindell committed
246 247
							readmail(user.number,MAIL_SENT); 
					}
248 249
					putuserrec(&cfg,user.number,U_MISC,8
						,ultoa(user.misc|INACTIVE,str,16));
rswindell's avatar
rswindell committed
250 251
					break; 
				}
252 253 254 255 256 257 258 259 260 261 262
				break;
			case 'E':
				if(!yesno(text[ChangeExemptionQ]))
					break;
				while(online) {
					bprintf(text[FlagEditing],ltoaf(user.exempt,tmp));
					c=(char)getkeys("ABCDEFGHIJKLMNOPQRSTUVWXYZ?\r",0);
					if(sys_status&SS_ABORT)
						break;
					if(c==CR) break;
					if(c=='?') {
263
						menu("exempt");
rswindell's avatar
rswindell committed
264 265
						continue; 
					}
266 267
					if(user.level>useron.level 
						&& !(useron.exempt&FLAG(c)) && console&CON_R_INPUT)
268 269
						continue;
					user.exempt^=FLAG(c);
rswindell's avatar
rswindell committed
270 271
					putuserrec(&cfg,user.number,U_EXEMPT,8,ultoa(user.exempt,tmp,16)); 
				}
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
				break;
			case 'F':
				i=1;
				while(online) {
					bprintf("\r\nFlag Set #%d\r\n",i);
					switch(i) {
						case 1:
							bprintf(text[FlagEditing],ltoaf(user.flags1,tmp));
							break;
						case 2:
							bprintf(text[FlagEditing],ltoaf(user.flags2,tmp));
							break;
						case 3:
							bprintf(text[FlagEditing],ltoaf(user.flags3,tmp));
							break;
						case 4:
							bprintf(text[FlagEditing],ltoaf(user.flags4,tmp));
rswindell's avatar
rswindell committed
289 290
							break; 
					}
291 292 293 294 295
					c=(char)getkeys("ABCDEFGHIJKLMNOPQRSTUVWXYZ?1234\r",0);
					if(sys_status&SS_ABORT)
						break;
					if(c==CR) break;
					if(c=='?') {
rswindell's avatar
rswindell committed
296
						SAFEPRINTF(str,"flags%d",i);
297
						menu(str);
rswindell's avatar
rswindell committed
298 299
						continue; 
					}
300 301
					if(isdigit(c)) {
						i=c&0xf;
rswindell's avatar
rswindell committed
302 303
						continue; 
					}
304
					if(user.level>useron.level && console&CON_R_INPUT)
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
						switch(i) {
							case 1:
								if(!(useron.flags1&FLAG(c)))
									continue;
								break;
							case 2:
								if(!(useron.flags2&FLAG(c)))
									continue;
								break;
							case 3:
								if(!(useron.flags3&FLAG(c)))
									continue;
								break;
							case 4:
								if(!(useron.flags4&FLAG(c)))
									continue;
rswindell's avatar
rswindell committed
321 322
								break; 
					}
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
					switch(i) {
						case 1:
							user.flags1^=FLAG(c);
							putuserrec(&cfg,user.number,U_FLAGS1,8
								,ultoa(user.flags1,tmp,16));
							break;
						case 2:
							user.flags2^=FLAG(c);
							putuserrec(&cfg,user.number,U_FLAGS2,8
								,ultoa(user.flags2,tmp,16));
							break;
						case 3:
							user.flags3^=FLAG(c);
							putuserrec(&cfg,user.number,U_FLAGS3,8
								,ultoa(user.flags3,tmp,16));
							break;
						case 4:
							user.flags4^=FLAG(c);
							putuserrec(&cfg,user.number,U_FLAGS4,8
								,ultoa(user.flags4,tmp,16));
rswindell's avatar
rswindell committed
343 344 345
							break; 
					} 
				}
346 347 348 349 350 351 352 353
				break;
			case 'G':
				bputs(text[GoToUser]);
				if(getstr(str,LEN_ALIAS,K_UPPER|K_LINE)) {
					if(isdigit(str[0])) {
						i=atoi(str);
						if(i>lastuser(&cfg))
							break;
rswindell's avatar
rswindell committed
354 355
						if(i) user.number=i; 
					}
356 357
					else {
						i=finduser(str);
rswindell's avatar
rswindell committed
358 359 360
						if(i) user.number=i; 
					} 
				}
361 362 363
				break;
			case 'H': /* edit user's information file */
				attr(LIGHTGRAY);
rswindell's avatar
rswindell committed
364
				SAFEPRINTF2(str,"%suser/%4.4u.msg", cfg.data_dir,user.number);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
				editfile(str);
				break;
			case 'I':
				maindflts(&user);
				break;
			case 'J':   /* Edit Minutes */
				bputs(text[UeditMinutes]);
				ultoa(user.min,str,10);
				if(getstr(str,10,K_NUMBER|K_LINE))
					putuserrec(&cfg,user.number,U_MIN,10,str);
				break;
			case 'K':	/* date changes */
				bputs(text[UeditLastOn]);
				unixtodstr(&cfg,user.laston,str);
				gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
				if(sys_status&SS_ABORT)
					break;
				user.laston=dstrtounix(&cfg,str);
383
				putuserrec(&cfg,user.number,U_LASTON,8,ultoa((ulong)user.laston,tmp,16));
384 385 386 387 388 389
				bputs(text[UeditFirstOn]);
				unixtodstr(&cfg,user.firston,str);
				gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
				if(sys_status&SS_ABORT)
					break;
				user.firston=dstrtounix(&cfg,str);
390
				putuserrec(&cfg,user.number,U_FIRSTON,8,ultoa((ulong)user.firston,tmp,16));
391 392 393 394 395 396
				bputs(text[UeditExpire]);
				unixtodstr(&cfg,user.expire,str);
				gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
				if(sys_status&SS_ABORT)
					break;
				user.expire=dstrtounix(&cfg,str);
397
				putuserrec(&cfg,user.number,U_EXPIRE,8,ultoa((ulong)user.expire,tmp,16));
398 399 400 401 402 403
				bputs(text[UeditPwModDate]);
				unixtodstr(&cfg,user.pwmod,str);
				gettmplt(str,"nn/nn/nn",K_LINE|K_EDIT);
				if(sys_status&SS_ABORT)
					break;
				user.pwmod=dstrtounix(&cfg,str);
404
				putuserrec(&cfg,user.number,U_PWMOD,8,ultoa((ulong)user.pwmod,tmp,16));
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
				break;
			case 'L':
				bputs(text[EnterYourAddress]);
				getstr(user.address,LEN_ADDRESS,K_LINE|K_EDIT|K_AUTODEL);
				if(sys_status&SS_ABORT)
					break;
				putuserrec(&cfg,user.number,U_ADDRESS,LEN_ADDRESS,user.address);
				bputs(text[EnterYourCityState]);
				getstr(user.location,LEN_LOCATION,K_LINE|K_EDIT|K_AUTODEL);
				if(sys_status&SS_ABORT)
					break;
				putuserrec(&cfg,user.number,U_LOCATION,LEN_LOCATION,user.location);
				bputs(text[EnterYourZipCode]);
				getstr(user.zipcode,LEN_ZIPCODE,K_LINE|K_EDIT|K_AUTODEL|K_UPPER);
				if(sys_status&SS_ABORT)
					break;
				putuserrec(&cfg,user.number,U_ZIPCODE,LEN_ZIPCODE,user.zipcode);
				break;
			case 'M':
				bputs(text[UeditML]);
rswindell's avatar
rswindell committed
425
				ultoa(user.level,str,10);
426 427 428 429 430 431 432
				if(getstr(str,2,K_NUMBER|K_LINE))
					if(!(atoi(str)>useron.level && console&CON_R_INPUT))
						putuserrec(&cfg,user.number,U_LEVEL,2,str);
				break;
			case 'N':
				bputs(text[UeditNote]);
				getstr(user.note,LEN_NOTE,K_LINE|K_EDIT|K_AUTODEL);
433 434
				if(sys_status&SS_ABORT)
					break;
435 436 437 438 439
				putuserrec(&cfg,user.number,U_NOTE,LEN_NOTE,user.note);
				break;
			case 'O':
				bputs(text[UeditComment]);
				getstr(user.comment,60,K_LINE|K_EDIT|K_AUTODEL);
440 441
				if(sys_status&SS_ABORT)
					break;
442 443 444 445 446
				putuserrec(&cfg,user.number,U_COMMENT,60,user.comment);
				break;
			case 'P':
				bputs(text[EnterYourPhoneNumber]);
				getstr(user.phone,LEN_PHONE,K_UPPER|K_LINE|K_EDIT|K_AUTODEL);
447 448
				if(sys_status&SS_ABORT)
					break;
449 450 451 452 453
				putuserrec(&cfg,user.number,U_PHONE,LEN_PHONE,user.phone);
				break;
			case 'Q':
				CLS;
				sys_status&=~SS_INUEDIT;
454
				FREE_AR(ar);	/* assertion here */
455 456 457 458
				return;
			case 'R':
				bputs(text[EnterYourRealName]);
				getstr(user.name,LEN_NAME,K_LINE|K_EDIT|K_AUTODEL);
459 460
				if(sys_status&SS_ABORT)
					break;
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
				putuserrec(&cfg,user.number,U_NAME,LEN_NAME,user.name);
				break;
			case 'S':
				bputs(text[EnterYourSex]);
				if(getstr(str,1,K_UPPER|K_LINE))
					putuserrec(&cfg,user.number,U_SEX,1,str);
				break;
			case 'T':   /* Text Search */
				bputs(text[SearchStringPrompt]);
				if(getstr(search,30,K_UPPER|K_LINE))
					stype=SEARCH_TXT;
				break;
			case 'U':
				bputs(text[UeditUlBytes]);
				ultoa(user.ulb,str,10);
				if(getstr(str,10,K_NUMBER|K_LINE|K_EDIT|K_AUTODEL))
					putuserrec(&cfg,user.number,U_ULB,10,str);
				if(sys_status&SS_ABORT)
					break;
				bputs(text[UeditUploads]);
rswindell's avatar
rswindell committed
481
				SAFEPRINTF(str,"%u",user.uls);
482 483 484 485 486 487 488 489 490 491 492
				if(getstr(str,5,K_NUMBER|K_LINE|K_EDIT|K_AUTODEL))
					putuserrec(&cfg,user.number,U_ULS,5,str);
				if(sys_status&SS_ABORT)
					break;
				bputs(text[UeditDlBytes]);
				ultoa(user.dlb,str,10);
				if(getstr(str,10,K_NUMBER|K_LINE|K_EDIT|K_AUTODEL))
					putuserrec(&cfg,user.number,U_DLB,10,str);
				if(sys_status&SS_ABORT)
					break;
				bputs(text[UeditDownloads]);
rswindell's avatar
rswindell committed
493
				SAFEPRINTF(str,"%u",user.dls);
494 495 496 497 498 499 500 501 502 503
				if(getstr(str,5,K_NUMBER|K_LINE|K_EDIT|K_AUTODEL))
					putuserrec(&cfg,user.number,U_DLS,5,str);
				break;
			case 'V':
				CLS;
				attr(LIGHTGRAY);
				for(i=0;i<10;i++) {
					bprintf(text[QuickValidateFmt]
						,i,cfg.val_level[i],ltoaf(cfg.val_flags1[i],str)
						,ltoaf(cfg.val_exempt[i],tmp)
rswindell's avatar
rswindell committed
504 505
						,ltoaf(cfg.val_rest[i],tmp3)); 
				}
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
				ASYNC;
				bputs(text[QuickValidatePrompt]);
				c=getkey(0);
				if(!isdigit(c))
					break;
				i=c&0xf;
				user.level=cfg.val_level[i];
				user.flags1=cfg.val_flags1[i];
				user.flags2=cfg.val_flags2[i];
				user.flags3=cfg.val_flags3[i];
				user.flags4=cfg.val_flags4[i];
				user.exempt=cfg.val_exempt[i];
				user.rest=cfg.val_rest[i];
				user.cdt+=cfg.val_cdt[i];
				now=time(NULL);
				if(cfg.val_expire[i]) {
					if(user.expire<now)
523
						user.expire=(time32_t)(now+((long)cfg.val_expire[i]*24L*60L*60L));
524
					else
rswindell's avatar
rswindell committed
525 526
						user.expire+=((long)cfg.val_expire[i]*24L*60L*60L); 
				}
527 528 529 530 531
				putuserdat(&cfg,&user);
				break;
			case 'W':
				bputs(text[UeditPassword]);
				getstr(user.pass,LEN_PASS,K_UPPER|K_LINE|K_EDIT|K_AUTODEL);
532 533
				if(sys_status&SS_ABORT)
					break;
534 535 536 537
				putuserrec(&cfg,user.number,U_PASS,LEN_PASS,user.pass);
				break;
			case 'X':
				attr(LIGHTGRAY);
rswindell's avatar
rswindell committed
538
				SAFEPRINTF2(str,"%suser/%4.4u.msg", cfg.data_dir,user.number);
539 540 541 542 543 544 545
				printfile(str,0);
				pause();
				break;
			case 'Y':
				if(!noyes(text[UeditCopyUserQ])) {
					bputs(text[UeditCopyUserToSlot]);
					i=getnum(lastuser(&cfg));
546
					if((int)i>0) {
547
						user.number=i;
548
						putusername(&cfg,user.number,user.alias);
rswindell's avatar
rswindell committed
549 550 551
						putuserdat(&cfg,&user); 
					} 
				}
552 553 554 555 556 557 558 559 560 561 562
				break;
			case 'Z':
				if(!yesno(text[ChangeRestrictsQ]))
					break;
				while(online) {
					bprintf(text[FlagEditing],ltoaf(user.rest,tmp));
					c=(char)getkeys("ABCDEFGHIJKLMNOPQRSTUVWXYZ?\r",0);
					if(sys_status&SS_ABORT)
						break;
					if(c==CR) break;
					if(c=='?') {
563
						menu("restrict");
rswindell's avatar
rswindell committed
564 565
						continue; 
					}
566
					user.rest^=FLAG(c);
rswindell's avatar
rswindell committed
567 568
					putuserrec(&cfg,user.number,U_REST,8,ultoa(user.rest,tmp,16)); 
				}
569 570 571
				break;
			case '?':
				CLS;
572
				menu("uedit");  /* Sysop Uedit Edit Menu */
573 574 575 576 577
				pause();
				break;
			case '~':
				bputs(text[UeditLeech]);
				if(getstr(str,2,K_NUMBER|K_LINE))
rswindell's avatar
rswindell committed
578
					putuserrec(&cfg,user.number,U_LEECH,2,ultoa(atoi(str),tmp,16));
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
				break;
			case '+':
				bputs(text[ModifyCredits]);
				getstr(str,10,K_UPPER|K_LINE);
				l=atol(str);
				if(strstr(str,"M"))
					l*=0x100000L;
				else if(strstr(str,"K"))
					l*=1024;
				else if(strstr(str,"$"))
					l*=cfg.cdt_per_dollar;
				if(l<0L && l*-1 > (long)user.cdt)
					user.cdt=0L;
				else
					user.cdt+=l;
				putuserrec(&cfg,user.number,U_CDT,10,ultoa(user.cdt,tmp,10));
				break;
			case '*':
				bputs(text[ModifyMinutes]);
				getstr(str,10,K_UPPER|K_LINE);
				l=atol(str);
				if(strstr(str,"H"))
					l*=60L;
				if(l<0L && l*-1 > (long)user.min)
					user.min=0L;
				else
					user.min+=l;
				putuserrec(&cfg,user.number,U_MIN,10,ultoa(user.min,tmp,10));
				break;
			case '#': /* read new user questionaire */
rswindell's avatar
rswindell committed
609
				SAFEPRINTF2(str,"%suser/%4.4u.dat", cfg.data_dir,user.number);
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
				if(!cfg.new_sof[0] || !fexist(str))
					break;
				read_sif_dat(cfg.new_sof,str);
				if(!noyes(text[DeleteQuestionaireQ]))
					remove(str);
				break;
			case '$':
				bputs(text[UeditCredits]);
				ultoa(user.cdt,str,10);
				if(getstr(str,10,K_NUMBER|K_LINE))
					putuserrec(&cfg,user.number,U_CDT,10,str);
				break;
			case '/':
				bputs(text[SearchStringPrompt]);
				if(getstr(artxt,40,K_UPPER|K_LINE))
					stype=SEARCH_ARS;
626
				FREE_AR(ar);
627
				ar=arstr(NULL,artxt,&cfg);
628 629 630 631 632 633 634 635 636 637 638
				break;
			case '{':
				if(stype==SEARCH_TXT)
					user.number=searchdn(search,user.number);
				else {
					if(!ar)
						break;
					k=user.number;
					for(i=k-1;i;i--) {
						user.number=i;
						getuserdat(&cfg,&user);
rswindell's avatar
rswindell committed
639
						if(chk_ar(ar,&user,/* client: */NULL)) {
640
							outchar(BEL);
rswindell's avatar
rswindell committed
641 642 643
							break; 
						} 
					}
644
					if(!i)
rswindell's avatar
rswindell committed
645 646
						user.number=k; 
				}
647 648 649 650 651 652 653 654 655 656 657 658
				break;
			case '}':
				if(stype==SEARCH_TXT)
					user.number=searchup(search,user.number);
				else {
					if(!ar)
						break;
					j=lastuser(&cfg);
					k=user.number;
					for(i=k+1;i<=j;i++) {
						user.number=i;
						getuserdat(&cfg,&user);
rswindell's avatar
rswindell committed
659
						if(chk_ar(ar,&user,/* client: */NULL)) {
660
							outchar(BEL);
rswindell's avatar
rswindell committed
661 662 663
							break; 
						} 
					}
664
					if(i>j)
rswindell's avatar
rswindell committed
665 666
						user.number=k; 
				}
667 668 669 670 671 672 673 674 675 676
				break;
			case ']':
				if(user.number==lastuser(&cfg))
					user.number=1;
				else user.number++;
				break;
			case '[':
				if(user.number==1)
					user.number=lastuser(&cfg);
				else user.number--;
677 678 679
				break; 
		} /* switch */
	} /* while */
680
	sys_status&=~SS_INUEDIT;
681
	FREE_AR(ar);
682 683 684 685 686 687 688 689 690 691 692 693 694 695
}

/****************************************************************************/
/* Seaches forward through the USER.DAT file for the ocurrance of 'search'  */
/* starting at the offset for usernum+1 and returning the usernumber of the */
/* record where the string was found or the original usernumber if the 		*/
/* string wasn't found														*/
/* Called from the function useredit										*/
/****************************************************************************/
int sbbs_t::searchup(char *search,int usernum)
{
	char userdat[U_LEN+1];
	int file,count;
	uint i=usernum+1;
rswindell's avatar
rswindell committed
696
	long flen;
697 698 699

	if(!search[0])
		return(usernum);
rswindell's avatar
rswindell committed
700
	SAFEPRINTF(userdat,"%suser/user.dat", cfg.data_dir);
701 702
	if((file=nopen(userdat,O_RDONLY|O_DENYNONE))==-1)
		return(usernum);
rswindell's avatar
rswindell committed
703

704
	flen=(long)filelength(file);
705 706
	lseek(file,(long)((long)usernum*U_LEN),0);

707
	while((i*U_LEN)<=(ulong)flen) {
708 709 710
		count=0;
		while(count<LOOP_NODEDAB
			&& lock(file,(long)((long)(i-1)*U_LEN),U_LEN)==-1) {
711 712 713 714
			if(count)
				mswait(100);
			count++; 
		}
715 716 717

		if(count>=LOOP_NODEDAB) {
			close(file);
718
			errormsg(WHERE,ERR_LOCK,"user.dat",i);
719 720
			return(usernum); 
		}
721 722 723 724

		if(read(file,userdat,U_LEN)!=U_LEN) {
			unlock(file,(long)((long)(i-1)*U_LEN),U_LEN);
			close(file);
725
			errormsg(WHERE,ERR_READ,"user.dat",U_LEN);
726 727
			return(usernum); 
		}
728 729 730 731 732

		unlock(file,(long)((long)(i-1)*U_LEN),U_LEN);
		userdat[U_LEN]=0;
		strupr(userdat);
		if(strstr(userdat,search)) {
733
			outchar(BEL);
734
			close(file);
735 736 737 738
			return(i); 
		}
		i++; 
	}
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
	close(file);
	return(usernum);
}

/****************************************************************************/
/* Seaches backward through the USER.DAT file for the ocurrance of 'search' */
/* starting at the offset for usernum-1 and returning the usernumber of the */
/* record where the string was found or the original usernumber if the 		*/
/* string wasn't found														*/
/* Called from the function useredit										*/
/****************************************************************************/
int sbbs_t::searchdn(char *search,int usernum)
{
	char userdat[U_LEN+1];
	int file,count;
	uint i=usernum-1;

	if(!search[0])
		return(usernum);
rswindell's avatar
rswindell committed
758
	SAFEPRINTF(userdat,"%suser/user.dat", cfg.data_dir);
759
	if((file=nopen(userdat,O_RDONLY|O_DENYNONE))==-1)
760 761 762 763 764 765
		return(usernum);
	while(i) {
		lseek(file,(long)((long)(i-1)*U_LEN),0);
		count=0;
		while(count<LOOP_NODEDAB
			&& lock(file,(long)((long)(i-1)*U_LEN),U_LEN)==-1) {
766 767 768 769
			if(count)
				mswait(100);
			count++; 
		}
770 771 772

		if(count>=LOOP_NODEDAB) {
			close(file);
773
			errormsg(WHERE,ERR_LOCK,"user.dat",i);
774 775
			return(usernum); 
		}
776 777 778 779 780

		if(read(file,userdat,U_LEN)==-1) {
			unlock(file,(long)((long)(i-1)*U_LEN),U_LEN);
			close(file);
			errormsg(WHERE,ERR_READ,"USER.DAT",U_LEN);
781 782
			return(usernum); 
		}
783 784 785 786
		unlock(file,(long)((long)(i-1)*U_LEN),U_LEN);
		userdat[U_LEN]=0;
		strupr(userdat);
		if(strstr(userdat,search)) {
787
			outchar(BEL);
788
			close(file);
789 790 791 792
			return(i); 
		}
		i--; 
	}
793 794 795 796 797 798 799 800 801
	close(file);
	return(usernum);
}

/****************************************************************************/
/* This function view/edits the users main default settings.				*/
/****************************************************************************/
void sbbs_t::maindflts(user_t* user)
{
802 803 804
	char	str[256],ch;
	char 	tmp[512];
	int		i;
805 806 807 808 809 810 811 812

	action=NODE_DFLT;
	while(online) {
		CLS;
		getuserdat(&cfg,user);
		if(user->rows)
			rows=user->rows;
		bprintf(text[UserDefaultsHdr],user->alias,user->number);
813 814
		long term = (user == &useron) ? term_supports() : user->misc;
		if(term&PETSCII)
rswindell's avatar
rswindell committed
815 816 817
			safe_snprintf(str,sizeof(str),"%sPETSCII %lu %s"
							,user->misc&AUTOTERM ? text[TerminalAutoDetect]:nulstr
							,cols, text[TerminalColumns]);
rswindell's avatar
rswindell committed
818
		else
819
			safe_snprintf(str,sizeof(str),"%s%s / %s %s%s"
rswindell's avatar
rswindell committed
820
							,user->misc&AUTOTERM ? text[TerminalAutoDetect]:nulstr
821 822
							,term_charset(term)
							,term_type(term)
rswindell's avatar
rswindell committed
823
							,term&COLOR ? (term&ICE_COLOR ? text[TerminalIceColor] : text[TerminalColor]) : text[TerminalMonochrome]
824
							,term&SWAP_DELETE ? "DEL=BS" : nulstr);
825
		bprintf(text[UserDefaultsTerminal], truncsp(str));
826
		if(user->rows)
rswindell's avatar
rswindell committed
827
			ultoa(user->rows,tmp,10);
828
		else
rswindell's avatar
rswindell committed
829 830
			SAFEPRINTF2(tmp,"%s%ld", text[TerminalAutoDetect], rows);
		bprintf(text[UserDefaultsRows], tmp, text[TerminalRows]);
831 832 833
		if(cfg.total_shells>1)
			bprintf(text[UserDefaultsCommandSet]
				,cfg.shell[user->shell]->name);
rswindell's avatar
rswindell committed
834 835 836
		if(cfg.total_xedits)
			bprintf(text[UserDefaultsXeditor]
				,user->xedit ? cfg.xedit[user->xedit-1]->name : "None");
837 838 839 840 841 842 843 844 845
		bprintf(text[UserDefaultsArcType]
			,user->tmpext);
		bprintf(text[UserDefaultsMenuMode]
			,user->misc&EXPERT ? text[On] : text[Off]);
		bprintf(text[UserDefaultsPause]
			,user->misc&UPAUSE ? text[On] : text[Off]);
		bprintf(text[UserDefaultsHotKey]
			,user->misc&COLDKEYS ? text[Off] : text[On]);
		bprintf(text[UserDefaultsCursor]
846
			,user->misc&SPIN ? text[On] : user->misc&NOPAUSESPIN ? text[Off] : "Pause Prompt Only");
847 848 849 850 851 852 853 854 855 856 857 858 859 860
		bprintf(text[UserDefaultsCLS]
			,user->misc&CLRSCRN ? text[On] : text[Off]);
		bprintf(text[UserDefaultsAskNScan]
			,user->misc&ASK_NSCAN ? text[On] : text[Off]);
		bprintf(text[UserDefaultsAskSScan]
			,user->misc&ASK_SSCAN ? text[On] : text[Off]);
		bprintf(text[UserDefaultsANFS]
			,user->misc&ANFSCAN ? text[On] : text[Off]);
		bprintf(text[UserDefaultsRemember]
			,user->misc&CURSUB ? text[On] : text[Off]);
		bprintf(text[UserDefaultsBatFlag]
			,user->misc&BATCHFLAG ? text[On] : text[Off]);
		if(cfg.sys_misc&SM_FWDTONET)
			bprintf(text[UserDefaultsNetMail]
861 862
				,user->misc&NETMAIL ? text[On] : text[Off]
				,user->netmail);
863
		if(startup->options&BBS_OPT_AUTO_LOGON && user->exempt&FLAG('V'))
864 865
			bprintf(text[UserDefaultsAutoLogon]
			,user->misc&AUTOLOGON ? text[On] : text[Off]);
866
		if(user->exempt&FLAG('Q') || user->misc&QUIET)
867 868
			bprintf(text[UserDefaultsQuiet]
				,user->misc&QUIET ? text[On] : text[Off]);
869 870 871 872 873 874 875
		SAFECOPY(str,"None");
		for(i=0;i<cfg.total_prots;i++) {
			if(user->prot==cfg.prot[i]->mnemonic) {
				SAFECOPY(str,cfg.prot[i]->name);
				break;
			}
		}
876
		bprintf(text[UserDefaultsProtocol],str
877
			,user->misc&AUTOHANG ? "(Auto-Hangup)":nulstr);
878 879 880 881 882
		if(cfg.sys_misc&SM_PWEDIT && !(user->rest&FLAG('G')))
			bputs(text[UserDefaultsPassword]);

		ASYNC;
		bputs(text[UserDefaultsWhich]);
rswindell's avatar
rswindell committed
883
		SAFECOPY(str,"HTBALPRSYFNCQXZ\r");
884 885
		if(cfg.sys_misc&SM_PWEDIT && !(user->rest&FLAG('G')))
			strcat(str,"W");
886
		if(user->exempt&FLAG('Q') || user->misc&QUIET)
887 888 889 890 891
			strcat(str,"D");
		if(cfg.total_xedits)
			strcat(str,"E");
		if(cfg.sys_misc&SM_FWDTONET)
			strcat(str,"M");
892
		if(startup->options&BBS_OPT_AUTO_LOGON && user->exempt&FLAG('V'))
893 894 895 896 897 898 899 900
			strcat(str,"I");
		if(cfg.total_shells>1)
			strcat(str,"K");

		ch=(char)getkeys(str,0);
		switch(ch) {
			case 'T':
				if(yesno(text[AutoTerminalQ])) {
rswindell's avatar
rswindell committed
901
					user->misc |= AUTOTERM;
902
					user->misc &= ~(ANSI|RIP|WIP|HTML|PETSCII|UTF8);
rswindell's avatar
rswindell committed
903
				}
904
				else
rswindell's avatar
rswindell committed
905 906 907
					user->misc &= ~AUTOTERM;
				if(sys_status&SS_ABORT)
					break;
908
				if(!(user->misc&AUTOTERM)) {
909 910 911 912
					if(!noyes(text[Utf8TerminalQ]))
						user->misc |= UTF8;
					else
						user->misc &= ~UTF8;
rswindell's avatar
rswindell committed
913 914 915
					if(yesno(text[AnsiTerminalQ])) {
						user->misc |= ANSI;
						user->misc &= ~PETSCII;
916
					} else if(!(user->misc&UTF8)) {
rswindell's avatar
rswindell committed
917 918 919 920 921 922
						user->misc &= ~(ANSI|COLOR|ICE_COLOR);
						if(!noyes(text[PetTerminalQ]))
							user->misc |= PETSCII|COLOR;
						else
							user->misc &= ~PETSCII;
					}
rswindell's avatar
rswindell committed
923
				}
rswindell's avatar
rswindell committed
924 925 926 927 928 929 930 931 932 933 934
				if(sys_status&SS_ABORT)
					break;
				term = (user == &useron) ? term_supports() : user->misc;
				if(term&(AUTOTERM|ANSI) && !(term&PETSCII)) {
					user->misc |= COLOR;
					user->misc &= ~ICE_COLOR;
					if(yesno(text[ColorTerminalQ])) {
						if(!noyes(text[IceColorTerminalQ]))
							user->misc |= ICE_COLOR;
					} else
						user->misc &= ~COLOR;
rswindell's avatar
rswindell committed
935
				}
rswindell's avatar
rswindell committed
936 937 938
				if(sys_status&SS_ABORT)
					break;
				if(!(term&PETSCII)) {
939
					if(!(user->misc&UTF8) && !yesno(text[ExAsciiTerminalQ]))
940 941 942
						user->misc|=NO_EXASCII;
					else
						user->misc&=~NO_EXASCII;
943 944 945 946 947 948 949 950 951 952 953 954 955 956
					user->misc &= ~SWAP_DELETE;
					while(text[HitYourBackspaceKey][0] && !(user->misc&SWAP_DELETE) && online) {
						bputs(text[HitYourBackspaceKey]);
						uchar key = getkey(K_NONE);
						bprintf(text[CharacterReceivedFmt], key, key);
						if(key == '\b')
							break;
						if(key == DEL) {
							if(text[SwapDeleteKeyQ][0] == 0 || yesno(text[SwapDeleteKeyQ]))
								user->misc |= SWAP_DELETE;
						}
						else
							bprintf(text[InvalidBackspaceKeyFmt], key, key);
					}
957
				}
rswindell's avatar
rswindell committed
958 959 960
				if(sys_status&SS_ABORT)
					break;
				if(!(user->misc&AUTOTERM) && (term&(ANSI|NO_EXASCII)) == ANSI) {
961 962 963
					if(!noyes(text[RipTerminalQ]))
						user->misc|=RIP;
					else
rswindell's avatar
rswindell committed
964 965
						user->misc&=~RIP; 
				}
rswindell's avatar
rswindell committed
966 967
				if(sys_status&SS_ABORT)
					break;
968 969 970 971 972 973 974
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'B':
				user->misc^=BATCHFLAG;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'E':
975
				if(noyes(text[UseExternalEditorQ])) {
976
					putuserrec(&cfg,user->number,U_XEDIT,8,nulstr);
rswindell's avatar
rswindell committed
977 978
					break; 
				}
979 980 981
				if(user->xedit)
					user->xedit--;
				for(i=0;i<cfg.total_xedits;i++)
982
					uselect(1,i,text[ExternalEditorHeading],cfg.xedit[i]->name, cfg.xedit[i]->ar);
983 984 985 986 987
				if((i=uselect(0,user->xedit,0,0,0))>=0)
					putuserrec(&cfg,user->number,U_XEDIT,8,cfg.xedit[i]->code);
				break;
			case 'K':   /* Command shell */
				for(i=0;i<cfg.total_shells;i++)
988
					uselect(1,i,text[CommandShellHeading],cfg.shell[i]->name,cfg.shell[i]->ar);
989 990 991 992 993
				if((i=uselect(0,user->shell,0,0,0))>=0)
					putuserrec(&cfg,user->number,U_SHELL,8,cfg.shell[i]->code);
				break;
			case 'A':
				for(i=0;i<cfg.total_fcomps;i++)
994
					uselect(1,i,text[ArchiveTypeHeading],cfg.fcomp[i]->ext,cfg.fcomp[i]->ar);
995 996 997 998 999
				if((i=uselect(0,0,0,0,0))>=0)
					putuserrec(&cfg,user->number,U_TMPEXT,3,cfg.fcomp[i]->ext);
				break;
			case 'L':
				bputs(text[HowManyRows]);
1000
				if((ch=(char)getnum(99))!=-1) {
rswindell's avatar
rswindell committed
1001
					putuserrec(&cfg,user->number,U_ROWS,2,ultoa(ch,tmp,10));
1002 1003 1004 1005 1006
					if(user==&useron) {
						useron.rows=ch;
						ansi_getlines();
					}
				}
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
				break;
			case 'P':
				user->misc^=UPAUSE;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'H':
				user->misc^=COLDKEYS;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'S':
				user->misc^=SPIN;
1018
				if(!(user->misc&SPIN)) {
1019
					if(!yesno(text[SpinningCursorOnPauseQ]))
1020
						user->misc|=NOPAUSESPIN;
1021 1022 1023
					else
						user->misc&=~NOPAUSESPIN;
				}
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'F':
				user->misc^=ANFSCAN;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'X':
				user->misc^=EXPERT;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'R':   /* Remember current sub/dir */
				user->misc^=CURSUB;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'Y':   /* Prompt for scanning message to you */
				user->misc^=ASK_SSCAN;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'N':   /* Prompt for new message/files scanning */
				user->misc^=ASK_NSCAN;
				putuserrec(&cfg,user->number,U_MISC,8,ultoa(user->misc,str,16));
				break;
			case 'M':   /* NetMail address */
1047
				bputs(text[EnterNetMailAddress]);
1048 1049
				getstr(user->netmail,LEN_NETMAIL,K_EDIT|K_AUTODEL|K_LINE);
				if(sys_status&SS_ABORT)
1050 1051 1052
					break;
				putuserrec(&cfg,user->number,U_NETMAIL,LEN_NETMAIL,user->netmail); 
				if(user->netmail[0] == 0 || noyes(text[ForwardMailQ]))