chk_ar.cpp 16.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* chk_ar.cpp */

/* Synchronet ARS checking routine */

/* $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
11
 * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
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
 *																			*
 * 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"

rswindell's avatar
rswindell committed
40
bool sbbs_t::ar_exp(const uchar **ptrptr, user_t* user, client_t* client)
41
{
42
	bool	result,_not,_or,equal;
43
44
	uint	i,n,artype,age;
	ulong	l;
45
	struct tm tm;
rswindell's avatar
rswindell committed
46
	const char*	p;
47
48
49
50
51
52
53
54

	result = true;

	for(;(**ptrptr);(*ptrptr)++) {

		if((**ptrptr)==AR_ENDNEST)
			break;

55
		_not=_or=equal = false;
56
57

		if((**ptrptr)==AR_OR) {
58
			_or=true;
59
60
			(*ptrptr)++; 
		}
61
62
		
		if((**ptrptr)==AR_NOT) {
63
			_not=true;
64
65
			(*ptrptr)++; 
		}
66
67
68

		if((**ptrptr)==AR_EQUAL) {
			equal=true;
69
70
			(*ptrptr)++; 
		}
71

72
		if((result && _or) || (!result && !_or))
73
74
75
76
			break;

		if((**ptrptr)==AR_BEGNEST) {
			(*ptrptr)++;
rswindell's avatar
rswindell committed
77
			if(ar_exp(ptrptr,user,client))
78
				result=!_not;
79
			else
80
				result=_not;
81
82
83
84
			while((**ptrptr)!=AR_ENDNEST && (**ptrptr)) /* in case of early exit */
				(*ptrptr)++;
			if(!(**ptrptr))
				break;
85
86
			continue; 
		}
87
88
89
90
91
92
93
94
95

		artype=(**ptrptr);
		switch(artype) {
			case AR_ANSI:				/* No arguments */
			case AR_RIP:
			case AR_WIP:
			case AR_LOCAL:
			case AR_EXPERT:
			case AR_SYSOP:
96
97
			case AR_GUEST:
			case AR_QNODE:
98
99
100
			case AR_QUIET:
			case AR_OS2:
			case AR_DOS:
101
102
103
			case AR_WIN32:
			case AR_UNIX:
			case AR_LINUX:
104
105
106
			case AR_ACTIVE:
			case AR_INACTIVE:
			case AR_DELETED:
107
108
109
				break;
			default:
				(*ptrptr)++;
110
111
				break; 
		}
112
113
114
115
116
117

		n=(**ptrptr);
		i=(*(short *)*ptrptr);
		switch(artype) {
			case AR_LEVEL:
				if((equal && user->level!=n) || (!equal && user->level<n))
118
					result=_not;
119
				else
120
					result=!_not;
121
122
				if(!result) {
					noaccess_str=text[NoAccessLevel];
123
124
					noaccess_val=n; 
				}
125
126
127
128
				break;
			case AR_AGE:
				age=getage(&cfg,user->birth);
				if((equal && age!=n) || (!equal && age<n))
129
					result=_not;
130
				else
131
					result=!_not;
132
133
				if(!result) {
					noaccess_str=text[NoAccessAge];
134
135
					noaccess_val=n; 
				}
136
137
138
				break;
			case AR_BPS:
				if((equal && cur_rate!=i) || (!equal && cur_rate<i))
139
					result=_not;
140
				else
141
					result=!_not;
142
143
144
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessBPS];
145
146
					noaccess_val=i; 
				}
147
148
149
				break;
			case AR_ANSI:
				if(!(user->misc&ANSI))
150
151
					result=_not;
				else result=!_not;
152
153
154
				break;
			case AR_RIP:
				if(!(user->misc&RIP))
155
156
					result=_not;
				else result=!_not;
157
158
159
				break;
			case AR_WIP:
				if(!(user->misc&WIP))
160
161
					result=_not;
				else result=!_not;
162
163
164
				break;
			case AR_OS2:
				#ifndef __OS2__
165
					result=_not;
166
				#else
167
					result=!_not;
168
169
170
				#endif
				break;
			case AR_DOS:
deuce's avatar
deuce committed
171
				result=_not;
172
173
174
				break;
			case AR_WIN32:
				#ifndef _WIN32
175
					result=_not;
176
				#else
177
					result=!_not;
178
179
180
181
				#endif
				break;
			case AR_UNIX:
				#ifndef __unix__
182
					result=_not;
183
				#else
184
					result=!_not;
185
186
187
188
				#endif
				break;
			case AR_LINUX:
				#ifndef __linux__
