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