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