189
					result=_not;
190
				#else
191
					result=!_not;
192
193
				#endif
				break;
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
			case AR_ACTIVE:
				if(user->misc&(DELETED|INACTIVE))
					result=_not;
				else result=!_not;
				break;
			case AR_INACTIVE:
				if(!(user->misc&INACTIVE))
					result=_not;
				else result=!_not;
				break;
			case AR_DELETED:
				if(!(user->misc&DELETED))
					result=_not;
				else result=!_not;
				break;
209
210
			case AR_EXPERT:
				if(!(user->misc&EXPERT))
211
212
					result=_not;
				else result=!_not;
213
214
215
				break;
			case AR_SYSOP:
				if(!SYSOP)
216
217
					result=_not;
				else result=!_not;
218
				break;
219
220
221
222
223
224
225
226
227
228
			case AR_GUEST:
				if(!(user->rest&FLAG('G')))
					result=_not;
				else result=!_not;
				break;
			case AR_QNODE:
				if(!(user->rest&FLAG('Q')))
					result=_not;
				else result=!_not;
				break;
229
230
			case AR_QUIET:
				if(thisnode.status!=NODE_QUIET)
231
232
					result=_not;
				else result=!_not;
233
234
235
				break;
			case AR_LOCAL:
				if(online!=ON_LOCAL)
236
237
					result=_not;
				else result=!_not;
238
239
240
				break;
			case AR_DAY:
				now=time(NULL);
241
242
243
				localtime_r(&now,&tm);
				if((equal && tm.tm_wday!=(int)n) 
					|| (!equal && tm.tm_wday<(int)n))
244
					result=_not;
245
				else
246
					result=!_not;
247
248
				if(!result) {
					noaccess_str=text[NoAccessDay];
249
250
					noaccess_val=n; 
				}
251
252
253
254
255
				break;
			case AR_CREDIT:
				l=(ulong)i*1024UL;
				if((equal && user->cdt+user->freecdt!=l)
					|| (!equal && user->cdt+user->freecdt<l))
256
					result=_not;
257
				else
258
					result=!_not;
259
260
261
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessCredit];
262
263
					noaccess_val=l; 
				}
264
265
266
				break;
			case AR_NODE:
				if((equal && cfg.node_num!=n) || (!equal && cfg.node_num<n))
267
					result=_not;
268
				else
269
					result=!_not;
270
271
				if(!result) {
					noaccess_str=text[NoAccessNode];
272
273
					noaccess_val=n; 
				}
274
275
276
				break;
			case AR_USER:
				if((equal && user->number!=i) || (!equal && user->number<i))
277
					result=_not;
278
				else
279
					result=!_not;
280
281
282
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessUser];
283
284
					noaccess_val=i; 
				}
285
286
287
288
289
290
291
				break;
			case AR_GROUP:
				if((equal
					&& (cursubnum>=cfg.total_subs
						|| cfg.sub[cursubnum]->grp!=i))
					|| (!equal && cursubnum<cfg.total_subs
						&& cfg.sub[cursubnum]->grp<i))
292
					result=_not;
293
				else
294
					result=!_not;
295
296
297
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessGroup];
298
299
					noaccess_val=i+1; 
				}
300
301
302
				break;
			case AR_SUB:
				if((equal && cursubnum!=i) || (!equal && cursubnum<i))
303
					result=_not;
304
				else
305
					result=!_not;
306
307
308
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessSub];
309
310
					noaccess_val=i+1; 
				}
311
312
313
				break;
			case AR_SUBCODE:
				if(cursubnum>=cfg.total_subs
rswindell's avatar
rswindell committed
314
					|| !findstr_in_string(cfg.sub[cursubnum]->code,(char*)*ptrptr))
315
					result=_not;
316
				else
317
					result=!_not;
318
319
320
321
322
323
324
325
326
327
328
				while(*(*ptrptr))
					(*ptrptr)++;
				if(!result)
					noaccess_str=text[NoAccessSub];
				break;
			case AR_LIB:
				if((equal
					&& (curdirnum>=cfg.total_dirs
						|| cfg.dir[curdirnum]->lib!=i))
					|| (!equal && curdirnum<cfg.total_dirs
						&& cfg.dir[curdirnum]->lib<i))
329
					result=_not;
330
				else
331
					result=!_not;
332
333
334
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessLib];
335
336
					noaccess_val=i+1; 
				}
337
338
339
				break;
			case AR_DIR:
				if((equal && curdirnum!=i) || (!equal && curdirnum<i))
