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