]> andersk Git - moira.git/blame - server/qsetup.pc
Add winconsoleshell column to the users table
[moira.git] / server / qsetup.pc
CommitLineData
7ac48069 1/* $Id$
73cf66ba 2 *
7ac48069 3 * Query setup routines
73cf66ba 4 *
7ac48069 5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
73cf66ba 8 */
9
73cf66ba 10#include <mit-copyright.h>
73cf66ba 11#include "mr_server.h"
03c05291 12#include "query.h"
7ac48069 13#include "qrtn.h"
14
03c05291 15#include <arpa/inet.h>
7ac48069 16#include <netinet/in.h>
17
18#include <ctype.h>
19#include <stdlib.h>
20#include <string.h>
21
73cf66ba 22EXEC SQL INCLUDE sqlca;
73cf66ba 23
7ac48069 24RCSID("$Header$");
25
26extern char *whoami;
03c05291 27extern int dbms_errno, mr_errcode;
73cf66ba 28
29EXEC SQL BEGIN DECLARE SECTION;
30extern char stmt_buf[];
31EXEC SQL END DECLARE SECTION;
32
03c05291 33EXEC SQL WHENEVER SQLERROR DO dbmserr();
34
17f585b5 35int hostname_check(char *name);
36int hostinfo_check(char *name, int num);
03c05291 37int prefetch_value(struct query *q, char **argv, client *cl);
38int check_nfs(int mach_idx, char *name, char *access);
73cf66ba 39
40/* Setup Routines */
41
42/* Setup routine for add_user
43 *
44 * Inputs: argv[0] - login
45 * argv[1] - uid
46 *
47 * Description:
48 *
49 * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid)
50 * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#<uid>"
51 */
52
5eaef520 53int setup_ausr(struct query *q, char *argv[], client *cl)
73cf66ba 54{
5eaef520 55 int row, err;
56 EXEC SQL BEGIN DECLARE SECTION;
57 int nuid;
58 EXEC SQL END DECLARE SECTION;
59
60 if (!strcmp(q->shortname, "uusr") || !strcmp(q->shortname, "uuac"))
61 row = 2;
62 else
63 row = 1;
cca8e7ef 64
65 if (strlen(argv[row + 2]) + strlen(argv[row + 3]) +
66 strlen(argv[row + 4]) + 2 > USERS_FULLNAME_SIZE)
67 return MR_ARG_TOO_LONG;
68
5eaef520 69 if (!strcmp(argv[row], UNIQUE_UID) || atoi(argv[row]) == -1)
70 {
71 if ((err = set_next_object_id("unix_uid", USERS_TABLE, 1)))
72 return err;
73 EXEC SQL SELECT value INTO :nuid FROM numvalues WHERE name = 'unix_uid';
74 if (sqlca.sqlerrd[2] != 1)
75 return MR_INTERNAL;
76 sprintf(argv[row], "%d", nuid);
73cf66ba 77 }
78
5eaef520 79 if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[row]) == -1)
80 sprintf(argv[0], "#%s", argv[row]);
73cf66ba 81
5eaef520 82 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
83 return mr_errcode;
73cf66ba 84
5eaef520 85 return MR_SUCCESS;
73cf66ba 86}
87
88
89/* setup_dusr - verify that the user is no longer being referenced
90 * and may safely be deleted.
91 */
92
7ac48069 93int setup_dusr(struct query *q, char *argv[], client *cl)
73cf66ba 94{
5eaef520 95 EXEC SQL BEGIN DECLARE SECTION;
96 int flag, id, cnt;
00c3d0fe 97 char resv[USERS_RESERVATIONS_SIZE];
5eaef520 98 EXEC SQL END DECLARE SECTION;
99
100 id = *(int *)argv[0];
101
00c3d0fe 102 /* For now, only allow users to be deleted if their status is 0
103 * and we have no reservations about deleting them.
104 */
105 EXEC SQL SELECT status, reservations INTO :flag, :resv
106 FROM users WHERE users_id = :id;
107 if ((flag != 0 && flag != 4) || *resv)
5eaef520 108 return MR_IN_USE;
109
5eaef520 110 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
111 WHERE member_id = :id AND member_type = 'USER';
112 if (cnt > 0)
113 return MR_IN_USE;
114 EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys
115 WHERE owner = :id;
116 if (cnt > 0)
117 return MR_IN_USE;
118 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
119 WHERE acl_id = :id AND acl_type = 'USER';
120 if (cnt > 0)
121 return MR_IN_USE;
122 EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers
123 WHERE acl_id = :id AND acl_type = 'USER';
124 if (cnt > 0)
125 return MR_IN_USE;
126 EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess
127 WHERE acl_id = :id AND acl_type = 'USER';
128 if (cnt > 0)
129 return MR_IN_USE;
130 if (dbms_errno)
131 return mr_errcode;
00c3d0fe 132
133 EXEC SQL DELETE FROM quota WHERE entity_id = :id AND type = 'USER';
134 EXEC SQL DELETE FROM krbmap WHERE users_id = :id;
5eaef520 135 return MR_SUCCESS;
73cf66ba 136}
137
138
73cf66ba 139/* setup_dpob: Take care of keeping track of the post office usage.
140 */
7ac48069 141int setup_dpob(struct query *q, char *argv[], client *cl)
73cf66ba 142{
5eaef520 143 EXEC SQL BEGIN DECLARE SECTION;
144 int id, user;
e688520a 145 char type[USERS_POTYPE_SIZE];
5eaef520 146 EXEC SQL END DECLARE SECTION;
147
148 user = *(int *)argv[0];
149 EXEC SQL SELECT potype, pop_id INTO :type, :id FROM users
150 WHERE users_id = :user;
151 if (dbms_errno)
152 return mr_errcode;
153
154 if (!strcmp(strtrim(type), "POP"))
155 set_pop_usage(id, -1);
156 return MR_SUCCESS;
73cf66ba 157}
158
159
160/* setup_dmac - verify that the machine is no longer being referenced
161 * and may safely be deleted.
162 */
163
7ac48069 164int setup_dmac(struct query *q, char *argv[], client *cl)
73cf66ba 165{
5eaef520 166 EXEC SQL BEGIN DECLARE SECTION;
167 int flag, id, cnt;
168 EXEC SQL END DECLARE SECTION;
169
170 id = *(int *)argv[0];
171
172 EXEC SQL SELECT status INTO :flag FROM machine
173 WHERE mach_id = :id;
174 if (flag != 3)
175 return MR_IN_USE;
176 EXEC SQL SELECT COUNT(login) INTO :cnt FROM users
177 WHERE potype = 'POP' AND pop_id = :id;
178 if (cnt > 0)
179 return MR_IN_USE;
180 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM serverhosts
181 WHERE mach_id = :id;
182 if (cnt > 0)
183 return MR_IN_USE;
184 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM nfsphys
185 WHERE mach_id = :id;
186 if (cnt > 0)
187 return MR_IN_USE;
188 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostaccess
189 WHERE mach_id = :id;
190 if (cnt > 0)
191 return MR_IN_USE;
2884200f 192 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printers
5eaef520 193 WHERE mach_id = :id;
194 if (cnt > 0)
195 return MR_IN_USE;
2884200f 196 EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers
197 WHERE rm = :id;
198 if (cnt > 0)
199 return MR_IN_USE;
200 EXEC SQL SELECT COUNT(rq) INTO :cnt FROM printers
201 WHERE rq = :id;
5eaef520 202 if (cnt > 0)
203 return MR_IN_USE;
1a9a0a59 204 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers
5eaef520 205 WHERE mach_id = :id;
206 if (cnt > 0)
207 return MR_IN_USE;
208 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostalias
209 WHERE mach_id = :id;
210 if (cnt > 0)
211 return MR_IN_USE;
212
213 EXEC SQL DELETE FROM mcmap WHERE mach_id = :id;
214 if (dbms_errno)
215 return mr_errcode;
216 return MR_SUCCESS;
73cf66ba 217}
218
219
220/* setup_dsnt - verify that the subnet is no longer being referenced
221 * and may safely be deleted.
222 */
223
7ac48069 224int setup_dsnt(struct query *q, char *argv[], client *cl)
73cf66ba 225{
5eaef520 226 EXEC SQL BEGIN DECLARE SECTION;
227 int id, cnt = 0;
228 EXEC SQL END DECLARE SECTION;
229
230 id = *(int *)argv[0];
231 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM machine
232 WHERE snet_id = :id;
233 if (cnt > 0)
234 return MR_IN_USE;
235 return MR_SUCCESS;
73cf66ba 236}
237
238
239/* setup_dclu - verify that the cluster is no longer being referenced
240 * and may safely be deleted.
241 */
242
7ac48069 243int setup_dclu(struct query *q, char *argv[], client *cl)
73cf66ba 244{
5eaef520 245 EXEC SQL BEGIN DECLARE SECTION;
246 int id, cnt;
247 EXEC SQL END DECLARE SECTION;
248
249 id = *(int *)argv[0];
250 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcmap
251 WHERE clu_id = :id;
252 if (cnt > 0)
253 return MR_IN_USE;
254 EXEC SQL SELECT COUNT(clu_id) INTO :cnt FROM svc
255 WHERE clu_id = :id;
256 if (cnt > 0)
257 return MR_IN_USE;
258 if (dbms_errno)
259 return mr_errcode;
260 return MR_SUCCESS;
73cf66ba 261}
262
263
264/* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate
265 * a new gid and put it in argv[6]. Otherwise if argv[6] is UNIQUE_ID but
266 * argv[5] is not, then remember that UNIQUE_ID is being stored by putting
267 * a -1 there. Remember that this is also used for ulis, with the indexes
268 * at 6 & 7. Also check that the list name does not contain uppercase
269 * characters, control characters, @, or :.
270 */
271
272static int badlistchars[] = {
5eaef520 273 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
274 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
0b452293 275 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, /* SPACE - / */
276 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */
5eaef520 277 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
0b452293 278 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */
5eaef520 279 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
0b452293 280 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
5eaef520 281 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
282 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
283 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
284 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
285 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
286 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
287 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
288 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
73cf66ba 289};
290
5eaef520 291int setup_alis(struct query *q, char *argv[], client *cl)
73cf66ba 292{
5eaef520 293 EXEC SQL BEGIN DECLARE SECTION;
17f585b5 294 int ngid, cnt;
295 char *name;
5eaef520 296 EXEC SQL END DECLARE SECTION;
297 unsigned char *p;
298 int idx, err;
299
300 if (!strcmp(q->shortname, "alis"))
301 idx = 0;
302 else if (!strcmp(q->shortname, "ulis"))
303 idx = 1;
17f585b5 304 name = argv[idx];
5eaef520 305
5a22fdf5 306 if (idx == 1)
307 {
308 EXEC SQL BEGIN DECLARE SECTION;
309 int lid = *(int *)argv[0];
310 EXEC SQL END DECLARE SECTION;
311
312 if (acl_access_check(lid, cl))
313 return MR_PERM;
314 }
315
17f585b5 316 for (p = (unsigned char *) name; *p; p++)
5eaef520 317 {
73cf66ba 318 if (badlistchars[*p])
5eaef520 319 return MR_BAD_CHAR;
320 }
321
17f585b5 322 /* Check that it doesn't conflict with a pre-existing weirdly-cased
323 * name. */
324 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
20315440 325 WHERE LOWER(name) = :name AND name != :name;
17f585b5 326 if (cnt)
327 return MR_EXISTS;
328
5eaef520 329 if (!strcmp(argv[6 + idx], UNIQUE_GID) || atoi(argv[6 + idx]) == -1)
330 {
331 if (atoi(argv[5 + idx]))
332 {
333 if ((err = set_next_object_id("gid", LIST_TABLE, 1)))
334 return err;
335 EXEC SQL SELECT value INTO :ngid FROM numvalues
336 WHERE name = 'gid';
337 if (dbms_errno)
338 return mr_errcode;
339 sprintf(argv[6 + idx], "%d", ngid);
73cf66ba 340 }
5eaef520 341 else
342 strcpy(argv[6 + idx], "-1");
73cf66ba 343 }
344
5eaef520 345 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
346 return mr_errcode;
73cf66ba 347
5eaef520 348 return MR_SUCCESS;
73cf66ba 349}
350
351
352/* setup_dlis - verify that the list is no longer being referenced
353 * and may safely be deleted.
354 */
355
7ac48069 356int setup_dlis(struct query *q, char *argv[], client *cl)
73cf66ba 357{
5eaef520 358 int id;
359 EXEC SQL BEGIN DECLARE SECTION;
360 int cnt;
361 EXEC SQL END DECLARE SECTION;
362
363 id = *(int *)argv[0];
364
365 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
366 WHERE member_id = :id AND member_type = 'LIST';
367 if (cnt > 0)
368 return MR_IN_USE;
369
370 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
371 WHERE member_id = :id AND member_type = 'LIST';
372 if (cnt > 0)
373 return MR_IN_USE;
374
375 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
376 WHERE list_id = :id;
377 if (cnt > 0)
378 return MR_IN_USE;
379
380 EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys WHERE owners = :id;
381 if (cnt > 0)
382 return MR_IN_USE;
383
384 EXEC SQL SELECT COUNT(tag) INTO :cnt FROM capacls WHERE list_id = :id;
385 if (cnt > 0)
386 return MR_IN_USE;
387
388 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
389 WHERE acl_id = :id AND acl_type = 'LIST' AND list_id != :id;
390 if (cnt > 0)
391 return MR_IN_USE;
392
393 EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers
394 WHERE acl_id = :id AND acl_type = 'LIST';
395 if (cnt > 0)
396 return MR_IN_USE;
397
398 EXEC SQL SELECT COUNT(entity_id) INTO :cnt FROM quota
399 WHERE entity_id = :id AND type = 'GROUP';
400 if (cnt > 0)
401 return MR_IN_USE;
402
403 EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess
404 WHERE acl_id = :id AND acl_type = 'LIST';
405 if (cnt > 0)
406 return MR_IN_USE;
407
408 EXEC SQL SELECT COUNT(class) INTO :cnt FROM zephyr z
409 WHERE z.xmt_type = 'LIST' AND z.xmt_id = :id
410 OR z.sub_type = 'LIST' AND z.sub_id = :id
411 OR z.iws_type = 'LIST' AND z.iws_id = :id
412 OR z.iui_type = 'LIST' AND z.iui_id = :id;
413 if (cnt > 0)
414 return MR_IN_USE;
415
1a9a0a59 416 EXEC SQL SELECT COUNT(name) INTO :cnt FROM printers
417 WHERE lpc_acl = :id OR ac = :id;
418 if (cnt > 0)
419 return MR_IN_USE;
420
421 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers
422 WHERE owner_type = 'LIST' AND owner_id = :id
423 OR lpc_acl = :id;
424 if (cnt > 0)
425 return MR_IN_USE;
426
5eaef520 427 return MR_SUCCESS;
73cf66ba 428}
429
430
431/* setup_dsin - verify that the service is no longer being referenced
432 * and may safely be deleted.
433 */
434
7ac48069 435int setup_dsin(struct query *q, char *argv[], client *cl)
73cf66ba 436{
5eaef520 437 EXEC SQL BEGIN DECLARE SECTION;
438 int ec, cnt;
439 char *svrname;
440 EXEC SQL END DECLARE SECTION;
441
442 svrname = argv[0];
443 EXEC SQL SELECT COUNT(service) INTO :cnt FROM serverhosts
444 WHERE service = UPPER(:svrname);
445 if (cnt > 0)
446 return MR_IN_USE;
447
448 EXEC SQL SELECT inprogress INTO :ec FROM servers
449 WHERE name = UPPER(:svrname);
450 if (dbms_errno)
451 return mr_errcode;
452 if (ec)
453 return MR_IN_USE;
454
455 return MR_SUCCESS;
73cf66ba 456}
457
458
459/* setup_dshi - verify that the service-host is no longer being referenced
460 * and may safely be deleted.
461 */
462
7ac48069 463int setup_dshi(struct query *q, char *argv[], client *cl)
73cf66ba 464{
5eaef520 465 EXEC SQL BEGIN DECLARE SECTION;
466 int id, ec;
467 char *svrname;
468 EXEC SQL END DECLARE SECTION;
469
470 svrname = argv[0];
471 id = *(int *)argv[1];
472
473 EXEC SQL SELECT inprogress INTO :ec FROM serverhosts
474 WHERE service = UPPER(:svrname) AND mach_id = :id;
475 if (dbms_errno)
476 return mr_errcode;
477 if (ec)
478 return MR_IN_USE;
479
480 return MR_SUCCESS;
73cf66ba 481}
482
483
484/**
485 ** setup_add_filesys - verify existance of referenced file systems
486 **
487 ** Inputs: Add
488 ** argv[1] - type
489 ** argv[2] - mach_id
490 ** argv[3] - name
03c05291 491 ** argv[5] - rwaccess
73cf66ba 492 **
493 ** Description:
494 ** - for type = RVD:
495 ** * allow anything
cc1bca5c 496 ** - for type = NFS/IMAP:
73cf66ba 497 ** * extract directory prefix from name
498 ** * verify mach_id/dir in nfsphys
03c05291 499 ** * verify rwaccess in {r, w, R, W}
73cf66ba 500 **
501 ** Side effect: sets variable _var_phys_id to the ID of the physical
502 ** filesystem (nfsphys_id for NFS, 0 for RVD)
503 **
504 ** Errors:
505 ** MR_NFS - specified directory not exported
506 ** MR_FILESYS_ACCESS - invalid filesys access
507 **
508 **/
509
510EXEC SQL BEGIN DECLARE SECTION;
511int _var_phys_id;
512EXEC SQL END DECLARE SECTION;
513
5eaef520 514int setup_afil(struct query *q, char *argv[], client *cl)
73cf66ba 515{
5eaef520 516 char *type, *name;
517 int mach_id;
518 EXEC SQL BEGIN DECLARE SECTION;
519 int ok;
e688520a 520 char ftype[FILESYS_TYPE_SIZE + 10], *rwaccess;
5eaef520 521 EXEC SQL END DECLARE SECTION;
522
523 type = argv[1];
524 mach_id = *(int *)argv[2];
525 name = argv[3];
526 rwaccess = argv[5];
527 _var_phys_id = 0;
528
529 sprintf(ftype, "fs_access_%s", type);
530 EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
531 WHERE name = :ftype AND type = 'TYPE' and trans = :rwaccess;
532 if (dbms_errno)
533 return mr_errcode;
534 if (ok == 0)
535 return MR_FILESYS_ACCESS;
536
537 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
538 return mr_errcode;
539
cc1bca5c 540 if (!strcmp(type, "NFS") || !strcmp(type, "IMAP"))
5eaef520 541 return check_nfs(mach_id, name, rwaccess);
542
543 return MR_SUCCESS;
73cf66ba 544}
545
546
547/* Verify the arguments, depending on the FStype. Also, if this is an
548 * NFS filesystem, then update any quotas for that filesystem to reflect
549 * the new phys_id.
550 */
551
5eaef520 552int setup_ufil(struct query *q, char *argv[], client *cl)
73cf66ba 553{
5eaef520 554 int mach_id, status;
555 char *type, *name;
556 EXEC SQL BEGIN DECLARE SECTION;
557 int fid, total, who, ok;
e688520a 558 char *entity, ftype[FILESYS_TYPE_SIZE + 10], *access;
5eaef520 559 short int total_null;
560 EXEC SQL END DECLARE SECTION;
561
562 _var_phys_id = 0;
563 type = argv[2];
564 mach_id = *(int *)argv[3];
565 name = argv[4];
566 access = argv[6];
567 fid = *(int *)argv[0];
568 who = cl->client_id;
569 entity = cl->entity;
570
571 sprintf(ftype, "fs_access_%s", type);
572 EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
573 WHERE name = :ftype AND type = 'TYPE' AND trans = :access;
574 if (dbms_errno)
575 return mr_errcode;
576 if (ok == 0)
577 return MR_FILESYS_ACCESS;
578
579 EXEC SQL SELECT type INTO :ftype FROM filesys
580 WHERE filsys_id = :fid;
581 if (dbms_errno)
582 return mr_errcode;
583
cc1bca5c 584 if (!strcmp(type, "NFS") || !strcmp(type, "IMAP"))
5eaef520 585 {
586 status = check_nfs(mach_id, name, access);
587 EXEC SQL UPDATE quota SET phys_id = :_var_phys_id
588 WHERE filsys_id = :fid;
589 if (dbms_errno)
590 return mr_errcode;
591 return status;
592 }
593 else if (!strcmp(type, "AFS") && strcmp(strtrim(ftype), "AFS")
594 && strcmp(strtrim(ftype), "ERR"))
595 {
596 total = 0;
597 EXEC SQL DELETE FROM quota
598 WHERE type = 'ANY' AND filsys_id = :fid;
599 EXEC SQL SELECT SUM (quota) INTO :total:total_null FROM quota
600 WHERE filsys_id = :fid AND phys_id != 0;
601 if (dbms_errno)
602 return mr_errcode;
603 if (!total_null && (total != 0))
604 {
605 EXEC SQL INSERT INTO quota (quota, filsys_id, phys_id, entity_id,
606 type, modtime, modby, modwith)
607 VALUES (:total, :fid, 0, 0, 'ANY', SYSDATE, :who, :entity);
608 if (dbms_errno)
609 return mr_errcode;
73cf66ba 610 }
73cf66ba 611 }
5eaef520 612 else
613 {
614 EXEC SQL UPDATE quota SET phys_id = 0 WHERE filsys_id = :fid;
615 if (dbms_errno)
616 return mr_errcode;
617 }
618 return MR_SUCCESS;
73cf66ba 619}
620
621
622/* Find the NFS physical partition that the named directory is on.
623 * This is done by comparing the dir against the mount point of the
624 * partition. To make sure we get the correct match when there is
625 * more than one, we sort the query in reverse order by dir name.
626 */
627
5eaef520 628int check_nfs(int mach_id, char *name, char *access)
73cf66ba 629{
5eaef520 630 EXEC SQL BEGIN DECLARE SECTION;
e688520a 631 char dir[NFSPHYS_DIR_SIZE];
5eaef520 632 int mid = mach_id;
633 EXEC SQL END DECLARE SECTION;
44d12d58 634 int status;
635 char *cp1;
636 char *cp2;
5eaef520 637
638 status = MR_NFS;
639 EXEC SQL DECLARE csr101 CURSOR FOR
640 SELECT nfsphys_id, dir FROM nfsphys
641 WHERE mach_id = :mid
642 ORDER BY 2 DESC;
643 if (dbms_errno)
644 return mr_errcode;
645 EXEC SQL OPEN csr101;
646 if (dbms_errno)
647 return mr_errcode;
648 while (1)
649 {
650 EXEC SQL FETCH csr101 INTO :_var_phys_id, :dir;
651 if (sqlca.sqlcode)
652 break;
653 cp1 = name;
654 cp2 = strtrim(dir);
655 while (*cp2)
656 {
657 if (*cp1++ != *cp2)
73cf66ba 658 break;
5eaef520 659 cp2++;
660 }
661 if (!*cp2)
662 {
663 status = MR_SUCCESS;
664 break;
73cf66ba 665 }
666 }
5eaef520 667 EXEC SQL CLOSE csr101;
668 if (dbms_errno)
669 return mr_errcode;
670 return status;
73cf66ba 671}
672
673
674/* setup_dfil: free any quota records and fsgroup info associated with
675 * a filesystem when it is deleted. Also adjust the allocation numbers.
676 */
677
5eaef520 678int setup_dfil(struct query *q, char **argv, client *cl)
73cf66ba 679{
5eaef520 680 EXEC SQL BEGIN DECLARE SECTION;
681 int id, total, phys_id;
682 short int none;
683 EXEC SQL END DECLARE SECTION;
684
685 id = *(int *)argv[0];
686 EXEC SQL SELECT SUM (quota) INTO :total:none FROM quota
687 WHERE filsys_id = :id;
688
689 if (none)
690 total = 0;
691
692 /** What if there are multiple phys_id's per f/s? (bad data) **/
693 EXEC SQL SELECT phys_id INTO :phys_id FROM filesys
694 WHERE filsys_id = :id;
695 EXEC SQL UPDATE nfsphys SET allocated = allocated - :total
696 WHERE nfsphys_id = :phys_id;
697
698 if (!none)
699 EXEC SQL DELETE FROM quota WHERE filsys_id = :id;
700 EXEC SQL DELETE FROM fsgroup WHERE filsys_id = :id;
701 EXEC SQL DELETE FROM fsgroup WHERE group_id = :id;
702 if (dbms_errno)
703 return mr_errcode;
704 return MR_SUCCESS;
73cf66ba 705}
706
707
708/* setup_dnfp: check to see that the nfs physical partition does not have
709 * any filesystems assigned to it before allowing it to be deleted.
710 */
711
5eaef520 712int setup_dnfp(struct query *q, char **argv, client *cl)
73cf66ba 713{
5eaef520 714 EXEC SQL BEGIN DECLARE SECTION;
715 int id, cnt;
716 char *dir;
717 EXEC SQL END DECLARE SECTION;
718
719 id = *(int *)argv[0];
720 dir = argv[1];
721 EXEC SQL SELECT count(fs.rowid) INTO :cnt FROM filesys fs, nfsphys np
722 WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id
723 AND np.mach_id = :id AND np.dir = :dir;
724 if (cnt > 0)
725 return MR_IN_USE;
726 if (dbms_errno)
727 return mr_errcode;
728 return MR_SUCCESS;
73cf66ba 729}
730
731
732/* setup_dqot: Remove allocation from nfsphys before deleting quota.
733 * argv[0] = filsys_id
734 * argv[1] = type if "update_quota" or "delete_quota"
735 * argv[2 or 1] = users_id or list_id
736 */
737
5eaef520 738int setup_dqot(struct query *q, char **argv, client *cl)
73cf66ba 739{
5eaef520 740 EXEC SQL BEGIN DECLARE SECTION;
741 int quota, fs, id, physid;
742 char *qtype;
743 EXEC SQL END DECLARE SECTION;
744
745 fs = *(int *)argv[0];
746 if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota"))
747 {
748 qtype = argv[1];
749 id = *(int *)argv[2];
750 }
751 else
752 {
753 qtype = "USER";
754 id = *(int *)argv[1];
73cf66ba 755 }
756
5eaef520 757 EXEC SQL SELECT quota INTO :quota FROM quota
758 WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs;
759 EXEC SQL SELECT phys_id INTO :physid FROM filesys
760 WHERE filsys_id = :fs;
761 EXEC SQL UPDATE nfsphys SET allocated = allocated - :quota
762 WHERE nfsphys_id = :physid;
73cf66ba 763
5eaef520 764 if (dbms_errno)
765 return mr_errcode;
766 return MR_SUCCESS;
73cf66ba 767}
768
769
7f426981 770/* prefetch_value():
771 * This routine fetches an appropriate value from the numvalues table.
772 * It is a little hack to get around the fact that SQL doesn't let you
773 * do something like INSERT INTO table (foo) VALUES (other_table.bar).
774 *
5eaef520 775 * It is called from the query table as (*v->pre_rtn)(q, Argv, cl) or
7f426981 776 * from within a setup_...() routine with the appropriate arguments.
777 *
778 * Correct functioning of this routine may depend on the assumption
779 * that this query is an APPEND.
780 */
781
5eaef520 782int prefetch_value(struct query *q, char **argv, client *cl)
7f426981 783{
5eaef520 784 EXEC SQL BEGIN DECLARE SECTION;
785 char *name = q->validate->object_id;
786 int value;
787 EXEC SQL END DECLARE SECTION;
788 int status, limit, argc;
789
790 /* set next object id, limiting it if necessary */
791 if (!strcmp(name, "unix_uid") || !strcmp(name, "gid"))
792 limit = 1; /* So far as I know, this isn't needed. Just CMA. */
793 else
794 limit = 0;
795 if ((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS)
796 return status;
797
798 /* fetch object id */
799 EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :name;
800 if (dbms_errno)
801 return mr_errcode;
802 if (sqlca.sqlerrd[2] != 1)
803 return MR_INTERNAL;
804
805 argc = q->argc + q->vcnt; /* end of Argv for APPENDs */
806 sprintf(argv[argc], "%d", value);
807
808 return MR_SUCCESS;
7f426981 809}
810
811/* prefetch_filesys():
812 * Fetches the phys_id from filesys based on the filsys_id in argv[0].
813 * Appends the filsys_id and the phys_id to the argv so they can be
814 * referenced in an INSERT into a table other than filesys. Also
815 * see comments at prefetch_value().
816 *
817 * Assumes the existence of a row where filsys_id = argv[0], since a
818 * filesys label has already been resolved to a filsys_id.
819 */
5eaef520 820int prefetch_filesys(struct query *q, char **argv, client *cl)
7f426981 821{
5eaef520 822 EXEC SQL BEGIN DECLARE SECTION;
823 int fid, phid;
824 EXEC SQL END DECLARE SECTION;
825 int argc;
7f426981 826
5eaef520 827 fid = *(int *)argv[0];
828 EXEC SQL SELECT phys_id INTO :phid FROM filesys WHERE filsys_id = :fid;
829 if (dbms_errno)
830 return mr_errcode;
7f426981 831
5eaef520 832 argc = q->argc + q->vcnt;
833 sprintf(argv[argc++], "%d", phid);
834 sprintf(argv[argc], "%d", fid);
7f426981 835
5eaef520 836 return MR_SUCCESS;
7f426981 837}
838
839
840/* setup_ahst():
841 */
842
5eaef520 843int setup_ahst(struct query *q, char **argv, client *cl)
7f426981 844{
5eaef520 845 EXEC SQL BEGIN DECLARE SECTION;
e688520a 846 char *name, oldname[MACHINE_NAME_SIZE], vendor[MACHINE_VENDOR_SIZE];
db9491c6 847 char model[MACHINE_MODEL_SIZE], os[MACHINE_OS_SIZE];
ff98438b 848 int value, id, ssaddr, smask, shigh, slow, cnt;
849 unsigned int saddr, mask, high, low;
5eaef520 850 EXEC SQL END DECLARE SECTION;
851 int row;
852 struct in_addr addr;
5eaef520 853
fde09f2c 854 id = *(int *)argv[0];
855
5eaef520 856 if (!strcmp(q->shortname, "uhst"))
85330553 857 {
858 row = 1;
859 EXEC SQL SELECT name, vendor, model, os
860 INTO :oldname, :vendor, :model, :os
861 FROM machine WHERE mach_id = :id;
862 }
5eaef520 863 else
864 row = 0;
865
9e252e6f 866 /* Sanity check name, vendor, model, and os. */
867 if ((row == 0 || strcasecmp(argv[1], oldname)) &&
868 !hostname_check(argv[row]))
869 return MR_BAD_CHAR;
870 if ((row == 0 || strcasecmp(argv[2], vendor)) &&
17f585b5 871 !hostinfo_check(argv[row + 1], 0))
9e252e6f 872 return MR_BAD_CHAR;
873 if ((row == 0 || strcasecmp(argv[3], model)) &&
17f585b5 874 !hostinfo_check(argv[row + 2], 1))
9e252e6f 875 return MR_BAD_CHAR;
876 if ((row == 0 || strcasecmp(argv[4], os)) &&
17f585b5 877 !hostinfo_check(argv[row + 3], 0))
9e252e6f 878 return MR_BAD_CHAR;
8ca3e341 879
5eaef520 880 /* check for duplicate name */
881 name = argv[row];
882 EXEC SQL SELECT count(mach_id) INTO :cnt FROM hostalias
ad06a10e 883 WHERE name = UPPER(:name);
5eaef520 884 if (dbms_errno)
885 return mr_errcode;
886 if (cnt != 0)
887 return MR_EXISTS;
888
889 /* check address */
890 if (!strcmp(argv[9 + row], "unassigned"))
891 value = -1;
892 else if (!strcmp(argv[9 + row], "unique"))
893 {
894 if (*(int *)argv[8 + row] == 0)
895 value = -1;
896 else
897 value = -2;
898 }
899 else
900 {
901 value = ntohl(inet_addr(argv[9 + row]));
902 if (value == -1)
903 return MR_ADDRESS;
5482f508 904 }
5eaef520 905 if (value == 0)
906 return MR_ADDRESS;
907 if (value != -1)
908 {
909 /*
910 * an address or unique was specified.
911 */
912 id = *(int *)argv[8 + row];
ff98438b 913 EXEC SQL SELECT saddr, mask, high, low INTO :ssaddr, :smask,
914 :shigh, :slow FROM subnet WHERE snet_id = :id;
5eaef520 915 if (dbms_errno)
916 return mr_errcode;
ff98438b 917 saddr = (unsigned) ssaddr;
918 mask = (unsigned) smask;
919 high = (unsigned) shigh;
920 low = (unsigned) slow;
5eaef520 921 if (value != -2)
922 {
923 /*
924 * someone specified an IP address for the host record
925 */
082466aa 926 if ((value & mask) != saddr || value < low || value > high)
5eaef520 927 return MR_ADDRESS;
928 /*
929 * run the address argument through inet_addr(). This
930 * has the effect that any out of bounds host addrs will
931 * be converted to a valid host addr. We do this now
932 * so that the uniqueness check works. We should also
933 * link in an inet_addr() that returns an error for
934 * this case.
935 */
936 addr.s_addr = inet_addr(argv[9 + row]);
937 name = inet_ntoa(addr);
938 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine
939 WHERE address = :name;
940 if (dbms_errno)
941 return mr_errcode;
942 if (cnt > 0)
943 {
944 /*
945 * make IP address is unique. If this a modify request
946 * (row == 1), then we expect one record to exist.
947 */
7ac48069 948 if (row == 0 || (row == 1 && cnt > 1))
5eaef520 949 return MR_ADDRESS;
950 if (row == 1 && cnt == 1)
951 {
952 EXEC SQL SELECT mach_id INTO :id FROM machine
953 WHERE address = :name;
954 if (id != *(int *)argv[0])
955 return MR_ADDRESS;
7f426981 956 }
957 }
5eaef520 958 }
959 else
960 {
961 /*
962 * a "unique" address was specified. Walk through the
963 * range specified in the network record, return
964 * error if no room left.
965 */
966 for (id = low; id <= high; id++)
967 {
968 if (((id & 0xff) == 0) || ((id & 0xff) == 255))
969 continue;
970 addr.s_addr = htonl(id);
7ac48069 971 name = inet_ntoa(addr);
5eaef520 972 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine
973 WHERE address = :name;
974 if (dbms_errno)
975 return mr_errcode;
976 if (cnt == 0)
977 break;
7f426981 978 }
5eaef520 979 if (cnt != 0)
6715bac2 980 return MR_NO_ID;
5eaef520 981 else
982 value = htonl(id);
7f426981 983 }
5eaef520 984 /*
985 * we have an address in value. Convert it to a string and store it.
986 */
987 addr.s_addr = htonl(value);
988 strcpy(argv[9 + row], inet_ntoa(addr));
7f426981 989 }
5eaef520 990 else
991 strcpy(argv[9 + row], "unassigned");
992
993 /* status checking */
994 value = atoi(argv[7 + row]);
995 if (row == 0 && !(value == 1 || value == 0))
996 return MR_TYPE;
997 if (row == 1)
998 {
999 id = *(int *)argv[0];
1000 EXEC SQL SELECT status INTO :cnt FROM machine WHERE mach_id = :id;
1001 if (dbms_errno)
1002 return mr_errcode;
1003 if (value != cnt)
1004 {
1005 EXEC SQL UPDATE machine SET statuschange = SYSDATE
1006 WHERE mach_id = :id;
7f426981 1007 }
1008 }
1009
5eaef520 1010 /*
1011 * If this is an update_host query, we're done.
1012 */
1013 if (row == 1)
1014 return MR_SUCCESS;
1015
1016 /*
1017 * For an add_host query, allocate and fill in a new machine id,
1018 * and then insert the creator id.
1019 */
1020 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
1021 return mr_errcode;
1022
1023 sprintf(argv[q->argc + q->vcnt + 1], "%d", cl->client_id);
1024 return MR_SUCCESS;
7f426981 1025}
1026
1027
1028/* setup_ahal():
1029 */
1030
5eaef520 1031int setup_ahal(struct query *q, char **argv, client *cl)
7f426981 1032{
5eaef520 1033 EXEC SQL BEGIN DECLARE SECTION;
1034 char *name;
1035 int cnt;
1036 EXEC SQL END DECLARE SECTION;
1037 char *p;
1038
84f3d463 1039 name = argv[0];
1040 if (!hostname_check(argv[0]))
5eaef520 1041 return MR_BAD_CHAR;
7f426981 1042
5eaef520 1043 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine WHERE
ad06a10e 1044 name = UPPER(:name);
5eaef520 1045 if (dbms_errno)
1046 return mr_errcode;
1047 if (cnt > 0)
1048 return MR_EXISTS;
7f426981 1049
5eaef520 1050 return MR_SUCCESS;
7f426981 1051}
84f3d463 1052
2884200f 1053/* setup_uhha(): Check characters in hwaddr, and make sure it's not
1054 * a duplicate.
1055 */
1056int setup_uhha(struct query *q, char **argv, client *cl)
1057{
1058 EXEC SQL BEGIN DECLARE SECTION;
1059 char *hwaddr = argv[1];
1060 int count;
1061 EXEC SQL END DECLARE SECTION;
1062 char *p;
1063
1064 if (*hwaddr && strcasecmp(hwaddr, "unknown"))
1065 {
1066 for (p = hwaddr; *p; p++)
1067 {
1068 if (isupper(*p))
1069 *p = tolower(*p);
1070 if (!isxdigit(*p))
1071 return MR_BAD_CHAR;
1072 }
1073 if (p != hwaddr + 12)
1074 return MR_ADDRESS;
1075
1076 EXEC SQL SELECT COUNT(hwaddr) INTO :count
1077 FROM machine WHERE hwaddr = :hwaddr;
1078 if (count)
1079 return MR_NOT_UNIQUE;
1080 }
1081
1082 return MR_SUCCESS;
1083}
1084
1085/* setup_aprn(): Make sure name/duplexname don't conflict with
1086 * anything. If [ANY] was specified for the spooling host, pick the
1087 * least loaded print server that serves this kind of printer.
1088 */
1089int setup_aprn(struct query *q, char **argv, client *cl)
1090{
1a9a0a59 1091 int best = -1, row;
2884200f 1092 char *p;
1093 EXEC SQL BEGIN DECLARE SECTION;
1094 int mid, usage, count;
1a9a0a59 1095 char types[STRINGS_STRING_SIZE], *hwaddr, *name, *duplexname, *oldname;
2884200f 1096 EXEC SQL END DECLARE SECTION;
1097
1a9a0a59 1098 /* Check for aprn or uprn. */
1099 if (q->type == APPEND)
1100 row = 0;
1101 else
1102 row = 1;
1103
1104 name = argv[PRN_NAME + row];
1105 duplexname = argv[PRN_DUPLEXNAME + row];
1106 oldname = argv[0];
1107
2884200f 1108 if (!*name)
1109 return MR_BAD_CHAR;
1110 else
1111 {
1a9a0a59 1112 if (q->type == APPEND)
1113 {
1114 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1115 WHERE name = :name OR duplexname = :name;
1116 }
1117 else
1118 {
1119 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1120 WHERE ( name = :name OR duplexname = :name )
1121 AND name != :oldname;
1122 }
2884200f 1123 if (dbms_errno)
1124 return mr_errcode;
1125 if (count)
1126 return MR_NOT_UNIQUE;
1127 }
1128
1a9a0a59 1129 if (*duplexname)
2884200f 1130 {
1a9a0a59 1131 if (q->type == APPEND)
1132 {
1133 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1134 WHERE name = :duplexname OR duplexname = :duplexname;
1135 }
1136 else
1137 {
1138 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1139 WHERE ( name = :duplexname OR duplexname = :duplexname )
1140 AND name != :oldname;
1141 }
1142
2884200f 1143 if (dbms_errno)
1144 return mr_errcode;
1145 if (count)
1146 return MR_NOT_UNIQUE;
1147 }
1148
1a9a0a59 1149 if (!strcmp(name, duplexname))
1150 return MR_NOT_UNIQUE;
1151
1152 mid = *(int *)argv[PRN_RM + row];
1153 if (mid == -1)
2884200f 1154 {
1155 EXEC SQL DECLARE csr_rm CURSOR FOR
1a9a0a59 1156 SELECT ps.mach_id, s.string FROM printservers ps, strings s
b3e26217 1157 WHERE ps.mach_id IN
1158 ( SELECT mach_id FROM serverhosts WHERE service = 'PRINT'
1159 AND enable = 1 )
1160 AND ps.printer_types = s.string_id;
2884200f 1161 if (dbms_errno)
1162 return mr_errcode;
1163 EXEC SQL OPEN csr_rm;
1164 if (dbms_errno)
1165 return mr_errcode;
1166
1167 while (1)
1168 {
1a9a0a59 1169 EXEC SQL FETCH csr_rm INTO :mid, :types;
2884200f 1170 if (sqlca.sqlcode)
1171 break;
1172
2884200f 1173 for (p = strtok(types, ", "); p; p = strtok(NULL, ", "))
1174 {
1a9a0a59 1175 if (!strcasecmp(argv[PRN_TYPE + row], p))
2884200f 1176 {
1a9a0a59 1177 EXEC SQL SELECT COUNT(name) INTO :usage
cc0ea77e 1178 FROM printers WHERE rm = :mid;
1a9a0a59 1179
119230bb 1180 if (best < 0 || usage < best)
1a9a0a59 1181 {
1182 best = usage;
1183 *(int *)argv[PRN_RM + row] = mid;
1184 break;
1185 }
2884200f 1186 }
1187 }
1188 }
1189 EXEC SQL CLOSE csr_rm;
1190 if (dbms_errno)
1191 return mr_errcode;
1192
1193 if (best == -1)
1194 return MR_SERVICE;
1195 }
2681538c 1196 else
1197 {
1198 EXEC SQL SELECT mach_id INTO :mid FROM printservers
1199 WHERE mach_id = :mid;
1200 if (sqlca.sqlcode)
1201 return MR_SERVICE;
1202 }
2884200f 1203
1204 return MR_SUCCESS;
1205}
1206
1a9a0a59 1207int setup_dpsv(struct query *q, char **argv, client *cl)
1208{
1209 int id;
1210 EXEC SQL BEGIN DECLARE SECTION;
1211 int cnt;
1212 EXEC SQL END DECLARE SECTION;
1213
1214 id = *(int *)argv[0];
1215
1216 EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers
1217 WHERE rm = :id;
1218 if (cnt > 0)
1219 return MR_IN_USE;
1220
1221 return MR_SUCCESS;
1222}
1223
84f3d463 1224/* hostname_check()
9e252e6f 1225 * validate the rfc1035/rfc1123-ness of a hostname
84f3d463 1226 */
1227
1228int hostname_check(char *name)
1229{
1230 char *p;
1231 int count;
1232
9e252e6f 1233 /* Sanity check name: must contain only letters, numerals, and
1234 * hyphen, and not start or end with a hyphen. Also make sure no
16affa56 1235 * label (the thing the .s seperate) is longer than 63 characters,
1236 * or empty.
84f3d463 1237 */
1238
9e252e6f 1239 for (p = name, count = 0; *p; p++)
84f3d463 1240 {
1241 count++;
1242 if ((!isalnum(*p) && *p != '-' && *p != '.') ||
1243 (*p == '-' && p[1] == '.'))
1244 return 0;
1245 if (*p == '.')
16affa56 1246 {
1247 if (count == 1)
1248 return 0;
1249 count = 0;
1250 }
84f3d463 1251 if (count == 64)
1252 return 0;
1253 }
1254 if (*(p - 1) == '-')
1255 return 0;
1256 return 1;
1257}
9e252e6f 1258
17f585b5 1259int hostinfo_check(char *info, int num)
9e252e6f 1260{
1261 char *p;
1262
1263 if (!*info)
1264 return 1;
1265
17f585b5 1266 /* Sanity check host hostinfo: must start with a letter (or number
1267 * if num is true), contain only letters, numerals, and hyphen, and
1268 * not end with a hyphen.
9e252e6f 1269 */
1270
17f585b5 1271 if (!isalpha(*info) && (!num || !isdigit(*info)))
9e252e6f 1272 return 0;
1273 for (p = info; *p; p++)
1274 {
1275 if ((!isalnum(*p) && *p != '-' && *p != '.') ||
1276 (*p == '-' && p[1] == '.'))
1277 return 0;
1278 }
1279 if (!isalnum(*(p - 1)))
1280 return 1;
1281}
This page took 0.263046 seconds and 5 git commands to generate.