340
					result=_not;
341
				else
342
					result=!_not;
343
344
345
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessDir];
346
347
					noaccess_val=i+1; 
				}
348
349
350
				break;
			case AR_DIRCODE:
				if(curdirnum>=cfg.total_dirs
rswindell's avatar
rswindell committed
351
					|| !findstr_in_string(cfg.dir[curdirnum]->code,(char *)*ptrptr))
352
					result=_not;
353
				else
354
					result=!_not;
355
356
357
358
359
360
361
362
				while(*(*ptrptr))
					(*ptrptr)++;
				if(!result)
					noaccess_str=text[NoAccessSub];
				break;
			case AR_EXPIRE:
				now=time(NULL);
				if(!user->expire || now+((long)i*24L*60L*60L)>user->expire)
363
					result=_not;
364
				else
365
					result=!_not;
366
367
368
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessExpire];
369
370
					noaccess_val=i; 
				}
371
372
				break;
			case AR_RANDOM:
373
				n=sbbs_random(i+1);
374
				if((equal && n!=i) || (!equal && n<i))
375
					result=_not;
376
				else
377
					result=!_not;
378
379
380
381
382
				(*ptrptr)++;
				break;
			case AR_LASTON:
				now=time(NULL);
				if((now-user->laston)/(24L*60L*60L)<(long)i)
383
					result=_not;
384
				else
385
					result=!_not;
386
387
388
389
				(*ptrptr)++;
				break;
			case AR_LOGONS:
				if((equal && user->logons!=i) || (!equal && user->logons<i))
390
					result=_not;
391
				else
392
					result=!_not;
393
394
395
396
				(*ptrptr)++;
				break;
			case AR_MAIN_CMDS:
				if((equal && main_cmds!=i) || (!equal && main_cmds<i))
397
					result=_not;
398
				else
399
					result=!_not;
400
401
402
403
				(*ptrptr)++;
				break;
			case AR_FILE_CMDS:
				if((equal && xfer_cmds!=i) || (!equal && xfer_cmds<i))
404
					result=_not;
405
				else
406
					result=!_not;
407
408
409
410
				(*ptrptr)++;
				break;
			case AR_TLEFT:
				if(timeleft/60<(ulong)n)
411
					result=_not;
412
				else
413
					result=!_not;
414
415
				if(!result) {
					noaccess_str=text[NoAccessTimeLeft];
416
417
					noaccess_val=n; 
				}
418
419
420
				break;
			case AR_TUSED:
				if((time(NULL)-logontime)/60<(long)n)
421
					result=_not;
422
				else
423
					result=!_not;
424
425
				if(!result) {
					noaccess_str=text[NoAccessTimeUsed];
426
427
					noaccess_val=n; 
				}
428
429
430
				break;
			case AR_TIME:
				now=time(NULL);
431
432
				localtime_r(&now,&tm);
				if((tm.tm_hour*60)+tm.tm_min<(int)i)
433
					result=_not;
434
				else
435
					result=!_not;
436
437
438
				(*ptrptr)++;
				if(!result) {
					noaccess_str=text[NoAccessTime];
439
440
					noaccess_val=i; 
				}
441
				break;
442
			case AR_PCR:	/* post/call ratio (by percentage) */
443
				if(user->logons>user->posts
444
					&& (!user->posts || (100/((float)user->logons/user->posts))<(long)n))
445
					result=_not;
446
				else
447
					result=!_not;
448
449
				if(!result) {
					noaccess_str=text[NoAccessPCR];
450
451
					noaccess_val=n; 
				}
452
				break;
453
			case AR_UDR:	/* up/download byte ratio (by percentage) */
454
455
456
				l=user->dlb;
				if(!l) l=1;
				if(user->dlb>user->ulb
457
					&& (!user->ulb || (100/((float)l/user->ulb))<n))
458
					result=_not;
459
				else
460
					result=!_not;
461
462
				if(!result) {
					noaccess_str=text[NoAccessUDR];
463
464
					noaccess_val=n; 
				}
465
				break;
466
			case AR_UDFR:	/* up/download file ratio (in percentage) */
467
468
469
				i=user->dls;
				if(!i) i=1;
				if(user->dls>user->uls
470
					&& (!user->uls || (100/((float)i/user->uls))<n))
471
					result=_not;
472
				else
473
					result=!_not;
474
475
				if(!result) {
					noaccess_str=text[NoAccessUDFR];
476
477
					noaccess_val=n; 
				}
478
				break;
