]> andersk Git - moira.git/blame - server/qsupport.qc
don't allow a user with non-zero status to be deleted; don't allow
[moira.git] / server / qsupport.qc
CommitLineData
05cdd922 1/*
2 * $Source$
3 * $Author$
4 * $Header$
5 *
6 * Copyright (C) 1987 by the Massachusetts Institute of Technology
7 *
05cdd922 8 */
9
10#ifndef lint
11static char *rcsid_qsupport_qc = "$Header$";
12#endif lint
13
14#include "query.h"
15#include "sms_server.h"
16#include <ctype.h>
17
05cdd922 18
822a6eba 19extern char *whoami, *strsave();
20
05cdd922 21
22/* Specialized Access Routines */
23
822a6eba 24/* access_user - verify that client name equals specified login name
25 *
26 * - since field validation routines are called first, a users_id is
27 * now in argv[0] instead of the login name.
28 */
05cdd922 29
30access_user(q, argv, cl)
31 struct query *q;
32 char *argv[];
33 client *cl;
822a6eba 34{
35 if (cl->users_id != *(int *)argv[0])
36 return(SMS_PERM);
37 else
38 return(SMS_SUCCESS);
39}
05cdd922 40
05cdd922 41
30967516 42
822a6eba 43/* access_login - verify that client name equals specified login name
44 *
45 * argv[0...n] contain search info. q->
46 */
47
48access_login(q, argv, cl)
30967516 49 struct query *q;
50 char *argv[];
51 client *cl;
52##{
822a6eba 53## int rowcount, id;
54## char qual[256];
55
56 build_qual(q->qual, q->argc, argv, qual);
57## retrieve (id = u.users_id) where qual
58## inquire_equel(rowcount = "rowcount")
59 if (rowcount != 1 || id != cl->users_id)
60 return(SMS_PERM);
61 else
62 return(SMS_SUCCESS);
30967516 63##}
64
822a6eba 65
66
67/* access_list - check access for most list operations
68 *
69 * Inputs: argv[0] - list_id
70 * q - query name
71 * argv[2] - member ID (only for queries "amtl" and "dmfl")
72 * cl - client name
73 *
74 * - check that client is a member of the access control list
75 * - OR, if the query is add_member_to_list or delete_member_from_list
76 * and the list is public, allow access if client = member
77 */
05cdd922 78
79access_list(q, argv, cl)
80 struct query *q;
81 char *argv[];
82 client *cl;
83##{
822a6eba 84## int list_id, acl_id, flags, rowcount;
85## char acl_type[9];
05cdd922 86 char *client_type;
822a6eba 87 int client_id, status;
05cdd922 88
89 list_id = *(int *)argv[0];
822a6eba 90## repeat retrieve (acl_id = list.#acl_id, acl_type = list.#acl_type,
91## flags = list.#public)
05cdd922 92## where list.#list_id = @list_id
822a6eba 93## inquire_equel(rowcount = "rowcount")
94 if (rowcount != 1)
95 return(SMS_INTERNAL);
05cdd922 96
97 /* parse client structure */
822a6eba 98 if ((status = get_client(cl, &client_type, &client_id)) != SMS_SUCCESS)
99 return(status);
05cdd922 100
101 /* if amtl or dmfl and list is public allow client to add or delete self */
822a6eba 102 if ((!strcmp("amtl", q->shortname) || !strcmp("dmfl", q->shortname)) &&
103 (flags && !strcmp("USER", argv[1]))) {
104 if (*(int *)argv[2] == client_id) return(SMS_SUCCESS);
05cdd922 105 }
106
107 /* check for client in access control list */
822a6eba 108 status = find_member(acl_type, acl_id, client_type, client_id, 0);
109 if (!status) return(SMS_PERM);
05cdd922 110
111 return(SMS_SUCCESS);
112##}
92a943d6 113
a6cb4d4c 114
822a6eba 115/* access_visible_list - allow access to list only if it is not hidden,
116 * or if the client is on the ACL
117 *
118 * Inputs: argv[0] - list_id
119 * cl - client identifier
120 */
121
122access_visible_list(q, argv, cl)
a6cb4d4c 123 struct query *q;
124 char *argv[];
125 client *cl;
126##{
822a6eba 127## int list_id, acl_id, flags, rowcount;
128## char acl_type[9];
129 char *client_type;
130 int client_id, status;
a6cb4d4c 131
132 list_id = *(int *)argv[0];
822a6eba 133## repeat retrieve (flags = list.hidden, acl_id = list.#acl_id,
134## acl_type = list.#acl_type) where list.#list_id = @list_id
135## inquire_equel(rowcount = "rowcount")
136 if (rowcount != 1)
137 return(SMS_INTERNAL);
138 if (!flags)
139 return(SMS_SUCCESS);
140
141 /* parse client structure */
142 if ((status = get_client(cl, &client_type, &client_id)) != SMS_SUCCESS)
143 return(status);
144
145 /* check for client in access control list */
146 status = find_member(acl_type, acl_id, client_type, client_id, 0);
147 if (!status)
148 return(SMS_PERM);
149
150 return(SMS_SUCCESS);
a6cb4d4c 151##}
152
92a943d6 153
822a6eba 154/* access_vis_list_by_name - allow access to list only if it is not hidden,
155 * or if the client is on the ACL
156 *
157 * Inputs: argv[0] - list name
158 * cl - client identifier
159 */
160
161access_vis_list_by_name(q, argv, cl)
92a943d6 162 struct query *q;
163 char *argv[];
164 client *cl;
92a943d6 165##{
822a6eba 166## int acl_id, flags, rowcount;
167## char acl_type[9], *listname;
168 char *client_type;
169 int client_id, status;
170
171 listname = argv[0];
172## repeat retrieve (flags = list.hidden, acl_id = list.#acl_id,
173## acl_type = list.#acl_type) where list.#name = @listname
174## inquire_equel(rowcount = "rowcount");
175 if (rowcount > 1)
176 return(SMS_WILDCARD);
177 if (rowcount == 0)
178 return(SMS_NO_MATCH);
179 if (!flags)
180 return(SMS_SUCCESS);
92a943d6 181
822a6eba 182 /* parse client structure */
183 if ((status = get_client(cl, &client_type, &client_id)) != SMS_SUCCESS)
184 return(status);
185
186 /* check for client in access control list */
187 status = find_member(acl_type, acl_id, client_type, client_id, 0);
188 if (!status)
189 return(SMS_PERM);
190
191 return(SMS_SUCCESS);
92a943d6 192##}
05cdd922 193
822a6eba 194
195/* access_member - allow user to access member of type "USER" and name matches
196 * username, or to access member of type "LIST" and list is one that user is
197 * on the acl of, or the list is visible.
198 */
199
200access_member(q, argv, cl)
05cdd922 201 struct query *q;
822a6eba 202 char *argv[];
05cdd922 203 client *cl;
822a6eba 204{
205 if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
206 return(access_visible_list(q, &argv[1], cl));
05cdd922 207
822a6eba 208 if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER")) {
209 if (!strcmp(cl->kname.name, argv[1]))
210 return(SMS_SUCCESS);
05cdd922 211 }
212
822a6eba 213 return(SMS_PERM);
214}
05cdd922 215
05cdd922 216
822a6eba 217/* access_qgli - special access routine for Qualified_get_lists. Allows
218 * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
219 */
220
221access_qgli(q, argv, cl)
222 struct query *q;
223 char *argv[];
224 client *cl;
225{
226 if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
227 return(SMS_SUCCESS);
228 return(SMS_PERM);
229}
230
231
232/* access_service - allow access if user is on ACL of service. Don't
233 * allow access if a wildcard is used.
234 */
05cdd922 235
822a6eba 236access_service(q, argv, cl)
05cdd922 237 struct query *q;
238 char *argv[];
822a6eba 239 client *cl;
05cdd922 240##{
822a6eba 241## int acl_id, rowcount;
242## char *name, acl_type[9];
243 int client_id, status;
244 char *client_type;
05cdd922 245
822a6eba 246 name = argv[0];
247## repeat retrieve (acl_id = servers.#acl_id, acl_type = servers.#acl_type)
44bd6f44 248## where servers.#name = uppercase(@name)
822a6eba 249## inquire_equel(rowcount = "rowcount")
250 if (rowcount > 1)
251 return(SMS_PERM);
05cdd922 252
822a6eba 253 /* parse client structure */
254 if ((status = get_client(cl, &client_type, &client_id)) != SMS_SUCCESS)
255 return(status);
92a943d6 256
822a6eba 257 /* check for client in access control list */
258 status = find_member(acl_type, acl_id, client_type, client_id, 0);
259 if (!status) return(SMS_PERM);
05cdd922 260
92a943d6 261 return(SMS_SUCCESS);
262##}
263
822a6eba 264
265
266/* access_filesys - verify that client is owner or on owners list of filesystem
267 * named by argv[0]
268 */
269
270access_filesys(q, argv, cl)
92a943d6 271 struct query *q;
272 char *argv[];
822a6eba 273 client *cl;
92a943d6 274##{
822a6eba 275## int rowcount, users_id, list_id;
276## char *name;
277 int status, client_id;
278 char *client_type;
92a943d6 279
822a6eba 280 name = argv[0];
281## repeat retrieve (users_id = filesys.owner, list_id = filesys.owners)
282## where filesys.label = @name
283## inquire_equel(rowcount = "rowcount")
284
285 if (rowcount != 1)
286 return(SMS_PERM);
287 if (users_id == cl->users_id)
288 return(SMS_SUCCESS);
289 if ((status = get_client(cl, &client_type, &client_id)) != SMS_SUCCESS)
290 return(status);
291 status = find_member("LIST", list_id, client_type, client_id, 0);
292 if (status)
293 return(SMS_SUCCESS);
294 return(SMS_PERM);
05cdd922 295##}
822a6eba 296
297
05cdd922 298\f
822a6eba 299/* Setup Routines */
300
301/* Setup routine for add_user
302 *
303 * Inputs: argv[0] - login
304 * argv[1] - uid
305 *
306 * Description:
307 *
308 * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid)
309 * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#<uid>"
310 */
c2408bd5 311
822a6eba 312setup_ausr(q, argv, cl)
c2408bd5 313 struct query *q;
822a6eba 314 register char *argv[];
315 client *cl;
316##{
317## int nuid, rowcount;
318
319 if (!strcmp(argv[1], UNIQUE_UID) || atoi(argv[1]) == -1) {
320 if (set_next_object_id("uid", "users"))
321 return(SMS_INGRES_ERR);
322## repeat retrieve (nuid = values.value) where values.name = "uid"
323## inquire_equel(rowcount = "rowcount")
324 if (rowcount != 1)
325 return(SMS_INTERNAL);
326 sprintf(argv[1], "%d", nuid);
327 }
c2408bd5 328
822a6eba 329 if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[1]) == -1) {
330 sprintf(argv[0], "#%s", argv[1]);
331 }
c2408bd5 332
822a6eba 333 return(SMS_SUCCESS);
334##}
c2408bd5 335
c2408bd5 336
822a6eba 337/* setup_dusr - verify that the user is no longer being referenced
338 * and may safely be deleted.
339 */
c2408bd5 340
822a6eba 341int setup_dusr(q, argv)
342 struct query *q;
343 char **argv;
344##{
345## int flag, id;
346
347 id = *(int *)argv[0];
331b982e 348
349 /* For now, only allow users to be deleted if their status is 0 */
350## repeat retrieve (flag = u.status) where u.users_id = @id
351 if (flag != 0)
352 return(SMS_IN_USE);
353
44bd6f44 354## repeat delete nfsquota where nfsquota.users_id = @id
822a6eba 355## repeat retrieve (flag = any(members.member_id where members.member_id=@id
356## and members.member_type = "USER"))
357 if (flag)
358 return(SMS_IN_USE);
359## repeat retrieve (flag = any(filesys.label where filesys.owner=@id))
360 if (flag)
361 return(SMS_IN_USE);
362## repeat retrieve (flag = any(list.name where list.acl_id=@id and
363## list.acl_type = "USER"))
364 if (flag)
365 return(SMS_IN_USE);
366## repeat retrieve (flag = any(servers.name where servers.acl_id=@id and
367## servers.acl_type = "USER"))
368 if (flag)
369 return(SMS_IN_USE);
44bd6f44 370## repeat retrieve (flag=any(hostaccess.acl_id where hostaccess.acl_id=@id and
371## hostaccess.acl_type = "USER"))
372 if (flag)
373 return(SMS_IN_USE);
c2408bd5 374 else
375 return(SMS_SUCCESS);
822a6eba 376##}
c2408bd5 377
822a6eba 378
379/* setup_spop: verify that there is already a valid POP machine_id in the
380 * pop_id field. Also take care of keeping track of the post office usage.
381 */
382int setup_spop(q, argv)
383struct query *q;
384char **argv;
c2408bd5 385##{
822a6eba 386## int id, flag;
387## char type[9];
388
389 id = *(int *)argv[0];
390## repeat retrieve (type = u.potype, flag = any(machine.name
391## where machine.mach_id = u.pop_id
392## and u.pop_id != 0
393## and u.users_id = @id))
4f16dc86 394## where u.users_id = @id
822a6eba 395 if (!flag)
396 return(SMS_MACHINE);
397 if (strcmp(type, "POP"))
398 set_pop_usage(id, 1);
399 return(SMS_SUCCESS);
400##}
c2408bd5 401
c2408bd5 402
822a6eba 403/* setup_dpob: Take care of keeping track of the post office usage.
404 */
405int setup_dpob(q, argv)
406struct query *q;
407char **argv;
408##{
409## int id, user;
410## char type[9];
411
412 user = *(int *)argv[0];
413## repeat retrieve (type = u.potype, id = u.pop_id)
414## where u.users_id = @user
c2408bd5 415
822a6eba 416 if (strcmp(type, "POP"))
417 set_pop_usage(id, -1);
c2408bd5 418 return(SMS_SUCCESS);
419##}
420
c2408bd5 421
822a6eba 422/* setup_dmac - verify that the machine is no longer being referenced
423 * and may safely be deleted.
424 */
c2408bd5 425
822a6eba 426int setup_dmac(q, argv)
05cdd922 427 struct query *q;
822a6eba 428 char **argv;
05cdd922 429##{
822a6eba 430## int flag, id;
431
432 id = *(int *)argv[0];
433## repeat retrieve (flag = any(users.login where users.potype = "POP"
434## and users.pop_id=@id))
435 if (flag)
436 return(SMS_IN_USE);
437## repeat retrieve (flag = any(serverhosts.mach_id
438## where serverhosts.mach_id=@id))
439 if (flag)
440 return(SMS_IN_USE);
441## repeat retrieve (flag = any(nfsphys.mach_id where nfsphys.mach_id=@id))
442 if (flag)
443 return(SMS_IN_USE);
444## repeat retrieve (flag = any(hostaccess.mach_id where hostaccess.mach_id=@id))
445 if (flag)
446 return(SMS_IN_USE);
331b982e 447## repeat retrieve (flag = any(pcap.mach_id where pcap.mach_id=@id))
448 if (flag)
449 return(SMS_IN_USE);
44bd6f44 450
451## repeat delete mcmap where mcmap.mach_id = @id
452 return(SMS_SUCCESS);
05cdd922 453##}
454
05cdd922 455
822a6eba 456/* setup_dclu - verify that the cluster is no longer being referenced
457 * and may safely be deleted.
458 */
05cdd922 459
822a6eba 460int setup_dclu(q, argv)
05cdd922 461 struct query *q;
822a6eba 462 char **argv;
05cdd922 463##{
822a6eba 464## int flag, id;
465
466 id = *(int *)argv[0];
467## repeat retrieve (flag = any(mcmap.mach_id where mcmap.clu_id=@id))
468 if (flag)
469 return(SMS_IN_USE);
470## repeat retrieve (flag = any(svc.clu_id where svc.clu_id=@id))
471 if (flag)
472 return(SMS_IN_USE);
473 else
474 return(SMS_SUCCESS);
05cdd922 475##}
476
05cdd922 477
822a6eba 478/* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate
479 * a new gid and put it in argv[6]. Otherwise if argv[6] is UNIQUE_ID but
480 * argv[5] is not, then remember that UNIQUE_ID is being stored by putting
481 * a -1 there. Remember that this is also used for ulis, with the indexes
482 * at 6 & 7.
483 */
05cdd922 484
822a6eba 485int setup_alis(q, argv)
486struct query *q;
487char **argv;
05cdd922 488##{
822a6eba 489## int ngid;
490 char *malloc();
491 int idx;
492
493 if (!strcmp(q->shortname, "alis"))
494 idx = 6;
495 else if (!strcmp(q->shortname, "ulis"))
496 idx = 7;
497
498 if (!strcmp(argv[idx], UNIQUE_GID) || atoi(argv[idx]) == -1) {
499 if (atoi(argv[idx - 1])) {
500 if (set_next_object_id("gid", "list"))
501 return(SMS_INGRES_ERR);
502## repeat retrieve (ngid = values.value) where values.name = "gid"
503 sprintf(argv[idx], "%d", ngid);
504 } else {
505 strcpy(argv[idx], "-1");
506 }
507 }
05cdd922 508
05cdd922 509 return(SMS_SUCCESS);
510##}
a6cb4d4c 511
822a6eba 512
513/* setup_dlist - verify that the list is no longer being referenced
514 * and may safely be deleted.
515 */
516
517int setup_dlis(q, argv)
a6cb4d4c 518 struct query *q;
822a6eba 519 char **argv;
a6cb4d4c 520##{
822a6eba 521## int flag, id;
522
523 id = *(int *)argv[0];
524## repeat retrieve (flag = any(members.member_id where members.member_id=@id
525## and members.member_type = "LIST"))
526 if (flag)
527 return(SMS_IN_USE);
528## repeat retrieve (flag = any(members.member_id where members.list_id=@id))
529 if (flag)
530 return(SMS_IN_USE);
531## repeat retrieve (flag = any(filesys.label where filesys.owners=@id))
532 if (flag)
533 return(SMS_IN_USE);
534## repeat retrieve (flag = any(capacls.tag where capacls.list_id=@id))
535 if (flag)
536 return(SMS_IN_USE);
537## repeat retrieve (flag = any(list.name where list.acl_id=@id and
4f16dc86 538## list.acl_type = "LIST" and list.list_id != @id))
822a6eba 539 if (flag)
540 return(SMS_IN_USE);
541## repeat retrieve (flag = any(servers.name where servers.acl_id=@id and
542## servers.acl_type = "LIST"))
543 if (flag)
544 return(SMS_IN_USE);
44bd6f44 545## repeat retrieve (flag=any(hostaccess.acl_id where hostaccess.acl_id=@id and
546## hostaccess.acl_type = "LIST"))
547 if (flag)
548 return(SMS_IN_USE);
822a6eba 549## repeat retrieve (flag = any(zephyr.class
550## where zephyr.xmt_type = "LIST" and zephyr.xmt_id = @id or
551## zephyr.sub_type = "LIST" and zephyr.sub_id = @id or
552## zephyr.iws_type = "LIST" and zephyr.iws_id = @id or
553## zephyr.iui_type = "LIST" and zephyr.iui_id = @id))
554 if (flag)
555 return(SMS_IN_USE);
556 else
557 return(SMS_SUCCESS);
558##}
a6cb4d4c 559
a6cb4d4c 560
822a6eba 561/* setup_dsin - verify that the service is no longer being referenced
562 * and may safely be deleted.
563 */
a6cb4d4c 564
822a6eba 565int setup_dsin(q, argv)
566 struct query *q;
567 char **argv;
568##{
569## int flag;
570## char *name;
571
572 name = argv[0];
44bd6f44 573## repeat retrieve (flag = any(serverhosts.service
574## where serverhosts.service=uppercase(@name)))
822a6eba 575 if (flag)
576 return(SMS_IN_USE);
577## repeat retrieve (flag = servers.inprogress) where servers.#name = @name
578 if (flag)
579 return(SMS_IN_USE);
580 else
a6cb4d4c 581 return(SMS_SUCCESS);
822a6eba 582##}
a6cb4d4c 583
a6cb4d4c 584
822a6eba 585/* setup_dshi - verify that the service-host is no longer being referenced
586 * and may safely be deleted.
587 */
a6cb4d4c 588
822a6eba 589int setup_dshi(q, argv)
590 struct query *q;
591 char **argv;
592##{
593## int flag, id;
594## char *name;
a6cb4d4c 595
822a6eba 596 name = argv[0];
597 id = *(int *)argv[1];
598## repeat retrieve (flag=serverhosts.inprogress)
44bd6f44 599## where serverhosts.service=uppercase(@name) and serverhosts.mach_id=@id
822a6eba 600 if (flag)
601 return(SMS_IN_USE);
602 else
603 return(SMS_SUCCESS);
a6cb4d4c 604##}
822a6eba 605
606
a6cb4d4c 607/**
822a6eba 608 ** setup_add_filesys - verify existance of referenced file systems
609 **
610 ** Inputs: Add
611 ** argv[1] - type
612 ** argv[2] - mach_id
613 ** argv[3] - name
614 ** argv[5] - access
a6cb4d4c 615 **
616 ** Description:
822a6eba 617 ** - for type = RVD:
618 ** * allow anything
619 ** - for type = NFS:
620 ** * extract directory prefix from name
621 ** * verify mach_id/dir in nfsphys
622 ** * verify access in {r, w, R, W}
623 **
624 ** Side effect: sets variable var_phys_id to the ID of the physical
625 ** filesystem (nfsphys_id for NFS, 0 for RVD)
a6cb4d4c 626 **
822a6eba 627 ** Errors:
628 ** SMS_NFS - specified directory not exported
629 ** SMS_FILESYS_ACCESS - invalid filesys access
a6cb4d4c 630 **
631 **/
632
822a6eba 633##static int var_phys_id;
a6cb4d4c 634
822a6eba 635setup_afil(q, argv)
a6cb4d4c 636 struct query *q;
637 char *argv[];
638{
822a6eba 639 char *type;
640 int mach_id;
641 char *name;
642 char *access;
a6cb4d4c 643
a6cb4d4c 644 type = argv[1];
645 mach_id = *(int *)argv[2];
822a6eba 646 name = argv[3];
647 access = argv[5];
648 var_phys_id = 0;
a6cb4d4c 649
822a6eba 650 if (!strcmp(type, "NFS"))
651 return (check_nfs(mach_id, name, access));
652 else
653 return(SMS_SUCCESS);
654}
05cdd922 655
30967516 656
822a6eba 657setup_ufil(q, argv)
30967516 658 struct query *q;
659 char *argv[];
822a6eba 660{
661 char *type;
662 int mach_id;
663 char *name;
664 char *access;
30967516 665
822a6eba 666 type = argv[2];
667 mach_id = *(int *)argv[3];
668 name = argv[4];
669 access = argv[6];
670 var_phys_id = 0;
a6cb4d4c 671
822a6eba 672 if (!strcmp(type, "NFS"))
673 return (check_nfs(mach_id, name, access));
674 else
675 return(SMS_SUCCESS);
676}
30967516 677
a6cb4d4c 678
822a6eba 679/* Find the NFS physical partition that the named directory is on.
680 * This is done by comparing the dir against the mount point of the
681 * partition. To make sure we get the correct match when there is
682 * more than one, we sort the query in reverse order by dir name.
683 */
a6cb4d4c 684
822a6eba 685##check_nfs(mach_id, name, access)
a6cb4d4c 686## int mach_id;
822a6eba 687 char *name;
688 char *access;
689##{
690## char dir[32];
691 char caccess;
692 register int status;
693 register char *cp1;
694 register char *cp2;
a6cb4d4c 695
822a6eba 696 caccess = (isupper(*access)) ? tolower(*access) : *access;
697 if (caccess != 'r' && caccess != 'w') return(SMS_FILESYS_ACCESS);
a6cb4d4c 698
822a6eba 699 status = SMS_NFS;
700## range of np is nfsphys
701## repeat retrieve (var_phys_id = np.#nfsphys_id, dir = trim(np.#dir))
702## where np.#mach_id = @mach_id sort by #dir:d {
703 cp1 = name;
704 cp2 = dir;
705 while (*cp2) {
706 if (*cp1++ != *cp2) break;
707 cp2++;
708 }
709 if (*cp2 == 0) {
710 status = SMS_SUCCESS;
a6cb4d4c 711## endretrieve
712 }
713## }
714
822a6eba 715 return(status);
30967516 716##}
a6cb4d4c 717
92a943d6 718
822a6eba 719/* setup_dfil: free any quota records associated with a filesystem
720 * when it is deleted.
721 */
92a943d6 722
822a6eba 723setup_dfil(q, argv, cl)
724 struct query *q;
725 char **argv;
726 client *cl;
727##{
728## int id;
92a943d6 729
822a6eba 730 id = *(int *)argv[0];
731## range of q is nfsquota
44bd6f44 732## range of fs is filesys
733## range of n is nfsphys
734## repeat replace n (allocated=n.allocated-sum(q.quota where q.filsys_id=@id))
735## where n.nfsphys_id = fs.phys_id and fs.filsys_id = @id
92a943d6 736
822a6eba 737## repeat delete q where q.filsys_id = @id
92a943d6 738 return(SMS_SUCCESS);
739##}
30967516 740
822a6eba 741
742/* setup_dnfp: check to see that the nfs physical partition does not have
743 * any filesystems assigned to it before allowing it to be deleted.
744 */
745
746setup_dnfp(q, argv, cl)
747 struct query *q;
748 char **argv;
92a943d6 749 client *cl;
92a943d6 750##{
822a6eba 751## int id, exists;
92a943d6 752
822a6eba 753 id = *(int *)argv[0];
754## repeat retrieve (exists = any(filesys.label where filesys.phys_id = @id))
755 if (exists)
756 return(SMS_IN_USE);
92a943d6 757 return(SMS_SUCCESS);
758##}
4b890cc4 759
4b890cc4 760
822a6eba 761/* setup_dnfq: Remove allocation from nfsphys before deleting quota.
762 * argv[0] = filsys_id
763 * argv[1] = users_id
764 */
765
766setup_dnfq(q, argv, cl)
767 struct query *q;
768 char **argv;
769 client *cl;
770##{
771## int quota, fs, user;
772
773 fs = *(int *)argv[0];
774 user = *(int *)argv[1];
775
776## range of q is nfsquota
777## repeat retrieve (quota = q.#quota) where q.users_id = @user and
778## q.filsys_id = @fs
779## repeat replace nfsphys (allocated = nfsphys.allocated - @quota)
780## where nfsphys.nfsphys_id = filesys.#phys_id and filesys.filsys_id = @fs
781 return(SMS_SUCCESS);
782##}
783
784
785\f
786/* FOLLOWUP ROUTINES */
787
788/* generic set_modtime routine. This takes the table name from the query,
789 * and will update the modtime, modby, and modwho fields in the entry in
790 * the table whose name field matches argv[0].
791 */
792
793set_modtime(q, argv, cl)
4b890cc4 794 struct query *q;
795 char *argv[];
822a6eba 796 client *cl;
4b890cc4 797##{
822a6eba 798## char *name, *entity, *table;
799## int who;
4b890cc4 800
822a6eba 801 entity = cl->entity;
802 who = cl->users_id;
803 table = q->rtable;
804 name = argv[0];
4b890cc4 805
822a6eba 806## replace table (modtime = "now", modby = who, modwith = entity)
807## where table.#name = name
4b890cc4 808 return(SMS_SUCCESS);
809##}
810
822a6eba 811/* generic set_modtime_by_id routine. This takes the table name from
812 * the query, and the id name from the validate record,
813 * and will update the modtime, modby, and modwho fields in the entry in
814 * the table whose id matches argv[0].
815 */
816
817set_modtime_by_id(q, argv, cl)
4b890cc4 818 struct query *q;
822a6eba 819 char **argv;
820 client *cl;
4b890cc4 821##{
822a6eba 822## char *entity, *table, *id_name;
823## int who, id;
824
825 entity = cl->entity;
826 who = cl->users_id;
827 table = q->rtable;
828 id_name = q->validate->object_id;
4b890cc4 829
822a6eba 830 id = *(int *)argv[0];
831## replace table (modtime = "now", modby = who, modwith = entity)
832## where table.id_name = id
4b890cc4 833 return(SMS_SUCCESS);
834##}
835
822a6eba 836
837/* Sets the finger modtime on a user record. The users_id will be in argv[0].
838 */
839
840set_finger_modtime(q, argv, cl)
4b890cc4 841 struct query *q;
842 char *argv[];
822a6eba 843 client *cl;
4b890cc4 844##{
822a6eba 845## int users_id, who;
846## char *entity;
847
848 entity = cl->entity;
849 who = cl->users_id;
850 users_id = *(int *)argv[0];
4b890cc4 851
822a6eba 852## repeat replace u (fmodtime = "now", fmodby = @who, fmodwith = @entity)
853## where u.#users_id = @users_id
4b890cc4 854 return(SMS_SUCCESS);
855##}
05cdd922 856
822a6eba 857
858/* Sets the pobox modtime on a user record. The users_id will be in argv[0].
859 */
860
861set_pobox_modtime(q, argv, cl)
05cdd922 862 struct query *q;
822a6eba 863 char **argv;
864 client *cl;
05cdd922 865##{
822a6eba 866## int users_id, who;
867## char *entity;
05cdd922 868
822a6eba 869 entity = cl->entity;
870 who = cl->users_id;
871 users_id = *(int *)argv[0];
05cdd922 872
822a6eba 873## repeat replace users (pmodtime = "now", pmodby = @who, pmodwith = @entity)
874## where users.#users_id = @users_id
875 return(SMS_SUCCESS);
876##}
05cdd922 877
05cdd922 878
822a6eba 879/* Sets the modtime on a machine record. The machine name is in argv[0].
880 * This routine is different from the generic set_modtime in that the
881 * name is uppercased first.
882 */
883
884set_mach_modtime(q, argv, cl)
885 struct query *q;
886 char **argv;
887 client *cl;
888##{
889## char *host, *entity;
890## int who;
891
892 entity = cl->entity;
893 who = cl->users_id;
05cdd922 894
822a6eba 895 host = argv[0];
896## repeat replace m (modtime = "now", modby = @who, modwith = @entity)
897## where m.name = uppercase(@host)
05cdd922 898 return(SMS_SUCCESS);
899##}
05cdd922 900
822a6eba 901
902/* Sets the modtime on the machine whose mach_id is in argv[0]. This routine
903 * is necessary for add_machine_to_cluster becuase the table that query
904 * operates on is "mcm", not "machine".
905 */
906
907set_mach_modtime_by_id(q, argv, cl)
05cdd922 908 struct query *q;
05cdd922 909 char **argv;
822a6eba 910 client *cl;
911##{
912## char *entity;
913## int who, id;
05cdd922 914
822a6eba 915 entity = cl->entity;
916 who = cl->users_id;
05cdd922 917
822a6eba 918 id = *(int *)argv[0];
919## range of m is machine
920## repeat replace m (modtime = "now", modby = @who, modwith = @entity)
921## where m.mach_id = @id
922 return(SMS_SUCCESS);
923##}
05cdd922 924
05cdd922 925
822a6eba 926/* Sets the modtime on the cluster whose mach_id is in argv[0]. This routine
927 * is necessary for add_cluster_data and delete_cluster_data becuase the
928 * table that query operates on is "svc", not "cluster".
929 */
930
931set_cluster_modtime_by_id(q, argv, cl)
932 struct query *q;
933 char **argv;
934 client *cl;
935##{
936## char *entity;
937## int who, id;
938
939 entity = cl->entity;
940 who = cl->users_id;
941
942 id = *(int *)argv[0];
943## range of c is cluster
944## repeat replace c (modtime = "now", modby = @who, modwith = @entity)
945## where c.clu_id = @id
05cdd922 946 return(SMS_SUCCESS);
947##}
948
822a6eba 949
950/* sets the modtime on the serverhost where the service name is in argv[0]
951 * and the mach_id is in argv[1].
952 */
953
954set_serverhost_modtime(q, argv, cl)
92a943d6 955 struct query *q;
822a6eba 956 char **argv;
957 client *cl;
92a943d6 958##{
822a6eba 959## char *entity, *serv;
960## int who, id;
961
962 entity = cl->entity;
963 who = cl->users_id;
964
965 serv = argv[0];
966 id = *(int *)argv[1];
967## repeat replace sh (modtime = "now", modby = @who, modwith = @entity)
44bd6f44 968## where sh.service = uppercase(@serv) and sh.mach_id = @id
822a6eba 969 return(SMS_SUCCESS);
970##}
971
972
973/* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
974 * directory name is in argv[1].
975 */
976
977set_nfsphys_modtime(q, argv, cl)
978 struct query *q;
92a943d6 979 char **argv;
822a6eba 980 client *cl;
981##{
982## char *entity, *dir;
983## int who, id;
92a943d6 984
822a6eba 985 entity = cl->entity;
986 who = cl->users_id;
92a943d6 987
822a6eba 988 id = *(int *)argv[0];
989 dir = argv[1];
990## repeat replace np (modtime = "now", modby = @who, modwith = @entity)
991## where np.#dir = @dir and np.mach_id = @id
992 return(SMS_SUCCESS);
993##}
92a943d6 994
92a943d6 995
822a6eba 996/* sets the modtime on a filesystem, where argv[0] contains the filesys
997 * label.
998 */
999
1000set_filesys_modtime(q, argv, cl)
1001 struct query *q;
1002 char *argv[];
1003 client *cl;
1004##{
1005## char *label, *entity;
1006## int who;
1007
1008 entity = cl->entity;
1009 who = cl->users_id;
1010
1011 label = argv[0];
1012 if (!strcmp(q->shortname, "ufil"))
1013 label = argv[1];
1014
1015## repeat replace fs (modtime = "now", modby = @who, modwith = @entity,
1016## #phys_id = @var_phys_id) where fs.#label = @label
92a943d6 1017 return(SMS_SUCCESS);
1018##}
05cdd922 1019
822a6eba 1020
1021/* sets the modtime on a zephyr class, where argv[0] contains the class
1022 * name.
1023 */
1024
1025set_zephyr_modtime(q, argv, cl)
1026 struct query *q;
1027 char *argv[];
1028 client *cl;
05cdd922 1029##{
822a6eba 1030## char *class, *entity;
1031## int who;
1032
1033 entity = cl->entity;
1034 who = cl->users_id;
1035
1036 class = argv[0];
1037
1038## repeat replace z (modtime = "now", modby = @who, modwith = @entity)
1039## where z.#class = @class
1040 return(SMS_SUCCESS);
1041##}
1042
1043
1044/* fixes the modby field. This will be the second to last thing in the
1045 * argv, the argv length is determined from the query structure. It is
1046 * passed as a pointer to an integer. This will either turn it into a
1047 * username, or # + the users_id.
1048 */
1049followup_fix_modby(q, sq, v, action, actarg, cl)
1050 struct query *q;
1051 register struct save_queue *sq;
1052 struct validate *v;
1053 register int (*action)();
1054 register int actarg;
1055 client *cl;
1056##{
1057 register int i, j;
1058 char **argv, *malloc();
1059## int id, rowcount;
05cdd922 1060## char *name;
05cdd922 1061
822a6eba 1062 i = q->vcnt - 2;
1063 while (sq_get_data(sq, &argv)) {
1064 id = atoi(argv[i]);
1065 free(argv[i]);
1066 argv[i] = malloc(9);
1067 name = argv[i];
1068## repeat retrieve (name = users.login) where users.users_id = @id
1069## inquire_equel(rowcount = "rowcount")
1070 if (rowcount != 1) {
1071 sprintf(argv[i], "#%d", id);
1072 }
1073 (*action)(q->vcnt, argv, actarg);
1074 for (j = 0; j < q->vcnt; j++)
1075 free(argv[j]);
1076 free(argv);
1077 }
1078 sq_destroy(sq);
1079 return(SMS_SUCCESS);
30967516 1080##}
1081
822a6eba 1082
30967516 1083/**
822a6eba 1084 ** followup_ausr - add finger and pobox entries, set_user_modtime
30967516 1085 **
1086 ** Inputs:
822a6eba 1087 ** argv[0] - login (add_user)
1088 ** argv[3] - last name
1089 ** argv[4] - first name
1090 ** argv[5] - middle name
30967516 1091 **
1092 **/
1093
822a6eba 1094followup_ausr(q, argv, cl)
30967516 1095 struct query *q;
822a6eba 1096 char *argv[];
1097 client *cl;
30967516 1098##{
822a6eba 1099## int who;
1100## char *login, *entity;
1101## char fullname[129];
30967516 1102
822a6eba 1103 login = argv[0];
1104 who = cl->users_id;
1105 entity = cl->entity;
30967516 1106
822a6eba 1107 /* build fullname */
1108 if (strlen(argv[4]) && strlen(argv[5]))
1109 sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
1110 else if (strlen(argv[4]))
1111 sprintf(fullname, "%s %s", argv[4], argv[3]);
1112 else
1113 sprintf(fullname, "%s", argv[3]);
30967516 1114
822a6eba 1115 /* create finger entry, pobox & set modtime on user */
1116## repeat replace u (modtime = "now", modby=@who, modwith=@entity,
1117## #fullname=@fullname, mit_affil = u.mit_year,
1118## fmodtime="now", fmodby=@who, fmodwith=@entity,
1119## potype="NONE", pmodtime="now", pmodby=@who, pmodwith=@entity)
1120## where u.#login = @login
30967516 1121
05cdd922 1122 return(SMS_SUCCESS);
1123##}
1124
822a6eba 1125
1126/* followup_gpob: fixes argv[2] based on the IDs currently there and the
1127 * type in argv[1]. Then completes the upcall to the user.
1128 *
1129 * argv[2] is of the form "123:234" where the first integer is the machine
1130 * ID if it is a pop box, and the second is the string ID if it is an SMTP
1131 * box. argv[1] should be "POP", "SMTP", or "NONE". Boxes of type NONE
1132 * are skipped.
1133 */
1134
1135followup_gpob(q, sq, v, action, actarg, cl)
1136 register struct query *q;
1137 register struct save_queue *sq;
1138 register struct validate *v;
1139 register int (*action)();
1140 int actarg;
1141 client *cl;
1142##{
1143 char **argv, *index();
1144 char *ptype, *p;
1145## char box[129], *name;
1146## int mid, sid, rowcount;
1147
1148 /* for each row */
1149 while (sq_get_data(sq, &argv)) {
1150 sms_trim_args(2, argv);
1151 ptype = argv[1];
1152 p = index(argv[2], ':');
1153 *p++ = 0;
1154 mid = atoi(argv[2]);
1155 sid = atoi(p);
1156 free(argv[2]);
1157
1158 if (!strcmp(ptype, "POP")) {
1159## repeat retrieve (box=machine.#name) where machine.mach_id=@mid
1160## inquire_equel(rowcount = "rowcount")
1161 if (rowcount != 1)
1162 return(SMS_MACHINE);
1163 } else if (!strcmp(ptype, "SMTP")) {
1164## repeat retrieve (box=strings.string) where strings.string_id=@sid
1165## inquire_equel(rowcount = "rowcount")
1166 if (rowcount != 1)
1167 return(SMS_STRING);
1168 } else /* ptype == "NONE" */ {
1169 goto skip;
1170 }
1171
1172 if (!strcmp(q->shortname, "gpob")) {
1173 sid = atoi(argv[4]);
1174 free(argv[4]);
1175 argv[4] = malloc(9);
1176 name = argv[4];
1177## repeat retrieve (name = users.login) where users.users_id = @sid
1178## inquire_equel(rowcount = "rowcount")
1179 if (rowcount != 1)
1180 sprintf(name, "#%d", sid);
1181 }
1182
1183 argv[2] = box;
1184 (*action)(q->vcnt, argv, actarg);
1185 skip:
1186 /* free saved data */
1187 free(argv[0]);
1188 free(argv[1]);
1189 free(argv);
1190 }
1191
1192 sq_destroy(sq);
1193 return (SMS_SUCCESS);
1194##}
1195
1196
44bd6f44 1197/* followup_glin: fix the ace_name in argv[8]. argv[7] will contain the
1198 * ace_type: "LIST", "USER", or "NONE". Decode the id in argv[8] into the
822a6eba 1199 * proper name based on the type, and repace that string in the argv.
1200 * Also fixes the modby field by called followup_fix_modby.
1201 */
1202
1203followup_glin(q, sq, v, action, actarg, cl)
1204 register struct query *q;
1205 register struct save_queue *sq;
1206 register struct validate *v;
1207 register int (*action)();
1208 int actarg;
1209 client *cl;
1210##{
1211 char **argv, *malloc(), *realloc(), *type;
1212## char *name;
1213## int id, rowcount;
1214 int i, idx;
1215
1216 idx = 8;
1217 if (!strcmp(q->shortname, "gsin"))
4f16dc86 1218 idx = 12;
822a6eba 1219
1220 while (sq_get_data(sq, &argv)) {
1221 sms_trim_args(q->vcnt, argv);
1222
1223 id = atoi(argv[i = q->vcnt - 2]);
1224 free(argv[i]);
1225 name = argv[i] = malloc(9);
1226## repeat retrieve (name = users.login) where users.users_id = @id
1227## inquire_equel(rowcount = "rowcount")
1228 if (rowcount != 1)
1229 sprintf(argv[i], "#%d", id);
1230
1231 id = atoi(argv[idx]);
1232 type = argv[idx - 1];
1233 if ((name = malloc(33)) == NULL)
1234 return(SMS_NO_MEM);
1235
1236 if (!strcmp(type, "LIST")) {
1237## repeat retrieve (name = list.#name) where list.list_id = @id
1238## inquire_equel(rowcount = "rowcount")
1239 if (rowcount != 1)
1240 strcpy(name, "???");
1241 } else if (!strcmp(type, "USER")) {
1242## repeat retrieve (name = users.login) where users.users_id = @id
1243## inquire_equel(rowcount = "rowcount")
1244 if (rowcount != 1)
1245 strcpy(name, "???");
1246 } else if (!strcmp(type, "NONE")) {
1247 strcpy(name, "NONE");
1248 } else
1249 strcpy(name, "???");
1250 free(argv[idx]);
1251 argv[idx] = name;
1252
1253 if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
1254 argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
1255 strcpy(argv[6], UNIQUE_GID);
1256 }
1257
1258 /* send the data */
1259 (*action)(q->vcnt, argv, actarg);
1260
1261 /* free saved data */
1262 for (i = 0; i < q->vcnt; i++)
1263 free(argv[i]);
1264 free(argv);
1265 }
1266
1267 sq_destroy(sq);
1268 return (SMS_SUCCESS);
1269##}
1270
1271
1272/** followup_amtl - followup for amtl and dmfl; when adding a list
1273 ** member to a maillist, make member list a maillist also
1274 ** unless list is a user-group.
1275 ** Then set_list_modtime_by_id.
92a943d6 1276 **
1277 ** Inputs:
1278 ** argv[0] - list_id
822a6eba 1279 ** argv[1] - member_type
1280 ** argv[2] - member_id
92a943d6 1281 **
1282 **/
1283
822a6eba 1284followup_amtl(q, argv, cl)
92a943d6 1285 struct query *q;
1286 char *argv[];
822a6eba 1287 client *cl;
92a943d6 1288##{
92a943d6 1289## int list_id;
822a6eba 1290## int member_id;
1291## int exists, who;
1292## char *entity;
92a943d6 1293
1294 list_id = *(int *)argv[0];
822a6eba 1295 entity = cl->entity;
1296 who = cl->users_id;
1297
1298## range of l is list
1299## repeat replace l (modtime = "now", modby = @who, modwith = @entity)
1300## where l.#list_id = @list_id
92a943d6 1301
822a6eba 1302 /* if query is not amtl or if member_type is not LIST then return */
1303 if (bcmp(q->shortname, "amtl", 4) || bcmp(argv[1], "LIST", 4))
1304 return(SMS_SUCCESS);
92a943d6 1305
822a6eba 1306 member_id = *(int *)argv[2];
1307
1308 /* is parent list a mailing list? */
1309## repeat retrieve (exists = l.maillist) where l.#list_id=@list_id
1310 if (!exists)
1311 return(SMS_SUCCESS);
1312
1313 /* list is not a user-group; add list to maillist table */
1314## repeat replace l (maillist = 1) where l.#list_id = @member_id
92a943d6 1315 return(SMS_SUCCESS);
1316##}
1317
822a6eba 1318
1319/* followup_anfq: Add allocation to nfsphys after creating quota.
1320 * argv[0] = filsys_id
1321 * argv[2] = ascii(quota)
1322 */
1323
1324followup_anfq(q, argv, cl)
1325 struct query *q;
1326 char **argv;
1327 client *cl;
1328##{
1329## int quota, user, fs, who;
1330## char *entity;
1331
1332 fs = *(int *)argv[0];
1333 user = *(int *)argv[1];
1334 quota = atoi(argv[2]);
1335 who = cl->users_id;
1336 entity = cl->entity;
1337
1338## repeat replace nq (modtime = "now", modby = @who, modwith = @entity)
1339## where nq.filsys_id = @fs and nq.users_id = @user
1340## repeat replace nfsphys (allocated = nfsphys.allocated + @quota)
1341## where nfsphys.nfsphys_id = filesys.#phys_id and filesys.filsys_id = @fs
1342 return(SMS_SUCCESS);
1343##}
1344
1345
1346/* followup_gzcl:
1347 */
1348
1349followup_gzcl(q, sq, v, action, actarg, cl)
1350 register struct query *q;
1351 register struct save_queue *sq;
1352 register struct validate *v;
1353 register int (*action)();
92a943d6 1354 int actarg;
822a6eba 1355 client *cl;
92a943d6 1356##{
822a6eba 1357## char *name;
1358## int rowcount, id;
1359 char **argv;
1360 int i;
92a943d6 1361
822a6eba 1362 while (sq_get_data(sq, &argv)) {
1363 sms_trim_args(q->vcnt, argv);
92a943d6 1364
822a6eba 1365 id = atoi(argv[i = q->vcnt - 2]);
1366 free(argv[i]);
1367 name = argv[i] = malloc(9);
1368## repeat retrieve (name = users.login) where users.users_id = @id
1369## inquire_equel(rowcount = "rowcount")
1370 if (rowcount != 1)
1371 sprintf(argv[i], "#%d", id);
1372
1373 for (i = 1; i < 8; i+=2) {
1374 id = atoi(argv[i+1]);
1375 free(argv[i+1]);
1376 if ((name = argv[i+1] = malloc(33)) == NULL)
1377 return(SMS_NO_MEM);
1378 if (!strcmp(argv[i], "LIST")) {
1379## repeat retrieve (name = list.#name) where list.list_id = @id
1380## inquire_equel(rowcount = "rowcount")
1381 if (rowcount != 1)
1382 strcpy(name, "???");
1383 } else if (!strcmp(argv[i], "USER")) {
1384## repeat retrieve (name = users.login) where users.users_id = @id
1385## inquire_equel(rowcount = "rowcount")
1386 if (rowcount != 1)
1387 strcpy(name, "???");
1388 } else if (!strcmp(argv[i], "NONE")) {
1389 strcpy(name, "NONE");
1390 } else {
1391 strcpy(name, "???");
1392 }
1393 }
92a943d6 1394
822a6eba 1395 /* send the data */
1396 (*action)(q->vcnt, argv, actarg);
1397
1398 /* free saved data */
1399 for (i = 0; i < q->vcnt; i++)
1400 free(argv[i]);
1401 free(argv);
1402 }
1403 sq_destroy(sq);
92a943d6 1404 return(SMS_SUCCESS);
1405##}
1406
05cdd922 1407
822a6eba 1408/* followup_gsha:
1409 */
1410
1411followup_gsha(q, sq, v, action, actarg, cl)
05cdd922 1412 register struct query *q;
822a6eba 1413 register struct save_queue *sq;
1414 register struct validate *v;
1415 register int (*action)();
1416 int actarg;
1417 client *cl;
05cdd922 1418##{
822a6eba 1419## char *name;
1420## int rowcount, id;
1421 char **argv;
1422 int i;
05cdd922 1423
822a6eba 1424 while (sq_get_data(sq, &argv)) {
1425 sms_trim_args(q->vcnt, argv);
05cdd922 1426
822a6eba 1427 id = atoi(argv[4]);
1428 free(argv[4]);
1429 name = argv[4] = malloc(9);
1430## repeat retrieve (name = users.login) where users.users_id = @id
1431## inquire_equel(rowcount = "rowcount")
1432 if (rowcount != 1)
1433 sprintf(argv[4], "#%d", id);
1434
1435 id = atoi(argv[2]);
1436 free(argv[2]);
1437 if ((name = argv[2] = malloc(33)) == NULL)
1438 return(SMS_NO_MEM);
1439 if (!strcmp(argv[1], "LIST")) {
1440## repeat retrieve (name = list.#name) where list.list_id = @id
1441## inquire_equel(rowcount = "rowcount")
1442 if (rowcount != 1)
1443 strcpy(name, "???");
1444 } else if (!strcmp(argv[1], "USER")) {
1445## repeat retrieve (name = users.login) where users.users_id = @id
1446## inquire_equel(rowcount = "rowcount")
1447 if (rowcount != 1)
1448 strcpy(name, "???");
1449 } else if (!strcmp(argv[1], "NONE")) {
1450 strcpy(name, "NONE");
1451 } else {
1452 strcpy(name, "???");
1453 }
05cdd922 1454
822a6eba 1455 /* send the data */
1456 (*action)(q->vcnt, argv, actarg);
1457
1458 /* free saved data */
1459 for (i = 0; i < q->vcnt; i++)
1460 free(argv[i]);
1461 free(argv);
1462 }
1463 sq_destroy(sq);
1464 return(SMS_SUCCESS);
1465##}
05cdd922 1466
05cdd922 1467
822a6eba 1468\f
1469/* Special query routines */
30967516 1470
822a6eba 1471/* set_pobox - this does all of the real work.
1472 * argv = user_id, type, box
1473 * if type is POP, then box should be a machine, and its ID should be put in
1474 * pop_id. If type is SMTP, then box should be a string and its ID should
1475 * be put in box_id. If type is NONE, then box doesn't matter.
1476 */
1477
1478int set_pobox(q, argv, cl)
1479 struct query *q;
1480 char **argv;
1481 client *cl;
1482##{
1483## int user, id, rowcount;
1484## char *box, potype[9];
1485
1486 box = argv[2];
1487 user = *(int *)argv[0];
1488
1489## repeat retrieve (id = users.pop_id, potype = users.#potype)
1490## where users.users_id = @user
1491 if (!strcmp(potype, "POP"))
1492 set_pop_usage(id, -1);
1493
1494 if (!strcmp(argv[1], "POP")) {
1495## repeat retrieve (id=machine.mach_id) where machine.name=uppercase(@box)
1496## inquire_equel(rowcount = "rowcount")
1497 if (rowcount != 1)
1498 return(SMS_MACHINE);
1499## repeat replace users (#potype = "POP", pop_id = @id)
1500## where users.users_id = @user
1501 set_pop_usage(id, 1);
1502 } else if (!strcmp(argv[1], "SMTP")) {
1503## range of s is strings
1504## repeat retrieve (id = s.string_id) where s.string = @box
1505## inquire_equel (rowcount = "rowcount")
1506 if (rowcount == 0) {
1507## range of v is values
1508## repeat retrieve (id = v.value) where v.name = "strings_id"
1509 id++;
1510## repeat replace v (value = @id) where v.name = "strings_id"
1511## append to strings (string_id = id, string = box)
1512 }
1513## repeat replace users (#potype = "SMTP", box_id = @id)
1514## where users.users_id = @user
1515 } else /* argv[1] == "NONE" */ {
1516## repeat replace users (#potype = "NONE") where users.users_id = @user
1517 }
1518
1519 set_pobox_modtime(q, argv, cl);
1520## repeat replace tblstats (updates = tblstats.updates + 1, modtime = "now")
1521## where tblstats.#table = "users"
30967516 1522 return(SMS_SUCCESS);
1523##}
1524
30967516 1525
822a6eba 1526/* get_list_info: passed a wildcard list name, returns lots of stuff about
1527 * each list. This is tricky: first build a queue of all requested
44bd6f44 1528 * data. Rest of processing consists of fixing gid, ace_name, and modby.
822a6eba 1529 */
1530
1531get_list_info(q, aargv, cl, action, actarg)
30967516 1532 register struct query *q;
822a6eba 1533 char **aargv;
1534 client *cl;
1535 register int (*action)();
1536 int actarg;
30967516 1537##{
822a6eba 1538 char *argv[13], *malloc(), *realloc();
1539## char *name, acl_type[9], listname[33], active[5], public[5], hidden[5];
1540## char maillist[5], group[5], gid[6], acl_name[33], desc[256], modtime[27];
1541## char modby[9], modwith[9];
1542## int id, rowcount, acl_id, hid, modby_id;
1543 int returned;
1544 struct save_queue *sq, *sq_create();
30967516 1545
822a6eba 1546 returned = rowcount = 0;
1547 name = aargv[0];
30967516 1548
822a6eba 1549 sq = sq_create();
1550## range of l is list
1551## repeat retrieve (id = l.list_id) where l.#name = @name {
1552 sq_save_data(sq, id);
1553 rowcount++;
1554## }
1555 if (rowcount == 0)
1556 return(SMS_NO_MATCH);
1557
1558 argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden;
1559 argv[4] = maillist; argv[5] = group; argv[6] = gid; argv[7] = acl_type;
1560 argv[8] = acl_name; argv[9] = desc; argv[10] = modtime; argv[11] = modby;
1561 argv[12] = modwith;
1562
1563 while (sq_get_data(sq, &id)) {
1564 if (id == 0)
1565 continue;
1566 argv[6] = gid;
1567## repeat retrieve (listname = l.#name, active = text(l.#active),
1568## public = text(l.#public), hidden = text(l.#hidden),
1569## hid = l.#hidden, maillist = text(l.#maillist),
1570## group = text(l.#group), gid = text(l.#gid),
1571## acl_type = trim(l.#acl_type), acl_id = l.#acl_id,
1572## desc = l.#desc, modtime = l.#modtime, modby_id = l.#modby,
1573## modwith =l.#modwith)
1574## where l.list_id = @id
1575
1576 if (atoi(gid) == -1)
1577 argv[6] = UNIQUE_GID;
1578
1579 if (!strcmp(acl_type, "LIST")) {
1580## repeat retrieve (acl_name = l.#name) where l.list_id = @acl_id
1581## inquire_equel(rowcount = "rowcount")
1582 if (rowcount != 1)
1583 strcpy(acl_name, "???");
1584 } else if (!strcmp(acl_type, "USER")) {
1585## repeat retrieve (acl_name = users.#login)
1586## where users.users_id = @acl_id
1587## inquire_equel(rowcount = "rowcount")
1588 if (rowcount != 1)
1589 strcpy(acl_name, "???");
1590 } else if (!strcmp(acl_type, "NONE")) {
1591 strcpy(acl_name, "NONE");
1592 } else
1593 strcpy(acl_name, "???");
1594
1595## repeat retrieve (modby = users.login) where users.users_id = @modby_id
1596## inquire_equel(rowcount = "rowcount")
1597 if (rowcount != 1)
1598 sprintf(modby, "#%d", id);
1599
1600 sms_trim_args(q->vcnt, argv);
1601 returned++;
1602 (*action)(q->vcnt, argv, actarg);
1603 }
30967516 1604
822a6eba 1605 sq_destroy(sq);
1606## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1607## where tblstats.#table = "list"
30967516 1608
822a6eba 1609 return (SMS_SUCCESS);
1610##}
1611
1612
44bd6f44 1613/* get_ace_use - given a type and a name, return a type and a name.
1614 * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0],
822a6eba 1615 * and argv[1] will contain the ID of the entity in question. The R*
1616 * types mean to recursively look at every containing list, not just
1617 * when the object in question is a direct member. On return, the
1618 * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR.
1619 */
1620
44bd6f44 1621int get_ace_use(q, argv, cl, action, actarg)
822a6eba 1622 struct query *q;
1623 char *argv[];
1624 client *cl;
1625 int (*action)();
1626 int actarg;
1627##{
1628 int found = 0;
1629## char *atype;
1630## int aid, listid, id;
1631 struct save_queue *sq, *sq_create();
1632
1633 atype = argv[0];
1634 aid = *(int *)argv[1];
1635 if (!strcmp(atype, "LIST") || !strcmp(atype, "USER")) {
44bd6f44 1636 return(get_ace_internal(atype, aid, action, actarg));
822a6eba 1637 }
1638
1639 sq = sq_create();
1640 if (!strcmp(atype, "RLIST")) {
1641 sq_save_data(sq, aid);
1642 /* get all the list_id's of containing lists */
1643## range of m is members
1644 while (sq_get_data(sq, &id)) {
1645## repeat retrieve (listid = m.list_id)
1646## where m.member_type = "LIST" and m.member_id = @id {
1647 sq_save_unique_data(sq, listid);
1648## }
1649 }
1650 /* now process each one */
1651 while (sq_get_data(sq, &id)) {
44bd6f44 1652 if (get_ace_internal("LIST", id, action, actarg) == SMS_SUCCESS)
822a6eba 1653 found++;
1654 }
1655 }
1656
1657 if (!strcmp(atype, "RUSER")) {
1658## range of m is members
1659## repeat retrieve (listid = m.list_id)
1660## where m.member_type = "USER" and m.member_id = @aid {
1661 sq_save_data(sq, listid);
1662## }
1663 /* get all the list_id's of containing lists */
1664 while (sq_get_data(sq, &id)) {
1665## repeat retrieve (listid = m.list_id)
1666## where m.member_type = "LIST" and m.member_id = @id {
1667 sq_save_unique_data(sq, listid);
1668## }
1669 }
1670 /* now process each one */
1671 while (sq_get_data(sq, &id)) {
44bd6f44 1672 if (get_ace_internal("LIST", id, action, actarg) == SMS_SUCCESS)
822a6eba 1673 found++;
1674 }
44bd6f44 1675 if (get_ace_internal("USER", aid, action, actarg) == SMS_SUCCESS)
822a6eba 1676 found++;
1677 }
05cdd922 1678
822a6eba 1679 sq_destroy(sq);
1680 if (!found) return(SMS_NO_MATCH);
05cdd922 1681 return(SMS_SUCCESS);
1682##}
92a943d6 1683
822a6eba 1684
44bd6f44 1685/* This looks up a single list or user for ace use. atype must be "USER"
822a6eba 1686 * or "LIST", and aid is the ID of the corresponding object. This is used
44bd6f44 1687 * by get_ace_use above.
822a6eba 1688 */
1689
44bd6f44 1690##get_ace_internal(atype, aid, action, actarg)
822a6eba 1691## char *atype;
1692## int aid;
1693 int (*action)();
1694 int actarg;
1695##{
1696 char *rargv[2];
1697 int found = 0;
1698## char name[33];
1699
1700 rargv[1] = name;
1701 if (!strcmp(atype, "LIST")) {
1702 rargv[0] = "FILESYS";
1703## repeat retrieve (name = filesys.label)
1704## where filesys.owners = @aid {
1705 (*action)(2, rargv, actarg);
1706 found++;
1707## }
1708
1709 rargv[0] = "QUERY";
1710## repeat retrieve (name = capacls.capability)
1711## where capacls.list_id = @aid {
1712 (*action)(2, rargv, actarg);
1713 found++;
1714## }
1715 } else if (!strcmp(atype, "USER")) {
1716 rargv[0] = "FILESYS";
1717## repeat retrieve (name = filesys.label)
1718## where filesys.owner = @aid {
1719 (*action)(2, rargv, actarg);
1720 found++;
1721## }
1722 }
1723
1724 rargv[0] = "LIST";
1725## repeat retrieve (name = list.#name)
1726## where list.acl_type = @atype and list.acl_id = @aid {
1727 (*action)(2, rargv, actarg);
1728 found++;
1729## }
1730
1731 rargv[0] = "SERVICE";
1732## repeat retrieve (name = servers.#name)
1733## where servers.acl_type = @atype and servers.acl_id = @aid {
1734 (*action)(2, rargv, actarg);
1735 found++;
1736## }
1737
1738 rargv[0] = "HOSTACCESS";
1739## repeat retrieve (name = machine.#name)
1740## where machine.mach_id = hostaccess.mach_id and
1741## hostaccess.acl_type = @atype and hostaccess.acl_id = @aid {
1742 (*action)(2, rargv, actarg);
1743 found++;
1744## }
1745 rargv[0] = "ZEPHYR";
1746## repeat retrieve (name = zephyr.class)
1747## where zephyr.xmt_type = @atype and zephyr.xmt_id = @aid or
1748## zephyr.sub_type = @atype and zephyr.sub_id = @aid or
1749## zephyr.iws_type = @atype and zephyr.iws_id = @aid or
1750## zephyr.iui_type = @atype and zephyr.iui_id = @aid {
1751 (*action)(2, rargv, actarg);
1752 found++;
1753## }
1754
1755 if (!found) return(SMS_NO_MATCH);
1756 return(SMS_SUCCESS);
1757##}
1758
1759
1760/* get_lists_of_member - given a type and a name, return the name and flags
1761 * of all of the lists of the given member. The member_type is one of
1762 * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0],
1763 * and argv[1] will contain the ID of the entity in question. The R*
1764 * types mean to recursively look at every containing list, not just
1765 * when the object in question is a direct member.
1766 */
1767
1768int get_lists_of_member(q, argv, cl, action, actarg)
92a943d6 1769 struct query *q;
1770 char *argv[];
822a6eba 1771 client *cl;
1772 int (*action)();
1773 int actarg;
92a943d6 1774##{
822a6eba 1775 int found = 0;
1776## char *atype;
1777## int aid, listid, id;
1778 struct save_queue *sq, *sq_create();
1779
1780 atype = argv[0];
1781 aid = *(int *)argv[1];
1782 if (!strcmp(atype, "LIST") ||
1783 !strcmp(atype, "USER") ||
1784 !strcmp(atype, "STRING")) {
1785 return(glom_internal(atype, aid, action, actarg));
1786 }
92a943d6 1787
822a6eba 1788 sq = sq_create();
1789 if (!strcmp(atype, "RLIST")) {
1790 sq_save_data(sq, aid);
1791 /* get all the list_id's of containing lists */
1792## range of m is members
1793 while (sq_get_data(sq, &id)) {
1794## repeat retrieve (listid = m.list_id)
1795## where m.member_type = "LIST" and m.member_id = @id {
1796 sq_save_unique_data(sq, listid);
1797## }
1798 }
1799 /* now process each one */
1800 while (sq_get_data(sq, &id)) {
1801 if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1802 found++;
1803 }
1804 }
92a943d6 1805
822a6eba 1806 if (!strcmp(atype, "RUSER")) {
1807## range of m is members
1808## repeat retrieve (listid = m.list_id)
1809## where m.member_type = "USER" and m.member_id = @aid {
1810 sq_save_data(sq, listid);
1811## }
1812 /* get all the list_id's of containing lists */
1813 while (sq_get_data(sq, &id)) {
1814## repeat retrieve (listid = m.list_id)
1815## where m.member_type = "LIST" and m.member_id = @id {
1816 sq_save_unique_data(sq, listid);
1817## }
1818 }
1819 /* now process each one */
1820 while (sq_get_data(sq, &id)) {
1821 if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1822 found++;
1823 }
1824 if (glom_internal("USER", aid, action, actarg) == SMS_SUCCESS)
1825 found++;
1826 }
92a943d6 1827
822a6eba 1828 if (!strcmp(atype, "RSTRING")) {
1829## range of m is members
1830## repeat retrieve (listid = m.list_id)
1831## where m.member_type = "STRING" and m.member_id = @aid {
1832 sq_save_data(sq, listid);
1833## }
1834 /* get all the list_id's of containing lists */
1835 while (sq_get_data(sq, &id)) {
1836## repeat retrieve (listid = m.list_id)
1837## where m.member_type = "LIST" and m.member_id = @id {
1838 sq_save_unique_data(sq, listid);
1839## }
1840 }
1841 /* now process each one */
1842 while (sq_get_data(sq, &id)) {
1843 if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1844 found++;
1845 }
1846 if (glom_internal("STRING", aid, action, actarg) == SMS_SUCCESS)
1847 found++;
92a943d6 1848 }
55534a87 1849
822a6eba 1850## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1851## where tblstats.#table = "members"
1852 sq_destroy(sq);
1853 if (!found) return(SMS_NO_MATCH);
1854 return(SMS_SUCCESS);
1855##}
1856
55534a87 1857
822a6eba 1858/* This looks up a single list, user, or string as a member. atype must be
1859 * "USER", "LIST", or "STRING" and aid is the ID of the corresponding object.
1860 * This is used by get_lists_of_members above.
1861 */
1862
1863##glom_internal(atype, aid, action, actarg)
1864## char *atype;
1865## int aid;
1866 int (*action)();
1867 int actarg;
1868##{
1869 char *rargv[6];
1870 int found = 0;
1871## char name[33], active[5], public[5], hidden[5], maillist[5], group[5];
1872
1873 rargv[0] = name;
1874 rargv[1] = active;
1875 rargv[2] = public;
1876 rargv[3] = hidden;
1877 rargv[4] = maillist;
1878 rargv[5] = group;
1879## repeat retrieve (name = list.#name, active = text(list.#active),
1880## public = text(list.#public), hidden = text(list.#hidden),
1881## maillist = text(list.#maillist), group = text(list.#group))
1882## where list.list_id = m.list_id and
1883## m.member_type = @atype and m.member_id = @aid {
1884 (*action)(6, rargv, actarg);
1885 found++;
1886## }
92a943d6 1887
822a6eba 1888 if (!found) return(SMS_NO_MATCH);
92a943d6 1889 return(SMS_SUCCESS);
1890##}
1891
822a6eba 1892
1893/* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of
1894 * the five flags associated with each list. It will return the name of
1895 * each list that meets the quailifications. It does this by building a
1896 * where clause based on the arguments, then doing a retrieve.
1897 */
1898
1899static char *lflags[5] = { "active", "public", "hidden", "maillist", "group" };
1900
1901int qualified_get_lists(q, argv, cl, action, actarg)
1902 struct query *q;
1903 char *argv[];
1904 client *cl;
1905 int (*action)();
1906 int actarg;
1907{
1908 return(qualified_get(q, argv, action, actarg, "l.list_id != 0",
1909 "l", "name", lflags));
1910}
1911
1912
1913/** get_members_of_list - optimized query for retrieval of list members
c2408bd5 1914 **
1915 ** Inputs:
1916 ** argv[0] - list_id
1917 **
1918 ** Description:
1919 ** - retrieve USER members, then LIST members, then STRING members
c2408bd5 1920 **/
1921
822a6eba 1922get_members_of_list(q, argv, cl, action, actarg)
c2408bd5 1923 struct query *q;
1924 char *argv[];
822a6eba 1925 client *cl;
c2408bd5 1926 int (*action)();
1927 int actarg;
1928##{
1929## int list_id;
1930## char member_name[129];
1931 char *targv[2];
1932
1933 list_id = *(int *)argv[0];
1934 targv[0] = "USER";
1935 targv[1] = member_name;
1936
1937## range of m is members
1938## repeat retrieve (member_name = users.login)
1939## where m.#list_id = @list_id and m.member_type = "USER"
1940## and m.member_id = users.users_id
92a943d6 1941## sort by #member_name
c2408bd5 1942## {
1943 (*action)(2, targv, actarg);
1944## }
1945
1946 targv[0] = "LIST";
1947## repeat retrieve (member_name = list.name)
1948## where m.#list_id = @list_id and m.member_type = "LIST"
1949## and m.member_id = list.#list_id
92a943d6 1950## sort by #member_name
c2408bd5 1951## {
1952 (*action)(2, targv, actarg);
1953## }
1954
1955 targv[0] = "STRING";
1956## repeat retrieve (member_name = strings.string)
1957## where m.#list_id = @list_id and m.member_type = "STRING"
1958## and m.member_id = strings.string_id
92a943d6 1959## sort by #member_name
c2408bd5 1960## {
1961 (*action)(2, targv, actarg);
1962## }
1963
822a6eba 1964## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1965## where tblstats.#table = "members"
c2408bd5 1966 return(SMS_SUCCESS);
1967##}
1968
92a943d6 1969
822a6eba 1970/* count_members_of_list: this is a simple query, but it cannot be done
1971 * through the dispatch table.
1972 */
1973
1974int count_members_of_list(q, argv, cl, action, actarg)
92a943d6 1975 struct query *q;
1976 char *argv[];
822a6eba 1977 client *cl;
92a943d6 1978 int (*action)();
1979 int actarg;
1980##{
822a6eba 1981## int list, ct = 0;
1982 char *rargv[1], countbuf[5];
1983
1984 list = *(int *)argv[0];
1985 rargv[0] = countbuf;
1986## repeat retrieve (ct = count(members.list_id where members.list_id = @list))
1987 sprintf(countbuf, "%d", ct);
1988 (*action)(1, rargv, actarg);
1989## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1990## where tblstats.#table = "members"
1991 return(SMS_SUCCESS);
1992##}
92a943d6 1993
92a943d6 1994
822a6eba 1995/* qualified_get_server: passed "TRUE", "FALSE", or "DONTCARE" for each of
1996 * the three flags associated with each service. It will return the name of
1997 * each service that meets the quailifications. It does this by building a
1998 * where clause based on the arguments, then doing a retrieve.
1999 */
92a943d6 2000
822a6eba 2001static char *sflags[3] = { "enable", "inprogress", "harderror" };
a6cb4d4c 2002
822a6eba 2003int qualified_get_server(q, argv, cl, action, actarg)
a6cb4d4c 2004 struct query *q;
2005 char *argv[];
822a6eba 2006 client *cl;
a6cb4d4c 2007 int (*action)();
2008 int actarg;
822a6eba 2009{
2010 return(qualified_get(q, argv, action, actarg, "s.name != \"\"",
2011 "s", "name", sflags));
2012}
a6cb4d4c 2013
a6cb4d4c 2014
822a6eba 2015/* generic qualified get routine, used by qualified_get_lists,
2016 * qualified_get_server, and qualified_get_serverhost.
2017 * Args:
2018 * start - a simple where clause, must not be empty
2019 * range - the name of the range variable
2020 * field - the field to return
2021 * flags - an array of strings, names of the flag variables
2022 */
c2408bd5 2023
822a6eba 2024int qualified_get(q, argv, action, actarg, start, range, field, flags)
c2408bd5 2025 struct query *q;
2026 char *argv[];
2027 int (*action)();
2028 int actarg;
822a6eba 2029 char *start;
2030 char *range;
2031 char *field;
2032 char *flags[];
c2408bd5 2033##{
822a6eba 2034## char name[33], qual[256], *rvar, *rtbl, *rfield;
2035 char *rargv[1], buf[32];
2036## int rowcount, i;
2037
2038 strcpy(qual, start);
2039 for (i = 0; i < q->argc; i++) {
2040 if (!strcmp(argv[i], "TRUE")) {
2041 sprintf(buf, " and %s.%s != 0", range, flags[i]);
2042 (void) strcat(qual, buf);
2043 } else if (!strcmp(argv[i], "FALSE")) {
2044 sprintf(buf, " and %s.%s = 0", range, flags[i]);
2045 (void) strcat(qual, buf);
2046 }
2047 }
2048
2049 rargv[0] = name;
2050 rvar = range;
2051 rtbl = q->rtable;
2052 rfield = field;
2053## range of rvar is rtbl
2054## retrieve (name = rvar.rfield) where qual {
2055 (*action)(1, rargv, actarg);
c2408bd5 2056## }
822a6eba 2057## inquire_equel(rowcount = "rowcount")
2058## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
2059## where tblstats.#table = @rtbl
2060 if (rowcount == 0)
2061 return(SMS_NO_MATCH);
2062 return(SMS_SUCCESS);
2063##}
c2408bd5 2064
c2408bd5 2065
822a6eba 2066/* qualified_get_serverhost: passed "TRUE", "FALSE", or "DONTCARE" for each of
2067 * the five flags associated with each serverhost. It will return the name of
2068 * each service and host that meets the quailifications. It does this by
2069 * building a where clause based on the arguments, then doing a retrieve.
2070 */
c2408bd5 2071
822a6eba 2072static char *shflags[6] = { "service", "enable", "override", "success",
2073 "inprogress", "hosterror" };
a6cb4d4c 2074
822a6eba 2075int qualified_get_serverhost(q, argv, cl, action, actarg)
a6cb4d4c 2076 struct query *q;
2077 char *argv[];
822a6eba 2078 client *cl;
a6cb4d4c 2079 int (*action)();
2080 int actarg;
2081##{
822a6eba 2082## char sname[33], mname[33], qual[256];
2083 char *rargv[2], buf[32];
2084## int rowcount, i;
2085
44bd6f44 2086 sprintf(qual, "machine.mach_id = sh.mach_id and sh.service = uppercase(\"%s\")",
822a6eba 2087 argv[0]);
2088 for (i = 1; i < q->argc; i++) {
2089 if (!strcmp(argv[i], "TRUE")) {
2090 sprintf(buf, " and sh.%s != 0", shflags[i]);
2091 strcat(qual, buf);
2092 } else if (!strcmp(argv[i], "FALSE")) {
2093 sprintf(buf, " and sh.%s = 0", shflags[i]);
2094 strcat(qual, buf);
2095 }
2096 }
2097
2098 rargv[0] = sname;
2099 rargv[1] = mname;
2100## range of sh is serverhosts
2101## retrieve (sname = sh.service, mname = machine.name) where qual {
2102 (*action)(2, rargv, actarg);
a6cb4d4c 2103## }
822a6eba 2104## inquire_equel(rowcount = "rowcount")
2105## repeat replace tblstats (retrieves = tblstats.retrieves + 1)
2106## where tblstats.#table = "serverhosts"
2107 if (rowcount == 0)
2108 return(SMS_NO_MATCH);
2109 return(SMS_SUCCESS);
2110##}
a6cb4d4c 2111
a6cb4d4c 2112
822a6eba 2113/* register_user - change user's login name and allocate a pobox, group,
2114 * filesystem, and quota for them. The user's status must start out as 0,
2115 * and is left as 2. Arguments are: user's UID, new login name, and user's
2116 * type for filesystem allocation (SMS_FS_STUDENT, SMS_FS_FACULTY,
2117 * SMS_FS_STAFF, SMS_FS_MISC).
2118 */
2119
2120register_user(q, argv, cl)
2121 struct query *q;
2122 char **argv;
2123 client *cl;
2124##{
2125## char *login, dir[65], *entity, *directory, machname[33];
2126## int who, rowcount, mid, uid, users_id, flag, utype, nid, list_id, quota;
2127## int size, alloc, pid, m_id;
2128 int maxsize;
2129
2130 entity = cl->entity;
2131 who = cl->users_id;
2132
2133 uid = atoi(argv[0]);
2134 login = argv[1];
2135 utype = atoi(argv[2]);
2136
2137## range of u is users
2138## range of l is list
2139## range of sh is serverhosts
2140## range of n is nfsphys
2141## range of m is machine
2142
2143 /* find user */
2144## repeat retrieve (users_id = u.#users_id)
2145## where u.#uid = @uid and u.status = 0
2146## inquire_equel(rowcount = "rowcount");
2147 if (rowcount == 0)
2148 return(SMS_NO_MATCH);
2149 if (rowcount > 1)
2150 return(SMS_NOT_UNIQUE);
2151
2152 /* check new login name */
2153## repeat retrieve (flag = any(u.#login where u.#login = @login))
2154 if (flag)
2155 return(SMS_IN_USE);
4f16dc86 2156## repeat retrieve (flag = any(l.#name where l.#name = @login))
822a6eba 2157 if (flag)
2158 return(SMS_IN_USE);
2159## repeat retrieve (flag = any(filesys.#name where filesys.#name = @login))
2160 if (flag)
2161 return(SMS_IN_USE);
2162 com_err(whoami, 0, "new login name OK");
2163
2164 /* choose place for pobox, put in mid */
2165## repeat retrieve (mid = sh.mach_id, machname = m.name)
44bd6f44 2166## where sh.service = "POP" and m.mach_id = sh.mach_id and
2167## sh.value2 - sh.value1 = max(sh.value2-sh.value1 where sh.service="POP")
822a6eba 2168## inquire_equel(rowcount = "rowcount");
2169 if (rowcount == 0)
44bd6f44 2170 return(SMS_NO_POBOX);
822a6eba 2171
2172 /* change login name, set pobox */
2173## repeat replace u (#login = @login, status = 2, modtime = "now",
2174## modby = @who, modwith = @entity, potype="POP",
2175## pop_id = @mid, pmodtime="now", pmodby=@who,
2176## pmodwith=@entity)
2177## where u.#users_id = @users_id
2178## inquire_equel(rowcount = "rowcount");
2179 if (rowcount != 1)
2180 return(SMS_INTERNAL);
2181 set_pop_usage(mid, 1);
2182 com_err(whoami, 0, "set login name to %s and pobox to %s", login,
2183 trim(machname));
2184
2185 /* create group list */
2186 if (set_next_object_id("gid", "list"))
44bd6f44 2187 return(SMS_NO_ID);
822a6eba 2188 if (set_next_object_id("list_id", "list"))
44bd6f44 2189 return(SMS_NO_ID);
822a6eba 2190## repeat retrieve (list_id = values.value) where values.name = "list_id"
2191## inquire_equel(rowcount = "rowcount");
2192 if (rowcount != 1)
2193 return(SMS_INTERNAL);
2194## repeat append list (name = @login, #list_id = @list_id, active = 1,
2195## public = 0, hidden = 0, maillist = 0, group = 1,
2196## #gid = values.value, desc = "User Group",
2197## acl_type = "USER", acl_id = @users_id, modtime = "now",
2198## modby = @who, modwith = @entity)
2199## where values.name = "gid"
44bd6f44 2200## inquire_equel(rowcount = "rowcount");
2201 if (rowcount != 1)
2202 return(SMS_INTERNAL);
2203## repeat append members (#list_id = @list_id, member_type = "USER",
2204## member_id = @users_id)
822a6eba 2205## inquire_equel(rowcount = "rowcount");
2206 if (rowcount != 1)
2207 return(SMS_INTERNAL);
2208 com_err(whoami, 0, "group list created");
2209
2210 /* decide where to put filesystem */
2211 maxsize = 0;
2212 directory = NULL;
2213## repeat retrieve (mid = n.mach_id, dir = trim(n.#dir), nid = n.nfsphys_id,
2214## flag = n.status, size = n.#size, alloc = n.allocated) {
2215 if ((flag & utype) && (size != 0) && (size - alloc > maxsize)) {
2216 maxsize = size - alloc;
2217 if (directory)
2218 free(directory);
2219 directory = strsave(dir);
2220 pid = nid;
2221 m_id = mid;
2222 }
a6cb4d4c 2223## }
822a6eba 2224 if (maxsize == 0)
44bd6f44 2225 return(SMS_NO_FILESYS);
822a6eba 2226
2227 /* create filesystem */
2228 if (set_next_object_id("filsys_id", "filesys"))
44bd6f44 2229 return(SMS_NO_ID);
822a6eba 2230## repeat append filesys (filsys_id = values.value, phys_id = @pid,
2231## label = @login, type = "NFS", mach_id = @m_id,
4f16dc86 2232## name = @directory + "/" + @login,
2233## mount = "/mit/" + @login,
822a6eba 2234## access = "w", comments = "User Locker",
2235## owner = @users_id, owners = @list_id, createflg = 1,
2236## lockertype = "HOMEDIR", modtime = "now",
2237## modby = @who, modwith = @entity)
2238## where values.name = "filsys_id"
2239## inquire_equel(rowcount = "rowcount");
2240 if (rowcount != 1)
2241 return(SMS_INTERNAL);
2242 com_err(whoami, 0, "filesys created on mach %d in %s/%s", m_id,
4f16dc86 2243 directory, login);
822a6eba 2244
2245 /* set quota */
2246## repeat retrieve (quota = values.value) where values.name = "def_quota"
2247## inquire_equel(rowcount = "rowcount");
2248 if (rowcount != 1)
44bd6f44 2249 return(SMS_NO_QUOTA);
822a6eba 2250## repeat append nfsquota (#users_id = @users_id, filsys_id = values.value,
2251## #quota = @quota, modtime = "now", modby = @who,
2252## modwith = @entity)
2253## where values.name = "filsys_id"
2254## inquire_equel(rowcount = "rowcount");
2255 if (rowcount != 1)
2256 return(SMS_INTERNAL);
2257## repeat replace nfsphys (allocated = nfsphys.allocated + @quota)
2258## where nfsphys.nfsphys_id = filesys.#phys_id and
2259## filesys.filsys_id = values.value and values.name = "filsys_id"
2260## inquire_equel(rowcount = "rowcount");
2261 if (rowcount != 1)
2262 return(SMS_INTERNAL);
2263 com_err(whoami, 0, "quota of %d assigned", quota);
2264
4f16dc86 2265## repeat replace tblstats (updates = tblstats.updates + 1, modtime = "now")
2266## where tblstats.table = "users"
2267## repeat replace tblstats (appends = tblstats.appends + 1, modtime = "now")
2268## where tblstats.table = "list" or tblstats.table = "filesys" or
2269## tblstats.table = "nfsquota"
822a6eba 2270 return(SMS_SUCCESS);
2271##}
2272
2273
2274
2275/** set_pop_usage - incr/decr usage count for pop server in serverhosts talbe
2276 **
2277 ** Inputs:
2278 ** id of machine
2279 ** delta (will be +/- 1)
2280 **
2281 ** Description:
2282 ** - incr/decr value field in serverhosts table for pop/mach_id
2283 **
2284 **/
2285
2286static int set_pop_usage(id, count)
2287int id;
2288int count;
2289##{
2290## int mach_id = id;
2291## int n = count;
2292
2293## range of sh is serverhosts
2294## repeat replace sh (value1 = sh.value1 + @n)
44bd6f44 2295## where sh.service = "POP" and sh.#mach_id = @mach_id
a6cb4d4c 2296
2297 return(SMS_SUCCESS);
2298##}
822a6eba 2299
2300
c2408bd5 2301\f
05cdd922 2302/* Validation Routines */
2303
2304validate_row(q, argv, v)
2305 register struct query *q;
2306 char *argv[];
2307 register struct validate *v;
2308##{
2309## char *rvar;
2310## char *table;
2311## char *name;
2312## char qual[128];
2313## int rowcount;
2314
2315 /* build where clause */
2316 build_qual(v->qual, v->argc, argv, qual);
2317
2318 /* setup ingres variables */
2319 rvar = q->rvar;
2320 table = q->rtable;
2321 name = v->field;
2322
b4182127 2323 if (log_flags & LOG_RES)
2324 /* tell the logfile what we're doing */
2325 com_err(whoami, 0, "validating row: %s", qual);
2326
05cdd922 2327 /* look for the record */
2328## range of rvar is table
2329## retrieve (rowcount = count(rvar.name where qual))
05cdd922 2330 if (rowcount == 0) return(SMS_NO_MATCH);
2331 if (rowcount > 1) return(SMS_NOT_UNIQUE);
2332 return(SMS_EXISTS);
2333##}
2334
2335validate_fields(q, argv, vo, n)
2336 struct query *q;
2337 register char *argv[];
2338 register struct valobj *vo;
2339 register int n;
2340{
2341 register int status;
05cdd922 2342
2343 while (--n >= 0) {
2344 switch (vo->type) {
2345 case V_NAME:
92a943d6 2346 if (log_flags & LOG_RES)
b4182127 2347 com_err(whoami, 0, "validating %s in %s: %s",
05cdd922 2348 vo->namefield, vo->table, argv[vo->index]);
05cdd922 2349 status = validate_name(argv, vo);
2350 break;
2351
2352 case V_ID:
92a943d6 2353 if (log_flags & LOG_RES)
b4182127 2354 com_err(whoami, 0, "validating %s in %s: %s",
05cdd922 2355 vo->idfield, vo->table, argv[vo->index]);
05cdd922 2356 status = validate_id(argv, vo);
2357 break;
2358
92a943d6 2359 case V_DATE:
2360 if (log_flags & LOG_RES)
2361 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
2362 status = validate_date(argv, vo);
2363 break;
2364
05cdd922 2365 case V_TYPE:
92a943d6 2366 if (log_flags & LOG_RES)
b4182127 2367 com_err(whoami, 0, "validating %s type: %s",
05cdd922 2368 vo->table, argv[vo->index]);
05cdd922 2369 status = validate_type(argv, vo);
2370 break;
2371
2372 case V_TYPEDATA:
92a943d6 2373 if (log_flags & LOG_RES)
2374 com_err(whoami, 0, "validating typed data (%s): %s",
2375 argv[vo->index - 1], argv[vo->index]);
05cdd922 2376 status = validate_typedata(q, argv, vo);
2377 break;
2378
822a6eba 2379 case V_RENAME:
2380 if (log_flags & LOG_RES)
2381 com_err(whoami, 0, "validating rename %s in %s",
2382 argv[vo->index], vo->table);
2383 status = validate_rename(argv, vo);
2384 break;
2385
2386 case V_CHAR:
2387 if (log_flags & LOG_RES)
2388 com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
2389 status = validate_chars(argv[vo->index]);
05cdd922 2390 break;
2391
92a943d6 2392 case V_SORT:
2393 status = SMS_EXISTS;
2394 break;
2395
05cdd922 2396 }
2397
2398 if (status != SMS_EXISTS) return(status);
2399 vo++;
2400 }
2401
2402 return(SMS_SUCCESS);
2403}
2404
822a6eba 2405
2406/* validate_chars: verify that there are no illegal characters in
2407 * the string. Legal characters are printing chars other than
331b982e 2408 * ", *, ?, \, [ and ].
822a6eba 2409 */
2410static int illegalchars[] = {
2411 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
2412 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
2413 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
2414 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
2415 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
331b982e 2416 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
822a6eba 2417 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
2418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
2419 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2420 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2421 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2422 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2423 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2424 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2425 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2426 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2427};
2428
2429validate_chars(s)
2430register char *s;
2431{
2432 while (*s)
2433 if (illegalchars[*s++])
2434 return(SMS_BAD_CHAR);
2435 return(SMS_EXISTS);
2436}
2437
2438
05cdd922 2439validate_id(argv, vo)
2440 char *argv[];
2441 register struct valobj *vo;
2442##{
2443## char *name;
2444## char *table;
2445## char *namefield;
2446## char *idfield;
2447## int id;
2448## int rowcount;
122ec372 2449 register char *c;
05cdd922 2450
2451 name = argv[vo->index];
2452 table = vo->table;
122ec372 2453 /* minor kludge to upcasify machine names */
822a6eba 2454 if (!strcmp(table, "machine"))
122ec372 2455 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
05cdd922 2456 namefield = vo->namefield;
2457 idfield = vo->idfield;
822a6eba 2458 if (!strcmp(namefield, "uid")) {
9119288e 2459## retrieve (id = table.idfield) where table.namefield = int4(name)
2460## inquire_equel (rowcount = "rowcount")
2461 } else {
2462## retrieve (id = table.idfield) where table.namefield = name
2463## inquire_equel (rowcount = "rowcount")
2464 }
05cdd922 2465 if (rowcount != 1) return(vo->error);
2466 *(int *)argv[vo->index] = id;
2467 return(SMS_EXISTS);
2468##}
2469
2470validate_name(argv, vo)
2471 char *argv[];
2472 register struct valobj *vo;
2473##{
2474## char *name;
2475## char *table;
2476## char *namefield;
2477## int rowcount;
44bd6f44 2478 register char *c;
05cdd922 2479
2480 name = argv[vo->index];
2481 table = vo->table;
2482 namefield = vo->namefield;
44bd6f44 2483 if (!strcmp(table, "servers") && !strcmp(namefield, "name")) {
2484 for (c = name; *c; c++)
2485 if (islower(*c))
2486 *c = toupper(*c);
2487 }
05cdd922 2488## retrieve (rowcount = countu(table.namefield
2489## where table.namefield = name))
2490 return ((rowcount == 1) ? SMS_EXISTS : vo->error);
2491##}
2492
92a943d6 2493validate_date(argv, vo)
2494 char *argv[];
2495 struct valobj *vo;
2496##{
2497## char *idate;
2498## double dd;
2499## int errorno;
2500
2501 idate = argv[vo->index];
2502
2503## retrieve (dd = interval("years", date(idate) - date("today")))
2504## inquire_equel (errorno = "errorno")
2505 if (errorno != 0 || dd > 5.0) return(SMS_DATE);
44bd6f44 2506 return(SMS_EXISTS);
92a943d6 2507##}
2508
822a6eba 2509
2510validate_rename(argv, vo)
2511char *argv[];
2512struct valobj *vo;
2513##{
2514## char *name, *table, *namefield, *idfield;
2515## int id;
2516 register char *c;
2517
2518 c = name = argv[vo->index];
2519 while (*c)
2520 if (illegalchars[*c++])
2521 return(SMS_BAD_CHAR);
2522 table = vo->table;
2523 /* minor kludge to upcasify machine names */
2524 if (!strcmp(table, "machine"))
2525 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
2526 namefield = vo->namefield;
2527 idfield = vo->idfield;
2528 id = -1;
2529 if (idfield == 0) {
2530 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
2531 return(SMS_EXISTS);
2532## retrieve (id = any(table.namefield where table.namefield = name))
2533 if (id)
2534 return(vo->error);
2535 else
2536 return(SMS_EXISTS);
2537 }
2538## retrieve (id = table.idfield) where table.namefield = name
2539 if (id == -1 || id == *(int *)argv[vo->index - 1])
2540 return(SMS_EXISTS);
2541 else
2542 return(vo->error);
2543##}
2544
2545
05cdd922 2546validate_type(argv, vo)
2547 char *argv[];
2548 register struct valobj *vo;
2549##{
2550## char *typename;
2551## char *value;
822a6eba 2552## int exists;
05cdd922 2553 register char *c;
2554
2555 typename = vo->table;
822a6eba 2556 c = value = argv[vo->index];
2557 while (*c)
2558 if (illegalchars[*c++])
2559 return(SMS_BAD_CHAR);
05cdd922 2560
2561 /* uppercase type fields */
2562 for (c = value; *c; c++) if (islower(*c)) *c = toupper(*c);
2563
2564## range of a is alias
822a6eba 2565## repeat retrieve (exists = any(a.trans where a.name = @typename and
05cdd922 2566## a.type = "TYPE" and
2567## a.trans = @value))
822a6eba 2568 return (exists ? SMS_EXISTS : vo->error);
05cdd922 2569##}
2570
2571/* validate member or type-specific data field */
2572
2573validate_typedata(q, argv, vo)
2574 register struct query *q;
2575 register char *argv[];
2576 register struct valobj *vo;
2577##{
2578## char *name;
2579## char *field_type;
822a6eba 2580## char data_type[129];
05cdd922 2581## int id;
05cdd922 2582## int rowcount;
822a6eba 2583 char *index();
a6cb4d4c 2584 register char *c;
05cdd922 2585
2586 /* get named object */
2587 name = argv[vo->index];
2588
2589 /* get field type string (known to be at index-1) */
2590 field_type = argv[vo->index-1];
2591
2592 /* get corresponding data type associated with field type name */
2593## repeat retrieve (data_type = alias.trans)
2594## where alias.#name = @field_type and alias.type = "TYPEDATA"
2595## inquire_equel (rowcount = "rowcount")
2596 if (rowcount != 1) return(SMS_TYPE);
2597
2598 /* now retrieve the record id corresponding to the named object */
822a6eba 2599 if (index(data_type, ' '))
2600 *index(data_type, ' ') = 0;
05cdd922 2601 if (!strcmp(data_type, "user")) {
2602 /* USER */
2603## repeat retrieve (id = users.users_id) where users.login = @name
2604## inquire_equel (rowcount = "rowcount")
2605 if (rowcount != 1) return(SMS_USER);
2606
2607 } else if (!strcmp(data_type, "list")) {
2608 /* LIST */
2609## repeat retrieve (id = list.list_id) where list.#name = @name
2610## inquire_equel (rowcount = "rowcount")
822a6eba 2611 if (rowcount != 1) {
2612 /* if idfield is non-zero, then if argv[0] matches the string
2613 * that we're trying to resolve, we should get the value of
2614 * values.[idfield] for the id.
2615 */
2616 if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
4f16dc86 2617 set_next_object_id(q->validate->object_id, q->rtable);
822a6eba 2618 name = vo->idfield;
4f16dc86 2619## repeat retrieve (id = values.value) where values.#name = @name
822a6eba 2620## inquire_equel(rowcount = "rowcount")
2621 if (rowcount != 1) return(SMS_LIST);
2622 } else
2623 return(SMS_LIST);
2624 }
05cdd922 2625 } else if (!strcmp(data_type, "machine")) {
2626 /* MACHINE */
a6cb4d4c 2627 for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
05cdd922 2628## repeat retrieve (id = machine.mach_id) where machine.#name = @name
2629## inquire_equel (rowcount = "rowcount")
2630 if (rowcount != 1) return(SMS_MACHINE);
2631
2632 } else if (!strcmp(data_type, "string")) {
2633 /* STRING */
2634## range of s is strings
822a6eba 2635## repeat retrieve (id = s.string_id) where s.string = @name
05cdd922 2636## inquire_equel (rowcount = "rowcount")
2637 if (rowcount == 0) {
2638 if (q->type != APPEND) return(SMS_STRING);
2639## range of v is values
2640## retrieve (id = v.value) where v.#name = "strings_id"
2641 id++;
2642## replace v (value = id) where v.#name = "strings_id"
822a6eba 2643## append to strings (string_id = id, string = name)
05cdd922 2644 }
822a6eba 2645 } else if (!strcmp(data_type, "none")) {
2646 id = 0;
05cdd922 2647 } else {
2648 return(SMS_TYPE);
2649 }
2650
2651 /* now set value in argv */
2652 *(int *)argv[vo->index] = id;
2653
2654 return (SMS_EXISTS);
2655##}
2656
05cdd922 2657
822a6eba 2658/* This looks up a login name and returns the SMS internal ID. It is used
2659 * by authenticate to put the users_id in the client structure.
2660 */
05cdd922 2661
822a6eba 2662int get_users_id(name)
2663char *name;
2664##{
2665## int id, rowcount;
2666## char *login;
05cdd922 2667
822a6eba 2668 login = name;
05cdd922 2669
822a6eba 2670## range of u is users
2671## repeat retrieve (id = u.#users_id) where u.#login = @login
2672## inquire_equel (rowcount = "rowcount")
2673
2674 if (rowcount == 1)
2675 return(id);
2676 else
2677 return(0);
2678##}
05cdd922 2679
05cdd922 2680
822a6eba 2681/* Check the database at startup time. For now this just resets the
2682 * inprogress flags that the DCM uses.
2683 */
05cdd922 2684
822a6eba 2685sanity_check_database()
2686##{
05cdd922 2687##}
This page took 0.490009 seconds and 5 git commands to generate.