]> andersk Git - moira.git/blob - server/qsupport.dc
Removed extraneous newlines
[moira.git] / server / qsupport.dc
1 /* #define GDSS */
2 /*
3  *      $Source$
4  *      $Author$
5  *      $Header$
6  *
7  *      Copyright (C) 1987 by the Massachusetts Institute of Technology
8  *      For copying and distribution information, please see the file
9  *      <mit-copyright.h>.
10  *
11  */
12
13 #ifndef lint
14 static char *rcsid_qsupport_dc = "$Header$";
15 #endif lint
16
17 #include <mit-copyright.h>
18 #include "query.h"
19 #include "mr_server.h"
20 #include <ctype.h>
21 #ifdef GDSS
22 #include "gdss.h"
23 #endif /* GDSS */
24 EXEC SQL INCLUDE sqlca;
25 EXEC SQL INCLUDE sqlda;
26 #include "qrtn.h"
27
28 extern char *whoami, *strsave();
29 extern int ingres_errno, mr_errcode;
30
31 EXEC SQL BEGIN DECLARE SECTION; 
32 int idummy;                            
33 extern char cdummy[];
34 extern char stmt_buf[];
35 EXEC SQL END DECLARE SECTION; 
36
37 /* Specialized Access Routines */
38
39 /* access_user - verify that client name equals specified login name
40  *
41  *  - since field validation routines are called first, a users_id is
42  *    now in argv[0] instead of the login name.
43  */
44
45 EXEC SQL WHENEVER SQLERROR CALL ingerr;
46
47 access_user(q, argv, cl)
48     struct query *q;
49     char *argv[];
50     client *cl;
51 {
52     if (cl->users_id != *(int *)argv[0])
53         return(MR_PERM);
54     else
55         return(MR_SUCCESS);
56 }
57
58     
59
60 /* access_login - verify that client name equals specified login name
61  *
62  *   argv[0...n] contain search info.  q->
63  */
64
65 access_login(q, argv, cl)
66     struct query *q;
67     char *argv[];
68     client *cl;
69 {
70     EXEC SQL BEGIN DECLARE SECTION; 
71     int id;
72     char qual[256];
73     EXEC SQL END DECLARE SECTION; 
74
75     build_qual(q->qual, q->argc, argv, qual);
76     EXEC SQL SELECT users_id INTO :id FROM users WHERE :qual;
77
78     if (sqlca.sqlerrd[2] != 1 || id != cl->users_id)
79         return(MR_PERM);
80     else
81         return(MR_SUCCESS);
82 }
83
84     
85
86 /* access_list - check access for most list operations
87  *
88  * Inputs: argv[0] - list_id
89  *          q - query name
90  *          argv[2] - member ID (only for queries "amtl" and  "dmfl")
91  *          argv[7] - group IID (only for query "ulis")
92  *          cl - client name
93  *
94  * - check that client is a member of the access control list
95  * - OR, if the query is add_member_to_list or delete_member_from_list
96  *      and the list is public, allow access if client = member
97  */ 
98
99 access_list(q, argv, cl)
100     struct query *q;
101     char *argv[];
102     client *cl;
103 {
104     EXEC SQL BEGIN DECLARE SECTION; 
105     int list_id, acl_id, flags, gid;
106     char acl_type[9];
107     EXEC SQL END DECLARE SECTION; 
108     char *client_type;
109     int client_id, status;
110
111     list_id = *(int *)argv[0];
112     EXEC SQL SELECT acl_id, acl_type, gid, publicflg 
113       INTO :acl_id, :acl_type, :gid, :flags
114       FROM list
115       WHERE list_id = :list_id;
116
117     if (sqlca.sqlerrd[2] != 1)
118       return(MR_INTERNAL);
119
120     /* parse client structure */
121     if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
122         return(status);
123
124     /* if amtl or dmfl and list is public allow client to add or delete self */
125     if (((!strcmp("amtl", q->shortname) && flags) ||
126          (!strcmp("dmfl", q->shortname))) &&
127         (!strcmp("USER", argv[1]))) {
128         if (*(int *)argv[2] == client_id) return(MR_SUCCESS);
129     /* if update_list, don't allow them to change the GID */
130     } else if (!strcmp("ulis", q->shortname)) {
131         if ((!strcmp(argv[7], UNIQUE_GID) && (gid != -1)) ||
132             (strcmp(argv[7], UNIQUE_GID) && (gid != atoi(argv[7]))))
133           return(MR_PERM);
134     }
135
136     /* check for client in access control list */
137     status = find_member(acl_type, acl_id, client_type, client_id, 0);
138     if (!status) return(MR_PERM);
139
140     return(MR_SUCCESS);
141 }
142
143
144 /* access_visible_list - allow access to list only if it is not hidden,
145  *      or if the client is on the ACL
146  *
147  * Inputs: argv[0] - list_id
148  *         cl - client identifier
149  */ 
150
151 access_visible_list(q, argv, cl)
152     struct query *q;
153     char *argv[];
154     client *cl;
155 {
156     EXEC SQL BEGIN DECLARE SECTION; 
157     int list_id, acl_id, flags ;
158     char acl_type[9];
159     EXEC SQL END DECLARE SECTION; 
160     char *client_type;
161     int client_id, status;
162
163     list_id = *(int *)argv[0];
164     EXEC SQL SELECT hidden, acl_id, acl_type 
165       INTO :flags, :acl_id, :acl_type
166       FROM list
167       WHERE list_id = :list_id;
168     if (sqlca.sqlerrd[2] != 1)
169       return(MR_INTERNAL);
170     if (!flags)
171         return(MR_SUCCESS);
172
173     /* parse client structure */
174     if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
175         return(status);
176
177     /* check for client in access control list */
178     status = find_member(acl_type, acl_id, client_type, client_id, 0);
179     if (!status)
180         return(MR_PERM);
181
182     return(MR_SUCCESS);
183 }
184
185
186 /* access_vis_list_by_name - allow access to list only if it is not hidden,
187  *      or if the client is on the ACL
188  *
189  * Inputs: argv[0] - list name
190  *         cl - client identifier
191  */ 
192
193 access_vis_list_by_name(q, argv, cl)
194     struct query *q;
195     char *argv[];
196     client *cl;
197 {
198     EXEC SQL BEGIN DECLARE SECTION; 
199     int acl_id, flags, rowcount;
200     char acl_type[9], *listname;
201     EXEC SQL END DECLARE SECTION; 
202     char *client_type;
203     int client_id, status;
204
205     listname = argv[0];
206     EXEC SQL SELECT hidden, acl_id, acl_type INTO :flags, :acl_id, :acl_type
207       FROM list WHERE name = :listname;
208
209     rowcount=sqlca.sqlerrd[2];
210     if (rowcount > 1)
211       return(MR_WILDCARD);
212     if (rowcount == 0)
213       return(MR_NO_MATCH);
214     if (!flags)
215         return(MR_SUCCESS);
216
217     /* parse client structure */
218     if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
219         return(status);
220
221     /* check for client in access control list */
222     status = find_member(acl_type, acl_id, client_type, client_id, 0);
223     if (!status)
224         return(MR_PERM);
225
226     return(MR_SUCCESS);
227 }
228
229
230 /* access_member - allow user to access member of type "USER" and name matches
231  * username, or to access member of type "LIST" and list is one that user is
232  * on the acl of, or the list is visible.
233  */
234
235 access_member(q, argv, cl)
236     struct query *q;
237     char *argv[];
238     client *cl;
239 {
240     if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
241       return(access_visible_list(q, &argv[1], cl));
242
243     if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER")) {
244         if (cl->users_id == *(int *)argv[1])
245           return(MR_SUCCESS);
246     }
247
248     return(MR_PERM);
249 }
250
251
252 /* access_qgli - special access routine for Qualified_get_lists.  Allows
253  * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
254  */
255
256 access_qgli(q, argv, cl)
257     struct query *q;
258     char *argv[];
259     client *cl;
260 {
261     if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
262       return(MR_SUCCESS);
263     return(MR_PERM);
264 }
265
266
267 /* access_service - allow access if user is on ACL of service.  Don't
268  * allow access if a wildcard is used.
269  */
270
271 access_service(q, argv, cl)
272     struct query *q;
273     char *argv[];
274     client *cl;
275 {
276     EXEC SQL BEGIN DECLARE SECTION; 
277     int acl_id;
278     char *name, acl_type[9];
279     EXEC SQL END DECLARE SECTION; 
280     int client_id, status;
281     char *client_type, *c;
282
283     name = argv[0]; 
284     for(c=name;*c;c++) if(islower(*c)) *c = toupper(*c);  /* uppercasify */
285     EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers
286       WHERE name = :name;
287     if (sqlca.sqlerrd[2] > 1)
288       return(MR_PERM);
289
290     /* parse client structure */
291     if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
292         return(status);
293
294     /* check for client in access control list */
295     status = find_member(acl_type, acl_id, client_type, client_id, 0);
296     if (!status) return(MR_PERM);
297
298     return(MR_SUCCESS);
299 }
300
301
302 /* access_filesys - verify that client is owner or on owners list of filesystem
303  *      named by argv[0]
304  */
305
306 access_filesys(q, argv, cl)
307     struct query *q;
308     char *argv[];
309     client *cl;
310 {
311     EXEC SQL BEGIN DECLARE SECTION; 
312     int users_id, list_id;
313     char *name;
314     EXEC SQL END DECLARE SECTION; 
315     int status, client_id;
316     char *client_type;
317
318     name = argv[0];   
319     EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys
320       WHERE label = :name;
321
322     if (sqlca.sqlerrd[2] != 1)
323       return(MR_PERM);
324     if (users_id == cl->users_id)
325       return(MR_SUCCESS);
326     if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
327       return(status);
328     status = find_member("LIST", list_id, client_type, client_id, 0);
329     if (status)
330       return(MR_SUCCESS);
331     else
332       return(MR_PERM);
333 }
334
335     
336 \f
337 /* Setup Routines */
338
339 /* Setup routine for add_user
340  *
341  * Inputs: argv[0] - login
342  *         argv[1] - uid
343  *
344  * Description:
345  *
346  * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid)
347  * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#<uid>"
348  */
349
350 setup_ausr(q, argv, cl)
351     struct query *q;
352     register char *argv[];
353     client *cl;
354 {
355     int row;
356     EXEC SQL BEGIN DECLARE SECTION; 
357     int nuid;
358     EXEC SQL END DECLARE SECTION; 
359
360     if (!strcmp(q->shortname, "uusr") || !strcmp(q->shortname, "uuac"))
361       row = 2;
362     else
363       row = 1;
364     if (!strcmp(argv[row], UNIQUE_UID) || atoi(argv[row]) == -1) {
365         if (set_next_object_id("uid", "users", 1))
366           return(MR_INGRES_ERR);
367         EXEC SQL SELECT value INTO :nuid FROM numvalues WHERE name = 'uid';
368         if (sqlca.sqlerrd[2] != 1)
369           return(MR_INTERNAL);
370         sprintf(argv[row], "%d", nuid);
371     }
372
373     if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[row]) == -1) {
374         sprintf(argv[0], "#%s", argv[row]);
375     }
376
377     if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
378       return(mr_errcode);
379
380     return(MR_SUCCESS);
381 }
382
383
384 /* setup_dusr - verify that the user is no longer being referenced 
385  * and may safely be deleted.
386  */
387
388 int setup_dusr(q, argv)
389     struct query *q;
390     char **argv;
391 {
392     EXEC SQL BEGIN DECLARE SECTION; 
393     int flag, id;
394     EXEC SQL END DECLARE SECTION; 
395
396     id = *(int *)argv[0];
397
398     /* For now, only allow users to be deleted if their status is 0 */
399     EXEC SQL REPEATED SELECT status INTO :flag FROM users 
400       WHERE users_id = :id;
401     if (flag != 0 && flag != 4)
402       return(MR_IN_USE);
403
404     EXEC SQL REPEATED DELETE FROM quota WHERE entity_id = :id AND type = 'USER';
405     EXEC SQL REPEATED DELETE FROM krbmap WHERE users_id = :id;
406     EXEC SQL REPEATED SELECT member_id INTO :idummy FROM imembers
407       WHERE member_id = :id AND member_type = 'USER';
408     if (sqlca.sqlerrd[2] > 0)
409         return(MR_IN_USE);
410     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys WHERE owner = :id;
411     if (sqlca.sqlerrd[2]> 0)
412         return(MR_IN_USE);
413     EXEC SQL REPEATED SELECT name INTO :cdummy FROM list
414       WHERE acl_id = :id AND acl_type = 'USER';
415     if (sqlca.sqlerrd[2] > 0)
416         return(MR_IN_USE);
417     EXEC SQL REPEATED SELECT name INTO :cdummy FROM servers
418       WHERE acl_id = :id AND acl_type = 'USER';
419     if (sqlca.sqlerrd[2] > 0)
420         return(MR_IN_USE);
421     EXEC SQL REPEATED SELECT acl_id INTO :idummy FROM hostaccess
422       WHERE acl_id = :id AND acl_type = 'USER';
423     if (sqlca.sqlerrd[2] > 0)
424         return(MR_IN_USE);
425     if (ingres_errno)
426         return(mr_errcode);
427     return(MR_SUCCESS);
428 }
429
430
431 /* setup_spop: verify that there is already a valid POP machine_id in the
432  * pop_id field.  Also take care of keeping track of the post office usage.
433  */
434 int setup_spop(q, argv)
435 struct query *q;
436 char **argv;
437 {
438     EXEC SQL BEGIN DECLARE SECTION; 
439     int id, mid, flag;
440     char type[9];
441     EXEC SQL END DECLARE SECTION; 
442
443     id = *(int *)argv[0]; 
444     EXEC SQL REPEATED SELECT potype, pop_id INTO :type, :mid FROM users
445       WHERE users_id = :id;
446     if(sqlca.sqlerrd[2] = 0)
447       return(MR_MACHINE);
448     EXEC SQL REPEATED SELECT mach_id INTO :mid FROM machine 
449       WHERE mach_id = :mid;
450     if (sqlca.sqlerrd[2] = 0)
451       return(MR_MACHINE);
452     if (strcmp(strtrim(type), "POP"))
453       set_pop_usage(mid, 1);
454     return(MR_SUCCESS);
455 }
456
457
458 /* setup_dpob:  Take care of keeping track of the post office usage.
459  */
460 int setup_dpob(q, argv)
461      struct query *q;
462      char **argv;
463 {
464     EXEC SQL BEGIN DECLARE SECTION; 
465     int id, user;
466     char type[9];
467     EXEC SQL END DECLARE SECTION; 
468
469     user = *(int *)argv[0];
470     EXEC SQL REPEATED SELECT potype, pop_id INTO :type, :id FROM users
471       WHERE users_id = :user;
472     if (ingres_errno) return(mr_errcode);
473
474     if (!strcmp(strtrim(type), "POP"))
475       set_pop_usage(id, -1);
476     return(MR_SUCCESS);
477 }
478
479
480 /* setup_dmac - verify that the machine is no longer being referenced 
481  * and may safely be deleted.
482  */
483
484 int setup_dmac(q, argv)
485     struct query *q;
486     char **argv;
487 {
488     EXEC SQL BEGIN DECLARE SECTION; 
489     int flag, id;
490     EXEC SQL END DECLARE SECTION;
491
492     id = *(int *)argv[0];
493     EXEC SQL REPEATED SELECT login INTO :cdummy FROM users
494       WHERE potype='POP' AND pop_id = :id;
495     if (sqlca.sqlerrd[2] > 0)
496         return(MR_IN_USE);
497     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM serverhosts
498       WHERE mach_id = :id;
499     if (sqlca.sqlerrd[2] > 0)
500         return(MR_IN_USE);
501     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM nfsphys
502       WHERE mach_id = :id;
503     if (sqlca.sqlerrd[2] > 0)
504         return(MR_IN_USE);
505     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM hostaccess 
506       WHERE mach_id = :id;
507     if (sqlca.sqlerrd[2] > 0)
508         return(MR_IN_USE);
509     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM printcap
510       WHERE mach_id = :id;
511     if (sqlca.sqlerrd[2] > 0)
512         return(MR_IN_USE);
513     EXEC SQL REPEATED SELECT quotaserver INTO :idummy FROM printcap 
514       WHERE quotaserver = :id;
515     if (sqlca.sqlerrd[2] > 0)
516         return(MR_IN_USE);
517     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM palladium
518       WHERE mach_id = :id;
519     if (sqlca.sqlerrd[2] > 0)
520         return(MR_IN_USE);
521
522     EXEC SQL REPEATED DELETE FROM mcmap WHERE mach_id = :id;
523     if (ingres_errno) return(mr_errcode);
524     return(MR_SUCCESS);
525 }
526
527
528 /* setup_dclu - verify that the cluster is no longer being referenced 
529  * and may safely be deleted.
530  */
531
532 int setup_dclu(q, argv)
533     struct query *q;
534     char **argv;
535 {
536     EXEC SQL BEGIN DECLARE SECTION; 
537     int id;
538     EXEC SQL END DECLARE SECTION; 
539
540     id = *(int *)argv[0];
541     EXEC SQL REPEATED SELECT mach_id INTO :idummy FROM mcmap
542       WHERE clu_id = :id;
543     if (sqlca.sqlerrd[2] > 0)
544         return(MR_IN_USE);
545     EXEC SQL REPEATED SELECT clu_id INTO :idummy FROM svc
546       WHERE clu_id = :id;
547     if (sqlca.sqlerrd[2] > 0)
548         return(MR_IN_USE);
549     if (ingres_errno)
550         return(mr_errcode);
551     return(MR_SUCCESS);
552 }
553
554
555 /* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate
556  * a new gid and put it in argv[6].  Otherwise if argv[6] is UNIQUE_ID but
557  * argv[5] is not, then remember that UNIQUE_ID is being stored by putting
558  * a -1 there.  Remember that this is also used for ulis, with the indexes
559  * at 6 & 7.
560  */
561
562 int setup_alis(q, argv, cl)
563     struct query *q;
564     char *argv[];
565     client *cl;
566 {
567     EXEC SQL BEGIN DECLARE SECTION; 
568     int ngid;
569     EXEC SQL END DECLARE SECTION; 
570     char *malloc();
571     int idx;
572
573     if (!strcmp(q->shortname, "alis"))
574       idx = 6;
575     else if (!strcmp(q->shortname, "ulis"))
576       idx = 7;
577
578     if (!strcmp(argv[idx], UNIQUE_GID) || atoi(argv[idx]) == -1) {
579         if (atoi(argv[idx - 1])) {
580             if (set_next_object_id("gid", "list", 1))
581               return(MR_INGRES_ERR);
582             EXEC SQL REPEATED SELECT value INTO :ngid FROM numvalues
583               WHERE name = 'gid';
584             if (ingres_errno) return(mr_errcode);
585             sprintf(argv[idx], "%d", ngid);
586         } else {
587             strcpy(argv[idx], "-1");
588         }
589     }
590
591     if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
592       return(mr_errcode);
593
594     return(MR_SUCCESS);
595 }
596
597
598 /* setup_dlis - verify that the list is no longer being referenced 
599  * and may safely be deleted.
600  */
601
602 int setup_dlis(q, argv)
603     struct query *q;
604     char *argv[];
605 {
606     EXEC SQL BEGIN DECLARE SECTION; 
607     int flag, id;
608     EXEC SQL END DECLARE SECTION; 
609
610     id = *(int *)argv[0];
611     EXEC SQL REPEATED SELECT member_id INTO :idummy FROM imembers
612       WHERE member_id = :id AND member_type='LIST';
613     if (sqlca.sqlerrd[2] > 0)
614         return(MR_IN_USE);
615     EXEC SQL REPEATED SELECT member_id INTO :idummy FROM imembers
616       WHERE list_id = :id;
617     if (sqlca.sqlerrd[2] > 0)
618         return(MR_IN_USE);
619     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys
620       WHERE owners = :id;
621     if (sqlca.sqlerrd[2] > 0)
622         return(MR_IN_USE);
623     EXEC SQL REPEATED SELECT tag INTO :cdummy FROM capacls
624       WHERE list_id = :id;
625     if (sqlca.sqlerrd[2] > 0)
626         return(MR_IN_USE);
627     EXEC SQL REPEATED SELECT name INTO :cdummy FROM list
628       WHERE acl_id = :id AND acl_type='LIST' AND list_id = :id;
629     if (sqlca.sqlerrd[2] > 0)
630         return(MR_IN_USE);
631     EXEC SQL REPEATED SELECT name INTO :cdummy FROM servers
632       WHERE acl_id = :id AND acl_type='LIST';
633     if (sqlca.sqlerrd[2] > 0)
634         return(MR_IN_USE);
635     EXEC SQL REPEATED SELECT entity_id INTO :idummy FROM quota
636       WHERE entity_id = :id AND type='GROUP';
637     if (sqlca.sqlerrd[2] > 0)
638         return(MR_IN_USE);
639     EXEC SQL REPEATED SELECT acl_id INTO :idummy FROM hostaccess
640       WHERE acl_id = :id AND acl_type='LIST';
641     if (sqlca.sqlerrd[2] > 0)
642         return(MR_IN_USE);
643     EXEC SQL REPEATED SELECT class INTO :cdummy FROM zephyr z
644       WHERE zephyr.xmt_type = 'LIST' AND z.xmt_id = :id 
645         OR z.sub_type = 'LIST' AND z.sub_id = :id 
646         OR z.iws_type = 'LIST' AND z.iws_id = :id
647         OR z.iui_type = 'LIST' AND z.iui_id = :id;
648     if (sqlca.sqlerrd[2] > 0)
649         return(MR_IN_USE);
650     if (ingres_errno)
651         return(mr_errcode);
652     return(MR_SUCCESS);
653 }
654
655
656 /* setup_dsin - verify that the service is no longer being referenced 
657  * and may safely be deleted.
658  */
659
660 int setup_dsin(q, argv)
661     struct query *q;
662     char **argv;
663 {
664     EXEC SQL BEGIN DECLARE SECTION; 
665     char *name;
666     EXEC SQL END DECLARE SECTION; 
667     char *c;
668
669     name = argv[0];
670     for(c=name;*c;c++) if(islower(*c)) *c = toupper(*c); 
671     EXEC SQL REPEATED SELECT service INTO :cdummy FROM serverhosts
672       WHERE service = :name;
673     if (sqlca.sqlerrd[2] > 0)
674         return(MR_IN_USE);
675     EXEC SQL REPEATED SELECT inprogress INTO :idummy FROM servers
676       WHERE name = :name;
677     if (sqlca.sqlerrd[2] > 0)
678         return(MR_IN_USE);
679     if (ingres_errno)
680         return(mr_errcode);
681     return(MR_SUCCESS);
682 }
683
684
685 /* setup_dshi - verify that the service-host is no longer being referenced 
686  * and may safely be deleted.
687  */
688
689 int setup_dshi(q, argv)
690     struct query *q;
691     char **argv;
692 {
693     EXEC SQL BEGIN DECLARE SECTION; 
694     int id;
695     char *name, *c;
696     EXEC SQL END DECLARE SECTION; 
697
698     name = argv[0];
699     for(c=name;*c;c++) if(islower(*c)) *c = toupper(*c);  /* to uppercase */
700     id = *(int *)argv[1];
701     EXEC SQL REPEATED SELECT inprogress INTO :idummy FROM serverhosts
702       WHERE service = :name AND mach_id = :id;
703     if (sqlca.sqlerrd[2] > 0)
704         return(MR_IN_USE);
705     if (ingres_errno)
706         return(mr_errcode);
707     return(MR_SUCCESS);
708 }
709
710
711 /**
712  ** setup_add_filesys - verify existance of referenced file systems
713  **
714  ** Inputs:     Add
715  **   argv[1] - type
716  **   argv[2] - mach_id
717  **   argv[3] - name
718  **   argv[5] - access
719  **
720  ** Description:
721  **   - for type = RVD:
722  **        * allow anything
723  **   - for type = NFS:
724  **        * extract directory prefix from name
725  **        * verify mach_id/dir in nfsphys
726  **        * verify access in {r, w, R, W}
727  **
728  **  Side effect: sets variable var_phys_id to the ID of the physical
729  **     filesystem (nfsphys_id for NFS, 0 for RVD)
730  **
731  ** Errors:
732  **   MR_NFS - specified directory not exported
733  **   MR_FILESYS_ACCESS - invalid filesys access
734  **
735  **/
736
737 EXEC SQL BEGIN DECLARE SECTION; 
738 static int var_phys_id;
739 EXEC SQL END DECLARE SECTION; 
740
741 setup_afil(q, argv, cl)
742     struct query *q;
743     char *argv[];
744     client *cl;
745 {
746     char *type, *name;
747     int mach_id;
748     EXEC SQL BEGIN DECLARE SECTION; 
749     int ok;
750     char ftype[32], *access;
751     EXEC SQL END DECLARE SECTION; 
752
753     type = argv[1];
754     mach_id = *(int *)argv[2];
755     name = argv[3];
756     access = argv[5];
757     var_phys_id = 0;
758
759     sprintf(ftype, "fs_access_%s", type);
760     EXEC SQL SELECT trans INTO :cdummy FROM alias 
761       WHERE name = :ftype AND type = 'TYPE' and trans = :access;
762     if (ingres_errno) return(mr_errcode);
763     if (sqlca.sqlerrd[2] == 0) return(MR_FILESYS_ACCESS);
764
765     if (!strcmp(type, "NFS"))
766         return (check_nfs(mach_id, name, access));
767     
768     if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
769       return(mr_errcode);
770
771     return(MR_SUCCESS);
772 }
773
774
775 /* Verify the arguments, depending on the FStype.  Also, if this is an
776  * NFS filesystem, then update any quotas for that filesystem to reflect
777  * the new phys_id.
778  */
779
780 setup_ufil(q, argv, cl)
781     struct query *q;
782     char *argv[];
783     client *cl;
784 {
785     int mach_id, status;
786     char *type, *name;  
787     EXEC SQL BEGIN DECLARE SECTION; 
788     int fid, total, who;
789     char *entity, ftype[32], *access;
790     int var_phys_id = 0;
791     EXEC SQL END DECLARE SECTION; 
792
793     type = argv[2];
794     mach_id = *(int *)argv[3];
795     name = argv[4];
796     access = argv[6];
797     fid = *(int *)argv[0];
798     who = cl->client_id;
799     entity = cl->entity;
800
801     sprintf(ftype, "fs_access_%s", type);
802     EXEC SQL SELECT trans INTO :cdummy FROM alias
803       WHERE name = :ftype AND type='TYPE' AND trans = :access;
804     if (ingres_errno) return(mr_errcode);
805     if (sqlca.sqlerrd[2] == 0) return(MR_FILESYS_ACCESS);
806
807     if (!strcmp(type, "NFS")) {
808         status = check_nfs(mach_id, name, access);
809         EXEC SQL UPDATE quota SET phys_id = :var_phys_id
810           WHERE filsys_id = :fid;
811         if (ingres_errno) return(mr_errcode);
812         return(status);
813     } else if (!strcmp(type, "AFS")) {
814         total = 0;
815         EXEC SQL REPEATED DELETE FROM quota 
816           WHERE type = 'ANY' AND filsys_id = :fid;
817         EXEC SQL SELECT SUM (quota) INTO :total FROM quota
818           WHERE filsys_id = :fid AND phys_id != 0;
819         if (ingres_errno) return(mr_errcode);
820         if (total != 0) {
821 /*
822  *             append quota (quota = total, filsys_id = fid,
823  *                           phys_id = 0, entity_id = 0, type = "ANY",
824  *                           modtime = "now", modby = who, modwith = entity)
825  */
826             EXEC SQL INSERT INTO quota (quota, filsys_id, phys_id, entity_id, 
827                                         type, modtime, modby, modwith)
828               VALUES (:total, :fid, 0, 0, 
829                       'ANY', 'now', :who, :entity) ;
830             if (ingres_errno) return(mr_errcode);
831         }
832     } else {
833         EXEC SQL UPDATE quota SET phys_id = 0 WHERE filsys_id = :fid;
834         if (ingres_errno) return(mr_errcode);
835     }
836     return(MR_SUCCESS);
837 }
838
839
840 /* Find the NFS physical partition that the named directory is on.
841  * This is done by comparing the dir against the mount point of the
842  * partition.  To make sure we get the correct match when there is 
843  * more than one, we sort the query in reverse order by dir name.
844  */
845
846 check_nfs(mach_id, name, access)
847     EXEC SQL BEGIN DECLARE SECTION; 
848     int mach_id;
849     EXEC SQL END DECLARE SECTION; 
850     char *name;
851     char *access;
852 {
853     EXEC SQL BEGIN DECLARE SECTION; 
854     char dir[81];
855     EXEC SQL END DECLARE SECTION; 
856     char caccess;
857     register int status;
858     register char *cp1;
859     register char *cp2;
860
861     status = MR_NFS;
862     EXEC SQL DECLARE csr101 CURSOR FOR
863       SELECT nfsphys_id, TRIM (dir) FROM nfsphys
864         WHERE mach_id = :mach_id 
865         ORDER BY 2 DESC;
866     EXEC SQL OPEN csr101;
867     while(1) {
868         EXEC SQL FETCH csr101 INTO :var_phys_id, :dir;
869         if(sqlca.sqlcode != 0) break;
870         cp1 = name;
871         cp2 = dir;
872         while (*cp2) {
873             if (*cp1++ != *cp2) break;
874             cp2++;
875         }
876         if (*cp2 == 0) {
877             status = MR_SUCCESS;
878             break;
879         }  
880     }
881     EXEC SQL CLOSE csr101;
882     if (ingres_errno)
883         return(mr_errcode);
884     return(status);
885 }
886
887
888 /* setup_dfil: free any quota records and fsgroup info associated with
889  * a filesystem when it is deleted.  Also adjust the allocation numbers.
890  */
891
892 setup_dfil(q, argv, cl)
893     struct query  *q;
894     char **argv;
895     client *cl;
896 {
897     EXEC SQL BEGIN DECLARE SECTION; 
898     int id, total;
899     EXEC SQL END DECLARE SECTION; 
900
901     id = *(int *)argv[0];
902     EXEC SQL REPEATED SELECT SUM (quota) INTO :total FROM quota 
903       WHERE filsys_id = :id;
904     EXEC SQL REPEATED UPDATE nfsphys SET allocated = allocated - :total
905       WHERE nfsphys_id = filesys.phys_id AND filesys.filsys_id = :id;
906     /** Is SQL smart enough to do the PRODUCT above? */
907     /** Or should we code it using another SELECT?  */
908
909     EXEC SQL REPEATED DELETE FROM quota WHERE filsys_id = :id;
910     EXEC SQL REPEATED DELETE FROM fsgroup WHERE filsys_id = :id;
911     EXEC SQL REPEATED DELETE FROM fsgroup WHERE group_id = :id;
912     if (ingres_errno) return(mr_errcode);
913     return(MR_SUCCESS);
914 }
915
916
917 /* setup_dnfp: check to see that the nfs physical partition does not have
918  * any filesystems assigned to it before allowing it to be deleted.
919  */
920
921 setup_dnfp(q, argv, cl)
922     struct query  *q;
923     char **argv;
924     client *cl;
925 {
926     EXEC SQL BEGIN DECLARE SECTION; 
927     int id;
928     char *dir;
929     EXEC SQL END DECLARE SECTION; 
930
931     id = *(int *)argv[0];
932     dir = argv[1];
933     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys fs, nfsphys np
934       WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id 
935         AND np.mach_id = :id AND np.dir = :dir;
936     if (sqlca.sqlerrd[2] > 0)
937       return(MR_IN_USE);
938     if (ingres_errno)
939       return(mr_errcode);
940     return(MR_SUCCESS);
941 }
942
943
944 /* setup_dqot: Remove allocation from nfsphys before deleting quota.
945  *   argv[0] = filsys_id
946  *   argv[1] = type if "update_quota" or "delete_quota"
947  *   argv[2 or 1] = users_id or list_id
948  */
949
950 setup_dqot(q, argv, cl)
951     struct query  *q;
952     char **argv;
953     client *cl;
954 {
955     EXEC SQL BEGIN DECLARE SECTION; 
956     int quota, fs, id;
957     char *qtype;
958     EXEC SQL END DECLARE SECTION; 
959
960     fs = *(int *)argv[0];
961     if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota")) {
962         qtype = argv[1];
963         id = *(int *)argv[2];
964     } else {
965         qtype = "USER";
966         id = *(int *)argv[1];
967     }
968
969     EXEC SQL REPEATED SELECT quota INTO :quota FROM quota
970       WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs;
971     EXEC SQL REPEATED UPDATE nfsphys 
972       SET allocated = nfsphys.allocated - :quota
973       WHERE nfsphys_id = filesys.physid AND filesys.filsys_id = :fs;
974     
975     if (ingres_errno) return(mr_errcode);
976     return(MR_SUCCESS);
977 }
978
979
980 /* setup_sshi: don't exclusive lock the machine table during
981  * set_server_host_internal.
982  */
983
984 setup_sshi(q, argv, cl)
985     struct query  *q;
986     char **argv;
987     client *cl;
988 {
989 #ifsql INGRES    
990     EXEC SQL set lockmode session where readlock = system;
991 #endsql
992 }
993
994
995 /* setup add_kerberos_user_mapping: add the string to the string 
996  * table if necessary.
997  */
998
999 setup_akum(q, argv, cl)
1000 struct query *q;
1001 char **argv;
1002 client *cl;
1003 {
1004     EXEC SQL BEGIN DECLARE SECTION; 
1005     int id, rowcount;
1006     char *name;
1007     EXEC SQL END DECLARE SECTION; 
1008
1009     name = argv[1];
1010     if (name_to_id(name, "STRING", &id) != MR_SUCCESS) {
1011         if (q->type != APPEND) return(MR_STRING);
1012         EXEC SQL SELECT value INTO :id FROM numvalues
1013           WHERE name = 'strings_id';
1014         id++;
1015         EXEC SQL UPDATE numvalues SET value = :id
1016           WHERE name = 'strings_id';
1017         EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
1018         cache_entry(name, "STRING", id);
1019     }
1020     if (ingres_errno) return(mr_errcode);
1021     *(int *)argv[1] = id;
1022     return(MR_SUCCESS);
1023 }
1024
1025
1026 \f
1027 /* FOLLOWUP ROUTINES */
1028
1029 /* generic set_modtime routine.  This takes the table name from the query,
1030  * and will update the modtime, modby, and modwho fields in the entry in
1031  * the table whose name field matches argv[0].
1032  */
1033
1034 set_modtime(q, argv, cl)
1035     struct query *q;
1036     char *argv[];
1037     client *cl;
1038 {
1039     char *name, *entity, *table;
1040     int who;
1041
1042     entity = cl->entity;
1043     who = cl->client_id;
1044     table = q->rtable;
1045     name = argv[0];
1046
1047     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, \
1048 modwith = '%s' WHERE %s.name = '%s'",table,who,entity,table,name);
1049     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1050
1051     return(MR_SUCCESS);
1052 }
1053
1054 /* generic set_modtime_by_id routine.  This takes the table name from
1055  * the query, and the id name from the validate record,
1056  * and will update the modtime, modby, and modwho fields in the entry in
1057  * the table whose id matches argv[0].
1058  */
1059
1060 set_modtime_by_id(q, argv, cl)
1061     struct query *q;
1062     char **argv;
1063     client *cl;
1064 {
1065     char *entity, *table, *id_name;
1066     int who, id;
1067
1068     entity = cl->entity;
1069     who = cl->client_id;
1070     table = q->rtable;
1071     id_name = q->validate->object_id;
1072
1073     id = *(int *)argv[0];
1074     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, \
1075 modwith = '%s' WHERE %s.%s = %d",table,who,entity,table,id_name,id);
1076     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1077     return(MR_SUCCESS);
1078 }
1079
1080
1081 /* Sets the finger modtime on a user record.  The users_id will be in argv[0].
1082  */
1083
1084 set_finger_modtime(q, argv, cl)
1085     struct query *q;
1086     char *argv[];
1087     client *cl;
1088 {
1089     EXEC SQL BEGIN DECLARE SECTION; 
1090     int users_id, who;
1091     char *entity;
1092     EXEC SQL END DECLARE SECTION; 
1093
1094     entity = cl->entity;
1095     who = cl->client_id;
1096     users_id = *(int *)argv[0];
1097
1098     EXEC SQL UPDATE users SET fmodtime='now', fmodby = :who, fmodwith = :entity
1099       WHERE users.users_id = :users_id;
1100
1101    return(MR_SUCCESS);
1102 }
1103
1104
1105 /* Sets the pobox modtime on a user record.  The users_id will be in argv[0].
1106  */
1107
1108 set_pobox_modtime(q, argv, cl)
1109     struct query *q;
1110     char **argv;
1111     client *cl;
1112 {
1113     EXEC SQL BEGIN DECLARE SECTION; 
1114     int users_id, who;
1115     char *entity;
1116     EXEC SQL END DECLARE SECTION; 
1117
1118     entity = cl->entity;
1119     who = cl->client_id;
1120     users_id = *(int *)argv[0];
1121
1122     EXEC SQL UPDATE users SET pmodtime='now', pmodby = :who, pmodwith = entity
1123       WHERE users.users_id = users_id;
1124
1125     return(MR_SUCCESS);
1126 }
1127
1128
1129 /* Like set_modtime, but uppercases the name first.
1130  */
1131
1132 set_uppercase_modtime(q, argv, cl)
1133     struct query *q;
1134     char **argv;
1135     client *cl;
1136 {
1137     char *name, *entity, *table, *c;
1138     int who;
1139
1140     entity = cl->entity;
1141     who = cl->client_id;
1142     table = q->rtable;
1143     name = argv[0];
1144     for(c=name;*c;c++) if(islower(*c)) *c = toupper(*c);  /** INGRES has an uppercase() functiuons, but it's not portable. */
1145
1146     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, \
1147 modwith = '%s' WHERE %s.name = '%s'",table,who,entity,table,name);
1148     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1149     
1150     return(MR_SUCCESS);
1151 }
1152
1153
1154 /* Sets the modtime on the machine whose mach_id is in argv[0].  This routine
1155  * is necessary for add_machine_to_cluster becuase the table that query
1156  * operates on is "mcm", not "machine".
1157  */
1158
1159 set_mach_modtime_by_id(q, argv, cl)
1160     struct query *q;
1161     char **argv;
1162     client *cl;
1163 {
1164     EXEC SQL BEGIN DECLARE SECTION; 
1165     char *entity;
1166     int who, id;
1167     EXEC SQL END DECLARE SECTION; 
1168
1169     entity = cl->entity;
1170     who = cl->client_id;
1171     id = *(int *)argv[0];
1172     EXEC SQL UPDATE machine SET modtime='now', modby = :who, modwith = :entity
1173       WHERE machine.mach_id = :id;
1174     
1175     return(MR_SUCCESS);
1176 }
1177
1178
1179 /* Sets the modtime on the cluster whose mach_id is in argv[0].  This routine
1180  * is necessary for add_cluster_data and delete_cluster_data becuase the
1181  * table that query operates on is "svc", not "cluster".
1182  */
1183
1184 set_cluster_modtime_by_id(q, argv, cl)
1185     struct query *q;
1186     char **argv;
1187     client *cl;
1188 {
1189     EXEC SQL BEGIN DECLARE SECTION; 
1190     char *entity;
1191     int who, id;
1192     EXEC SQL END DECLARE SECTION; 
1193
1194     entity = cl->entity;
1195     who = cl->client_id;
1196
1197     id = *(int *)argv[0];
1198     EXEC SQL UPDATE cluster SET modtime='now', modby = :who, modwith = :entity
1199       WHERE cluster.clu_id = :id;
1200     return(MR_SUCCESS);
1201 }
1202
1203
1204 /* sets the modtime on the serverhost where the service name is in argv[0]
1205  * and the mach_id is in argv[1].
1206  */
1207
1208 set_serverhost_modtime(q, argv, cl)
1209     struct query *q;
1210     char **argv;
1211     client *cl;
1212 {
1213     EXEC SQL BEGIN DECLARE SECTION; 
1214     char *entity, *serv;
1215     int who, id;
1216     EXEC SQL END DECLARE SECTION; 
1217
1218     entity = cl->entity;
1219     who = cl->client_id;
1220
1221     serv = argv[0];
1222     id = *(int *)argv[1];
1223     EXEC SQL UPDATE serverhosts 
1224       SET modtime = 'now', modby = :who, modwith = :entity
1225       WHERE service = :serv AND mach_id = :id; 
1226     return(MR_SUCCESS);
1227 }
1228
1229
1230 /* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
1231  * directory name is in argv[1].
1232  */
1233
1234 set_nfsphys_modtime(q, argv, cl)
1235     struct query *q;
1236     char **argv;
1237     client *cl;
1238 {
1239     EXEC SQL BEGIN DECLARE SECTION; 
1240     char *entity, *dir;
1241     int who, id;
1242     EXEC SQL END DECLARE SECTION; 
1243
1244     entity = cl->entity;
1245     who = cl->client_id;
1246
1247     id = *(int *)argv[0];
1248     dir = argv[1];
1249     EXEC SQL UPDATE nfsphys SET modtime = 'now', modby = :who, modwith = :entity
1250       WHERE dir = :dir AND mach_id = :id;
1251     return(MR_SUCCESS);
1252 }
1253
1254
1255 /* sets the modtime on a filesystem, where argv[0] contains the filesys
1256  * label.
1257  */
1258
1259 set_filesys_modtime(q, argv, cl)
1260     struct query *q;
1261     char *argv[];
1262     client *cl;
1263 {
1264     EXEC SQL BEGIN DECLARE SECTION; 
1265     char *label, *entity;
1266     int who;
1267     EXEC SQL END DECLARE SECTION; 
1268
1269     entity = cl->entity;
1270     who = cl->client_id;
1271
1272     label = argv[0];
1273     if (!strcmp(q->shortname, "ufil"))
1274       label = argv[1];
1275
1276     EXEC SQL UPDATE filesys SET modtime = 'now', modby = :who, 
1277         modwith = :entity, phys_id = :var_phys_id
1278       WHERE label = :label;
1279     return(MR_SUCCESS);
1280 }
1281
1282
1283 /* sets the modtime on a zephyr class, where argv[0] contains the class
1284  * name.
1285  */
1286
1287 set_zephyr_modtime(q, argv, cl)
1288     struct query *q;
1289     char *argv[];
1290     client *cl;
1291 {
1292     EXEC SQL BEGIN DECLARE SECTION; 
1293     char *class, *entity;
1294     int who;
1295     EXEC SQL END DECLARE SECTION; 
1296
1297     entity = cl->entity;
1298     who = cl->client_id;
1299
1300     class = argv[0];
1301
1302     EXEC SQL UPDATE zephyr SET modtime = 'now', modby = :who, modwith = :entity
1303       WHERE class = :class;
1304     
1305     return(MR_SUCCESS);
1306 }
1307
1308
1309 /* fixes the modby field.  This will be the second to last thing in the
1310  * argv, the argv length is determined from the query structure.  It is
1311  * passed as a pointer to an integer.  This will either turn it into a
1312  * username, or # + the users_id.
1313  */
1314 followup_fix_modby(q, sq, v, action, actarg, cl)
1315     struct query *q;
1316     register struct save_queue *sq;
1317     struct validate *v;
1318     register int (*action)();
1319     register int actarg;
1320     client *cl;
1321 {
1322     register int i, j;
1323     char **argv, *malloc();
1324     int id, status;
1325
1326     i = q->vcnt - 2;
1327     while (sq_get_data(sq, &argv)) {
1328         id = atoi(argv[i]);
1329         if (id > 0)
1330           status = id_to_name(id, "USER", &argv[i]);
1331         else
1332           status = id_to_name(-id, "STRING", &argv[i]);
1333         if (status && status != MR_NO_MATCH)
1334           return(status);
1335         (*action)(q->vcnt, argv, actarg);
1336         for (j = 0; j < q->vcnt; j++)
1337           free(argv[j]);
1338         free(argv);
1339     }
1340     sq_destroy(sq);
1341     return(MR_SUCCESS);
1342 }
1343
1344
1345 /* After retrieving a user account, fix the modby field and signature.
1346  * The modby field is the second to last thing in the
1347  * argv, the argv length is determined from the query structure.  It is
1348  * passed as a pointer to an integer.  This will either turn it into a
1349  * username, or # + the users_id.  Only "gua*" queries have a signature,
1350  * these are ones with U_END return values.  "gub*" queries also use this
1351  * routine but don't have a signature.
1352  */
1353 followup_guax(q, sq, v, action, actarg, cl)
1354     struct query *q;
1355     register struct save_queue *sq;
1356     struct validate *v;
1357     register int (*action)();
1358     register int actarg;
1359     client *cl;
1360 {
1361     register int i, j;
1362     char **argv, *malloc();
1363 #ifdef GDSS
1364     char sigbuf[256], *rawsig, *kname;
1365     SigInfo  si;
1366 #endif /* GDSS */
1367     int id, status;
1368
1369     i = q->vcnt - 2;
1370     while (sq_get_data(sq, &argv)) {
1371         id = atoi(argv[i]);
1372         if (id > 0)
1373           status = id_to_name(id, "USER", &argv[i]);
1374         else
1375           status = id_to_name(-id, "STRING", &argv[i]);
1376         if (status && status != MR_NO_MATCH)
1377           return(status);
1378 #ifdef GDSS
1379         if (q->vcnt == U_END) {
1380             com_err(whoami, 0, "compressing signature");
1381             rawsig = argv[U_SIGNATURE];
1382             bcopy(&rawsig[0], &id, sizeof(int));
1383             id = ntohl(id);
1384             status = id_to_name(id, "STRING", &kname);
1385             bcopy(&rawsig[4], &si.timestamp, sizeof(int));
1386             si.timestamp = ntohl(si.timestamp);
1387             si.SigInfoVersion = 0; /* XXXXX this isn't used */
1388             kname_parse(si.pname, si.pinst, si.prealm, kname);
1389             si.rawsig = (unsigned char *)&rawsig[8];
1390             GDSS_Recompose(&si, sigbuf);
1391             argv[U_SIGNATURE] = strsave(sigbuf);
1392         }
1393 #endif /* GDSS */
1394         (*action)(q->vcnt, argv, actarg);
1395         for (j = 0; j < q->vcnt; j++)
1396           free(argv[j]);
1397         free(argv);
1398     }
1399     sq_destroy(sq);
1400     return(MR_SUCCESS);
1401 }
1402
1403
1404 /**
1405  ** followup_ausr - add finger and pobox entries, set_user_modtime
1406  **
1407  ** Inputs:
1408  **     argv[0] - login (add_user)
1409  **     argv[3] - last name
1410  **     argv[4] - first name
1411  **     argv[5] - middle name
1412  **
1413  **/
1414
1415 followup_ausr(q, argv, cl)
1416     struct query *q;
1417     char *argv[];
1418     client *cl;
1419 {
1420     EXEC SQL BEGIN DECLARE SECTION; 
1421     int who, status, sigwho, id;
1422     char *login, *entity, *src, *dst, *name;
1423     char fullname[129];
1424     EXEC SQL END DECLARE SECTION; 
1425 #ifdef GDSS
1426     char databuf[32], *kname_unparse();
1427     EXEC SQL BEGIN DECLARE SECTION; 
1428     char rawsig[128];
1429     EXEC SQL END DECLARE SECTION; 
1430     SigInfo si;
1431 #endif /* GDSS */
1432
1433     /* build fullname */
1434     if (strlen(argv[4]) && strlen(argv[5]))
1435         sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
1436     else if (strlen(argv[4]))
1437         sprintf(fullname, "%s %s", argv[4], argv[3]);
1438     else
1439         sprintf(fullname, "%s", argv[3]);
1440
1441 #ifdef GDSS
1442     sprintf(databuf, "%s:%s", argv[U_NAME], argv[U_MITID]);
1443     /* skip bytes for timestamp & kname */
1444     si.rawsig = (unsigned char *)&rawsig[8];
1445     status = GDSS_Verify(databuf, strlen(databuf), argv[U_SIGNATURE], &si);
1446     if (status == 0) {
1447         name = kname_unparse(si.pname, si.pinst, si.prealm);
1448         status = name_to_id(name, "STRING", &sigwho);
1449         if (status == MR_NO_MATCH) {
1450             EXEC SQL REPEATED SELECT value INTO :sigwho FROM numvalues
1451               WHERE name = 'strings_id';
1452             sigwho++;
1453             EXEC SQL REPEATED UPDATE numvalues SET value = :sigwho
1454               WHERE name = 'strings_id';
1455             EXEC SQL INSERT INTO strings (string_id, string)
1456               VALUES (:sigwho, :name);
1457         } else if (status)
1458           return(status);
1459         sigwho = htonl(sigwho);
1460         bcopy(&sigwho, &rawsig[0], sizeof(int));
1461         si.timestamp = htonl(si.timestamp);
1462         bcopy(&si.timestamp, &rawsig[4], sizeof(int));
1463      } else
1464        return(status);
1465 #endif /* GDSS */
1466
1467     login = argv[0];
1468     who = cl->client_id;
1469     entity = cl->entity;
1470
1471     /* create finger entry, pobox & set modtime on user */
1472 #ifdef GDSS
1473     EXEC SQL REPEATED UPDATE users 
1474       SET modtime='now', modby=:who, modwith = :entity, 
1475           fullname = :fullname, affiliation = type, 
1476           signature = :rawsig, 
1477           fmodtime='now', fmodby = :who, fmodwith = :entity,
1478           potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
1479       WHERE login = :login;
1480 #else /* GDSS */
1481     EXEC SQL REPEATED UPDATE users
1482       SET modtime='now', modby=:who, modwith = :entity, 
1483           fullname = :fullname, affiliation = type, 
1484           fmodtime='now', fmodby = :who, fmodwith = :entity,
1485           potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
1486       WHERE login = :login;
1487 #endif /* GDSS */
1488
1489     return(MR_SUCCESS);
1490 }
1491
1492
1493 /* followup_gpob: fixes argv[2] based on the IDs currently there and the
1494  * type in argv[1].  Then completes the upcall to the user.
1495  *
1496  * argv[2] is of the form "123:234" where the first integer is the machine
1497  * ID if it is a pop box, and the second is the string ID if it is an SMTP
1498  * box.  argv[1] should be "POP", "SMTP", or "NONE".  Boxes of type NONE
1499  * are skipped.
1500  */
1501
1502 followup_gpob(q, sq, v, action, actarg, cl)
1503     register struct query *q;
1504     register struct save_queue *sq;
1505     register struct validate *v;
1506     register int (*action)();
1507     int actarg;
1508     client *cl;
1509 {
1510     char **argv, *index();
1511     char *ptype, *p;
1512     int mid, sid, status;
1513
1514     /* for each row */
1515     while (sq_get_data(sq, &argv)) {
1516         mr_trim_args(2, argv);
1517         ptype = argv[1];
1518         p = index(argv[2], ':');
1519         *p++ = 0;
1520         mid = atoi(argv[2]);
1521         sid = atoi(p);
1522
1523         if (!strcmp(ptype, "POP")) {
1524             status = id_to_name(mid, "MACHINE", &argv[2]);
1525             if (status == MR_NO_MATCH)
1526               return(MR_MACHINE);
1527         } else if (!strcmp(ptype, "SMTP")) {
1528             status = id_to_name(sid, "STRING", &argv[2]);
1529             if (status == MR_NO_MATCH)
1530               return(MR_STRING);
1531         } else /* ptype == "NONE" */ {
1532             goto skip;
1533         }
1534         if (status) return(status);
1535
1536         if (!strcmp(q->shortname, "gpob")) {
1537             sid = atoi(argv[4]);
1538             if (sid > 0)
1539               status = id_to_name(sid, "USER", &argv[4]);
1540             else
1541               status = id_to_name(-sid, "STRING", &argv[4]);
1542         }
1543         if (status && status != MR_NO_MATCH) return(status);
1544
1545         (*action)(q->vcnt, argv, actarg);
1546     skip:
1547         /* free saved data */
1548         free(argv[0]);
1549         free(argv[1]);
1550         free(argv[4]);
1551         free(argv);
1552     }
1553
1554     sq_destroy(sq);
1555     return (MR_SUCCESS);
1556 }
1557
1558
1559 /* followup_glin: fix the ace_name in argv[8].  argv[7] will contain the
1560  * ace_type: "LIST", "USER", or "NONE".  Decode the id in argv[8] into the
1561  * proper name based on the type, and repace that string in the argv.
1562  * Also fixes the modby field by called followup_fix_modby.
1563  */
1564
1565 followup_glin(q, sq, v, action, actarg, cl)
1566     register struct query *q;
1567     register struct save_queue *sq;
1568     register struct validate *v;
1569     register int (*action)();
1570     int actarg;
1571     client *cl;
1572 {
1573     char **argv, *malloc(), *realloc(), *type;
1574     int id, i, idx, status;
1575
1576     idx = 8;
1577     if (!strcmp(q->shortname, "gsin"))
1578       idx = 12;
1579
1580     while (sq_get_data(sq, &argv)) {
1581         mr_trim_args(q->vcnt, argv);
1582
1583         id = atoi(argv[i = q->vcnt - 2]);
1584         if (id > 0)
1585           status = id_to_name(id, "USER", &argv[i]);
1586         else
1587           status = id_to_name(-id, "STRING", &argv[i]);
1588         if (status && status != MR_NO_MATCH)
1589           return(status);
1590
1591         id = atoi(argv[idx]);
1592         type = argv[idx - 1];
1593
1594         if (!strcmp(type, "LIST")) {
1595             status = id_to_name(id, "LIST", &argv[idx]);
1596         } else if (!strcmp(type, "USER")) {
1597             status = id_to_name(id, "USER", &argv[idx]);
1598         } else if (!strcmp(type, "KERBEROS")) {
1599             status = id_to_name(id, "STRING", &argv[idx]);
1600         } else if (!strcmp(type, "NONE")) {
1601             status = 0;
1602             free(argv[idx]);
1603             argv[idx] = strsave("NONE");
1604         } else {
1605             status = 0;
1606             free(argv[idx]);
1607             argv[idx] = strsave("???");
1608         }
1609         if (status && status != MR_NO_MATCH)
1610           return(status);
1611
1612         if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
1613             argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
1614             strcpy(argv[6], UNIQUE_GID);
1615         }
1616
1617         /* send the data */
1618         (*action)(q->vcnt, argv, actarg);
1619
1620         /* free saved data */
1621         for (i = 0; i < q->vcnt; i++) 
1622             free(argv[i]);
1623         free(argv);
1624     }
1625
1626     sq_destroy(sq);
1627     return (MR_SUCCESS);
1628 }
1629
1630
1631 /* followup_gqot: Fix the entity name, directory name & modby fields
1632  *   argv[0] = filsys_id
1633  *   argv[1] = type
1634  *   argv[2] = entity_id
1635  *   argv[3] = ascii(quota)
1636  */
1637
1638 followup_gqot(q, sq, v, action, actarg, cl)
1639     struct query *q;
1640     register struct save_queue *sq;
1641     struct validate *v;
1642     register int (*action)();
1643     register int actarg;
1644     client *cl;
1645 {
1646     register int j;
1647     char **argv, *malloc();
1648     EXEC SQL BEGIN DECLARE SECTION; 
1649     int id;
1650     char *name, *label;
1651     EXEC SQL END DECLARE SECTION; 
1652     int status, idx;
1653
1654     if (!strcmp(q->name, "get_quota") ||
1655         !strcmp(q->name, "get_quota_by_filesys"))
1656       idx = 4;
1657     else
1658       idx = 3;
1659     while (sq_get_data(sq, &argv)) {
1660         if (idx == 4) {
1661             switch (argv[1][0]) {
1662             case 'U':
1663                 status = id_to_name(atoi(argv[2]), "USER", &argv[2]);
1664                 break;
1665             case 'G':
1666             case 'L':
1667                 status = id_to_name(atoi(argv[2]), "LIST", &argv[2]);
1668                 break;
1669             case 'A':
1670                 free(argv[2]);
1671                 argv[2] = strsave("system:anyuser");
1672                 break;
1673             default:
1674                 id = atoi(argv[2]);
1675                 argv[2] = malloc(8);
1676                 sprintf(argv[2], "%d", id);
1677             }
1678         }
1679         id = atoi(argv[idx]);
1680         free(argv[idx]);
1681         argv[idx] = malloc(256);
1682         name = argv[idx];
1683         if (id == 0) {
1684             label = argv[0];
1685             EXEC SQL REPEATED SELECT name INTO :name FROM filesys 
1686               WHERE label = :label;
1687         } else {
1688             EXEC SQL REPEATED SELECT dir INTO :name FROM nfsphys 
1689               WHERE nfsphys_id = :id;
1690         }
1691         if (sqlca.sqlerrd[2] != 1) {
1692             sprintf(argv[idx], "#%d", id);
1693         }
1694
1695         id = atoi(argv[idx+3]);
1696         if (id > 0)
1697           status = id_to_name(id, "USER", &argv[idx+3]);
1698         else
1699           status = id_to_name(-id, "STRING", &argv[idx+3]);
1700         if (status && status != MR_NO_MATCH)
1701           return(status);
1702         (*action)(q->vcnt, argv, actarg);
1703         for (j = 0; j < q->vcnt; j++)
1704           free(argv[j]);
1705         free(argv);
1706     }
1707     sq_destroy(sq);
1708     return(MR_SUCCESS);
1709 }
1710
1711
1712 /* followup_aqot: Add allocation to nfsphys after creating quota.
1713  *   argv[0] = filsys_id
1714  *   argv[1] = type if "add_quota" or "update_quota"
1715  *   argv[2 or 1] = id
1716  *   argv[3 or 2] = ascii(quota)
1717  */
1718
1719 followup_aqot(q, argv, cl)
1720     struct query  *q;
1721     char **argv;
1722     client *cl;
1723 {
1724     EXEC SQL BEGIN DECLARE SECTION; 
1725     int quota, id, fs, who;
1726     char *entity, *qtype;
1727     EXEC SQL END DECLARE SECTION; 
1728
1729     fs = *(int *)argv[0];
1730     if (!strcmp(q->name, "add_quota") || !strcmp(q->name, "update_quota")) {
1731         qtype = argv[1];
1732         id = *(int *)argv[2];
1733         quota = atoi(argv[3]);
1734     } else {
1735         qtype = "USER";
1736         id = *(int *)argv[1];
1737         quota = atoi(argv[2]);
1738     }
1739     who = cl->client_id;
1740     entity = cl->entity;
1741
1742     EXEC SQL REPEATED UPDATE quota
1743       SET modtime = 'now', modby = :who, modwith = :entity
1744       WHERE filsys_id = :fs and type = :qtype and entity_id = :id;
1745     EXEC SQL REPEATED UPDATE nfsphys
1746       SET allocated = allocated + :quota
1747       WHERE nfsphys_id = filesys.phys_id AND filesys.filsys_id = :fs;
1748     if (ingres_errno) return(mr_errcode);
1749     return(MR_SUCCESS);
1750 }
1751
1752
1753 followup_gpce(q, sq, v, action, actarg, cl)
1754     struct query *q;
1755     register struct save_queue *sq;
1756     struct validate *v;
1757     register int (*action)();
1758     register int actarg;
1759     client *cl;
1760 {
1761     register int i, j;
1762     char **argv, *malloc();
1763     int id, status;
1764
1765     i = q->vcnt - 2;
1766     while (sq_get_data(sq, &argv)) {
1767         id = atoi(argv[PCAP_QSERVER]);
1768         status = id_to_name(id, "MACHINE", &argv[PCAP_QSERVER]);
1769         if (status) return (status);
1770         id = atoi(argv[i]);
1771         if (id > 0)
1772           status = id_to_name(id, "USER", &argv[i]);
1773         else
1774           status = id_to_name(-id, "STRING", &argv[i]);
1775         if (status && status != MR_NO_MATCH)
1776           return(status);
1777         (*action)(q->vcnt, argv, actarg);
1778         for (j = 0; j < q->vcnt; j++)
1779           free(argv[j]);
1780         free(argv);
1781     }
1782     sq_destroy(sq);
1783     return(MR_SUCCESS);
1784 }
1785
1786
1787 /* followup_gzcl:
1788  */
1789
1790 followup_gzcl(q, sq, v, action, actarg, cl)
1791     register struct query *q;
1792     register struct save_queue *sq;
1793     register struct validate *v;
1794     register int (*action)();
1795     int actarg;
1796     client *cl;
1797 {
1798     int id, i, status;
1799     char **argv;
1800
1801     while (sq_get_data(sq, &argv)) {
1802         mr_trim_args(q->vcnt, argv);
1803
1804         id = atoi(argv[i = q->vcnt - 2]);
1805         if (id > 0)
1806           status = id_to_name(id, "USER", &argv[i]);
1807         else
1808           status = id_to_name(-id, "STRING", &argv[i]);
1809         if (status && status != MR_NO_MATCH)
1810           return(status);
1811
1812         for (i = 1; i < 8; i+=2) {
1813             id = atoi(argv[i+1]);
1814             if (!strcmp(argv[i], "LIST")) {
1815                 status = id_to_name(id, "LIST", &argv[i+1]);
1816             } else if (!strcmp(argv[i], "USER")) {
1817                 status = id_to_name(id, "USER", &argv[i+1]);
1818             } else if (!strcmp(argv[i], "KERBEROS")) {
1819                 status = id_to_name(id, "STRING", &argv[i+1]);
1820             } else if (!strcmp(argv[i], "NONE")) {
1821                 status = 0;
1822                 free(argv[i+1]);
1823                 argv[i+1] = strsave("NONE");
1824             } else {
1825                 status = 0;
1826                 free(argv[i+1]);
1827                 argv[i+1] = strsave("???");
1828             }
1829             if (status && status != MR_NO_MATCH)
1830               return(status);
1831         }
1832
1833         /* send the data */
1834         (*action)(q->vcnt, argv, actarg);
1835
1836         /* free saved data */
1837         for (i = 0; i < q->vcnt; i++) 
1838             free(argv[i]);
1839         free(argv);
1840     }
1841     sq_destroy(sq);
1842     return(MR_SUCCESS);
1843 }
1844
1845
1846 /* followup_gsha:
1847  */
1848
1849 followup_gsha(q, sq, v, action, actarg, cl)
1850     register struct query *q;
1851     register struct save_queue *sq;
1852     register struct validate *v;
1853     register int (*action)();
1854     int actarg;
1855     client *cl;
1856 {
1857     char **argv;
1858     int i, id, status;
1859
1860     while (sq_get_data(sq, &argv)) {
1861         mr_trim_args(q->vcnt, argv);
1862
1863         id = atoi(argv[4]);
1864         if (id > 0)
1865           status = id_to_name(id, "USER", &argv[4]);
1866         else
1867           status = id_to_name(-id, "STRING", &argv[4]);
1868         if (status && status != MR_NO_MATCH)
1869           return(status);
1870
1871         id = atoi(argv[2]);
1872         if (!strcmp(argv[1], "LIST")) {
1873             status = id_to_name(id, "LIST", &argv[2]);
1874         } else if (!strcmp(argv[1], "USER")) {
1875             status = id_to_name(id, "USER", &argv[2]);
1876         } else if (!strcmp(argv[1], "KERBEROS")) {
1877             status = id_to_name(id, "STRING", &argv[2]);
1878         } else if (!strcmp(argv[1], "NONE")) {
1879             status = 0;
1880             free(argv[2]);
1881             argv[2] = strsave("NONE");
1882         } else {
1883             status = 0;
1884             free(argv[2]);
1885             argv[2] = strsave("???");
1886         }
1887         if (status && status != MR_NO_MATCH)
1888           return(status);
1889
1890         /* send the data */
1891         (*action)(q->vcnt, argv, actarg);
1892
1893         /* free saved data */
1894         for (i = 0; i < q->vcnt; i++) 
1895             free(argv[i]);
1896         free(argv);
1897     }
1898     sq_destroy(sq);
1899     return(MR_SUCCESS);
1900 }
1901
1902
1903 \f
1904 /* Special query routines */
1905
1906 /* set_pobox - this does all of the real work.
1907  *       argv = user_id, type, box
1908  * if type is POP, then box should be a machine, and its ID should be put in
1909  * pop_id.  If type is SMTP, then box should be a string and its ID should
1910  * be put in box_id.  If type is NONE, then box doesn't matter.
1911  */
1912
1913 int set_pobox(q, argv, cl)
1914     struct query *q;
1915     char **argv;
1916     client *cl;
1917 {
1918     EXEC SQL BEGIN DECLARE SECTION; 
1919     int user, id;
1920     char *box, potype[9];
1921     EXEC SQL END DECLARE SECTION; 
1922     int status;
1923
1924     box = argv[2];
1925     user = *(int *)argv[0];
1926
1927     EXEC SQL REPEATED SELECT pop_id, potype INTO :id, :potype FROM users
1928       WHERE users_id = :user;
1929     if (ingres_errno) return(mr_errcode);
1930     if (!strcmp(strtrim(potype), "POP"))
1931       set_pop_usage(id, -1);
1932
1933     if (!strcmp(argv[1], "POP")) {
1934         status = name_to_id(box, "MACHINE", &id);
1935         if (status == MR_NO_MATCH)
1936           return(MR_MACHINE);
1937         else if (status)
1938           return(status);
1939         EXEC SQL REPEATED UPDATE users SET potype = 'POP', pop_id = :id
1940           WHERE users_id = :user;
1941         set_pop_usage(id, 1);
1942     } else if (!strcmp(argv[1], "SMTP")) {
1943         if (index(box, '/') || index(box, '|'))
1944           return(MR_BAD_CHAR);
1945         status = name_to_id(box, "STRING", &id);
1946         if (status == MR_NO_MATCH) {
1947             EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
1948               WHERE name='strings_id';
1949             id++;
1950             EXEC SQL REPEATED UPDATE numvalues SET value = :id
1951               WHERE name='strings_id';
1952             EXEC SQL INSERT INTO strings (string_id, string)
1953               VALUES (:id, :box);
1954         } else if (status)
1955           return(status);
1956         EXEC SQL REPEATED UPDATE users SET potype='SMTP', box_id = :id
1957           WHERE users_id = :user;
1958     } else /* argv[1] == "NONE" */ {
1959         EXEC SQL REPEATED UPDATE users SET potype='NONE'
1960           WHERE users_id = :user;
1961     }
1962
1963     set_pobox_modtime(q, argv, cl);
1964     EXEC SQL REPEATED UPDATE tblstats SET updates = updates+1, modtime='now'
1965       WHERE tblstats.table_name='users';
1966     if (ingres_errno) return(mr_errcode);
1967     return(MR_SUCCESS);
1968 }
1969
1970
1971 /* get_list_info:  passed a wildcard list name, returns lots of stuff about
1972  * each list.  This is tricky:  first build a queue of all requested
1973  * data.  Rest of processing consists of fixing gid, ace_name, and modby.
1974  */
1975
1976 get_list_info(q, aargv, cl, action, actarg)
1977     register struct query *q;
1978     char **aargv;
1979     client *cl;
1980     register int (*action)();
1981     int actarg;
1982 {
1983     char *argv[13], *malloc(), *realloc();
1984     EXEC SQL BEGIN DECLARE SECTION; 
1985     char *name, acl_type[9], listname[33], active[5], public[5], hidden[5];
1986     char maillist[5], grouplist[5], gid_str[6], acl_name[256], desc[256];
1987     char modtime[27], modby[256], modwith[9];
1988     int id, rowcount, acl_id, hid, modby_id;
1989     EXEC SQL END DECLARE SECTION; 
1990     int returned, status;
1991     struct save_queue *sq, *sq_create();
1992
1993     returned = rowcount = 0;
1994     name = aargv[0];
1995
1996     sq = sq_create();
1997     EXEC SQL DECLARE csr102 CURSOR FOR SELECT list_id FROM list 
1998       WHERE name = :name;
1999     EXEC SQL OPEN csr102;
2000     while(1)
2001     {
2002         EXEC SQL FETCH csr102 INTO :id;
2003         if(sqlca.sqlcode!=0) break;
2004         sq_save_data(sq, id);
2005         rowcount++;
2006     }
2007     EXEC SQL CLOSE csr102;
2008
2009     if (ingres_errno) return(mr_errcode);
2010     if (rowcount == 0)
2011       return(MR_NO_MATCH);
2012
2013     argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden;
2014     argv[4] = maillist; argv[5] = grouplist; argv[6] = gid_str; 
2015     argv[7] = acl_type; argv[9] = desc; argv[10] = modtime; argv[12] = modwith;
2016
2017     while (sq_get_data(sq, &id)) {
2018         if (id == 0)
2019           continue;
2020         argv[6] = gid_str;
2021 /*
2022  *      repeat retrieve (listname = l.#name, active = text(l.#active), 
2023  *              public = text(l.#public), hidden = text(l.#hidden),
2024  *              hid = l.#hidden, maillist = text(l.#maillist),
2025  *              group = text(l.#group), gid = text(l.#gid),
2026  *              acl_type = trim(l.#acl_type), acl_id = l.#acl_id,
2027  *              desc = l.#desc, modtime = l.#modtime, modby_id = l.#modby,
2028  *              modwith =l.#modwith)
2029  *          where l.list_id = :id
2030  */
2031         /** Won't the TRIM() die a horrible INGRES death? **/
2032         EXEC SQL REPEATED SELECT name, CHAR(active), CHAR(publicflg),
2033             CHAR(hidden), hidden, CHAR(maillist), CHAR(grouplist), CHAR(gid),
2034             TRIM(acl_type), acl_id, description, CHAR(modtime), modby, modwith 
2035           INTO :listname, :active, :public, :hidden, :hid, :maillist,
2036             :grouplist, :gid_str, :acl_type, :acl_id, :desc, 
2037             :modtime, :modby_id, :modwith
2038           FROM list WHERE list_id = :id;
2039
2040         if (ingres_errno) return(mr_errcode);
2041
2042         if (atoi(gid_str) == -1)
2043             argv[6] = UNIQUE_GID;
2044
2045         argv[8] = malloc(0);
2046         if (!strcmp(acl_type, "LIST")) {
2047             status = id_to_name(acl_id, "LIST", &argv[8]);
2048         } else if (!strcmp(acl_type, "USER")) {
2049             status = id_to_name(acl_id, "USER", &argv[8]);
2050         } else if (!strcmp(acl_type, "KERBEROS")) {
2051             status = id_to_name(acl_id, "STRING", &argv[8]);
2052         } else if (!strcmp(acl_type, "NONE")) {
2053             status = 0;
2054             free(argv[8]);
2055             argv[8] = strsave("NONE");
2056         } else {
2057             status = 0;
2058             free(argv[8]);
2059             argv[8] = strsave("???");
2060         }
2061         if (status && status != MR_NO_MATCH) return(status);
2062
2063         argv[11] = malloc(0);
2064         if (modby_id > 0)
2065           status = id_to_name(modby_id, "USER", &argv[11]);
2066         else
2067           status = id_to_name(-modby_id, "STRING", &argv[11]);
2068         if (status && status != MR_NO_MATCH) return(status);
2069
2070         mr_trim_args(q->vcnt, argv);
2071         returned++;
2072         (*action)(q->vcnt, argv, actarg);
2073         free(argv[8]);
2074         free(argv[11]);
2075     }
2076
2077     sq_destroy(sq);
2078     if (ingres_errno) return(mr_errcode);
2079     return (MR_SUCCESS);
2080 }
2081
2082
2083 /* Add_member_to_list: do list flattening as we go!  MAXLISTDEPTH is
2084  * how many different ancestors a member is allowed to have.
2085  */
2086
2087 #define MAXLISTDEPTH    1024
2088
2089 int add_member_to_list(q, argv, cl)
2090     struct query *q;
2091     char **argv;
2092     client *cl;
2093 {
2094     EXEC SQL BEGIN DECLARE SECTION; 
2095     int id, lid, mid, error, who, ref;
2096     char *mtype, dtype[9], *entity;
2097     EXEC SQL END DECLARE SECTION; 
2098     int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
2099     int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
2100     int status;
2101     char *dtypes[MAXLISTDEPTH];
2102     char *iargv[3], *buf;
2103
2104     lid = *(int *)argv[0];
2105     mtype = argv[1];
2106     mid = *(int *)argv[2];
2107     /* if the member is already a direct member of the list, punt */
2108 /*
2109  *  repeat retrieve (exists = any(m.list_id where m.list_id=@lid and 
2110  *                         m.member_id = :mid and m.member_type = :mtype
2111  *                         and m.direct = 1))
2112  */
2113     EXEC SQL REPEATED SELECT list_id INTO :idummy FROM imembers 
2114       WHERE list_id = :lid AND member_id = :mid 
2115         AND member_type = :mtype AND direct = 1;
2116     if (sqlca.sqlerrd[2] > 0)
2117       return(MR_EXISTS);
2118     if (!strcasecmp(mtype, "STRING")) {
2119         buf = malloc(0);
2120         status = id_to_name(mid, "STRING", &buf);
2121         if (status) return(status);
2122         if (index(buf, '/') || index(buf, '|')) {
2123             free(buf);
2124             return(MR_BAD_CHAR);
2125         }
2126         free(buf);
2127     }
2128
2129     ancestors[0] = lid;
2130     aref[0] = 1;
2131     acount = 1;
2132     EXEC SQL DECLARE csr103 CURSOR FOR
2133       SELECT list_id, ref_count FROM imembers
2134         WHERE member_id = :lid AND member_type='LIST';
2135     EXEC SQL OPEN csr103;
2136     while(1) {
2137         EXEC SQL FETCH csr103 INTO :id, :ref;
2138         if(sqlca.sqlcode != 0) break;
2139         aref[acount] = ref;
2140         ancestors[acount++] = id;
2141         if (acount >= MAXLISTDEPTH) break; 
2142     }
2143     EXEC SQL CLOSE csr103;
2144     if (ingres_errno) return(mr_errcode);
2145     if (acount >= MAXLISTDEPTH) {
2146         return(MR_INTERNAL);
2147     }
2148     descendants[0] = mid;
2149     dtypes[0] = mtype;
2150     dref[0] = 1;
2151     dcount = 1;
2152     error = 0;
2153     if (!strcmp(mtype, "LIST")) {
2154         EXEC SQL DECLARE csr104 CURSOR FOR 
2155           SELECT member_id, member_type, ref_count
2156           FROM imembers
2157           WHERE list_id = :mid;
2158         EXEC SQL OPEN csr104;
2159         while(1) {
2160             EXEC SQL FETCH csr104 INTO :id, :dtype, :ref;
2161             if(sqlca.sqlcode != 0) break;
2162             switch (dtype[0]) {
2163             case 'L':
2164                 dtypes[dcount] = "LIST";
2165                 break;
2166             case 'U':
2167                 dtypes[dcount] = "USER";
2168                 break;
2169             case 'S':
2170                 dtypes[dcount] = "STRING";
2171                 break;
2172             case 'K':
2173                 dtypes[dcount] = "KERBEROS";
2174                 break;
2175             default:
2176                 error++;
2177                 break;
2178             }
2179             dref[dcount] = ref;
2180             descendants[dcount++] = id;
2181             if (dcount >= MAXLISTDEPTH) {
2182                 error++;
2183                 break;
2184             }
2185         }
2186         EXEC SQL CLOSE csr104;
2187         if (ingres_errno) return(mr_errcode);
2188         if (error)
2189           return(MR_INTERNAL);
2190     }
2191     for (a = 0; a < acount; a++) {
2192         lid = ancestors[a];
2193         for (d = 0; d < dcount; d++) {
2194             mid = descendants[d];
2195             mtype = dtypes[d];
2196             if (mid == lid && !strcmp(mtype, "LIST")) {
2197                 return(MR_LISTLOOP);
2198             }
2199 /*
2200  *          repeat retrieve (exists = any(m.ref_count where m.list_id = :lid
2201  *                                       and m.member_id = :mid
2202  *                                       and m.member_type = :mtype))
2203  */
2204             EXEC SQL REPEATED SELECT ref_count INTO :idummy FROM imembers
2205               WHERE list_id = :lid AND member_id = :mid 
2206                 AND m.member_type = :mtype;
2207             ref = aref[a] * dref[d];
2208             if (sqlca.sqlerrd[2] > 0) {
2209                 if (a == 0 && d == 0) {
2210                     EXEC SQL UPDATE imembers 
2211                       SET ref_count = ref_count+ref, direct=1
2212                       WHERE list_id = :lid AND member_id = :mid
2213                         AND member_type = :mtype;
2214                 } else {
2215                     EXEC SQL UPDATE imembers
2216                       SET ref_count = ref_count+ref
2217                       WHERE list_id = :lid AND member_id = :mid
2218                         AND member_type = :mtype;
2219                 }
2220             } else {
2221                 incremental_clear_before();
2222                 if (a == 0 && d == 0) {
2223                     EXEC SQL INSERT INTO imembers 
2224                       (list_id, member_id, direct, member_type, ref_count)
2225                       VALUES (:lid, :mid, 1, :mtype, 1);
2226                 } else {
2227                     EXEC SQL INSERT INTO imembers
2228                       (list_id, member_id, member_type, ref_count)
2229                       VALUES (:lid, :mid, :mtype, 1);
2230                 }
2231                 iargv[0] = (char *)lid;
2232                 iargv[1] = mtype;
2233                 iargv[2] = (char *)mid;
2234                 incremental_after("members", 0, iargv);
2235             }
2236         }
2237     }
2238     lid = *(int *)argv[0];
2239     entity = cl->entity;
2240     who = cl->client_id;
2241     EXEC SQL REPEATED UPDATE list 
2242       SET modtime='now', modby = :who, modwith = :entity
2243       WHERE list_id = :lid;
2244     if (ingres_errno) return(mr_errcode);
2245     return(MR_SUCCESS);
2246 }
2247
2248
2249 /* Delete_member_from_list: do list flattening as we go!
2250  */
2251
2252 int delete_member_from_list(q, argv, cl)
2253     struct query *q;
2254     char **argv;
2255     client *cl;
2256 {
2257     EXEC SQL BEGIN DECLARE SECTION; 
2258     int id, lid, mid, cnt, error, who, ref;
2259     char *mtype, dtype[9], *entity;
2260     EXEC SQL END DECLARE SECTION; 
2261     int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
2262     int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
2263     char *dtypes[MAXLISTDEPTH];
2264     char *iargv[3];
2265
2266     lid = *(int *)argv[0];
2267     mtype = argv[1];
2268     mid = *(int *)argv[2];
2269     /* if the member is not a direct member of the list, punt */
2270 /*
2271  *  repeat retrieve (exists = any(m.list_id where m.list_id=@lid and 
2272  *                         m.member_id = :mid and m.member_type = :mtype
2273  *                         and m.direct = 1))
2274  */
2275     EXEC SQL REPEATED SELECT list_id INTO :idummy FROM imembers
2276       WHERE list_id = :lid AND member_id = :mid 
2277         AND member_type = :mtype AND direct = 1;
2278     if (ingres_errno) return(mr_errcode);
2279     if (sqlca.sqlcode == 100)
2280       return(MR_NO_MATCH);
2281     ancestors[0] = lid;
2282     aref[0] = 1;
2283     acount = 1;
2284     EXEC SQL DECLARE csr105 CURSOR FOR 
2285       SELECT list_id, ref_count FROM imembers
2286         WHERE member_id = :lid AND member_type = 'LIST';
2287     EXEC SQL OPEN csr105;
2288     while(1) {
2289         EXEC SQL FETCH csr105 INTO :id, :ref;
2290         if(sqlca.sqlcode!=0) break;
2291         aref[acount] = ref;
2292         ancestors[acount++] = id;
2293         if (acount >= MAXLISTDEPTH) break;
2294     }
2295     EXEC SQL CLOSE csr105;
2296     if (ingres_errno) 
2297       return(mr_errcode);
2298     if (acount >= MAXLISTDEPTH)
2299       return(MR_INTERNAL);
2300     descendants[0] = mid;
2301     dtypes[0] = mtype;
2302     dref[0] = 1;
2303     dcount = 1;
2304     error = 0;
2305     if (!strcmp(mtype, "LIST")) {
2306         EXEC SQL DECLARE csr106 CURSOR FOR 
2307           SELECT member_id, member_type, ref_count FROM imembers
2308             WHERE list_id = :mid;
2309         EXEC SQL OPEN csr106;
2310         while(1) {
2311             EXEC SQL FETCH csr106 INTO :id, :dtype, :ref;
2312             if(sqlca.sqlcode!=0) break;
2313             switch (dtype[0]) {
2314             case 'L':
2315                 dtypes[dcount] = "LIST";
2316                 break;
2317             case 'U':
2318                 dtypes[dcount] = "USER";
2319                 break;
2320             case 'S':
2321                 dtypes[dcount] = "STRING";
2322                 break;
2323             case 'K':
2324                 dtypes[dcount] = "KERBEROS";
2325                 break;
2326             default:
2327                 error++;
2328                 break;
2329             }
2330             dref[dcount] = ref;
2331             descendants[dcount++] = id;
2332             if (dcount >= MAXLISTDEPTH) break;
2333         }
2334         EXEC SQL CLOSE csr106;
2335         if (ingres_errno) 
2336           return(mr_errcode);
2337         if (error)
2338           return(MR_INTERNAL);
2339     }
2340     for (a = 0; a < acount; a++) {
2341         lid = ancestors[a];
2342         for (d = 0; d < dcount; d++) {
2343             mid = descendants[d];
2344             mtype = dtypes[d];
2345             if (mid == lid && !strcmp(mtype, "LIST")) {
2346                 return(MR_LISTLOOP);
2347             }
2348             EXEC SQL REPEATED SELECT ref_count INTO :cnt FROM imembers
2349               WHERE list_id = :lid AND member_id = :mid AND member_type = :mtype;
2350             ref = aref[a] * dref[d];
2351             if (cnt <= ref) {
2352                 iargv[0] = (char *)lid;
2353                 iargv[1] = mtype;
2354                 iargv[2] = (char *)mid;
2355                 incremental_before("members", 0, iargv);
2356                 EXEC SQL DELETE FROM imembers 
2357                   WHERE list_id = :lid AND member_id = :mid 
2358                     AND member_type= :mtype;
2359                 incremental_clear_after();
2360             } else if (a == 0 && d == 0) {
2361                 EXEC SQL UPDATE imembers 
2362                   SET ref_count = refcount - :ref, direct = 0
2363                   WHERE list_id = :lid AND member_id = :mid 
2364                     AND member_type = :mtype;
2365             } else {
2366                 EXEC SQL UPDATE imembers 
2367                   SET ref_count=refcount-:ref
2368                   WHERE list_id = :lid AND member_id = :mid 
2369                     AND member_type = :mtype;
2370             }
2371         }
2372     }
2373     lid = *(int *)argv[0];
2374     entity = cl->entity;
2375     who = cl->client_id;
2376     EXEC SQL UPDATE list SET modtime = 'now', modby = :who, modwith = :entity
2377       WHERE list_id = :lid;
2378     if (ingres_errno) return(mr_errcode);
2379     return(MR_SUCCESS);
2380 }
2381
2382
2383 /* get_ace_use - given a type and a name, return a type and a name.
2384  * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0],
2385  * and argv[1] will contain the ID of the entity in question.  The R*
2386  * types mean to recursively look at every containing list, not just
2387  * when the object in question is a direct member.  On return, the
2388  * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR.
2389  */
2390
2391 int get_ace_use(q, argv, cl, action, actarg)
2392     struct query *q;
2393     char *argv[];
2394     client *cl;
2395     int (*action)();
2396     int actarg;
2397 {
2398     int found = 0;
2399     EXEC SQL BEGIN DECLARE SECTION; 
2400     char *atype;
2401     int aid, listid, id;
2402     EXEC SQL END DECLARE SECTION; 
2403     struct save_queue *sq, *sq_create();
2404
2405     atype = argv[0];
2406     aid = *(int *)argv[1];
2407     if (!strcmp(atype, "LIST") || !strcmp(atype, "USER") ||
2408         !strcmp(atype, "KERBEROS")) {
2409         return(get_ace_internal(atype, aid, action, actarg));
2410     }
2411
2412     sq = sq_create();
2413     if (!strcmp(atype, "RLIST")) {
2414         sq_save_data(sq, aid);
2415         /* get all the list_id's of containing lists */
2416         EXEC SQL DECLARE csr107 CURSOR FOR 
2417           SELECT list_id FROM imembers
2418             WHERE member_type='LIST' AND member_id = :aid;
2419         EXEC SQL OPEN csr107;
2420         while(1) {
2421             EXEC SQL FETCH csr107 INTO :listid;
2422             if(sqlca.sqlcode != 0) break;
2423             sq_save_unique_data(sq, listid);
2424         }
2425         EXEC SQL CLOSE csr107;
2426         /* now process each one */
2427         while (sq_get_data(sq, &id)) {
2428             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2429               found++;
2430         }
2431     }
2432
2433     if (!strcmp(atype, "RUSER")) {
2434         EXEC SQL DECLARE csr108 CURSOR FOR 
2435           SELECT list_id FROM imembers
2436             WHERE member_type='USER' AND member_id = :aid;
2437         EXEC SQL OPEN csr108;
2438         while(1) {
2439             EXEC SQL FETCH csr108 INTO :listid;
2440             if(sqlca.sqlcode != 0) break;
2441             sq_save_data(sq, listid);
2442         }
2443         EXEC SQL CLOSE csr108;
2444         /* now process each one */
2445         while (sq_get_data(sq, &id)) {
2446             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2447               found++;
2448         }
2449         if (get_ace_internal("USER", aid, action, actarg) == MR_SUCCESS)
2450           found++;
2451     }
2452
2453     if (!strcmp(atype, "RKERBERO")) {
2454         EXEC SQL DECLARE csr109 CURSOR FOR 
2455           SELECT list_id FROM imembers
2456             WHERE member_type='KERBEROS' AND member_id = :aid;
2457         EXEC SQL OPEN csr109;
2458         while(1) {
2459             EXEC SQL FETCH csr109 INTO :listid;
2460             if(sqlca.sqlcode != 0) break;
2461             sq_save_data(sq, listid);
2462         }
2463         EXEC SQL CLOSE csr109;
2464         /* now process each one */
2465         while (sq_get_data(sq, &id)) {
2466             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2467               found++;
2468         }
2469         if (get_ace_internal("KERBEROS", aid, action, actarg) == MR_SUCCESS)
2470           found++;
2471     }
2472
2473     sq_destroy(sq);     
2474     if (ingres_errno) return(mr_errcode);
2475     if (!found) return(MR_NO_MATCH);
2476     return(MR_SUCCESS);
2477 }
2478
2479
2480 /* This looks up a single list or user for ace use.  atype must be "USER"
2481  * or "LIST", and aid is the ID of the corresponding object.  This is used
2482  * by get_ace_use above.
2483  */
2484
2485 get_ace_internal(atype, aid, action, actarg)
2486     EXEC SQL BEGIN DECLARE SECTION; 
2487     char *atype;
2488     int aid;
2489     EXEC SQL END DECLARE SECTION; 
2490     int (*action)();
2491     int actarg;
2492 {
2493     char *rargv[2];
2494     int found = 0;
2495     EXEC SQL BEGIN DECLARE SECTION; 
2496     char name[33];
2497     EXEC SQL END DECLARE SECTION; 
2498
2499     rargv[1] = name;
2500     if (!strcmp(atype, "LIST")) {
2501         rargv[0] = "FILESYS";
2502         EXEC SQL DECLARE csr110 CURSOR FOR 
2503           SELECT label FROM filesys
2504             WHERE owners = :aid;
2505         EXEC SQL OPEN csr110;
2506         while(1) {
2507             EXEC SQL FETCH csr110 INTO :name;
2508             if(sqlca.sqlcode != 0) break;
2509             (*action)(2, rargv, actarg);
2510             found++;
2511         }
2512         EXEC SQL CLOSE csr110;
2513
2514         rargv[0] = "QUERY";
2515         EXEC SQL DECLARE csr111 CURSOR FOR 
2516           SELECT capability FROM capacls
2517             WHERE list_id = :aid ;
2518         EXEC SQL OPEN csr111;
2519         while(1) {
2520             EXEC SQL FETCH csr111 INTO :name ; 
2521             if(sqlca.sqlcode != 0) break;
2522             (*action)(2, rargv, actarg);
2523             found++;
2524         }
2525         EXEC SQL CLOSE csr111;
2526     } else if (!strcmp(atype, "USER")) {
2527         rargv[0] = "FILESYS";
2528         EXEC SQL DECLARE csr112 CURSOR FOR 
2529           SELECT label FROM filesys
2530             WHERE owner = :aid;
2531         EXEC SQL OPEN csr112;
2532         while(1) {
2533             EXEC SQL FETCH csr112 INTO :name ;
2534             if(sqlca.sqlcode != 0) break;
2535             (*action)(2, rargv, actarg);
2536             found++;
2537         }
2538         EXEC SQL CLOSE csr112; 
2539     }
2540     
2541     rargv[0] = "LIST";
2542     EXEC SQL DECLARE csr113 CURSOR FOR 
2543       SELECT name FROM list
2544         WHERE acl_type = :atype AND acl_id = :aid;
2545     EXEC SQL OPEN csr113;
2546     while(1) {
2547         EXEC SQL FETCH csr113 INTO :name; 
2548         if(sqlca.sqlcode != 0) break;
2549         (*action)(2, rargv, actarg);
2550         found++;
2551     }
2552     EXEC SQL CLOSE csr113;
2553     
2554     rargv[0] = "SERVICE";
2555     EXEC SQL DECLARE csr114 CURSOR FOR 
2556       SELECT name FROM servers
2557         WHERE acl_type = :atype AND acl_id = :aid;
2558     EXEC SQL OPEN csr114;
2559     while(1) {
2560         EXEC SQL FETCH csr114 INTO :name;
2561         if(sqlca.sqlcode != 0) break;
2562         (*action)(2, rargv, actarg);
2563         found++;
2564     }
2565     EXEC SQL CLOSE csr114; 
2566
2567     rargv[0] = "HOSTACCESS";
2568     EXEC SQL DECLARE csr115 CURSOR FOR 
2569       SELECT name FROM machine, hostaccess
2570         WHERE mach_id = hostaccess.mach_id AND hostaccess.acl_type = :atype
2571           AND hostaccess.acl_id = :aid;
2572     EXEC SQL OPEN csr115;
2573     while(1) {
2574         EXEC SQL FETCH csr115 INTO :name;
2575         if(sqlca.sqlcode != 0) break;
2576         (*action)(2, rargv, actarg);
2577         found++;
2578     }
2579     EXEC SQL CLOSE csr115;
2580
2581     rargv[0] = "ZEPHYR";
2582     EXEC SQL DECLARE csr116 CURSOR FOR 
2583       SELECT class FROM zephyr
2584         WHERE zephyr.xmt_type = :atype AND zephyr.xmt_id = :aid 
2585           OR zephyr.sub_type = :atype AND zephyr.sub_id = :aid 
2586           OR zephyr.iws_type = :atype AND zephyr.iws_id = :aid 
2587           OR zephyr.iui_type = :atype AND zephyr.iui_id = :aid;
2588     EXEC SQL OPEN csr116;
2589     while(1) {
2590         EXEC SQL FETCH csr116 INTO :name; 
2591         if(sqlca.sqlcode != 0) break;
2592         (*action)(2, rargv, actarg);
2593         found++;
2594     }
2595     EXEC SQL CLOSE csr116;
2596
2597     if (!found) return(MR_NO_MATCH);
2598     return(MR_SUCCESS);
2599 }
2600
2601
2602 /* get_lists_of_member - given a type and a name, return the name and flags
2603  * of all of the lists of the given member.  The member_type is one of
2604  * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0],
2605  * and argv[1] will contain the ID of the entity in question.  The R*
2606  * types mean to recursively look at every containing list, not just
2607  * when the object in question is a direct member.
2608  */
2609
2610 int get_lists_of_member(q, argv, cl, action, actarg)
2611     struct query *q;
2612     char *argv[];
2613     client *cl;
2614     int (*action)();
2615     int actarg;
2616 {
2617     int found = 0, direct = 1;
2618     char *rargv[6];
2619     EXEC SQL BEGIN DECLARE SECTION; 
2620     char *atype;
2621     int aid, listid, id;
2622     char name[33], active[5], public[5], hidden[5], maillist[5], grouplist[5];
2623     EXEC SQL END DECLARE SECTION; 
2624
2625     atype = argv[0];
2626     aid = *(int *)argv[1];
2627     if (!strcmp(atype, "RLIST")) {
2628         atype = "LIST";
2629         direct = 0;
2630     }
2631     if (!strcmp(atype, "RUSER")) {
2632         atype = "USER";
2633         direct = 0;
2634     }
2635     if (!strcmp(atype, "RSTRING")) {
2636         atype = "STRING";
2637         direct = 0;
2638     }
2639     if (!strcmp(atype, "RKERBEROS")) {
2640         atype = "KERBEROS";
2641         direct = 0;
2642     }
2643
2644     rargv[0] = name;
2645     rargv[1] = active;
2646     rargv[2] = public;
2647     rargv[3] = hidden;
2648     rargv[4] = maillist;
2649     rargv[5] = grouplist;
2650     if (direct) {
2651 /*
2652  *    repeat retrieve (name = list.#name, active = text(list.#active), 
2653  *                   public = text(list.#public), hidden = text(list.#hidden),
2654  *                   maillist = text(list.#maillist), group = text(list.#group))
2655  *              where list.list_id = m.list_id and m.direct = 1 and
2656  *                    m.member_type = :atype and m.member_id = :aid 
2657  */
2658         EXEC SQL DECLARE csr117a CURSOR FOR 
2659           SELECT name, CHAR(active), CHAR(publicflg), CHAR(hidden), 
2660               CHAR(maillist), CHAR(grouplist)
2661             FROM list l, imembers m
2662             WHERE l.list_id = m.list_id AND m.direct = 1 
2663               AND m.member_type = :atype AND m.member_id = :aid;
2664         EXEC SQL OPEN csr117a;
2665         while(1) {
2666             EXEC SQL FETCH csr117a 
2667               INTO :name, :active, :public, :hidden, :maillist, :grouplist;
2668             if(sqlca.sqlcode != 0) break; 
2669             (*action)(6, rargv, actarg);
2670             found++;
2671         }
2672         EXEC SQL CLOSE csr117a;
2673     } else {
2674         EXEC SQL DECLARE csr117b CURSOR FOR 
2675           SELECT name, CHAR(active), CHAR(publicflg), CHAR(hidden), 
2676               CHAR(maillist), CHAR(grouplist)
2677             FROM list l, imembers m
2678             WHERE l.list_id = m.list_id 
2679               AND m.member_type = :atype AND m.member_id = :aid;
2680         EXEC SQL OPEN csr117b;
2681         while(1) {
2682             EXEC SQL FETCH csr117b 
2683               INTO :name, :active, :public, :hidden, :maillist, :grouplist;
2684             if(sqlca.sqlcode != 0) break; 
2685             (*action)(6, rargv, actarg);
2686             found++;
2687         }
2688         EXEC SQL CLOSE csr117b;
2689     }
2690
2691     if (ingres_errno) return(mr_errcode);
2692     if (!found) return(MR_NO_MATCH);
2693     return(MR_SUCCESS);
2694 }
2695
2696
2697 /* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of
2698  * the five flags associated with each list.  It will return the name of
2699  * each list that meets the quailifications.  It does this by building a
2700  * where clause based on the arguments, then doing a retrieve.
2701  */
2702
2703 static char *lflags[5] = { "active", "publicflg", "hidden", "maillist", "group" };
2704
2705 int qualified_get_lists(q, argv, cl, action, actarg)
2706     struct query *q;
2707     char *argv[];
2708     client *cl;
2709     int (*action)();
2710     int actarg;
2711 {
2712     return(qualified_get(q, argv, action, actarg, "l.list_id != 0",
2713                          "l", "name", lflags));
2714 }
2715
2716
2717 /** get_members_of_list - optimized query for retrieval of list members
2718  **
2719  ** Inputs:
2720  **   argv[0] - list_id
2721  **
2722  ** Description:
2723  **   - retrieve USER members, then LIST members, then STRING members
2724  **/
2725
2726 get_members_of_list(q, argv, cl, action, actarg)
2727     struct query *q;
2728     char *argv[];
2729     client *cl;
2730     int (*action)();
2731     int actarg;
2732 {
2733     EXEC SQL BEGIN DECLARE SECTION; 
2734     int list_id, member_id;
2735     char member_name[129], member_type[9];
2736     EXEC SQL END DECLARE SECTION; 
2737     char *targv[2];
2738     int members;
2739     struct save_queue *sq;
2740
2741     list_id = *(int *)argv[0];
2742     members = 0;
2743     sq = sq_create();
2744
2745     EXEC SQL DECLARE csr118 CURSOR FOR 
2746       SELECT member_type, member_id FROM imembers
2747         WHERE list_id = :list_id AND direct=1;
2748     EXEC SQL OPEN csr118;
2749     while(1) {
2750         EXEC SQL FETCH csr118 INTO :member_type, :member_id; 
2751         if (sqlca.sqlcode != 0) break;
2752         if (members++ > 49) 
2753           break;
2754         sq_save_data(sq, ((int)member_type[0] << 24) | (member_id & 0xffffff));
2755     }
2756     EXEC SQL CLOSE csr118;
2757
2758     if (members <= 49) {
2759         targv[1] = malloc(0);
2760         while (sq_remove_data(sq, &member_id)) {
2761             switch (member_id >> 24) {
2762             case 'U':
2763                 targv[0] = "USER";
2764                 id_to_name(member_id & 0xffffff, "USER", &targv[1]);
2765                 (*action)(2, targv, actarg);
2766                 break;
2767             case 'L':
2768                 targv[0] = "LIST";
2769                 id_to_name(member_id & 0xffffff, "LIST", &targv[1]);
2770                 (*action)(2, targv, actarg);
2771                 break;
2772             case 'S':
2773                 targv[0] = "STRING";
2774                 id_to_name(member_id & 0xffffff, "STRING", &targv[1]);
2775                 (*action)(2, targv, actarg);
2776                 break;
2777             case 'K':
2778                 targv[0] = "KERBEROS";
2779                 id_to_name(member_id & 0xffffff, "STRING", &targv[1]);
2780                 (*action)(2, targv, actarg);
2781                 break;
2782             default:
2783                 sq_destroy(sq);
2784                 return(MR_INTERNAL);
2785             }
2786         }
2787         free(targv[1]);
2788         sq_destroy(sq);
2789         return(MR_SUCCESS);
2790     }
2791     sq_destroy(sq);
2792
2793     targv[1] = member_name;
2794     targv[0] = "USER";
2795     EXEC SQL DECLARE csr119 CURSOR FOR 
2796       SELECT users.login FROM users, imembers
2797         WHERE imembers.list_id = :list_id AND imembers.member_type = 'USER'
2798           AND imembers.member_id = users.users_id AND  imembers.direct=1
2799         ORDER BY 1;
2800     EXEC SQL OPEN csr119;
2801     while(1) {
2802         EXEC SQL FETCH csr119 INTO :member_name;
2803         if(sqlca.sqlcode != 0) break;
2804         (*action)(2, targv, actarg);
2805     }
2806     EXEC SQL CLOSE csr119;
2807     if (ingres_errno) return(mr_errcode);
2808
2809     targv[0] = "LIST";
2810     EXEC SQL DECLARE csr120 CURSOR FOR 
2811       SELECT list.name FROM list, imembers
2812         WHERE imembers.list_id = :list_id AND imembers.member_type='LIST'
2813           AND imembers.member_id = list.list_id AND imembers.direct=1
2814         ORDER BY 1;
2815     EXEC SQL OPEN csr120;
2816     while(1) {
2817         EXEC SQL FETCH csr120 INTO :member_name; 
2818         if(sqlca.sqlcode != 0) break;
2819         (*action)(2, targv, actarg);
2820     }
2821     EXEC SQL CLOSE csr120;
2822     if (ingres_errno) return(mr_errcode);
2823
2824     targv[0] = "STRING";
2825     EXEC SQL DECLARE csr121 CURSOR FOR 
2826       SELECT strings.string FROM strings, imembers
2827         WHERE imembers.list_id = :list_id AND imembers.member_type='STRING'
2828           AND imembers.member_id = strings.string_id AND imembers.direct=1
2829         ORDER BY 1;
2830     EXEC SQL OPEN csr121;
2831     while(1) {
2832         EXEC SQL FETCH csr121 INTO :member_name; 
2833         if(sqlca.sqlcode != 0) break;
2834         (*action)(2, targv, actarg);
2835     }
2836     EXEC SQL CLOSE csr121;
2837     if (ingres_errno) return(mr_errcode);
2838
2839     targv[0] = "KERBEROS";
2840     EXEC SQL DECLARE csr122 CURSOR FOR 
2841       SELECT strings.string FROM strings, imembers
2842         WHERE imembers.list_id = :list_id AND imembers.member_type='KERBEROS'
2843           AND imembers.member_id = strings.string_id AND imembers.direct=1
2844         ORDER BY 1;
2845     EXEC SQL OPEN csr122;
2846     while(1) {
2847         EXEC SQL FETCH csr122 INTO :member_name; 
2848         if(sqlca.sqlcode != 0) break;
2849         (*action)(2, targv, actarg);
2850     }
2851     EXEC SQL CLOSE csr122;
2852     if (ingres_errno) return(mr_errcode);
2853
2854     return(MR_SUCCESS);
2855 }
2856
2857
2858 /* count_members_of_list: this is a simple query, but it cannot be done
2859  * through the dispatch table.
2860  */
2861
2862 int count_members_of_list(q, argv, cl, action, actarg)
2863     struct query *q;
2864     char *argv[];
2865     client *cl;
2866     int (*action)();
2867     int actarg;
2868 {
2869     EXEC SQL BEGIN DECLARE SECTION; 
2870     int  list, ct = 0;
2871     EXEC SQL END DECLARE SECTION; 
2872     char *rargv[1], countbuf[5];
2873
2874     list = *(int *)argv[0];
2875     rargv[0] = countbuf;
2876     EXEC SQL REPEATED SELECT count (*) INTO :ct FROM imembers
2877       WHERE list_id = :list AND direct=1;
2878     if (ingres_errno) return(mr_errcode);
2879     sprintf(countbuf, "%d", ct);
2880     (*action)(1, rargv, actarg);
2881     return(MR_SUCCESS);
2882 }
2883
2884
2885 /* qualified_get_server: passed "TRUE", "FALSE", or "DONTCARE" for each of
2886  * the three flags associated with each service.  It will return the name of
2887  * each service that meets the quailifications.  It does this by building a
2888  * where clause based on the arguments, then doing a retrieve.
2889  */
2890
2891 static char *sflags[3] = { "enable", "inprogress", "harderror" };
2892
2893 int qualified_get_server(q, argv, cl, action, actarg)
2894     struct query *q;
2895     char *argv[];
2896     client *cl;
2897     int (*action)();
2898     int actarg;
2899 {
2900     return(qualified_get(q, argv, action, actarg, "s.name != ''",
2901                          "s", "name", sflags));
2902 }
2903
2904
2905 /* generic qualified get routine, used by qualified_get_lists,
2906  * qualified_get_server, and qualified_get_serverhost.
2907  *   Args:
2908  *      start - a simple where clause, must not be empty
2909  *      range - the name of the range variable
2910  *      field - the field to return
2911  *      flags - an array of strings, names of the flag variables
2912  */
2913
2914 int qualified_get(q, argv, action, actarg, start, range, field, flags)
2915     struct query *q;
2916     char *argv[];
2917     int (*action)();
2918     int actarg;
2919     char *start;
2920     char *range;
2921     char *field;
2922     char *flags[];
2923 {
2924     char name[33], qual[256];
2925     int rowcount=0, i;
2926     char *rargv[1], buf[32];
2927
2928     strcpy(qual, start);
2929     for (i = 0; i < q->argc; i++) {
2930         if (!strcmp(argv[i], "TRUE")) {
2931             sprintf(buf, " and %s.%s != 0", range, flags[i]);
2932             (void) strcat(qual, buf);
2933         } else if (!strcmp(argv[i], "FALSE")) {
2934             sprintf(buf, " and %s.%s = 0", range, flags[i]);
2935             (void) strcat(qual, buf);
2936         }
2937     }
2938       
2939     rargv[0] = SQLDA->sqlvar[0].sqldata;
2940     sprintf(stmt_buf,"SELECT %s.%s FROM %s %s WHERE %s",q->rtable,field,q->rtable,q->rvar,qual);
2941     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
2942     if((mr_errcode=mr_check_SQLDA(SQLDA)) != MR_SUCCESS)
2943       return(mr_errcode);
2944     EXEC SQL DECLARE csr123 CURSOR FOR stmt;
2945     EXEC SQL OPEN csr123;
2946     while(1) {
2947         EXEC SQL FETCH csr123 USING DESCRIPTOR :SQLDA;
2948         if(sqlca.sqlcode != 0) break;
2949         rowcount++;
2950         (*action)(1, rargv, actarg);
2951     }
2952     EXEC SQL CLOSE csr123;
2953     if (ingres_errno) return(mr_errcode);
2954     if (rowcount == 0)
2955       return(MR_NO_MATCH);
2956     return(MR_SUCCESS);
2957 }
2958
2959
2960 /* qualified_get_serverhost: passed "TRUE", "FALSE", or "DONTCARE" for each of
2961  * the five flags associated with each serverhost.  It will return the name of
2962  * each service and host that meets the quailifications.  It does this by 
2963  * building a where clause based on the arguments, then doing a retrieve.
2964  */
2965
2966 static char *shflags[6] = { "service", "enable", "override", "success",
2967                             "inprogress", "hosterror" };
2968
2969 int qualified_get_serverhost(q, argv, cl, action, actarg)
2970     struct query *q;
2971     char *argv[];
2972     client *cl;
2973     int (*action)();
2974     int actarg;
2975 {
2976     EXEC SQL BEGIN DECLARE SECTION; 
2977     char sname[33], mname[33], qual[256];
2978     EXEC SQL END DECLARE SECTION; 
2979     char *rargv[2], buf[32];
2980     int i, rowcount;
2981
2982     /** the uppercase() function is INGRES-specific */
2983     sprintf(qual, "machine.mach_id = serverhosts.mach_id AND \
2984 serverhosts.service = uppercase('%s')",
2985             argv[0]);
2986     for (i = 1; i < q->argc; i++) {
2987         if (!strcmp(argv[i], "TRUE")) {
2988             sprintf(buf, " AND serverhosts.%s != 0", shflags[i]);
2989             strcat(qual, buf);
2990         } else if (!strcmp(argv[i], "FALSE")) {
2991             sprintf(buf, " AND serverhosts.%s = 0", shflags[i]);
2992             strcat(qual, buf);
2993         }
2994     }
2995       
2996     rargv[0] = sname;
2997     rargv[1] = mname;
2998     EXEC SQL DECLARE csr124 CURSOR FOR 
2999       SELECT serverhosts.service, machine.name FROM serverhosts, machine 
3000         WHERE :qual; 
3001     EXEC SQL OPEN csr124;
3002     while(1) {
3003         EXEC SQL FETCH csr124 INTO :sname, :mname;
3004         if(sqlca.sqlcode != 0) break;
3005         rowcount++;
3006         (*action)(2, rargv, actarg);
3007     }
3008     EXEC SQL CLOSE csr124;
3009
3010     if (ingres_errno) return(mr_errcode);
3011     if (rowcount == 0)
3012       return(MR_NO_MATCH);
3013     return(MR_SUCCESS);
3014 }
3015
3016
3017 /* register_user - change user's login name and allocate a pobox, group,
3018  * filesystem, and quota for them.  The user's status must start out as 0,
3019  * and is left as 2.  Arguments are: user's UID, new login name, and user's
3020  * type for filesystem allocation (MR_FS_STUDENT, MR_FS_FACULTY, 
3021  * MR_FS_STAFF, MR_FS_MISC).
3022  */
3023
3024 register_user(q, argv, cl)
3025     struct query *q;
3026     char **argv;
3027     client *cl;
3028 {
3029     EXEC SQL BEGIN DECLARE SECTION; 
3030     char *login, dir[65], *entity, *directory, machname[33];
3031     int who, rowcount, mid, uid, users_id, flag, utype, nid, list_id, quota;
3032     int size, alloc, pid, m_id, ostatus, nstatus, gidval, fsidval;
3033     EXEC SQL END DECLARE SECTION; 
3034     char buffer[256], *aargv[3];
3035     int maxsize;
3036
3037     entity = cl->entity;
3038     who = cl->client_id;
3039
3040     uid = atoi(argv[0]);
3041     login = argv[1];
3042     utype = atoi(argv[2]);
3043
3044     /* find user */
3045     EXEC SQL REPEATED SELECT users_id, status INTO :users_id, :ostatus
3046       FROM users
3047       WHERE uid = :uid AND (status=0 OR status=5 OR status=6);
3048
3049     if (sqlca.sqlerrd[2] == 0)
3050       return(MR_NO_MATCH);
3051     if (sqlca.sqlerrd[2] > 1)
3052       return(MR_NOT_UNIQUE);
3053
3054     /* check new login name */
3055     EXEC SQL REPEATED SELECT login INTO :cdummy FROM users 
3056       WHERE login = :login AND users_id != :users_id;
3057     if (ingres_errno) return(mr_errcode);
3058     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3059     EXEC SQL REPEATED SELECT name INTO :cdummy FROM list
3060       WHERE name = :login;
3061     if (ingres_errno) return(mr_errcode);
3062     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3063     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys
3064       WHERE label = :login;
3065     if (ingres_errno) return(mr_errcode);
3066     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3067     com_err(whoami, 0, "login name OK");
3068
3069     /* choose place for pobox, put in mid */
3070     EXEC SQL REPEATED SELECT sh.mach_id, m.name
3071       INTO :mid, :machname FROM serverhosts sh, machine m
3072       WHERE sh.service='POP'  AND sh.value2 - sh.value1 =
3073         (SELECT MAX(value2 - value1) FROM serverhosts
3074            WHERE service = 'POP');
3075     if (ingres_errno) return(mr_errcode);
3076     if (sqlca.sqlerrd[2] == 0)
3077       return(MR_NO_POBOX);
3078
3079     /* change login name, set pobox */
3080     sprintf(buffer, "users.users_id = %d", users_id);
3081     incremental_before("users", buffer, 0);
3082     nstatus = 2;
3083     if (ostatus == 5 || ostatus == 6)
3084       nstatus = 1;
3085     EXEC SQL REPEATED UPDATE users SET login = :login, status = :nstatus,
3086         modtime='now', modby = :who, modwith = :entity, potype='POP',
3087         pop_id = :mid, pmodtime='now', pmodby = :who, pmodwith = :entity
3088       WHERE users_id = :users_id;
3089
3090     if (ingres_errno) return(mr_errcode);
3091     if (sqlca.sqlerrd[2] != 1)
3092       return(MR_INTERNAL);
3093     set_pop_usage(mid, 1);
3094     com_err(whoami, 0, "set login name to %s and pobox to %s", login,
3095             strtrim(machname));
3096     incremental_after("users", buffer, 0);
3097
3098     /* create group list */
3099     if (set_next_object_id("gid", "list", 1))
3100       return(MR_NO_ID);
3101     if (set_next_object_id("list_id", "list", 0))
3102       return(MR_NO_ID);
3103     EXEC SQL REPEATED SELECT value INTO :list_id FROM numvalues 
3104       WHERE name='list_id';
3105     if (ingres_errno) return(mr_errcode);
3106     if (sqlca.sqlerrd[2] != 1)
3107       return(MR_INTERNAL);
3108     incremental_clear_before();
3109     EXEC SQL SELECT value INTO :gidval FROM numvalues WHERE name = 'gid';
3110     EXEC SQL REPEATED INSERT INTO list
3111              (name, list_id, active, publicflg, hidden, maillist, grouplist,
3112               gid, description, acl_type, acl_id, 
3113               modtime, modby, modwith)
3114       VALUES (:login, :list_id, 1, 0, 0, 0, 1,
3115               :gidval, 'User Group', 'USER', :users_id,
3116               'now', :who, :entity);
3117     if (ingres_errno) return(mr_errcode);
3118     if (sqlca.sqlerrd[2] != 1)
3119       return(MR_INTERNAL);
3120     sprintf(buffer, "l.list_id = %d", list_id);
3121     incremental_after("list", buffer, 0);
3122     aargv[0] = (char *) list_id;
3123     aargv[1] = "USER";
3124     aargv[2] = (char *) users_id;
3125     incremental_clear_before();
3126     EXEC SQL REPEATED INSERT INTO imembers 
3127              (list_id, member_type, member_id, ref_count, direct)
3128       VALUES (:list_id, 'USER', :users_id, 1, 1);
3129     if (ingres_errno) return(mr_errcode);
3130     if (sqlca.sqlerrd[2] != 1)
3131       return(MR_INTERNAL);
3132     incremental_after("members", 0, aargv);
3133
3134     /* decide where to put filesystem */
3135     maxsize = 0;
3136     directory = NULL;
3137     EXEC SQL DECLARE csr125 CURSOR FOR
3138       SELECT mach_id, trim(nfsphys.dir), nfsphys_id, status, size, allocated
3139         FROM nfsphys;
3140     EXEC SQL OPEN csr125;
3141     while(1) {
3142         EXEC SQL FETCH csr125 INTO :mid, :dir, :nid, :flag, :size, :alloc;
3143         if(sqlca.sqlcode != 0) break;
3144         if ((flag & utype) && (size != 0) && (size - alloc > maxsize)) {
3145             maxsize = size - alloc;
3146             if (directory)
3147               free(directory);
3148             directory = strsave(dir);
3149             pid = nid;
3150             m_id = mid;
3151         }
3152     }
3153     EXEC SQL CLOSE csr125;
3154     if (ingres_errno) return(mr_errcode);
3155     if (maxsize == 0)
3156       return(MR_NO_FILESYS);
3157
3158     /* create filesystem */
3159     if (set_next_object_id("filsys_id", "filesys", 0))
3160       return(MR_NO_ID);
3161     incremental_clear_before(); 
3162     EXEC SQL SELECT value INTO :fsidval FROM numvalues
3163       WHERE numvalues.name='filsys_id';
3164     EXEC SQL REPEATED INSERT INTO filesys
3165         (filsys_id, phys_id, label, type, mach_id, name, 
3166          mount, access, comments, owner, owners, createflg, 
3167          lockertype, modtime, modby, modwith)
3168       VALUES
3169         (:fsidval, :pid, :login, 'NFS', :m_id, :directory+'/'+:login,
3170          '/mit/'+:login, 'w', 'User Locker', :users_id, :list_id, 1,
3171          'HOMEDIR', 'now', :who, :entity)        ;
3172     if (ingres_errno) return(mr_errcode);
3173     if (sqlca.sqlerrd[2]  != 1)
3174       return(MR_INTERNAL);
3175     incremental_after("filesys", 
3176         "fs.filsys_id = numvalues.value and numvalues.name = 'filsys_id'",
3177                       0);
3178     com_err(whoami, 0, "filesys on mach %d in %s/%s", m_id,
3179             directory, login);
3180
3181     /* set quota */
3182     EXEC SQL REPEATED SELECT value INTO :quota FROM numvalues
3183       WHERE name='def_quota';
3184     if (ingres_errno) return(mr_errcode);
3185     if (sqlca.sqlerrd[2] != 1)
3186       return(MR_NO_QUOTA);
3187     incremental_clear_before();
3188     EXEC SQL SELECT value INTO :fsidval FROM numvalues
3189       WHERE numvalues.name = 'filsys_id';
3190     EXEC SQL REPEATED INSERT INTO quota 
3191         (entity_id, filsys_id, type, quota, phys_id, modtime, modby, modwith)
3192       VALUES
3193         (:users_id, :fsidval, 'USER', :quota, :pid, 'now', :who, :entity);
3194     if (ingres_errno) return(mr_errcode);
3195     if (sqlca.sqlerrd[2] != 1)
3196       return(MR_INTERNAL);
3197     EXEC SQL REPEATED UPDATE nfsphys SET allocated=allocated + :quota
3198       WHERE nfsphys_id = filesys.phys_id 
3199         AND filesys.filsys_id = numvalues.value
3200         AND numvalues.name = 'filsys_id';
3201     if (ingres_errno) return(mr_errcode);
3202     if (sqlca.sqlerrd[2] != 1)
3203       return(MR_INTERNAL);
3204     aargv[0] = login;
3205     aargv[1] = "USER";
3206     aargv[2] = login;
3207     sprintf(buffer, "q.entity_id = %d and q.filsys_id = numvalues.value and q.type = 'USER' and numvalues.name = 'filsys_id'", users_id);
3208     incremental_after("quota", buffer, aargv);
3209     com_err(whoami, 0, "quota of %d assigned", quota);
3210     if (ingres_errno) return(mr_errcode);
3211
3212     cache_entry(login, "USER", users_id);
3213
3214     EXEC SQL REPEATED UPDATE tblstats SET updates=updates+1, modtime='now'
3215       WHERE table_name='users';
3216     EXEC SQL REPEATED UPDATE tblstats SET appends=appends+1, modtime='now'
3217       WHERE table_name='list' OR table_name='filesys' OR table_name='quota';
3218     if (ingres_errno) return(mr_errcode);
3219     return(MR_SUCCESS);
3220 }
3221
3222
3223
3224 /** set_pop_usage - incr/decr usage count for pop server in serverhosts talbe
3225  **
3226  ** Inputs:
3227  **   id of machine
3228  **   delta (will be +/- 1)
3229  **
3230  ** Description:
3231  **   - incr/decr value field in serverhosts table for pop/mach_id
3232  **
3233  **/
3234
3235 static int set_pop_usage(id, cnt)
3236     EXEC SQL BEGIN DECLARE SECTION; 
3237     int id;
3238     int cnt;
3239     EXEC SQL END DECLARE SECTION; 
3240 {
3241     EXEC SQL REPEATED UPDATE serverhosts SET value1 = value1 + :cnt
3242       WHERE serverhosts.service = 'POP' AND serverhosts.mach_id = :id;
3243       
3244     if (ingres_errno) return(mr_errcode);
3245     return(MR_SUCCESS);
3246 }
3247
3248
3249 \f
3250 /* Validation Routines */
3251
3252 validate_row(q, argv, v)
3253     register struct query *q;
3254     char *argv[];
3255     register struct validate *v;
3256 {
3257     EXEC SQL BEGIN DECLARE SECTION; 
3258     char *name;
3259     char qual[128];
3260     int rowcount;
3261     EXEC SQL END DECLARE SECTION; 
3262
3263     /* build where clause */
3264     build_qual(v->qual, v->argc, argv, qual);
3265
3266 #ifdef NEVER
3267     /** I'm pretty sure we don't need this now */
3268     /* setup ingres variables */
3269     name = v->field;
3270 #endif
3271
3272     if (log_flags & LOG_VALID)
3273         /* tell the logfile what we're doing */
3274         com_err(whoami, 0, "validating row: %s", qual);
3275     
3276     /* look for the record */
3277     sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual);
3278     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3279     EXEC SQL DECLARE csr126 CURSOR FOR stmt;
3280     EXEC SQL OPEN csr126;
3281     EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA;
3282     EXEC SQL CLOSE csr126;
3283     rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
3284
3285     if (ingres_errno) return(mr_errcode);
3286     if (rowcount == 0) return(MR_NO_MATCH);
3287     if (rowcount > 1) return(MR_NOT_UNIQUE);
3288     return(MR_EXISTS);
3289 }
3290
3291 validate_fields(q, argv, vo, n)
3292     struct query *q;
3293     register char *argv[];
3294     register struct valobj *vo;
3295     register int n;
3296 {
3297     register int status;
3298
3299     while (--n >= 0) {
3300         switch (vo->type) {
3301         case V_NAME:
3302             if (log_flags & LOG_VALID)
3303                 com_err(whoami, 0, "validating %s in %s: %s", 
3304                     vo->namefield, vo->table, argv[vo->index]);
3305             status = validate_name(argv, vo);
3306             break;
3307
3308         case V_ID:
3309             if (log_flags & LOG_VALID)
3310                 com_err(whoami, 0, "validating %s in %s: %s", 
3311                     vo->idfield, vo->table, argv[vo->index]);
3312             status = validate_id(q, argv, vo);
3313             break;
3314
3315         case V_DATE:
3316             if (log_flags & LOG_VALID)
3317                 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
3318             status = validate_date(argv, vo);
3319             break;
3320
3321         case V_TYPE:
3322             if (log_flags & LOG_VALID)
3323                 com_err(whoami, 0, "validating %s type: %s",
3324                     vo->table, argv[vo->index]);
3325             status = validate_type(argv, vo);
3326             break;
3327
3328         case V_TYPEDATA:
3329             if (log_flags & LOG_VALID)
3330                 com_err(whoami, 0, "validating typed data (%s): %s",
3331                     argv[vo->index - 1], argv[vo->index]);
3332             status = validate_typedata(q, argv, vo);
3333             break;
3334
3335         case V_RENAME:
3336             if (log_flags & LOG_VALID)
3337                 com_err(whoami, 0, "validating rename %s in %s",
3338                         argv[vo->index], vo->table);
3339             status = validate_rename(argv, vo);
3340             break;
3341
3342         case V_CHAR:
3343             if (log_flags & LOG_VALID)
3344               com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
3345             status = validate_chars(argv[vo->index]);
3346             break;
3347
3348         case V_SORT:
3349             status = MR_EXISTS;
3350             break;
3351
3352         case V_LOCK:
3353             status = lock_table(vo);
3354             break;
3355         }
3356
3357         if (status != MR_EXISTS) return(status);
3358         vo++;
3359     }
3360
3361     if (ingres_errno) return(mr_errcode);
3362     return(MR_SUCCESS);
3363 }
3364
3365
3366 /* validate_chars: verify that there are no illegal characters in
3367  * the string.  Legal characters are printing chars other than 
3368  * ", *, ?, \, [ and ].
3369  */
3370 static int illegalchars[] = {
3371     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
3372     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
3373     0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
3374     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
3375     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
3376     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
3377     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
3378     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
3379     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3380     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3381     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3382     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3383     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3384     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3385     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3386     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3387 };
3388
3389 validate_chars(s)
3390 register char *s;
3391 {
3392     while (*s)
3393       if (illegalchars[*s++])
3394         return(MR_BAD_CHAR);
3395     return(MR_EXISTS);
3396 }
3397
3398
3399 validate_id(q, argv, vo)
3400     struct query *q;
3401     char *argv[];
3402     register struct valobj *vo;
3403 {
3404     EXEC SQL BEGIN DECLARE SECTION; 
3405     char *name, *tbl, *namefield, *idfield;
3406     int id, rowcount;
3407     EXEC SQL END DECLARE SECTION; 
3408     int status;
3409     register char *c;
3410
3411     name = argv[vo->index];
3412     tbl = vo->table;
3413     namefield = vo->namefield;
3414     idfield = vo->idfield;
3415
3416     if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) ||
3417         !strcmp(tbl, "machine") ||
3418         !strcmp(tbl, "filesys") ||
3419         !strcmp(tbl, "list") ||
3420         !strcmp(tbl, "cluster") ||
3421         !strcmp(tbl, "strings")) {
3422         if (!strcmp(tbl, "machine"))
3423           for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3424         status = name_to_id(name, tbl, &id);
3425         if (status == 0) {
3426             *(int *)argv[vo->index] = id;
3427             return(MR_EXISTS);
3428         } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") &&
3429                    (q->type == APPEND || q->type == UPDATE)) {
3430             EXEC SQL SELECT value INTO :id FROM numvalues
3431               WHERE name = 'strings_id';
3432             id++;
3433             EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'string_id';
3434             EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
3435             cache_entry(name, "STRING", id);
3436             *(int *)argv[vo->index] = id;
3437             return(MR_EXISTS);
3438         } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
3439           return(vo->error);
3440         else
3441           return(status);
3442     }
3443
3444     if (!strcmp(namefield, "uid")) {
3445         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name);
3446     } else {
3447         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name);
3448     }
3449     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3450     EXEC SQL DECLARE csr127 CURSOR FOR stmt;
3451     EXEC SQL OPEN csr127;
3452     rowcount=0;
3453     EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
3454     if(sqlca.sqlcode == 0) {
3455         rowcount++;
3456         EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
3457         if(sqlca.sqlcode == 0) rowcount++;
3458     }
3459     EXEC SQL CLOSE csr127;
3460     if (ingres_errno) 
3461       return(mr_errcode);
3462
3463     if (rowcount != 1) return(vo->error);
3464     *argv[vo->index] = *SQLDA->sqlvar[0].sqldata;
3465     return(MR_EXISTS);
3466 }
3467
3468 validate_name(argv, vo)
3469     char *argv[];
3470     register struct valobj *vo;
3471 {
3472     EXEC SQL BEGIN DECLARE SECTION; 
3473     char *name, *tbl, *namefield;
3474     int rowcount;
3475     EXEC SQL END DECLARE SECTION; 
3476     register char *c;
3477
3478     name = argv[vo->index];
3479     tbl = vo->table;
3480     namefield = vo->namefield;
3481     if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) {
3482         for (c = name; *c; c++)
3483           if (islower(*c))
3484             *c = toupper(*c);
3485     }
3486     sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
3487             tbl,tbl,namefield,name);
3488     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3489     EXEC SQL DECLARE csr128 CURSOR FOR stmt;
3490     EXEC SQL OPEN csr128;
3491     EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA; 
3492     rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
3493     EXEC SQL CLOSE csr128;
3494
3495     if (ingres_errno) return(mr_errcode);
3496     return ((rowcount == 1) ? MR_EXISTS : vo->error);
3497 }
3498
3499 validate_date(argv, vo)
3500     char *argv[];
3501     struct valobj *vo;
3502 {
3503     EXEC SQL BEGIN DECLARE SECTION; 
3504     char *idate;
3505     double dd;
3506     int errorno;
3507     EXEC SQL END DECLARE SECTION; 
3508
3509     idate = argv[vo->index];
3510 /*
3511  *  retrieve (dd = interval("years", date(idate) - date("today")))
3512  */
3513     EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
3514
3515     if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
3516     return(MR_EXISTS);
3517 }
3518
3519
3520 validate_rename(argv, vo)
3521 char *argv[];
3522 struct valobj *vo;
3523 {
3524     EXEC SQL BEGIN DECLARE SECTION; 
3525     char *name, *tbl, *namefield, *idfield;
3526     int id;
3527     EXEC SQL END DECLARE SECTION; 
3528     int status;
3529     register char *c;
3530
3531     c = name = argv[vo->index];
3532     while (*c)
3533       if (illegalchars[*c++])
3534         return(MR_BAD_CHAR);
3535     tbl = vo->table;
3536     /* minor kludge to upcasify machine names */
3537     if (!strcmp(tbl, "machine"))
3538         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3539     namefield = vo->namefield;
3540     idfield = vo->idfield;
3541     id = -1;
3542     if (idfield == 0) {
3543         if (!strcmp(argv[vo->index], argv[vo->index - 1]))
3544           return(MR_EXISTS);
3545 /*
3546  *      retrieve (id = any(table.namefield where table.namefield = name))
3547  */
3548         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s.%s = '%s'",
3549                 namefield,tbl,tbl,namefield,name);
3550         EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3551         EXEC SQL DECLARE csr129 CURSOR FOR stmt;
3552         EXEC SQL OPEN csr129;
3553         EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA; 
3554         if(sqlca.sqlcode == 0) id=1; else id=0;
3555         EXEC SQL CLOSE csr129;
3556         
3557         if (ingres_errno) return(mr_errcode);
3558         if (id)
3559           return(vo->error);
3560         else
3561           return(MR_EXISTS);
3562     }
3563     status = name_to_id(name, tbl, &id);
3564     if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
3565       return(MR_EXISTS);
3566     else
3567       return(vo->error);
3568 }
3569
3570
3571 validate_type(argv, vo)
3572     char *argv[];
3573     register struct valobj *vo;
3574 {
3575     EXEC SQL BEGIN DECLARE SECTION; 
3576     char *typename;
3577     char *val;
3578     EXEC SQL END DECLARE SECTION; 
3579     register char *c;
3580
3581     typename = vo->table;
3582     c = val = argv[vo->index];
3583     while (*c) {
3584         if (illegalchars[*c++])
3585           return(MR_BAD_CHAR);
3586     }
3587
3588     /* uppercase type fields */
3589     for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
3590
3591     EXEC SQL SELECT trans INTO :cdummy FROM alias
3592       WHERE name = :typename AND type='TYPE' AND trans = :val;
3593     if (ingres_errno) return(mr_errcode);
3594     return (sqlca.sqlerrd[2] ? MR_EXISTS : vo->error);
3595 }
3596
3597 /* validate member or type-specific data field */
3598
3599 validate_typedata(q, argv, vo)
3600     register struct query *q;
3601     register char *argv[];
3602     register struct valobj *vo;
3603 {
3604     EXEC SQL BEGIN DECLARE SECTION; 
3605     char *name;
3606     char *field_type;
3607     char data_type[129];
3608     int id;
3609     EXEC SQL END DECLARE SECTION; 
3610     int status;
3611     char *index();
3612     register char *c;
3613
3614     /* get named object */
3615     name = argv[vo->index];
3616
3617     /* get field type string (known to be at index-1) */
3618     field_type = argv[vo->index-1];
3619
3620     /* get corresponding data type associated with field type name */
3621     EXEC SQL SELECT trans INTO :data_type FROM alias 
3622       WHERE name = :field_type AND type='TYPEDATA';
3623     if (ingres_errno) return(mr_errcode);
3624     if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
3625
3626     /* now retrieve the record id corresponding to the named object */
3627     if (index(data_type, ' '))
3628         *index(data_type, ' ') = 0;
3629     if (!strcmp(data_type, "user")) {
3630         /* USER */
3631         status = name_to_id(name, data_type, &id);
3632         if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
3633           return(MR_USER);
3634         if (status) return(status);
3635     } else if (!strcmp(data_type, "list")) {
3636         /* LIST */
3637         status = name_to_id(name, data_type, &id);
3638         if (status && status == MR_NOT_UNIQUE)
3639           return(MR_LIST);
3640         if (status == MR_NO_MATCH) {
3641             /* if idfield is non-zero, then if argv[0] matches the string
3642              * that we're trying to resolve, we should get the value of
3643              * numvalues.[idfield] for the id.
3644              */
3645             if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
3646                 set_next_object_id(q->validate->object_id, q->rtable, 0);
3647                 name = vo->idfield;
3648                 EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
3649                   WHERE name = :name;
3650                 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
3651             } else
3652               return(MR_LIST);
3653         } else if (status) return(status);
3654     } else if (!strcmp(data_type, "machine")) {
3655         /* MACHINE */
3656         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3657         status = name_to_id(name, data_type, &id);
3658         if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
3659           return(MR_MACHINE);
3660         if (status) return(status);
3661     } else if (!strcmp(data_type, "string")) {
3662         /* STRING */
3663         status = name_to_id(name, data_type, &id);
3664         if (status && status == MR_NOT_UNIQUE)
3665           return(MR_STRING);
3666         if (status == MR_NO_MATCH) {
3667             if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
3668             EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
3669             id++;
3670             EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
3671             EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
3672             cache_entry(name, "STRING", id);
3673         } else if (status) return(status);
3674     } else if (!strcmp(data_type, "none")) {
3675         id = 0;
3676     } else {
3677         return(MR_TYPE);
3678     }
3679
3680     /* now set value in argv */
3681     *(int *)argv[vo->index] = id;
3682     
3683     return (MR_EXISTS);
3684 }
3685
3686
3687 /* Lock the table named by the validation object */
3688
3689 lock_table(vo)
3690 struct valobj *vo;
3691 {
3692     sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0",
3693             vo->table,vo->table,vo->idfield);
3694     EXEC SQL EXECUTE IMMEDIATE :stmt_buf; 
3695     if (ingres_errno) return(mr_errcode);
3696     if (sqlca.sqlerrd[2] != 1)
3697       return(vo->error);
3698     else
3699       return(MR_EXISTS);
3700 }
3701
3702
3703 /* Check the database at startup time.  For now this just resets the
3704  * inprogress flags that the DCM uses.
3705  */
3706
3707 sanity_check_database()
3708 {
3709 }
3710
3711
3712 /* Dynamic SQL support routines */
3713 MR_SQLDA_T *mr_alloc_SQLDA()
3714 {
3715     MR_SQLDA_T *it;
3716     short *null_indicators;
3717     register int j;
3718
3719     if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) {
3720         com_err(whoami, MR_NO_MEM, "setting up SQLDA");
3721         exit(1);
3722     }
3723
3724     if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) {
3725         com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators");
3726         exit(1);
3727     }
3728
3729     for(j=0; j<QMAXARGS; j++) {
3730         if((it->sqlvar[j].sqldata=malloc(sizeof(short)+QMAXARGSIZE))==NULL) {
3731             com_err(whoami, MR_NO_MEM, "setting up SQLDA variables");
3732             exit(1);
3733         }
3734         it->sqlvar[j].sqllen=QMAXARGSIZE;
3735         it->sqlvar[j].sqlind=null_indicators+j;
3736         null_indicators[j]=0;
3737     }
3738     it->sqln=QMAXARGS;
3739     return it;
3740 }
3741
3742
3743 /* Use mr_check_SQLDA before OPEN CURSOR or EXECUTE */
3744 mr_check_SQLDA(da)
3745     MR_SQLDA_T *da;
3746 {
3747     if(da->sqld > da->sqln) {
3748         com_err(whoami, MR_INTERNAL,
3749                 "Internal arg count error preparing for dynamic query");
3750         return(MR_INTERNAL);
3751     }
3752
3753     return(MR_SUCCESS);
3754 }
3755
3756 /* Use this after FETCH USING DESCRIPTOR one or more 
3757  * result columns may contain NULLs.  This routine is
3758  * not currently needed, since db/schema creates all
3759  * columns with a NOT NULL WITH DEFAULT clause.
3760  */
3761 mr_fix_nulls_in_SQLDA(da)
3762     MR_SQLDA_T *da;
3763 {
3764     register IISQLVAR *var;
3765     register int j;
3766     int *intp;
3767
3768     for(j=0, var=da->sqlvar; j<da->sqld; j++, var++) {
3769         switch(var->sqltype) {
3770           case -IISQ_CHA_TYPE:
3771             if(*var->sqlind)
3772               *var->sqldata='\0';
3773             break;
3774           case -IISQ_INT_TYPE:
3775             if(*var->sqlind) {
3776                 intp=(int *)var->sqldata;
3777                 *intp=0;
3778             }
3779             break;
3780         } 
3781     }
3782 }
3783
3784 /* prefetch_value():
3785  * This routine fetches an appropriate value from the numvalues table.
3786  * It is a little hack to get around the fact that SQL doesn't let you
3787  * do something like INSERT INTO table (foo) VALUES (other_table.bar).
3788  *
3789  * It is called from the query table as (*v->pre_rtn)(q,Argv,cl) or
3790  * from within a setup_...() routine with the appropriate arguments.
3791  *
3792  * Correct functioning of this routine may depend on the assumption
3793  * that this query is an APPEND.
3794  */ 
3795
3796 prefetch_value(q,argv,cl)
3797     struct query *q;
3798     char **argv;
3799     client *cl;
3800 {
3801     EXEC SQL BEGIN DECLARE SECTION; 
3802     char *name = q->validate->object_id;
3803     int value;
3804     EXEC SQL END DECLARE SECTION; 
3805     int status, limit, argc;
3806
3807     /* set next object id, limiting it if necessary */
3808     if(!strcmp(name, "uid") || !strcmp(name, "gid"))  
3809       limit = 1; /* So far as I know, this isn't needed.  Just CMA. */
3810     else 
3811       limit = 0;
3812     if((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS)
3813       return(status);
3814
3815     /* fetch object id */
3816     EXEC SQL SELECT value INTO :value FROM numvalues WHERE name=:name;
3817     if(ingres_errno) return(mr_errcode);
3818     if(sqlca.sqlerrd[2] != 1) return(MR_INTERNAL);
3819
3820     argc = q->argc + q->vcnt;   /* end of Argv for APPENDs */
3821     sprintf(argv[argc],"%d",value);
3822
3823     return(MR_SUCCESS);
3824 }
3825
3826 /* eof:qsupport.dc */
This page took 0.340568 seconds and 5 git commands to generate.