rswindell's avatar
rswindell committed
479
480
481
482
483
484
485
			case AR_ULS:
				if((equal && user->uls!=i) || (!equal && user->uls<i))
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
486
487
488
489
490
491
492
493
494
			case AR_ULK:
				if((equal && (user->ulb/1024)!=i) || (!equal && (user->ulb/1024)<i))
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
			case AR_ULM:
				if((equal && (user->ulb/(1024*1024))!=i) || (!equal && (user->ulb/(1024*1024))<i))
rswindell's avatar
rswindell committed
495
496
497
498
499
500
501
502
503
504
505
506
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
			case AR_DLS:
				if((equal && user->dls!=i) || (!equal && user->dls<i))
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
507
508
509
510
511
512
513
514
515
			case AR_DLK:
				if((equal && user->dlb/1024!=i) || (!equal && user->dlb/1024<i))
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
			case AR_DLM:
				if((equal && user->dlb/(1024*1024)!=i) || (!equal && user->dlb/(1024*1024)<i))
rswindell's avatar
rswindell committed
516
517
518
519
520
					result=_not;
				else
					result=!_not;
				(*ptrptr)++;
				break;
521
522
523
			case AR_FLAG1:
				if((!equal && !(user->flags1&FLAG(n)))
					|| (equal && user->flags1!=FLAG(n)))
524
					result=_not;
525
				else
526
					result=!_not;
527
528
				if(!result) {
					noaccess_str=text[NoAccessFlag1];
529
530
					noaccess_val=n; 
				}
531
532
533
534
				break;
			case AR_FLAG2:
				if((!equal && !(user->flags2&FLAG(n)))
					|| (equal && user->flags2!=FLAG(n)))
535
					result=_not;
536
				else
537
					result=!_not;
538
539
				if(!result) {
					noaccess_str=text[NoAccessFlag2];
540
541
					noaccess_val=n; 
				}
542
543
544
545
				break;
			case AR_FLAG3:
				if((!equal && !(user->flags3&FLAG(n)))
					|| (equal && user->flags3!=FLAG(n)))
546
					result=_not;
547
				else
548
					result=!_not;
549
550
				if(!result) {
					noaccess_str=text[NoAccessFlag3];
551
552
					noaccess_val=n; 
				}
553
554
555
556
				break;
			case AR_FLAG4:
				if((!equal && !(user->flags4&FLAG(n)))
					|| (equal && user->flags4!=FLAG(n)))
557
					result=_not;
558
				else
559
					result=!_not;
560
561
				if(!result) {
					noaccess_str=text[NoAccessFlag4];
562
563
					noaccess_val=n; 
				}
564
565
566
567
				break;
			case AR_REST:
				if((!equal && !(user->rest&FLAG(n)))
					|| (equal && user->rest!=FLAG(n)))
568
					result=_not;
569
				else
570
					result=!_not;
571
572
				if(!result) {
					noaccess_str=text[NoAccessRest];
573
574
					noaccess_val=n; 
				}
575
576
577
578
				break;
			case AR_EXEMPT:
				if((!equal && !(user->exempt&FLAG(n)))
					|| (equal && user->exempt!=FLAG(n)))
579
					result=_not;
580
				else
581
					result=!_not;
582
583
				if(!result) {
					noaccess_str=text[NoAccessExempt];
584
585
					noaccess_val=n; 
				}
586
587
588
				break;
			case AR_SEX:
				if(user->sex!=n)
589
					result=_not;
590
				else
591
					result=!_not;
592
593
				if(!result) {
					noaccess_str=text[NoAccessSex];
594
595
					noaccess_val=n; 
				}
596
597
598
				break; 
			case AR_SHELL:
				if(user->shell>=cfg.total_shells
rswindell's avatar
rswindell committed
599
					|| !findstr_in_string(cfg.shell[user->shell]->code,(char*)*ptrptr))
600
601
602
603
604
605
					result=_not;
				else
					result=!_not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
606
			case AR_PROT:
rswindell's avatar
rswindell committed
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
				if(client!=NULL)
					p=client->protocol;
				else
					p=user->modem;
				if(!findstr_in_string(p,(char*)*ptrptr))
					result=_not;
				else
					result=!_not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_HOST:
				if(client!=NULL)
					p=client->host;
				else
					p=user->comp;
				if(!findstr_in_string(p,(char*)*ptrptr))
					result=_not;
				else
					result=!_not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_IP:
				if(client!=NULL)
					p=client->addr;
				else
deuce's avatar
deuce committed
634
					p=user->ipaddr;
