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