]> andersk Git - moira.git/blob - server/qsupport.qc
use private do_sms_query with timeout check. Also fixed
[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 /* Sets the modtime on a machine record.  The machine name is in argv[0].
895  * This routine is different from the generic set_modtime in that the
896  * name is uppercased first.
897  */
898
899 set_mach_modtime(q, argv, cl)
900     struct query *q;
901     char **argv;
902     client *cl;
903 ##{
904 ##  char *host, *entity;
905 ##  int who;
906
907     entity = cl->entity;
908     who = cl->users_id;
909
910     host = argv[0];
911 ##  repeat replace m (modtime = "now", modby = @who, modwith = @entity)
912 ##       where m.name = uppercase(@host)
913     return(SMS_SUCCESS);
914 ##}
915
916
917 /* Sets the modtime on the machine whose mach_id is in argv[0].  This routine
918  * is necessary for add_machine_to_cluster becuase the table that query
919  * operates on is "mcm", not "machine".
920  */
921
922 set_mach_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 m is machine
935 ##  repeat replace m (modtime = "now", modby = @who, modwith = @entity)
936 ##       where m.mach_id = @id
937     return(SMS_SUCCESS);
938 ##}
939
940
941 /* Sets the modtime on the cluster whose mach_id is in argv[0].  This routine
942  * is necessary for add_cluster_data and delete_cluster_data becuase the
943  * table that query operates on is "svc", not "cluster".
944  */
945
946 set_cluster_modtime_by_id(q, argv, cl)
947     struct query *q;
948     char **argv;
949     client *cl;
950 ##{
951 ##  char *entity;
952 ##  int who, id;
953
954     entity = cl->entity;
955     who = cl->users_id;
956
957     id = *(int *)argv[0];
958 ##  range of c is cluster
959 ##  repeat replace c (modtime = "now", modby = @who, modwith = @entity)
960 ##       where c.clu_id = @id
961     return(SMS_SUCCESS);
962 ##}
963
964
965 /* sets the modtime on the serverhost where the service name is in argv[0]
966  * and the mach_id is in argv[1].
967  */
968
969 set_serverhost_modtime(q, argv, cl)
970     struct query *q;
971     char **argv;
972     client *cl;
973 ##{
974 ##  char *entity, *serv;
975 ##  int who, id;
976
977     entity = cl->entity;
978     who = cl->users_id;
979
980     serv = argv[0];
981     id = *(int *)argv[1];
982 ##  repeat replace sh (modtime = "now", modby = @who, modwith = @entity)
983 ##       where sh.service = uppercase(@serv) and sh.mach_id = @id
984     return(SMS_SUCCESS);
985 ##}
986
987
988 /* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
989  * directory name is in argv[1].
990  */
991
992 set_nfsphys_modtime(q, argv, cl)
993     struct query *q;
994     char **argv;
995     client *cl;
996 ##{
997 ##  char *entity, *dir;
998 ##  int who, id;
999
1000     entity = cl->entity;
1001     who = cl->users_id;
1002
1003     id = *(int *)argv[0];
1004     dir = argv[1];
1005 ##  repeat replace np (modtime = "now", modby = @who, modwith = @entity)
1006 ##       where np.#dir = @dir and np.mach_id = @id
1007     return(SMS_SUCCESS);
1008 ##}
1009
1010
1011 /* sets the modtime on a filesystem, where argv[0] contains the filesys
1012  * label.
1013  */
1014
1015 set_filesys_modtime(q, argv, cl)
1016     struct query *q;
1017     char *argv[];
1018     client *cl;
1019 ##{
1020 ##  char *label, *entity;
1021 ##  int who;
1022
1023     entity = cl->entity;
1024     who = cl->users_id;
1025
1026     label = argv[0];
1027     if (!strcmp(q->shortname, "ufil"))
1028       label = argv[1];
1029
1030 ##  repeat replace fs (modtime = "now", modby = @who, modwith = @entity,
1031 ##                     #phys_id = @var_phys_id)  where fs.#label = @label
1032     return(SMS_SUCCESS);
1033 ##}
1034
1035
1036 /* sets the modtime on a zephyr class, where argv[0] contains the class
1037  * name.
1038  */
1039
1040 set_zephyr_modtime(q, argv, cl)
1041     struct query *q;
1042     char *argv[];
1043     client *cl;
1044 ##{
1045 ##  char *class, *entity;
1046 ##  int who;
1047
1048     entity = cl->entity;
1049     who = cl->users_id;
1050
1051     class = argv[0];
1052
1053 ##  repeat replace z (modtime = "now", modby = @who, modwith = @entity)
1054 ##                     where z.#class = @class
1055     return(SMS_SUCCESS);
1056 ##}
1057
1058
1059 /* fixes the modby field.  This will be the second to last thing in the
1060  * argv, the argv length is determined from the query structure.  It is
1061  * passed as a pointer to an integer.  This will either turn it into a
1062  * username, or # + the users_id.
1063  */
1064 followup_fix_modby(q, sq, v, action, actarg, cl)
1065     struct query *q;
1066     register struct save_queue *sq;
1067     struct validate *v;
1068     register int (*action)();
1069     register int actarg;
1070     client *cl;
1071 ##{
1072     register int i, j;
1073     char **argv, *malloc();
1074 ##  int id, rowcount;
1075 ##  char *name;
1076
1077     i = q->vcnt - 2;
1078     while (sq_get_data(sq, &argv)) {
1079         id = atoi(argv[i]);
1080         free(argv[i]);
1081         argv[i] = malloc(9);
1082         name = argv[i];
1083 ##      repeat retrieve (name = users.login) where users.users_id = @id
1084 ##      inquire_equel(rowcount = "rowcount")
1085         if (rowcount != 1) {
1086             sprintf(argv[i], "#%d", id);
1087         }
1088         (*action)(q->vcnt, argv, actarg);
1089         for (j = 0; j < q->vcnt; j++)
1090           free(argv[j]);
1091         free(argv);
1092     }
1093     sq_destroy(sq);
1094     return(SMS_SUCCESS);
1095 ##}
1096
1097
1098 /**
1099  ** followup_ausr - add finger and pobox entries, set_user_modtime
1100  **
1101  ** Inputs:
1102  **     argv[0] - login (add_user)
1103  **     argv[3] - last name
1104  **     argv[4] - first name
1105  **     argv[5] - middle name
1106  **
1107  **/
1108
1109 followup_ausr(q, argv, cl)
1110     struct query *q;
1111     char *argv[];
1112     client *cl;
1113 ##{
1114 ##  int who;
1115 ##  char *login, *entity;
1116 ##  char fullname[129];
1117
1118     login = argv[0];
1119     who = cl->users_id;
1120     entity = cl->entity;
1121
1122     /* build fullname */
1123     if (strlen(argv[4]) && strlen(argv[5]))
1124         sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
1125     else if (strlen(argv[4]))
1126         sprintf(fullname, "%s %s", argv[4], argv[3]);
1127     else
1128         sprintf(fullname, "%s", argv[3]);
1129
1130     /* create finger entry, pobox & set modtime on user */
1131 ##  repeat replace u (modtime = "now", modby=@who, modwith=@entity,
1132 ##           #fullname=@fullname, mit_affil = u.mit_year,
1133 ##           fmodtime="now", fmodby=@who, fmodwith=@entity,
1134 ##           potype="NONE", pmodtime="now", pmodby=@who, pmodwith=@entity)
1135 ##      where u.#login = @login
1136
1137     return(SMS_SUCCESS);
1138 ##}
1139
1140
1141 /* followup_gpob: fixes argv[2] based on the IDs currently there and the
1142  * type in argv[1].  Then completes the upcall to the user.
1143  *
1144  * argv[2] is of the form "123:234" where the first integer is the machine
1145  * ID if it is a pop box, and the second is the string ID if it is an SMTP
1146  * box.  argv[1] should be "POP", "SMTP", or "NONE".  Boxes of type NONE
1147  * are skipped.
1148  */
1149
1150 followup_gpob(q, sq, v, action, actarg, cl)
1151     register struct query *q;
1152     register struct save_queue *sq;
1153     register struct validate *v;
1154     register int (*action)();
1155     int actarg;
1156     client *cl;
1157 ##{
1158     char **argv, *index();
1159     char *ptype, *p;
1160 ##  char box[129], *name;
1161 ##  int mid, sid, rowcount;
1162
1163     /* for each row */
1164     while (sq_get_data(sq, &argv)) {
1165         sms_trim_args(2, argv);
1166         ptype = argv[1];
1167         p = index(argv[2], ':');
1168         *p++ = 0;
1169         mid = atoi(argv[2]);
1170         sid = atoi(p);
1171         free(argv[2]);
1172
1173         if (!strcmp(ptype, "POP")) {
1174 ##          repeat retrieve (box=machine.#name) where machine.mach_id=@mid
1175 ##          inquire_equel(rowcount = "rowcount")
1176             if (rowcount != 1)
1177               return(SMS_MACHINE);
1178         } else if (!strcmp(ptype, "SMTP")) {
1179 ##          repeat retrieve (box=strings.string) where strings.string_id=@sid
1180 ##          inquire_equel(rowcount = "rowcount")
1181             if (rowcount != 1)
1182               return(SMS_STRING);
1183         } else /* ptype == "NONE" */ {
1184             goto skip;
1185         }
1186
1187         if (!strcmp(q->shortname, "gpob")) {
1188             sid = atoi(argv[4]);
1189             free(argv[4]);
1190             argv[4] = malloc(9);
1191             name = argv[4];
1192 ##          repeat retrieve (name = users.login) where users.users_id = @sid
1193 ##          inquire_equel(rowcount = "rowcount")
1194             if (rowcount != 1)
1195               sprintf(name, "#%d", sid);
1196         }
1197
1198         argv[2] = box;
1199         (*action)(q->vcnt, argv, actarg);
1200     skip:
1201         /* free saved data */
1202         free(argv[0]);
1203         free(argv[1]);
1204         free(argv);
1205     }
1206
1207     sq_destroy(sq);
1208     return (SMS_SUCCESS);
1209 ##}
1210
1211
1212 /* followup_glin: fix the ace_name in argv[8].  argv[7] will contain the
1213  * ace_type: "LIST", "USER", or "NONE".  Decode the id in argv[8] into the
1214  * proper name based on the type, and repace that string in the argv.
1215  * Also fixes the modby field by called followup_fix_modby.
1216  */
1217
1218 followup_glin(q, sq, v, action, actarg, cl)
1219     register struct query *q;
1220     register struct save_queue *sq;
1221     register struct validate *v;
1222     register int (*action)();
1223     int actarg;
1224     client *cl;
1225 ##{
1226     char **argv, *malloc(), *realloc(), *type;
1227 ##  char *name;
1228 ##  int id, rowcount;
1229     int i, idx;
1230
1231     idx = 8;
1232     if (!strcmp(q->shortname, "gsin"))
1233       idx = 12;
1234
1235     while (sq_get_data(sq, &argv)) {
1236         sms_trim_args(q->vcnt, argv);
1237
1238         id = atoi(argv[i = q->vcnt - 2]);
1239         free(argv[i]);
1240         name = argv[i] = malloc(9);
1241 ##      repeat retrieve (name = users.login) where users.users_id = @id
1242 ##      inquire_equel(rowcount = "rowcount")
1243         if (rowcount != 1)
1244           sprintf(argv[i], "#%d", id);
1245
1246         id = atoi(argv[idx]);
1247         type = argv[idx - 1];
1248         if ((name = malloc(33)) == NULL)
1249             return(SMS_NO_MEM);
1250
1251         if (!strcmp(type, "LIST")) {
1252 ##          repeat retrieve (name = list.#name) where list.list_id = @id
1253 ##          inquire_equel(rowcount = "rowcount")
1254             if (rowcount != 1)
1255                 strcpy(name, "???");
1256         } else if (!strcmp(type, "USER")) {
1257 ##          repeat retrieve (name = users.login) where users.users_id = @id
1258 ##          inquire_equel(rowcount = "rowcount")
1259             if (rowcount != 1)
1260                 strcpy(name, "???");
1261         } else if (!strcmp(type, "NONE")) {
1262             strcpy(name, "NONE");
1263         } else
1264           strcpy(name, "???");
1265         free(argv[idx]);
1266         argv[idx] = name;
1267
1268         if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
1269             argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
1270             strcpy(argv[6], UNIQUE_GID);
1271         }
1272
1273         /* send the data */
1274         (*action)(q->vcnt, argv, actarg);
1275
1276         /* free saved data */
1277         for (i = 0; i < q->vcnt; i++) 
1278             free(argv[i]);
1279         free(argv);
1280     }
1281
1282     sq_destroy(sq);
1283     return (SMS_SUCCESS);
1284 ##}
1285
1286
1287 /** followup_amtl - followup for amtl and dmfl; when adding a list 
1288  **                 member to a maillist, make member list a maillist also
1289  **                 unless list is a user-group.
1290  **                 Then set_list_modtime_by_id.
1291  **
1292  ** Inputs:
1293  **   argv[0] - list_id
1294  **   argv[1] - member_type
1295  **   argv[2] - member_id
1296  **
1297  **/
1298
1299 followup_amtl(q, argv, cl)
1300     struct query *q;
1301     char *argv[];
1302     client *cl;
1303 ##{
1304 ##  int list_id;
1305 ##  int member_id;
1306 ##  int exists, who;
1307 ##  char *entity;
1308
1309     list_id = *(int *)argv[0];
1310     entity = cl->entity;
1311     who = cl->users_id;
1312
1313 ##  range of l is list
1314 ##  repeat replace l (modtime = "now", modby = @who, modwith = @entity)
1315 ##       where l.#list_id = @list_id
1316
1317     /* if query is not amtl or if member_type is not LIST then return */
1318     if (bcmp(q->shortname, "amtl", 4) || bcmp(argv[1], "LIST", 4)) 
1319         return(SMS_SUCCESS);
1320
1321     member_id = *(int *)argv[2];
1322
1323     /* is parent list a mailing list? */
1324 ##  repeat retrieve (exists = l.maillist) where l.#list_id=@list_id
1325     if (!exists)
1326         return(SMS_SUCCESS);
1327
1328     /* list is not a user-group; add list to maillist table */
1329 ##  repeat replace l (maillist = 1) where l.#list_id = @member_id
1330     return(SMS_SUCCESS);
1331 ##}
1332
1333
1334 /* followup_anfq: Add allocation to nfsphys after creating quota.
1335  *   argv[0] = filsys_id
1336  *   argv[2] = ascii(quota)
1337  */
1338
1339 followup_anfq(q, argv, cl)
1340     struct query  *q;
1341     char **argv;
1342     client *cl;
1343 ##{
1344 ##  int quota, user, fs, who;
1345 ##  char *entity;
1346
1347     fs = *(int *)argv[0];
1348     user = *(int *)argv[1];
1349     quota = atoi(argv[2]);
1350     who = cl->users_id;
1351     entity = cl->entity;
1352
1353 ##  repeat replace nq (modtime = "now", modby = @who, modwith = @entity)
1354 ##      where nq.filsys_id = @fs and nq.users_id = @user
1355 ##  repeat replace nfsphys (allocated = nfsphys.allocated + @quota)
1356 ##      where nfsphys.nfsphys_id = filesys.#phys_id and filesys.filsys_id = @fs
1357     return(SMS_SUCCESS);
1358 ##}
1359
1360
1361 /* followup_gzcl:
1362  */
1363
1364 followup_gzcl(q, sq, v, action, actarg, cl)
1365     register struct query *q;
1366     register struct save_queue *sq;
1367     register struct validate *v;
1368     register int (*action)();
1369     int actarg;
1370     client *cl;
1371 ##{
1372 ##  char *name;
1373 ##  int rowcount, id;
1374     char **argv;
1375     int i;
1376
1377     while (sq_get_data(sq, &argv)) {
1378         sms_trim_args(q->vcnt, argv);
1379
1380         id = atoi(argv[i = q->vcnt - 2]);
1381         free(argv[i]);
1382         name = argv[i] = malloc(9);
1383 ##      repeat retrieve (name = users.login) where users.users_id = @id
1384 ##      inquire_equel(rowcount = "rowcount")
1385         if (rowcount != 1)
1386           sprintf(argv[i], "#%d", id);
1387
1388         for (i = 1; i < 8; i+=2) {
1389             id = atoi(argv[i+1]);
1390             free(argv[i+1]);
1391             if ((name = argv[i+1] = malloc(33)) == NULL)
1392               return(SMS_NO_MEM);
1393             if (!strcmp(argv[i], "LIST")) {
1394 ##              repeat retrieve (name = list.#name) where list.list_id = @id
1395 ##              inquire_equel(rowcount = "rowcount")
1396                 if (rowcount != 1)
1397                   strcpy(name, "???");
1398             } else if (!strcmp(argv[i], "USER")) {
1399 ##              repeat retrieve (name = users.login) where users.users_id = @id
1400 ##              inquire_equel(rowcount = "rowcount")
1401                 if (rowcount != 1)
1402                   strcpy(name, "???");
1403             } else if (!strcmp(argv[i], "NONE")) {
1404                 strcpy(name, "NONE");
1405             } else {
1406                 strcpy(name, "???");
1407             }
1408         }
1409
1410         /* send the data */
1411         (*action)(q->vcnt, argv, actarg);
1412
1413         /* free saved data */
1414         for (i = 0; i < q->vcnt; i++) 
1415             free(argv[i]);
1416         free(argv);
1417     }
1418     sq_destroy(sq);
1419     return(SMS_SUCCESS);
1420 ##}
1421
1422
1423 /* followup_gsha:
1424  */
1425
1426 followup_gsha(q, sq, v, action, actarg, cl)
1427     register struct query *q;
1428     register struct save_queue *sq;
1429     register struct validate *v;
1430     register int (*action)();
1431     int actarg;
1432     client *cl;
1433 ##{
1434 ##  char *name;
1435 ##  int rowcount, id;
1436     char **argv;
1437     int i;
1438
1439     while (sq_get_data(sq, &argv)) {
1440         sms_trim_args(q->vcnt, argv);
1441
1442         id = atoi(argv[4]);
1443         free(argv[4]);
1444         name = argv[4] = malloc(9);
1445 ##      repeat retrieve (name = users.login) where users.users_id = @id
1446 ##      inquire_equel(rowcount = "rowcount")
1447         if (rowcount != 1)
1448           sprintf(argv[4], "#%d", id);
1449
1450         id = atoi(argv[2]);
1451         free(argv[2]);
1452         if ((name = argv[2] = malloc(33)) == NULL)
1453           return(SMS_NO_MEM);
1454         if (!strcmp(argv[1], "LIST")) {
1455 ##          repeat retrieve (name = list.#name) where list.list_id = @id
1456 ##          inquire_equel(rowcount = "rowcount")
1457             if (rowcount != 1)
1458               strcpy(name, "???");
1459         } else if (!strcmp(argv[1], "USER")) {
1460 ##          repeat retrieve (name = users.login) where users.users_id = @id
1461 ##          inquire_equel(rowcount = "rowcount")
1462             if (rowcount != 1)
1463               strcpy(name, "???");
1464         } else if (!strcmp(argv[1], "NONE")) {
1465             strcpy(name, "NONE");
1466         } else {
1467             strcpy(name, "???");
1468         }
1469
1470         /* send the data */
1471         (*action)(q->vcnt, argv, actarg);
1472
1473         /* free saved data */
1474         for (i = 0; i < q->vcnt; i++) 
1475             free(argv[i]);
1476         free(argv);
1477     }
1478     sq_destroy(sq);
1479     return(SMS_SUCCESS);
1480 ##}
1481
1482
1483 \f
1484 /* Special query routines */
1485
1486 /* set_pobox - this does all of the real work.
1487  *       argv = user_id, type, box
1488  * if type is POP, then box should be a machine, and its ID should be put in
1489  * pop_id.  If type is SMTP, then box should be a string and its ID should
1490  * be put in box_id.  If type is NONE, then box doesn't matter.
1491  */
1492
1493 int set_pobox(q, argv, cl)
1494     struct query *q;
1495     char **argv;
1496     client *cl;
1497 ##{
1498 ##  int user, id, rowcount;
1499 ##  char *box, potype[9];
1500
1501     box = argv[2];
1502     user = *(int *)argv[0];
1503
1504 ##  repeat retrieve (id = users.pop_id, potype = users.#potype)
1505 ##              where users.users_id = @user
1506     if (!strcmp(strtrim(potype), "POP"))
1507       set_pop_usage(id, -1);
1508
1509     if (!strcmp(argv[1], "POP")) {
1510 ##      repeat retrieve (id=machine.mach_id) where machine.name=uppercase(@box)
1511 ##      inquire_equel(rowcount = "rowcount")
1512         if (rowcount != 1)
1513             return(SMS_MACHINE);
1514 ##      repeat replace users (#potype = "POP", pop_id = @id)
1515 ##              where users.users_id = @user
1516         set_pop_usage(id, 1);
1517     } else if (!strcmp(argv[1], "SMTP")) {
1518 ##      range of s is strings
1519 ##      repeat retrieve (id = s.string_id) where s.string = @box
1520 ##      inquire_equel (rowcount = "rowcount")
1521         if (rowcount == 0) {
1522 ##          range of v is values
1523 ##          repeat retrieve (id = v.value) where v.name = "strings_id"
1524             id++;
1525 ##          repeat replace v (value = @id) where v.name = "strings_id"
1526 ##          append to strings (string_id = id, string = box)
1527         }
1528 ##      repeat replace users (#potype = "SMTP", box_id = @id) 
1529 ##             where users.users_id = @user
1530     } else /* argv[1] == "NONE" */ {
1531 ##      repeat replace users (#potype = "NONE") where users.users_id = @user
1532     }
1533
1534     set_pobox_modtime(q, argv, cl);
1535 ##  repeat replace tblstats (updates = tblstats.updates + 1, modtime = "now")
1536 ##      where tblstats.#table = "users"
1537     return(SMS_SUCCESS);
1538 ##}
1539
1540
1541 /* get_list_info:  passed a wildcard list name, returns lots of stuff about
1542  * each list.  This is tricky:  first build a queue of all requested
1543  * data.  Rest of processing consists of fixing gid, ace_name, and modby.
1544  */
1545
1546 get_list_info(q, aargv, cl, action, actarg)
1547     register struct query *q;
1548     char **aargv;
1549     client *cl;
1550     register int (*action)();
1551     int actarg;
1552 ##{
1553     char *argv[13], *malloc(), *realloc();
1554 ##  char *name, acl_type[9], listname[33], active[5], public[5], hidden[5];
1555 ##  char maillist[5], group[5], gid[6], acl_name[33], desc[256], modtime[27];
1556 ##  char modby[9], modwith[9];
1557 ##  int id, rowcount, acl_id, hid, modby_id;
1558     int returned;
1559     struct save_queue *sq, *sq_create();
1560
1561     returned = rowcount = 0;
1562     name = aargv[0];
1563
1564     sq = sq_create();
1565 ##  range of l is list
1566 ##  repeat retrieve (id = l.list_id) where l.#name = @name {
1567         sq_save_data(sq, id);
1568         rowcount++;
1569 ##  }
1570     if (rowcount == 0)
1571       return(SMS_NO_MATCH);
1572
1573     argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden;
1574     argv[4] = maillist; argv[5] = group; argv[6] = gid; argv[7] = acl_type;
1575     argv[8] = acl_name; argv[9] = desc; argv[10] = modtime; argv[11] = modby;
1576     argv[12] = modwith;
1577
1578     while (sq_get_data(sq, &id)) {
1579         if (id == 0)
1580           continue;
1581         argv[6] = gid;
1582 ##      repeat retrieve (listname = l.#name, active = text(l.#active), 
1583 ##              public = text(l.#public), hidden = text(l.#hidden),
1584 ##              hid = l.#hidden, maillist = text(l.#maillist),
1585 ##              group = text(l.#group), gid = text(l.#gid),
1586 ##              acl_type = trim(l.#acl_type), acl_id = l.#acl_id,
1587 ##              desc = l.#desc, modtime = l.#modtime, modby_id = l.#modby,
1588 ##              modwith =l.#modwith)
1589 ##          where l.list_id = @id
1590
1591         if (atoi(gid) == -1)
1592             argv[6] = UNIQUE_GID;
1593
1594         if (!strcmp(acl_type, "LIST")) {
1595 ##          repeat retrieve (acl_name = l.#name) where l.list_id = @acl_id
1596 ##          inquire_equel(rowcount = "rowcount")
1597             if (rowcount != 1)
1598                 strcpy(acl_name, "???");
1599         } else if (!strcmp(acl_type, "USER")) {
1600 ##          repeat retrieve (acl_name = users.#login)
1601 ##              where users.users_id = @acl_id
1602 ##          inquire_equel(rowcount = "rowcount")
1603             if (rowcount != 1)
1604                 strcpy(acl_name, "???");
1605         } else if (!strcmp(acl_type, "NONE")) {
1606             strcpy(acl_name, "NONE");
1607         } else
1608           strcpy(acl_name, "???");
1609
1610 ##      repeat retrieve (modby = users.login) where users.users_id = @modby_id
1611 ##      inquire_equel(rowcount = "rowcount")
1612         if (rowcount != 1)
1613           sprintf(modby, "#%d", id);
1614
1615         sms_trim_args(q->vcnt, argv);
1616         returned++;
1617         (*action)(q->vcnt, argv, actarg);
1618     }
1619
1620     sq_destroy(sq);
1621 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1622 ##      where tblstats.#table = "list"
1623
1624     return (SMS_SUCCESS);
1625 ##}
1626
1627
1628 /* get_ace_use - given a type and a name, return a type and a name.
1629  * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0],
1630  * and argv[1] will contain the ID of the entity in question.  The R*
1631  * types mean to recursively look at every containing list, not just
1632  * when the object in question is a direct member.  On return, the
1633  * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR.
1634  */
1635
1636 int get_ace_use(q, argv, cl, action, actarg)
1637     struct query *q;
1638     char *argv[];
1639     client *cl;
1640     int (*action)();
1641     int actarg;
1642 ##{
1643     int found = 0;
1644 ##  char *atype;
1645 ##  int aid, listid, id;
1646     struct save_queue *sq, *sq_create();
1647
1648     atype = argv[0];
1649     aid = *(int *)argv[1];
1650     if (!strcmp(atype, "LIST") || !strcmp(atype, "USER")) {
1651         return(get_ace_internal(atype, aid, action, actarg));
1652     }
1653
1654     sq = sq_create();
1655     if (!strcmp(atype, "RLIST")) {
1656         sq_save_data(sq, aid);
1657         /* get all the list_id's of containing lists */
1658 ##      range of m is members
1659         while (sq_get_data(sq, &id)) {
1660 ##          repeat retrieve (listid = m.list_id)
1661 ##              where m.member_type = "LIST" and m.member_id = @id {
1662               sq_save_unique_data(sq, listid);
1663 ##          }
1664           }
1665         /* now process each one */
1666         while (sq_get_data(sq, &id)) {
1667             if (get_ace_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1668               found++;
1669         }
1670     }
1671
1672     if (!strcmp(atype, "RUSER")) {
1673 ##      range of m is members
1674 ##      repeat retrieve (listid = m.list_id)
1675 ##              where m.member_type = "USER" and m.member_id = @aid {
1676             sq_save_data(sq, listid);
1677 ##      }
1678         /* get all the list_id's of containing lists */
1679         while (sq_get_data(sq, &id)) {
1680 ##          repeat retrieve (listid = m.list_id)
1681 ##              where m.member_type = "LIST" and m.member_id = @id {
1682               sq_save_unique_data(sq, listid);
1683 ##          }
1684           }
1685         /* now process each one */
1686         while (sq_get_data(sq, &id)) {
1687             if (get_ace_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1688               found++;
1689         }
1690         if (get_ace_internal("USER", aid, action, actarg) == SMS_SUCCESS)
1691           found++;
1692     }
1693
1694     sq_destroy(sq);     
1695     if (!found) return(SMS_NO_MATCH);
1696     return(SMS_SUCCESS);
1697 ##}
1698
1699
1700 /* This looks up a single list or user for ace use.  atype must be "USER"
1701  * or "LIST", and aid is the ID of the corresponding object.  This is used
1702  * by get_ace_use above.
1703  */
1704
1705 ##get_ace_internal(atype, aid, action, actarg)
1706 ##  char *atype;
1707 ##  int aid;
1708     int (*action)();
1709     int actarg;
1710 ##{
1711     char *rargv[2];
1712     int found = 0;
1713 ##  char name[33];
1714
1715     rargv[1] = name;
1716     if (!strcmp(atype, "LIST")) {
1717         rargv[0] = "FILESYS";
1718 ##      repeat retrieve (name = filesys.label) 
1719 ##              where filesys.owners = @aid {
1720             (*action)(2, rargv, actarg);
1721             found++;
1722 ##      }
1723
1724         rargv[0] = "QUERY";
1725 ##      repeat retrieve (name = capacls.capability)
1726 ##              where capacls.list_id = @aid {
1727             (*action)(2, rargv, actarg);
1728             found++;
1729 ##      }
1730     } else if (!strcmp(atype, "USER")) {
1731         rargv[0] = "FILESYS";
1732 ##      repeat retrieve (name = filesys.label) 
1733 ##              where filesys.owner = @aid {
1734             (*action)(2, rargv, actarg);
1735             found++;
1736 ##      }
1737     }
1738
1739     rargv[0] = "LIST";
1740 ##  repeat retrieve (name = list.#name) 
1741 ##              where list.acl_type = @atype and list.acl_id = @aid {
1742          (*action)(2, rargv, actarg);
1743          found++;
1744 ##  }
1745
1746     rargv[0] = "SERVICE";
1747 ##  repeat retrieve (name = servers.#name) 
1748 ##              where servers.acl_type = @atype and servers.acl_id = @aid {
1749          (*action)(2, rargv, actarg);
1750          found++;
1751 ##  }
1752
1753     rargv[0] = "HOSTACCESS";
1754 ##  repeat retrieve (name = machine.#name)
1755 ##              where machine.mach_id = hostaccess.mach_id and 
1756 ##                   hostaccess.acl_type = @atype and hostaccess.acl_id = @aid {
1757         (*action)(2, rargv, actarg);
1758         found++;
1759 ##  }
1760     rargv[0] = "ZEPHYR";
1761 ##  repeat retrieve (name = zephyr.class) 
1762 ##              where zephyr.xmt_type = @atype and zephyr.xmt_id = @aid or
1763 ##                    zephyr.sub_type = @atype and zephyr.sub_id = @aid or
1764 ##                    zephyr.iws_type = @atype and zephyr.iws_id = @aid or
1765 ##                    zephyr.iui_type = @atype and zephyr.iui_id = @aid {
1766          (*action)(2, rargv, actarg);
1767          found++;
1768 ##  }
1769
1770     if (!found) return(SMS_NO_MATCH);
1771     return(SMS_SUCCESS);
1772 ##}
1773
1774
1775 /* get_lists_of_member - given a type and a name, return the name and flags
1776  * of all of the lists of the given member.  The member_type is one of
1777  * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0],
1778  * and argv[1] will contain the ID of the entity in question.  The R*
1779  * types mean to recursively look at every containing list, not just
1780  * when the object in question is a direct member.
1781  */
1782
1783 int get_lists_of_member(q, argv, cl, action, actarg)
1784     struct query *q;
1785     char *argv[];
1786     client *cl;
1787     int (*action)();
1788     int actarg;
1789 ##{
1790     int found = 0;
1791 ##  char *atype;
1792 ##  int aid, listid, id;
1793     struct save_queue *sq, *sq_create();
1794
1795     atype = argv[0];
1796     aid = *(int *)argv[1];
1797     if (!strcmp(atype, "LIST") ||
1798         !strcmp(atype, "USER") ||
1799         !strcmp(atype, "STRING")) {
1800       return(glom_internal(atype, aid, action, actarg));
1801     }
1802
1803     sq = sq_create();
1804     if (!strcmp(atype, "RLIST")) {
1805         sq_save_data(sq, aid);
1806         /* get all the list_id's of containing lists */
1807 ##      range of m is members
1808         while (sq_get_data(sq, &id)) {
1809 ##          repeat retrieve (listid = m.list_id)
1810 ##              where m.member_type = "LIST" and m.member_id = @id {
1811               sq_save_unique_data(sq, listid);
1812 ##          }
1813           }
1814         /* now process each one */
1815         while (sq_get_data(sq, &id)) {
1816             if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1817               found++;
1818         }
1819     }
1820
1821     if (!strcmp(atype, "RUSER")) {
1822 ##      range of m is members
1823 ##      repeat retrieve (listid = m.list_id)
1824 ##              where m.member_type = "USER" and m.member_id = @aid {
1825             sq_save_data(sq, listid);
1826 ##      }
1827         /* get all the list_id's of containing lists */
1828         while (sq_get_data(sq, &id)) {
1829 ##          repeat retrieve (listid = m.list_id)
1830 ##              where m.member_type = "LIST" and m.member_id = @id {
1831               sq_save_unique_data(sq, listid);
1832 ##          }
1833           }
1834         /* now process each one */
1835         while (sq_get_data(sq, &id)) {
1836             if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1837               found++;
1838         }
1839         if (glom_internal("USER", aid, action, actarg) == SMS_SUCCESS)
1840           found++;
1841     }
1842
1843     if (!strcmp(atype, "RSTRING")) {
1844 ##      range of m is members
1845 ##      repeat retrieve (listid = m.list_id)
1846 ##              where m.member_type = "STRING" and m.member_id = @aid {
1847             sq_save_data(sq, listid);
1848 ##      }
1849         /* get all the list_id's of containing lists */
1850         while (sq_get_data(sq, &id)) {
1851 ##          repeat retrieve (listid = m.list_id)
1852 ##              where m.member_type = "LIST" and m.member_id = @id {
1853               sq_save_unique_data(sq, listid);
1854 ##          }
1855           }
1856         /* now process each one */
1857         while (sq_get_data(sq, &id)) {
1858             if (glom_internal("LIST", id, action, actarg) == SMS_SUCCESS)
1859               found++;
1860         }
1861         if (glom_internal("STRING", aid, action, actarg) == SMS_SUCCESS)
1862           found++;
1863     }
1864
1865 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1866 ##      where tblstats.#table = "members"
1867     sq_destroy(sq);     
1868     if (!found) return(SMS_NO_MATCH);
1869     return(SMS_SUCCESS);
1870 ##}
1871
1872
1873 /* This looks up a single list, user, or string as a member.  atype must be
1874  * "USER", "LIST", or "STRING" and aid is the ID of the corresponding object.
1875  * This is used by get_lists_of_members above.
1876  */
1877
1878 ##glom_internal(atype, aid, action, actarg)
1879 ##  char *atype;
1880 ##  int aid;
1881     int (*action)();
1882     int actarg;
1883 ##{
1884     char *rargv[6];
1885     int found = 0;
1886 ##  char name[33], active[5], public[5], hidden[5], maillist[5], group[5];
1887
1888     rargv[0] = name;
1889     rargv[1] = active;
1890     rargv[2] = public;
1891     rargv[3] = hidden;
1892     rargv[4] = maillist;
1893     rargv[5] = group;
1894 ##  repeat retrieve (name = list.#name, active = text(list.#active), 
1895 ##                   public = text(list.#public), hidden = text(list.#hidden),
1896 ##                   maillist = text(list.#maillist), group = text(list.#group))
1897 ##              where list.list_id = m.list_id and
1898 ##                    m.member_type = @atype and m.member_id = @aid {
1899          (*action)(6, rargv, actarg);
1900          found++;
1901 ##  }
1902
1903     if (!found) return(SMS_NO_MATCH);
1904     return(SMS_SUCCESS);
1905 ##}
1906
1907
1908 /* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of
1909  * the five flags associated with each list.  It will return the name of
1910  * each list that meets the quailifications.  It does this by building a
1911  * where clause based on the arguments, then doing a retrieve.
1912  */
1913
1914 static char *lflags[5] = { "active", "public", "hidden", "maillist", "group" };
1915
1916 int qualified_get_lists(q, argv, cl, action, actarg)
1917     struct query *q;
1918     char *argv[];
1919     client *cl;
1920     int (*action)();
1921     int actarg;
1922 {
1923     return(qualified_get(q, argv, action, actarg, "l.list_id != 0",
1924                          "l", "name", lflags));
1925 }
1926
1927
1928 /** get_members_of_list - optimized query for retrieval of list members
1929  **
1930  ** Inputs:
1931  **   argv[0] - list_id
1932  **
1933  ** Description:
1934  **   - retrieve USER members, then LIST members, then STRING members
1935  **/
1936
1937 get_members_of_list(q, argv, cl, action, actarg)
1938     struct query *q;
1939     char *argv[];
1940     client *cl;
1941     int (*action)();
1942     int actarg;
1943 ##{
1944 ##  int list_id;
1945 ##  char member_name[129];
1946     char *targv[2];
1947
1948     list_id = *(int *)argv[0];
1949     targv[0] = "USER";
1950     targv[1] = member_name;
1951
1952 ##  range of m is members
1953 ##  repeat retrieve (member_name = users.login)
1954 ##             where m.#list_id = @list_id and m.member_type = "USER"
1955 ##                   and m.member_id = users.users_id
1956 ##             sort by #member_name
1957 ##  {
1958          (*action)(2, targv, actarg);
1959 ##  }
1960
1961     targv[0] = "LIST";
1962 ##  repeat retrieve (member_name = list.name)
1963 ##             where m.#list_id = @list_id and m.member_type = "LIST"
1964 ##                   and m.member_id = list.#list_id
1965 ##             sort by #member_name
1966 ##  {
1967          (*action)(2, targv, actarg);
1968 ##  }
1969
1970     targv[0] = "STRING";
1971 ##  repeat retrieve (member_name = strings.string)
1972 ##             where m.#list_id = @list_id and m.member_type = "STRING"
1973 ##                   and m.member_id = strings.string_id
1974 ##             sort by #member_name
1975 ##  {
1976          (*action)(2, targv, actarg);
1977 ##  }
1978
1979 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
1980 ##      where tblstats.#table = "members"
1981     return(SMS_SUCCESS);
1982 ##}
1983
1984
1985 /* count_members_of_list: this is a simple query, but it cannot be done
1986  * through the dispatch table.
1987  */
1988
1989 int count_members_of_list(q, argv, cl, action, actarg)
1990     struct query *q;
1991     char *argv[];
1992     client *cl;
1993     int (*action)();
1994     int actarg;
1995 ##{
1996 ##  int  list, ct = 0;
1997     char *rargv[1], countbuf[5];
1998
1999     list = *(int *)argv[0];
2000     rargv[0] = countbuf;
2001 ##  repeat retrieve (ct = count(members.list_id where members.list_id = @list))
2002     sprintf(countbuf, "%d", ct);
2003     (*action)(1, rargv, actarg);
2004 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
2005 ##      where tblstats.#table = "members"
2006     return(SMS_SUCCESS);
2007 ##}
2008
2009
2010 /* qualified_get_server: passed "TRUE", "FALSE", or "DONTCARE" for each of
2011  * the three flags associated with each service.  It will return the name of
2012  * each service that meets the quailifications.  It does this by building a
2013  * where clause based on the arguments, then doing a retrieve.
2014  */
2015
2016 static char *sflags[3] = { "enable", "inprogress", "harderror" };
2017
2018 int qualified_get_server(q, argv, cl, action, actarg)
2019     struct query *q;
2020     char *argv[];
2021     client *cl;
2022     int (*action)();
2023     int actarg;
2024 {
2025     return(qualified_get(q, argv, action, actarg, "s.name != \"\"",
2026                          "s", "name", sflags));
2027 }
2028
2029
2030 /* generic qualified get routine, used by qualified_get_lists,
2031  * qualified_get_server, and qualified_get_serverhost.
2032  *   Args:
2033  *      start - a simple where clause, must not be empty
2034  *      range - the name of the range variable
2035  *      field - the field to return
2036  *      flags - an array of strings, names of the flag variables
2037  */
2038
2039 int qualified_get(q, argv, action, actarg, start, range, field, flags)
2040     struct query *q;
2041     char *argv[];
2042     int (*action)();
2043     int actarg;
2044     char *start;
2045     char *range;
2046     char *field;
2047     char *flags[];
2048 ##{
2049 ##  char name[33], qual[256], *rvar, *rtbl, *rfield;
2050     char *rargv[1], buf[32];
2051 ##  int rowcount, i;
2052
2053     strcpy(qual, start);
2054     for (i = 0; i < q->argc; i++) {
2055         if (!strcmp(argv[i], "TRUE")) {
2056             sprintf(buf, " and %s.%s != 0", range, flags[i]);
2057             (void) strcat(qual, buf);
2058         } else if (!strcmp(argv[i], "FALSE")) {
2059             sprintf(buf, " and %s.%s = 0", range, flags[i]);
2060             (void) strcat(qual, buf);
2061         }
2062     }
2063       
2064     rargv[0] = name;
2065     rvar = range;
2066     rtbl = q->rtable;
2067     rfield = field;
2068 ##  range of rvar is rtbl
2069 ##  retrieve (name = rvar.rfield) where qual {
2070         (*action)(1, rargv, actarg);
2071 ##  }
2072 ##  inquire_equel(rowcount = "rowcount")
2073 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
2074 ##      where tblstats.#table = @rtbl
2075     if (rowcount == 0)
2076       return(SMS_NO_MATCH);
2077     return(SMS_SUCCESS);
2078 ##}
2079
2080
2081 /* qualified_get_serverhost: passed "TRUE", "FALSE", or "DONTCARE" for each of
2082  * the five flags associated with each serverhost.  It will return the name of
2083  * each service and host that meets the quailifications.  It does this by 
2084  * building a where clause based on the arguments, then doing a retrieve.
2085  */
2086
2087 static char *shflags[6] = { "service", "enable", "override", "success",
2088                             "inprogress", "hosterror" };
2089
2090 int qualified_get_serverhost(q, argv, cl, action, actarg)
2091     struct query *q;
2092     char *argv[];
2093     client *cl;
2094     int (*action)();
2095     int actarg;
2096 ##{
2097 ##  char sname[33], mname[33], qual[256];
2098     char *rargv[2], buf[32];
2099 ##  int rowcount, i;
2100
2101     sprintf(qual, "machine.mach_id = sh.mach_id and sh.service = uppercase(\"%s\")",
2102             argv[0]);
2103     for (i = 1; i < q->argc; i++) {
2104         if (!strcmp(argv[i], "TRUE")) {
2105             sprintf(buf, " and sh.%s != 0", shflags[i]);
2106             strcat(qual, buf);
2107         } else if (!strcmp(argv[i], "FALSE")) {
2108             sprintf(buf, " and sh.%s = 0", shflags[i]);
2109             strcat(qual, buf);
2110         }
2111     }
2112       
2113     rargv[0] = sname;
2114     rargv[1] = mname;
2115 ##  range of sh is serverhosts
2116 ##  retrieve (sname = sh.service, mname = machine.name) where qual {
2117         (*action)(2, rargv, actarg);
2118 ##  }
2119 ##  inquire_equel(rowcount = "rowcount")
2120 ##  repeat replace tblstats (retrieves = tblstats.retrieves + 1)
2121 ##      where tblstats.#table = "serverhosts"
2122     if (rowcount == 0)
2123       return(SMS_NO_MATCH);
2124     return(SMS_SUCCESS);
2125 ##}
2126
2127
2128 /* register_user - change user's login name and allocate a pobox, group,
2129  * filesystem, and quota for them.  The user's status must start out as 0,
2130  * and is left as 2.  Arguments are: user's UID, new login name, and user's
2131  * type for filesystem allocation (SMS_FS_STUDENT, SMS_FS_FACULTY, 
2132  * SMS_FS_STAFF, SMS_FS_MISC).
2133  */
2134
2135 register_user(q, argv, cl)
2136     struct query *q;
2137     char **argv;
2138     client *cl;
2139 ##{
2140 ##  char *login, dir[65], *entity, *directory, machname[33];
2141 ##  int who, rowcount, mid, uid, users_id, flag, utype, nid, list_id, quota;
2142 ##  int size, alloc, pid, m_id;
2143     int maxsize;
2144
2145     entity = cl->entity;
2146     who = cl->users_id;
2147
2148     uid = atoi(argv[0]);
2149     login = argv[1];
2150     utype = atoi(argv[2]);
2151
2152 ##  range of u is users
2153 ##  range of l is list
2154 ##  range of sh is serverhosts
2155 ##  range of n is nfsphys
2156 ##  range of m is machine
2157
2158     /* find user */
2159 ##  repeat retrieve (users_id = u.#users_id)
2160 ##      where u.#uid = @uid and u.status = 0
2161 ##  inquire_equel(rowcount = "rowcount");
2162     if (rowcount == 0)
2163       return(SMS_NO_MATCH);
2164     if (rowcount > 1)
2165       return(SMS_NOT_UNIQUE);
2166
2167     /* check new login name */
2168 ##  repeat retrieve (flag = any(u.#login where u.#login = @login))
2169     if (flag)
2170       return(SMS_IN_USE);
2171 ##  repeat retrieve (flag = any(l.#name where l.#name = @login))
2172     if (flag)
2173       return(SMS_IN_USE);
2174 ##  repeat retrieve (flag = any(filesys.#name where filesys.#name = @login))
2175     if (flag)
2176       return(SMS_IN_USE);
2177     com_err(whoami, 0, "new login name OK");
2178
2179     /* choose place for pobox, put in mid */
2180 ##  repeat retrieve (mid = sh.mach_id, machname = m.name)
2181 ##    where sh.service = "POP" and m.mach_id = sh.mach_id and
2182 ##      sh.value2 - sh.value1 = max(sh.value2-sh.value1 where sh.service="POP")
2183 ##  inquire_equel(rowcount = "rowcount");
2184     if (rowcount == 0)
2185       return(SMS_NO_POBOX);
2186
2187     /* change login name, set pobox */
2188 ##  repeat replace u (#login = @login, status = 2, modtime = "now",
2189 ##                    modby = @who, modwith = @entity, potype="POP",
2190 ##                    pop_id = @mid, pmodtime="now", pmodby=@who,
2191 ##                    pmodwith=@entity)
2192 ##      where u.#users_id = @users_id
2193 ##  inquire_equel(rowcount = "rowcount");
2194     if (rowcount != 1)
2195       return(SMS_INTERNAL);
2196     set_pop_usage(mid, 1);
2197     com_err(whoami, 0, "set login name to %s and pobox to %s", login,
2198             trim(machname));
2199
2200     /* create group list */
2201     if (set_next_object_id("gid", "list"))
2202       return(SMS_NO_ID);
2203     if (set_next_object_id("list_id", "list"))
2204       return(SMS_NO_ID);
2205 ##  repeat retrieve (list_id = values.value) where values.name = "list_id"
2206 ##  inquire_equel(rowcount = "rowcount");
2207     if (rowcount != 1)
2208       return(SMS_INTERNAL);
2209 ##  repeat append list (name = @login, #list_id = @list_id, active = 1,
2210 ##                      public = 0, hidden = 0, maillist = 0, group = 1,
2211 ##                      #gid = values.value, desc = "User Group",
2212 ##                      acl_type = "USER", acl_id = @users_id, modtime = "now",
2213 ##                      modby = @who, modwith = @entity)
2214 ##      where values.name = "gid"
2215 ##  inquire_equel(rowcount = "rowcount");
2216     if (rowcount != 1)
2217       return(SMS_INTERNAL);
2218 ##  repeat append members (#list_id = @list_id, member_type = "USER",
2219 ##                         member_id = @users_id)
2220 ##  inquire_equel(rowcount = "rowcount");
2221     if (rowcount != 1)
2222       return(SMS_INTERNAL);
2223     com_err(whoami, 0, "group list created");
2224
2225     /* decide where to put filesystem */
2226     maxsize = 0;
2227     directory = NULL;
2228 ##  repeat retrieve (mid = n.mach_id, dir = trim(n.#dir), nid = n.nfsphys_id,
2229 ##                   flag = n.status, size = n.#size, alloc = n.allocated) {
2230         if ((flag & utype) && (size != 0) && (size - alloc > maxsize)) {
2231             maxsize = size - alloc;
2232             if (directory)
2233               free(directory);
2234             directory = strsave(dir);
2235             pid = nid;
2236             m_id = mid;
2237         }
2238 ##  }
2239     if (maxsize == 0)
2240       return(SMS_NO_FILESYS);
2241
2242     /* create filesystem */
2243     if (set_next_object_id("filsys_id", "filesys"))
2244       return(SMS_NO_ID);
2245 ##  repeat append filesys (filsys_id = values.value, phys_id = @pid,
2246 ##                         label = @login, type = "NFS", mach_id = @m_id,
2247 ##                         name = @directory + "/" + @login,
2248 ##                         mount = "/mit/" + @login,
2249 ##                         access = "w", comments = "User Locker",
2250 ##                         owner = @users_id, owners = @list_id, createflg = 1,
2251 ##                         lockertype = "HOMEDIR", modtime = "now",
2252 ##                         modby = @who, modwith = @entity)
2253 ##      where values.name = "filsys_id"
2254 ##  inquire_equel(rowcount = "rowcount");
2255     if (rowcount != 1)
2256       return(SMS_INTERNAL);
2257     com_err(whoami, 0, "filesys created on mach %d in %s/%s", m_id,
2258             directory, login);
2259
2260     /* set quota */
2261 ##  repeat retrieve (quota = values.value) where values.name = "def_quota"
2262 ##  inquire_equel(rowcount = "rowcount");
2263     if (rowcount != 1)
2264       return(SMS_NO_QUOTA);
2265 ##  repeat append nfsquota (#users_id = @users_id, filsys_id = values.value,
2266 ##                          #quota = @quota, phys_id = @pid, modtime = "now",
2267 ##                          modby = @who, modwith = @entity)
2268 ##      where values.name = "filsys_id"
2269 ##  inquire_equel(rowcount = "rowcount");
2270     if (rowcount != 1)
2271       return(SMS_INTERNAL);
2272 ##  repeat replace nfsphys (allocated = nfsphys.allocated + @quota)
2273 ##      where nfsphys.nfsphys_id = filesys.#phys_id and
2274 ##            filesys.filsys_id = values.value and values.name = "filsys_id"
2275 ##  inquire_equel(rowcount = "rowcount");
2276     if (rowcount != 1)
2277       return(SMS_INTERNAL);
2278     com_err(whoami, 0, "quota of %d assigned", quota);
2279
2280 ##  repeat replace tblstats (updates = tblstats.updates + 1, modtime = "now")
2281 ##      where tblstats.table = "users"
2282 ##  repeat replace tblstats (appends = tblstats.appends + 1, modtime = "now")
2283 ##      where tblstats.table = "list" or tblstats.table = "filesys" or
2284 ##            tblstats.table = "nfsquota"
2285     return(SMS_SUCCESS);
2286 ##}
2287
2288
2289
2290 /** set_pop_usage - incr/decr usage count for pop server in serverhosts talbe
2291  **
2292  ** Inputs:
2293  **   id of machine
2294  **   delta (will be +/- 1)
2295  **
2296  ** Description:
2297  **   - incr/decr value field in serverhosts table for pop/mach_id
2298  **
2299  **/
2300
2301 static int set_pop_usage(id, count)
2302 int id;
2303 int count;
2304 ##{
2305 ##  int mach_id = id;
2306 ##  int n = count;
2307
2308 ##  range of sh is serverhosts
2309 ##  repeat replace sh (value1 = sh.value1 + @n)
2310 ##         where sh.service = "POP" and sh.#mach_id = @mach_id
2311
2312     return(SMS_SUCCESS);
2313 ##}
2314
2315
2316 \f
2317 /* Validation Routines */
2318
2319 validate_row(q, argv, v)
2320     register struct query *q;
2321     char *argv[];
2322     register struct validate *v;
2323 ##{
2324 ##  char *rvar;
2325 ##  char *table;
2326 ##  char *name;
2327 ##  char qual[128];
2328 ##  int rowcount;
2329
2330     /* build where clause */
2331     build_qual(v->qual, v->argc, argv, qual);
2332
2333     /* setup ingres variables */
2334     rvar = q->rvar;
2335     table = q->rtable;
2336     name = v->field;
2337
2338     if (log_flags & LOG_VALID)
2339         /* tell the logfile what we're doing */
2340         com_err(whoami, 0, "validating row: %s", qual);
2341     
2342     /* look for the record */
2343 ##  range of rvar is table
2344 ##  retrieve (rowcount = count(rvar.name where qual))
2345     if (rowcount == 0) return(SMS_NO_MATCH);
2346     if (rowcount > 1) return(SMS_NOT_UNIQUE);
2347     return(SMS_EXISTS);
2348 ##}
2349
2350 validate_fields(q, argv, vo, n)
2351     struct query *q;
2352     register char *argv[];
2353     register struct valobj *vo;
2354     register int n;
2355 {
2356     register int status;
2357
2358     while (--n >= 0) {
2359         switch (vo->type) {
2360         case V_NAME:
2361             if (log_flags & LOG_VALID)
2362                 com_err(whoami, 0, "validating %s in %s: %s", 
2363                     vo->namefield, vo->table, argv[vo->index]);
2364             status = validate_name(argv, vo);
2365             break;
2366
2367         case V_ID:
2368             if (log_flags & LOG_VALID)
2369                 com_err(whoami, 0, "validating %s in %s: %s", 
2370                     vo->idfield, vo->table, argv[vo->index]);
2371             status = validate_id(argv, vo);
2372             break;
2373
2374         case V_DATE:
2375             if (log_flags & LOG_VALID)
2376                 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
2377             status = validate_date(argv, vo);
2378             break;
2379
2380         case V_TYPE:
2381             if (log_flags & LOG_VALID)
2382                 com_err(whoami, 0, "validating %s type: %s",
2383                     vo->table, argv[vo->index]);
2384             status = validate_type(argv, vo);
2385             break;
2386
2387         case V_TYPEDATA:
2388             if (log_flags & LOG_VALID)
2389                 com_err(whoami, 0, "validating typed data (%s): %s",
2390                     argv[vo->index - 1], argv[vo->index]);
2391             status = validate_typedata(q, argv, vo);
2392             break;
2393
2394         case V_RENAME:
2395             if (log_flags & LOG_VALID)
2396                 com_err(whoami, 0, "validating rename %s in %s",
2397                         argv[vo->index], vo->table);
2398             status = validate_rename(argv, vo);
2399             break;
2400
2401         case V_CHAR:
2402             if (log_flags & LOG_VALID)
2403               com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
2404             status = validate_chars(argv[vo->index]);
2405             break;
2406
2407         case V_SORT:
2408             status = SMS_EXISTS;
2409             break;
2410
2411         }
2412
2413         if (status != SMS_EXISTS) return(status);
2414         vo++;
2415     }
2416
2417     return(SMS_SUCCESS);
2418 }
2419
2420
2421 /* validate_chars: verify that there are no illegal characters in
2422  * the string.  Legal characters are printing chars other than 
2423  * ", *, ?, \, [ and ].
2424  */
2425 static int illegalchars[] = {
2426     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
2427     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
2428     0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
2429     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
2430     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
2431     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
2432     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
2433     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
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     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2442 };
2443
2444 validate_chars(s)
2445 register char *s;
2446 {
2447     while (*s)
2448       if (illegalchars[*s++])
2449         return(SMS_BAD_CHAR);
2450     return(SMS_EXISTS);
2451 }
2452
2453
2454 validate_id(argv, vo)
2455     char *argv[];
2456     register struct valobj *vo;
2457 ##{
2458 ##  char *name;
2459 ##  char *table;
2460 ##  char *namefield;
2461 ##  char *idfield;
2462 ##  int id;
2463 ##  int rowcount;
2464     register char *c;
2465
2466     name = argv[vo->index];
2467     table = vo->table;
2468     /* minor kludge to upcasify machine names */
2469     if (!strcmp(table, "machine"))
2470         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
2471     namefield = vo->namefield;
2472     idfield = vo->idfield;
2473     if (!strcmp(namefield, "uid")) {
2474 ##    retrieve (id = table.idfield) where table.namefield = int4(name)
2475 ##    inquire_equel (rowcount = "rowcount")
2476     } else {
2477 ##    retrieve (id = table.idfield) where table.namefield = name
2478 ##    inquire_equel (rowcount = "rowcount")
2479     }
2480     if (rowcount != 1) return(vo->error);
2481     *(int *)argv[vo->index] = id;
2482     return(SMS_EXISTS);
2483 ##}
2484
2485 validate_name(argv, vo)
2486     char *argv[];
2487     register struct valobj *vo;
2488 ##{
2489 ##  char *name;
2490 ##  char *table;
2491 ##  char *namefield;
2492 ##  int rowcount;
2493     register char *c;
2494
2495     name = argv[vo->index];
2496     table = vo->table;
2497     namefield = vo->namefield;
2498     if (!strcmp(table, "servers") && !strcmp(namefield, "name")) {
2499         for (c = name; *c; c++)
2500           if (islower(*c))
2501             *c = toupper(*c);
2502     }
2503 ##  retrieve (rowcount = countu(table.namefield 
2504 ##            where table.namefield = name))
2505     return ((rowcount == 1) ? SMS_EXISTS : vo->error);
2506 ##}
2507
2508 validate_date(argv, vo)
2509     char *argv[];
2510     struct valobj *vo;
2511 ##{
2512 ##  char *idate;
2513 ##  double dd;
2514 ##  int errorno;
2515
2516     idate = argv[vo->index];
2517
2518 ##  retrieve (dd = interval("years", date(idate) - date("today")))
2519 ##  inquire_equel (errorno = "errorno")
2520     if (errorno != 0 || dd > 5.0) return(SMS_DATE);
2521     return(SMS_EXISTS);
2522 ##}
2523
2524
2525 validate_rename(argv, vo)
2526 char *argv[];
2527 struct valobj *vo;
2528 ##{
2529 ##  char *name, *table, *namefield, *idfield;
2530 ##  int id;
2531     register char *c;
2532
2533     c = name = argv[vo->index];
2534     while (*c)
2535       if (illegalchars[*c++])
2536         return(SMS_BAD_CHAR);
2537     table = vo->table;
2538     /* minor kludge to upcasify machine names */
2539     if (!strcmp(table, "machine"))
2540         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
2541     namefield = vo->namefield;
2542     idfield = vo->idfield;
2543     id = -1;
2544     if (idfield == 0) {
2545         if (!strcmp(argv[vo->index], argv[vo->index - 1]))
2546           return(SMS_EXISTS);
2547 ##      retrieve (id = any(table.namefield where table.namefield = name))
2548         if (id)
2549           return(vo->error);
2550         else
2551           return(SMS_EXISTS);
2552     }
2553 ##  retrieve (id = table.idfield) where table.namefield = name
2554     if (id == -1 || id == *(int *)argv[vo->index - 1])
2555       return(SMS_EXISTS);
2556     else
2557       return(vo->error);
2558 ##}
2559
2560
2561 validate_type(argv, vo)
2562     char *argv[];
2563     register struct valobj *vo;
2564 ##{
2565 ##  char *typename;
2566 ##  char *value;
2567 ##  int exists;
2568     register char *c;
2569
2570     typename = vo->table;
2571     c = value = argv[vo->index];
2572     while (*c)
2573       if (illegalchars[*c++])
2574         return(SMS_BAD_CHAR);
2575
2576     /* uppercase type fields */
2577     for (c = value; *c; c++) if (islower(*c)) *c = toupper(*c);
2578
2579 ##  range of a is alias
2580 ##  repeat retrieve (exists = any(a.trans where a.name = @typename and
2581 ##                                    a.type = "TYPE" and
2582 ##                                    a.trans = @value))
2583     return (exists ? SMS_EXISTS : vo->error);
2584 ##}
2585
2586 /* validate member or type-specific data field */
2587
2588 validate_typedata(q, argv, vo)
2589     register struct query *q;
2590     register char *argv[];
2591     register struct valobj *vo;
2592 ##{
2593 ##  char *name;
2594 ##  char *field_type;
2595 ##  char data_type[129];
2596 ##  int id;
2597 ##  int rowcount;
2598     char *index();
2599     register char *c;
2600
2601     /* get named object */
2602     name = argv[vo->index];
2603
2604     /* get field type string (known to be at index-1) */
2605     field_type = argv[vo->index-1];
2606
2607     /* get corresponding data type associated with field type name */
2608 ##  repeat retrieve (data_type = alias.trans) 
2609 ##         where alias.#name = @field_type and alias.type = "TYPEDATA"
2610 ##  inquire_equel (rowcount = "rowcount")
2611     if (rowcount != 1) return(SMS_TYPE);
2612
2613     /* now retrieve the record id corresponding to the named object */
2614     if (index(data_type, ' '))
2615         *index(data_type, ' ') = 0;
2616     if (!strcmp(data_type, "user")) {
2617         /* USER */
2618 ##      repeat retrieve (id = users.users_id) where users.login = @name
2619 ##      inquire_equel (rowcount = "rowcount")
2620         if (rowcount != 1) return(SMS_USER);
2621
2622     } else if (!strcmp(data_type, "list")) {
2623         /* LIST */
2624 ##      repeat retrieve (id = list.list_id) where list.#name = @name
2625 ##      inquire_equel (rowcount = "rowcount")
2626         if (rowcount != 1) {
2627             /* if idfield is non-zero, then if argv[0] matches the string
2628              * that we're trying to resolve, we should get the value of
2629              * values.[idfield] for the id.
2630              */
2631             if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
2632                 set_next_object_id(q->validate->object_id, q->rtable);
2633                 name = vo->idfield;
2634 ##              repeat retrieve (id = values.value) where values.#name = @name
2635 ##              inquire_equel(rowcount = "rowcount")
2636                 if (rowcount != 1) return(SMS_LIST);
2637             } else
2638               return(SMS_LIST);
2639         }
2640     } else if (!strcmp(data_type, "machine")) {
2641         /* MACHINE */
2642         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
2643 ##      repeat retrieve (id = machine.mach_id) where machine.#name = @name
2644 ##      inquire_equel (rowcount = "rowcount")
2645         if (rowcount != 1) return(SMS_MACHINE);
2646
2647     } else if (!strcmp(data_type, "string")) {
2648         /* STRING */
2649 ##      range of s is strings
2650 ##      repeat retrieve (id = s.string_id) where s.string = @name
2651 ##      inquire_equel (rowcount = "rowcount")
2652         if (rowcount == 0) {
2653             if (q->type != APPEND) return(SMS_STRING);
2654 ##          range of v is values
2655 ##          retrieve (id = v.value) where v.#name = "strings_id"
2656             id++;
2657 ##          replace v (value = id) where v.#name = "strings_id"
2658 ##          append to strings (string_id = id, string = name)
2659         }
2660     } else if (!strcmp(data_type, "none")) {
2661         id = 0;
2662     } else {
2663         return(SMS_TYPE);
2664     }
2665
2666     /* now set value in argv */
2667     *(int *)argv[vo->index] = id;
2668     
2669     return (SMS_EXISTS);
2670 ##}
2671
2672
2673 /* This looks up a login name and returns the SMS internal ID.  It is used
2674  * by authenticate to put the users_id in the client structure.
2675  */
2676
2677 int get_users_id(name)
2678 char *name;
2679 ##{
2680 ##  int id, rowcount;
2681 ##  char *login;
2682
2683     login = name;
2684
2685 ##  range of u is users
2686 ##  repeat retrieve (id = u.#users_id) where u.#login = @login
2687 ##  inquire_equel (rowcount = "rowcount")
2688     
2689     if (rowcount == 1)
2690         return(id);
2691     else
2692         return(0);
2693 ##}
2694
2695
2696 /* Check the database at startup time.  For now this just resets the
2697  * inprogress flags that the DCM uses.
2698  */
2699
2700 sanity_check_database()
2701 ##{
2702 ##}
This page took 3.840759 seconds and 5 git commands to generate.