rswindell's avatar
rswindell committed
635
				if(!findstr_in_string(p,(char*)*ptrptr))
636
637
638
639
640
641
					result=_not;
				else
					result=!_not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
642
643
		}
	}
644
645
646
	return(result);
}

rswindell's avatar
rswindell committed
647
bool sbbs_t::chk_ar(const uchar *ar, user_t* user, client_t* client)
648
{
649
	const uchar *p;
650
651
652
653

	if(ar==NULL)
		return(true);
	p=ar;
rswindell's avatar
rswindell committed
654
	return(ar_exp(&p,user,client));
655
656
657
658
659
660
661
662
663
664
665
666
}


/****************************************************************************/
/* This function fills the usrsub, usrsubs, usrgrps, curgrp, and cursub     */
/* variables based on the security clearance of the current user (useron)   */
/****************************************************************************/
void sbbs_t::getusrsubs()
{
    uint i,j,k,l;

	for(j=0,i=0;i<cfg.total_grps;i++) {
rswindell's avatar
rswindell committed
667
		if(!chk_ar(cfg.grp[i]->ar,&useron,&client))
668
669
670
			continue;
		for(k=0,l=0;l<cfg.total_subs;l++) {
			if(cfg.sub[l]->grp!=i) continue;
rswindell's avatar
rswindell committed
671
			if(!chk_ar(cfg.sub[l]->ar,&useron,&client))
672
				continue;
673
674
			usrsub[j][k++]=l; 
		}
675
676
677
		usrsubs[j]=k;
		if(!k)          /* No subs accessible in group */
			continue;
678
679
		usrgrp[j++]=i; 
	}
680
	usrgrps=j;
681
682
	if(usrgrps==0)
		return;
683
684
685
686
687
688
689
690
691
692
693
694
695
696
	while((curgrp>=usrgrps || !usrsubs[curgrp]) && curgrp) curgrp--;
	while(cursub[curgrp]>=usrsubs[curgrp] && cursub[curgrp]) cursub[curgrp]--;
}

/****************************************************************************/
/* This function fills the usrdir, usrdirs, usrlibs, curlib, and curdir     */
/* variables based on the security clearance of the current user (useron)   */
/****************************************************************************/
void sbbs_t::getusrdirs()
{
    uint i,j,k,l;

	if(useron.rest&FLAG('T')) {
		usrlibs=0;
697
698
		return; 
	}
699
	for(j=0,i=0;i<cfg.total_libs;i++) {
rswindell's avatar
rswindell committed
700
		if(!chk_ar(cfg.lib[i]->ar,&useron,&client))
701
702
703
			continue;
		for(k=0,l=0;l<cfg.total_dirs;l++) {
			if(cfg.dir[l]->lib!=i) continue;
rswindell's avatar
rswindell committed
704
			if(!chk_ar(cfg.dir[l]->ar,&useron,&client))
705
				continue;
706
707
			usrdir[j][k++]=l; 
		}
708
709
710
		usrdirs[j]=k;
		if(!k)          /* No dirs accessible in lib */
			continue;
711
712
		usrlib[j++]=i; 
	}
713
	usrlibs=j;
714
715
	if(usrlibs==0)
		return;
716
717
718
719
	while((curlib>=usrlibs || !usrdirs[curlib]) && curlib) curlib--;
	while(curdir[curlib]>=usrdirs[curlib] && curdir[curlib]) curdir[curlib]--;
}

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
uint sbbs_t::getusrgrp(uint subnum)
{
	uint	ugrp;

	if(subnum==INVALID_SUB)
		return(0);

	if(usrgrps<=0)
		return(0);

	for(ugrp=0;ugrp<usrgrps;ugrp++)
		if(usrgrp[ugrp]==cfg.sub[subnum]->grp)
			break;

	return(ugrp+1);
}

uint sbbs_t::getusrsub(uint subnum)
{
	uint	usub;
	uint	ugrp;

	ugrp = getusrgrp(subnum);
	if(ugrp<=0)
		return(0);
745
	ugrp--;
746
747
748
749
750
751
752
	for(usub=0;usub<usrsubs[ugrp];usub++)
		if(usrsub[ugrp][usub]==subnum)
			break;

	return(usub+1);
}

753
754
int sbbs_t::dir_op(uint dirnum)
{
755
	return(SYSOP || (cfg.dir[dirnum]->op_ar!=NULL && cfg.dir[dirnum]->op_ar[0] && chk_ar(cfg.dir[dirnum]->op_ar,&useron,&client)));
756
757
758
}