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