]> andersk Git - moira.git/blob - server/qsupport.dc
92b7d76f5a515a12db092b66dbcbac23678e0451
[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 z.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_aftg: needed only because using the query table would
918  * create an invalid SQL statement.  *sigh*  Basically just validates
919  * that the first argument has type 'FSGROUP' in table 'filesys'.
920  */
921
922 setup_aftg(q, argv, cl)
923     struct query  *q;
924     char **argv;
925     client *cl;
926 {
927     EXEC SQL SELECT filsys_id INTO :idummy FROM filesys
928       WHERE type='FSGROUP';
929
930     if (sqlca.sqlcode != 0)
931       return(MR_NO_MATCH);    /* Is there a better error code to return? */
932
933     return(MR_SUCCESS);
934 }
935
936
937 /* setup_dnfp: check to see that the nfs physical partition does not have
938  * any filesystems assigned to it before allowing it to be deleted.
939  */
940
941 setup_dnfp(q, argv, cl)
942     struct query  *q;
943     char **argv;
944     client *cl;
945 {
946     EXEC SQL BEGIN DECLARE SECTION; 
947     int id;
948     char *dir;
949     EXEC SQL END DECLARE SECTION; 
950
951     id = *(int *)argv[0];
952     dir = argv[1];
953     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys fs, nfsphys np
954       WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id 
955         AND np.mach_id = :id AND np.dir = :dir;
956     if (sqlca.sqlerrd[2] > 0)
957       return(MR_IN_USE);
958     if (ingres_errno)
959       return(mr_errcode);
960     return(MR_SUCCESS);
961 }
962
963
964 /* setup_dqot: Remove allocation from nfsphys before deleting quota.
965  *   argv[0] = filsys_id
966  *   argv[1] = type if "update_quota" or "delete_quota"
967  *   argv[2 or 1] = users_id or list_id
968  */
969
970 setup_dqot(q, argv, cl)
971     struct query  *q;
972     char **argv;
973     client *cl;
974 {
975     EXEC SQL BEGIN DECLARE SECTION; 
976     int quota, fs, id;
977     char *qtype;
978     EXEC SQL END DECLARE SECTION; 
979
980     fs = *(int *)argv[0];
981     if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota")) {
982         qtype = argv[1];
983         id = *(int *)argv[2];
984     } else {
985         qtype = "USER";
986         id = *(int *)argv[1];
987     }
988
989     EXEC SQL REPEATED SELECT quota INTO :quota FROM quota
990       WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs;
991     EXEC SQL REPEATED UPDATE nfsphys 
992       SET allocated = nfsphys.allocated - :quota
993       WHERE nfsphys_id = filesys.physid AND filesys.filsys_id = :fs;
994     
995     if (ingres_errno) return(mr_errcode);
996     return(MR_SUCCESS);
997 }
998
999
1000 /* setup_sshi: don't exclusive lock the machine table during
1001  * set_server_host_internal.
1002  */
1003
1004 setup_sshi(q, argv, cl)
1005     struct query  *q;
1006     char **argv;
1007     client *cl;
1008 {
1009 #ifsql INGRES    
1010     EXEC SQL set lockmode session where readlock = system;
1011 #endsql
1012 }
1013
1014
1015 /* setup add_kerberos_user_mapping: add the string to the string 
1016  * table if necessary.
1017  */
1018
1019 setup_akum(q, argv, cl)
1020 struct query *q;
1021 char **argv;
1022 client *cl;
1023 {
1024     EXEC SQL BEGIN DECLARE SECTION; 
1025     int id, rowcount;
1026     char *name;
1027     EXEC SQL END DECLARE SECTION; 
1028
1029     name = argv[1];
1030     if (name_to_id(name, "STRING", &id) != MR_SUCCESS) {
1031         if (q->type != APPEND) return(MR_STRING);
1032         EXEC SQL SELECT value INTO :id FROM numvalues
1033           WHERE name = 'strings_id';
1034         id++;
1035         EXEC SQL UPDATE numvalues SET value = :id
1036           WHERE name = 'strings_id';
1037         EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
1038         cache_entry(name, "STRING", id);
1039     }
1040     if (ingres_errno) return(mr_errcode);
1041     *(int *)argv[1] = id;
1042     return(MR_SUCCESS);
1043 }
1044
1045
1046 \f
1047 /* FOLLOWUP ROUTINES */
1048
1049 /* generic set_modtime routine.  This takes the table name from the query,
1050  * and will update the modtime, modby, and modwho fields in the entry in
1051  * the table whose name field matches argv[0].
1052  */
1053
1054 set_modtime(q, argv, cl)
1055     struct query *q;
1056     char *argv[];
1057     client *cl;
1058 {
1059     char *name, *entity, *table;
1060     int who;
1061
1062     entity = cl->entity;
1063     who = cl->client_id;
1064     table = q->rtable;
1065     name = argv[0];
1066
1067     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, modwith = '%s' WHERE %s.name = LEFT('%s',SIZE(%s.name))",table,who,entity,table,name,table);
1068     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1069
1070     return(MR_SUCCESS);
1071 }
1072
1073 /* generic set_modtime_by_id routine.  This takes the table name from
1074  * the query, and the id name from the validate record,
1075  * and will update the modtime, modby, and modwho fields in the entry in
1076  * the table whose id matches argv[0].
1077  */
1078
1079 set_modtime_by_id(q, argv, cl)
1080     struct query *q;
1081     char **argv;
1082     client *cl;
1083 {
1084     char *entity, *table, *id_name;
1085     int who, id;
1086
1087     entity = cl->entity;
1088     who = cl->client_id;
1089     table = q->rtable;
1090     id_name = q->validate->object_id;
1091
1092     id = *(int *)argv[0];
1093     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, \
1094 modwith = '%s' WHERE %s.%s = %d",table,who,entity,table,id_name,id);
1095     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1096     return(MR_SUCCESS);
1097 }
1098
1099
1100 /* Sets the finger modtime on a user record.  The users_id will be in argv[0].
1101  */
1102
1103 set_finger_modtime(q, argv, cl)
1104     struct query *q;
1105     char *argv[];
1106     client *cl;
1107 {
1108     EXEC SQL BEGIN DECLARE SECTION; 
1109     int users_id, who;
1110     char *entity;
1111     EXEC SQL END DECLARE SECTION; 
1112
1113     entity = cl->entity;
1114     who = cl->client_id;
1115     users_id = *(int *)argv[0];
1116
1117     EXEC SQL UPDATE users SET fmodtime='now', fmodby = :who, fmodwith = :entity
1118       WHERE users.users_id = :users_id;
1119
1120    return(MR_SUCCESS);
1121 }
1122
1123
1124 /* Sets the pobox modtime on a user record.  The users_id will be in argv[0].
1125  */
1126
1127 set_pobox_modtime(q, argv, cl)
1128     struct query *q;
1129     char **argv;
1130     client *cl;
1131 {
1132     EXEC SQL BEGIN DECLARE SECTION; 
1133     int users_id, who;
1134     char *entity;
1135     EXEC SQL END DECLARE SECTION; 
1136
1137     entity = cl->entity;
1138     who = cl->client_id;
1139     users_id = *(int *)argv[0];
1140
1141     EXEC SQL UPDATE users SET pmodtime='now', pmodby = :who, pmodwith = :entity
1142       WHERE users.users_id = :users_id;
1143
1144     return(MR_SUCCESS);
1145 }
1146
1147
1148 /* Like set_modtime, but uppercases the name first.
1149  */
1150
1151 set_uppercase_modtime(q, argv, cl)
1152     struct query *q;
1153     char **argv;
1154     client *cl;
1155 {
1156     char *name, *entity, *table;
1157     int who;
1158
1159     entity = cl->entity;
1160     who = cl->client_id;
1161     table = q->rtable;
1162     name = argv[0];
1163
1164     sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, modwith = '%s' WHERE %s.name = UPPERCASE(LEFT('%s',SIZE(%s.name)))",table,who,entity,table,name,table);
1165     EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
1166     
1167     return(MR_SUCCESS);
1168 }
1169
1170
1171 /* Sets the modtime on the machine whose mach_id is in argv[0].  This routine
1172  * is necessary for add_machine_to_cluster becuase the table that query
1173  * operates on is "mcm", not "machine".
1174  */
1175
1176 set_mach_modtime_by_id(q, argv, cl)
1177     struct query *q;
1178     char **argv;
1179     client *cl;
1180 {
1181     EXEC SQL BEGIN DECLARE SECTION; 
1182     char *entity;
1183     int who, id;
1184     EXEC SQL END DECLARE SECTION; 
1185
1186     entity = cl->entity;
1187     who = cl->client_id;
1188     id = *(int *)argv[0];
1189     EXEC SQL UPDATE machine SET modtime='now', modby = :who, modwith = :entity
1190       WHERE machine.mach_id = :id;
1191     
1192     return(MR_SUCCESS);
1193 }
1194
1195
1196 /* Sets the modtime on the cluster whose mach_id is in argv[0].  This routine
1197  * is necessary for add_cluster_data and delete_cluster_data becuase the
1198  * table that query operates on is "svc", not "cluster".
1199  */
1200
1201 set_cluster_modtime_by_id(q, argv, cl)
1202     struct query *q;
1203     char **argv;
1204     client *cl;
1205 {
1206     EXEC SQL BEGIN DECLARE SECTION; 
1207     char *entity;
1208     int who, id;
1209     EXEC SQL END DECLARE SECTION; 
1210
1211     entity = cl->entity;
1212     who = cl->client_id;
1213
1214     id = *(int *)argv[0];
1215     EXEC SQL UPDATE cluster SET modtime='now', modby = :who, modwith = :entity
1216       WHERE cluster.clu_id = :id;
1217     return(MR_SUCCESS);
1218 }
1219
1220
1221 /* sets the modtime on the serverhost where the service name is in argv[0]
1222  * and the mach_id is in argv[1].
1223  */
1224
1225 set_serverhost_modtime(q, argv, cl)
1226     struct query *q;
1227     char **argv;
1228     client *cl;
1229 {
1230     EXEC SQL BEGIN DECLARE SECTION; 
1231     char *entity, *serv;
1232     int who, id;
1233     EXEC SQL END DECLARE SECTION; 
1234
1235     entity = cl->entity;
1236     who = cl->client_id;
1237
1238     serv = argv[0];
1239     id = *(int *)argv[1];
1240     EXEC SQL UPDATE serverhosts 
1241       SET modtime = 'now', modby = :who, modwith = :entity
1242       WHERE service = :serv AND mach_id = :id; 
1243     return(MR_SUCCESS);
1244 }
1245
1246
1247 /* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
1248  * directory name is in argv[1].
1249  */
1250
1251 set_nfsphys_modtime(q, argv, cl)
1252     struct query *q;
1253     char **argv;
1254     client *cl;
1255 {
1256     EXEC SQL BEGIN DECLARE SECTION; 
1257     char *entity, *dir;
1258     int who, id;
1259     EXEC SQL END DECLARE SECTION; 
1260
1261     entity = cl->entity;
1262     who = cl->client_id;
1263
1264     id = *(int *)argv[0];
1265     dir = argv[1];
1266     EXEC SQL UPDATE nfsphys SET modtime = 'now', modby = :who, modwith = :entity
1267       WHERE dir = :dir AND mach_id = :id;
1268     return(MR_SUCCESS);
1269 }
1270
1271
1272 /* sets the modtime on a filesystem, where argv[0] contains the filesys
1273  * label.
1274  */
1275
1276 set_filesys_modtime(q, argv, cl)
1277     struct query *q;
1278     char *argv[];
1279     client *cl;
1280 {
1281     EXEC SQL BEGIN DECLARE SECTION; 
1282     char *label, *entity;
1283     int who;
1284     EXEC SQL END DECLARE SECTION; 
1285
1286     entity = cl->entity;
1287     who = cl->client_id;
1288
1289     label = argv[0];
1290     if (!strcmp(q->shortname, "ufil"))
1291       label = argv[1];
1292
1293     EXEC SQL UPDATE filesys SET modtime = 'now', modby = :who, 
1294         modwith = :entity, phys_id = :var_phys_id
1295       WHERE label = LEFT(:label,SIZE(label));
1296     return(MR_SUCCESS);
1297 }
1298
1299
1300 /* sets the modtime on a zephyr class, where argv[0] contains the class
1301  * name.
1302  */
1303
1304 set_zephyr_modtime(q, argv, cl)
1305     struct query *q;
1306     char *argv[];
1307     client *cl;
1308 {
1309     EXEC SQL BEGIN DECLARE SECTION; 
1310     char *class, *entity;
1311     int who;
1312     EXEC SQL END DECLARE SECTION; 
1313
1314     entity = cl->entity;
1315     who = cl->client_id;
1316
1317     class = argv[0];
1318
1319     EXEC SQL UPDATE zephyr SET modtime = 'now', modby = :who, modwith = :entity
1320       WHERE class = LEFT(:class,SIZE(class));
1321     
1322     return(MR_SUCCESS);
1323 }
1324
1325
1326 /* fixes the modby field.  This will be the second to last thing in the
1327  * argv, the argv length is determined from the query structure.  It is
1328  * passed as a pointer to an integer.  This will either turn it into a
1329  * username, or # + the users_id.
1330  */
1331 followup_fix_modby(q, sq, v, action, actarg, cl)
1332     struct query *q;
1333     register struct save_queue *sq;
1334     struct validate *v;
1335     register int (*action)();
1336     register int actarg;
1337     client *cl;
1338 {
1339     register int i, j;
1340     char **argv, *malloc();
1341     int id, status;
1342
1343     i = q->vcnt - 2;
1344     while (sq_get_data(sq, &argv)) {
1345         id = atoi(argv[i]);
1346         if (id > 0)
1347           status = id_to_name(id, "USER", &argv[i]);
1348         else
1349           status = id_to_name(-id, "STRING", &argv[i]);
1350         if (status && status != MR_NO_MATCH)
1351           return(status);
1352         (*action)(q->vcnt, argv, actarg);
1353         for (j = 0; j < q->vcnt; j++)
1354           free(argv[j]);
1355         free(argv);
1356     }
1357     sq_destroy(sq);
1358     return(MR_SUCCESS);
1359 }
1360
1361
1362 /* After retrieving a user account, fix the modby field and signature.
1363  * The modby field is the second to last thing in the
1364  * argv, the argv length is determined from the query structure.  It is
1365  * passed as a pointer to an integer.  This will either turn it into a
1366  * username, or # + the users_id.  Only "gua*" queries have a signature,
1367  * these are ones with U_END return values.  "gub*" queries also use this
1368  * routine but don't have a signature.
1369  */
1370 followup_guax(q, sq, v, action, actarg, cl)
1371     struct query *q;
1372     register struct save_queue *sq;
1373     struct validate *v;
1374     register int (*action)();
1375     register int actarg;
1376     client *cl;
1377 {
1378     register int i, j;
1379     char **argv, *malloc();
1380 #ifdef GDSS
1381     char sigbuf[256], *rawsig, *kname;
1382     SigInfo  si;
1383 #endif /* GDSS */
1384     int id, status;
1385
1386     i = q->vcnt - 2;
1387     while (sq_get_data(sq, &argv)) {
1388         id = atoi(argv[i]);
1389         if (id > 0)
1390           status = id_to_name(id, "USER", &argv[i]);
1391         else
1392           status = id_to_name(-id, "STRING", &argv[i]);
1393         if (status && status != MR_NO_MATCH)
1394           return(status);
1395 #ifdef GDSS
1396         if (q->vcnt == U_END) {
1397             com_err(whoami, 0, "compressing signature");
1398             rawsig = argv[U_SIGNATURE];
1399             bcopy(&rawsig[0], &id, sizeof(int));
1400             id = ntohl(id);
1401             status = id_to_name(id, "STRING", &kname);
1402             bcopy(&rawsig[4], &si.timestamp, sizeof(int));
1403             si.timestamp = ntohl(si.timestamp);
1404             si.SigInfoVersion = 0; /* XXXXX this isn't used */
1405             kname_parse(si.pname, si.pinst, si.prealm, kname);
1406             si.rawsig = (unsigned char *)&rawsig[8];
1407             GDSS_Recompose(&si, sigbuf);
1408             argv[U_SIGNATURE] = strsave(sigbuf);
1409         }
1410 #endif /* GDSS */
1411         (*action)(q->vcnt, argv, actarg);
1412         for (j = 0; j < q->vcnt; j++)
1413           free(argv[j]);
1414         free(argv);
1415     }
1416     sq_destroy(sq);
1417     return(MR_SUCCESS);
1418 }
1419
1420
1421 /**
1422  ** followup_ausr - add finger and pobox entries, set_user_modtime
1423  **
1424  ** Inputs:
1425  **     argv[0] - login (add_user)
1426  **     argv[3] - last name
1427  **     argv[4] - first name
1428  **     argv[5] - middle name
1429  **
1430  **/
1431
1432 followup_ausr(q, argv, cl)
1433     struct query *q;
1434     char *argv[];
1435     client *cl;
1436 {
1437     EXEC SQL BEGIN DECLARE SECTION; 
1438     int who, status, sigwho, id;
1439     char *login, *entity, *src, *dst, *name;
1440     char fullname[129];
1441     EXEC SQL END DECLARE SECTION; 
1442 #ifdef GDSS
1443     char databuf[32], *kname_unparse();
1444     EXEC SQL BEGIN DECLARE SECTION; 
1445     char rawsig[128];
1446     EXEC SQL END DECLARE SECTION; 
1447     SigInfo si;
1448 #endif /* GDSS */
1449
1450     /* build fullname */
1451     if (strlen(argv[4]) && strlen(argv[5]))
1452         sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
1453     else if (strlen(argv[4]))
1454         sprintf(fullname, "%s %s", argv[4], argv[3]);
1455     else
1456         sprintf(fullname, "%s", argv[3]);
1457
1458 #ifdef GDSS
1459     sprintf(databuf, "%s:%s", argv[U_NAME], argv[U_MITID]);
1460     /* skip bytes for timestamp & kname */
1461     si.rawsig = (unsigned char *)&rawsig[8];
1462     status = GDSS_Verify(databuf, strlen(databuf), argv[U_SIGNATURE], &si);
1463     if (status == 0) {
1464         name = kname_unparse(si.pname, si.pinst, si.prealm);
1465         status = name_to_id(name, "STRING", &sigwho);
1466         if (status == MR_NO_MATCH) {
1467             EXEC SQL REPEATED SELECT value INTO :sigwho FROM numvalues
1468               WHERE name = 'strings_id';
1469             sigwho++;
1470             EXEC SQL REPEATED UPDATE numvalues SET value = :sigwho
1471               WHERE name = 'strings_id';
1472             EXEC SQL INSERT INTO strings (string_id, string)
1473               VALUES (:sigwho, :name);
1474         } else if (status)
1475           return(status);
1476         sigwho = htonl(sigwho);
1477         bcopy(&sigwho, &rawsig[0], sizeof(int));
1478         si.timestamp = htonl(si.timestamp);
1479         bcopy(&si.timestamp, &rawsig[4], sizeof(int));
1480      } else
1481        return(status);
1482 #endif /* GDSS */
1483
1484     login = argv[0];
1485     who = cl->client_id;
1486     entity = cl->entity;
1487
1488     /* create finger entry, pobox & set modtime on user */
1489 #ifdef GDSS
1490     EXEC SQL REPEATED UPDATE users 
1491       SET modtime='now', modby=:who, modwith = :entity, 
1492           fullname = :fullname, affiliation = type, 
1493           signature = :rawsig, 
1494           fmodtime='now', fmodby = :who, fmodwith = :entity,
1495           potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
1496       WHERE login = :login;
1497 #else /* GDSS */
1498     EXEC SQL REPEATED UPDATE users
1499       SET modtime='now', modby=:who, modwith = :entity, 
1500           fullname = :fullname, affiliation = type, 
1501           fmodtime='now', fmodby = :who, fmodwith = :entity,
1502           potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
1503       WHERE login = :login;
1504 #endif /* GDSS */
1505
1506     return(MR_SUCCESS);
1507 }
1508
1509
1510 /* followup_gpob: fixes argv[2] based on the IDs currently there and the
1511  * type in argv[1].  Then completes the upcall to the user.
1512  *
1513  * argv[2] is of the form "123:234" where the first integer is the machine
1514  * ID if it is a pop box, and the second is the string ID if it is an SMTP
1515  * box.  argv[1] should be "POP", "SMTP", or "NONE".  Boxes of type NONE
1516  * are skipped.
1517  */
1518
1519 followup_gpob(q, sq, v, action, actarg, cl)
1520     register struct query *q;
1521     register struct save_queue *sq;
1522     register struct validate *v;
1523     register int (*action)();
1524     int actarg;
1525     client *cl;
1526 {
1527     char **argv, *index();
1528     char *ptype, *p;
1529     int mid, sid, status, i;
1530
1531     /* for each row */
1532     while (sq_get_data(sq, &argv)) {
1533         mr_trim_args(2, argv);
1534         ptype = argv[1];
1535         p = index(argv[2], ':');
1536         *p++ = 0;
1537         mid = atoi(argv[2]);
1538         sid = atoi(p);
1539
1540         if (!strcmp(ptype, "POP")) {
1541             status = id_to_name(mid, "MACHINE", &argv[2]);
1542             if (status == MR_NO_MATCH)
1543               return(MR_MACHINE);
1544         } else if (!strcmp(ptype, "SMTP")) {
1545             status = id_to_name(sid, "STRING", &argv[2]);
1546             if (status == MR_NO_MATCH)
1547               return(MR_STRING);
1548         } else /* ptype == "NONE" */ {
1549             goto skip;
1550         }
1551         if (status) return(status);
1552
1553         if (!strcmp(q->shortname, "gpob")) {
1554             sid = atoi(argv[4]);
1555             if (sid > 0)
1556               status = id_to_name(sid, "USER", &argv[4]);
1557             else
1558               status = id_to_name(-sid, "STRING", &argv[4]);
1559         }
1560         if (status && status != MR_NO_MATCH) return(status);
1561
1562         (*action)(q->vcnt, argv, actarg);
1563     skip:
1564         /* free saved data */
1565         for (i = 0; i < q->vcnt; i++) 
1566             free(argv[i]);
1567         free(argv);
1568     }
1569
1570     sq_destroy(sq);
1571     return (MR_SUCCESS);
1572 }
1573
1574
1575 /* followup_glin: fix the ace_name in argv[8].  argv[7] will contain the
1576  * ace_type: "LIST", "USER", or "NONE".  Decode the id in argv[8] into the
1577  * proper name based on the type, and repace that string in the argv.
1578  * Also fixes the modby field by called followup_fix_modby.
1579  */
1580
1581 followup_glin(q, sq, v, action, actarg, cl)
1582     register struct query *q;
1583     register struct save_queue *sq;
1584     register struct validate *v;
1585     register int (*action)();
1586     int actarg;
1587     client *cl;
1588 {
1589     char **argv, *malloc(), *realloc(), *type;
1590     int id, i, idx, status;
1591
1592     idx = 8;
1593     if (!strcmp(q->shortname, "gsin"))
1594       idx = 12;
1595
1596     while (sq_get_data(sq, &argv)) {
1597         mr_trim_args(q->vcnt, argv);
1598
1599         id = atoi(argv[i = q->vcnt - 2]);
1600         if (id > 0)
1601           status = id_to_name(id, "USER", &argv[i]);
1602         else
1603           status = id_to_name(-id, "STRING", &argv[i]);
1604         if (status && status != MR_NO_MATCH)
1605           return(status);
1606
1607         id = atoi(argv[idx]);
1608         type = argv[idx - 1];
1609
1610         if (!strcmp(type, "LIST")) {
1611             status = id_to_name(id, "LIST", &argv[idx]);
1612         } else if (!strcmp(type, "USER")) {
1613             status = id_to_name(id, "USER", &argv[idx]);
1614         } else if (!strcmp(type, "KERBEROS")) {
1615             status = id_to_name(id, "STRING", &argv[idx]);
1616         } else if (!strcmp(type, "NONE")) {
1617             status = 0;
1618             free(argv[idx]);
1619             argv[idx] = strsave("NONE");
1620         } else {
1621             status = 0;
1622             free(argv[idx]);
1623             argv[idx] = strsave("???");
1624         }
1625         if (status && status != MR_NO_MATCH)
1626           return(status);
1627
1628         if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
1629             argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
1630             strcpy(argv[6], UNIQUE_GID);
1631         }
1632
1633         /* send the data */
1634         (*action)(q->vcnt, argv, actarg);
1635
1636         /* free saved data */
1637         for (i = 0; i < q->vcnt; i++) 
1638             free(argv[i]);
1639         free(argv);
1640     }
1641
1642     sq_destroy(sq);
1643     return (MR_SUCCESS);
1644 }
1645
1646
1647 /* followup_gqot: Fix the entity name, directory name & modby fields
1648  *   argv[0] = filsys_id
1649  *   argv[1] = type
1650  *   argv[2] = entity_id
1651  *   argv[3] = ascii(quota)
1652  */
1653
1654 followup_gqot(q, sq, v, action, actarg, cl)
1655     struct query *q;
1656     register struct save_queue *sq;
1657     struct validate *v;
1658     register int (*action)();
1659     register int actarg;
1660     client *cl;
1661 {
1662     register int j;
1663     char **argv, *malloc();
1664     EXEC SQL BEGIN DECLARE SECTION; 
1665     int id;
1666     char *name, *label;
1667     EXEC SQL END DECLARE SECTION; 
1668     int status, idx;
1669
1670     if (!strcmp(q->name, "get_quota") ||
1671         !strcmp(q->name, "get_quota_by_filesys"))
1672       idx = 4;
1673     else
1674       idx = 3;
1675     while (sq_get_data(sq, &argv)) {
1676         if (idx == 4) {
1677             switch (argv[1][0]) {
1678             case 'U':
1679                 status = id_to_name(atoi(argv[2]), "USER", &argv[2]);
1680                 break;
1681             case 'G':
1682             case 'L':
1683                 status = id_to_name(atoi(argv[2]), "LIST", &argv[2]);
1684                 break;
1685             case 'A':
1686                 free(argv[2]);
1687                 argv[2] = strsave("system:anyuser");
1688                 break;
1689             default:
1690                 id = atoi(argv[2]);
1691                 argv[2] = malloc(8);
1692                 sprintf(argv[2], "%d", id);
1693             }
1694         }
1695         id = atoi(argv[idx]);
1696         free(argv[idx]);
1697         argv[idx] = malloc(256);
1698         name = argv[idx];
1699         if (id == 0) {
1700             label = argv[0];
1701             EXEC SQL REPEATED SELECT name INTO :name FROM filesys 
1702               WHERE label = :label;
1703         } else {
1704             EXEC SQL REPEATED SELECT dir INTO :name FROM nfsphys 
1705               WHERE nfsphys_id = :id;
1706         }
1707         if (sqlca.sqlerrd[2] != 1) {
1708             sprintf(argv[idx], "#%d", id);
1709         }
1710
1711         id = atoi(argv[idx+3]);
1712         if (id > 0)
1713           status = id_to_name(id, "USER", &argv[idx+3]);
1714         else
1715           status = id_to_name(-id, "STRING", &argv[idx+3]);
1716         if (status && status != MR_NO_MATCH)
1717           return(status);
1718         (*action)(q->vcnt, argv, actarg);
1719         for (j = 0; j < q->vcnt; j++)
1720           free(argv[j]);
1721         free(argv);
1722     }
1723     sq_destroy(sq);
1724     return(MR_SUCCESS);
1725 }
1726
1727
1728 /* followup_aqot: Add allocation to nfsphys after creating quota.
1729  *   argv[0] = filsys_id
1730  *   argv[1] = type if "add_quota" or "update_quota"
1731  *   argv[2 or 1] = id
1732  *   argv[3 or 2] = ascii(quota)
1733  */
1734
1735 followup_aqot(q, argv, cl)
1736     struct query  *q;
1737     char **argv;
1738     client *cl;
1739 {
1740     EXEC SQL BEGIN DECLARE SECTION; 
1741     int quota, id, fs, who;
1742     char *entity, *qtype;
1743     EXEC SQL END DECLARE SECTION; 
1744
1745     fs = *(int *)argv[0];
1746     if (!strcmp(q->name, "add_quota") || !strcmp(q->name, "update_quota")) {
1747         qtype = argv[1];
1748         id = *(int *)argv[2];
1749         quota = atoi(argv[3]);
1750     } else {
1751         qtype = "USER";
1752         id = *(int *)argv[1];
1753         quota = atoi(argv[2]);
1754     }
1755     who = cl->client_id;
1756     entity = cl->entity;
1757
1758     EXEC SQL REPEATED UPDATE quota
1759       SET modtime = 'now', modby = :who, modwith = :entity
1760       WHERE filsys_id = :fs and type = :qtype and entity_id = :id;
1761     EXEC SQL REPEATED UPDATE nfsphys
1762       SET allocated = allocated + :quota
1763       WHERE nfsphys_id = filesys.phys_id AND filesys.filsys_id = :fs;
1764     if (ingres_errno) return(mr_errcode);
1765     return(MR_SUCCESS);
1766 }
1767
1768
1769 followup_gpce(q, sq, v, action, actarg, cl)
1770     struct query *q;
1771     register struct save_queue *sq;
1772     struct validate *v;
1773     register int (*action)();
1774     register int actarg;
1775     client *cl;
1776 {
1777     register int i, j;
1778     char **argv, *malloc();
1779     int id, status;
1780
1781     i = q->vcnt - 2;
1782     while (sq_get_data(sq, &argv)) {
1783         id = atoi(argv[PCAP_QSERVER]);
1784         status = id_to_name(id, "MACHINE", &argv[PCAP_QSERVER]);
1785         if (status) return (status);
1786         id = atoi(argv[i]);
1787         if (id > 0)
1788           status = id_to_name(id, "USER", &argv[i]);
1789         else
1790           status = id_to_name(-id, "STRING", &argv[i]);
1791         if (status && status != MR_NO_MATCH)
1792           return(status);
1793         (*action)(q->vcnt, argv, actarg);
1794         for (j = 0; j < q->vcnt; j++)
1795           free(argv[j]);
1796         free(argv);
1797     }
1798     sq_destroy(sq);
1799     return(MR_SUCCESS);
1800 }
1801
1802
1803 /* followup_gzcl:
1804  */
1805
1806 followup_gzcl(q, sq, v, action, actarg, cl)
1807     register struct query *q;
1808     register struct save_queue *sq;
1809     register struct validate *v;
1810     register int (*action)();
1811     int actarg;
1812     client *cl;
1813 {
1814     int id, i, status;
1815     char **argv;
1816
1817     while (sq_get_data(sq, &argv)) {
1818         mr_trim_args(q->vcnt, argv);
1819
1820         id = atoi(argv[i = q->vcnt - 2]);
1821         if (id > 0)
1822           status = id_to_name(id, "USER", &argv[i]);
1823         else
1824           status = id_to_name(-id, "STRING", &argv[i]);
1825         if (status && status != MR_NO_MATCH)
1826           return(status);
1827
1828         for (i = 1; i < 8; i+=2) {
1829             id = atoi(argv[i+1]);
1830             if (!strcmp(argv[i], "LIST")) {
1831                 status = id_to_name(id, "LIST", &argv[i+1]);
1832             } else if (!strcmp(argv[i], "USER")) {
1833                 status = id_to_name(id, "USER", &argv[i+1]);
1834             } else if (!strcmp(argv[i], "KERBEROS")) {
1835                 status = id_to_name(id, "STRING", &argv[i+1]);
1836             } else if (!strcmp(argv[i], "NONE")) {
1837                 status = 0;
1838                 free(argv[i+1]);
1839                 argv[i+1] = strsave("NONE");
1840             } else {
1841                 status = 0;
1842                 free(argv[i+1]);
1843                 argv[i+1] = strsave("???");
1844             }
1845             if (status && status != MR_NO_MATCH)
1846               return(status);
1847         }
1848
1849         /* send the data */
1850         (*action)(q->vcnt, argv, actarg);
1851
1852         /* free saved data */
1853         for (i = 0; i < q->vcnt; i++) 
1854             free(argv[i]);
1855         free(argv);
1856     }
1857     sq_destroy(sq);
1858     return(MR_SUCCESS);
1859 }
1860
1861
1862 /* followup_gsha:
1863  */
1864
1865 followup_gsha(q, sq, v, action, actarg, cl)
1866     register struct query *q;
1867     register struct save_queue *sq;
1868     register struct validate *v;
1869     register int (*action)();
1870     int actarg;
1871     client *cl;
1872 {
1873     char **argv;
1874     int i, id, status;
1875
1876     while (sq_get_data(sq, &argv)) {
1877         mr_trim_args(q->vcnt, argv);
1878
1879         id = atoi(argv[4]);
1880         if (id > 0)
1881           status = id_to_name(id, "USER", &argv[4]);
1882         else
1883           status = id_to_name(-id, "STRING", &argv[4]);
1884         if (status && status != MR_NO_MATCH)
1885           return(status);
1886
1887         id = atoi(argv[2]);
1888         if (!strcmp(argv[1], "LIST")) {
1889             status = id_to_name(id, "LIST", &argv[2]);
1890         } else if (!strcmp(argv[1], "USER")) {
1891             status = id_to_name(id, "USER", &argv[2]);
1892         } else if (!strcmp(argv[1], "KERBEROS")) {
1893             status = id_to_name(id, "STRING", &argv[2]);
1894         } else if (!strcmp(argv[1], "NONE")) {
1895             status = 0;
1896             free(argv[2]);
1897             argv[2] = strsave("NONE");
1898         } else {
1899             status = 0;
1900             free(argv[2]);
1901             argv[2] = strsave("???");
1902         }
1903         if (status && status != MR_NO_MATCH)
1904           return(status);
1905
1906         /* send the data */
1907         (*action)(q->vcnt, argv, actarg);
1908
1909         /* free saved data */
1910         for (i = 0; i < q->vcnt; i++) 
1911             free(argv[i]);
1912         free(argv);
1913     }
1914     sq_destroy(sq);
1915     return(MR_SUCCESS);
1916 }
1917
1918
1919 \f
1920 /* Special query routines */
1921
1922 /* set_pobox - this does all of the real work.
1923  *       argv = user_id, type, box
1924  * if type is POP, then box should be a machine, and its ID should be put in
1925  * pop_id.  If type is SMTP, then box should be a string and its ID should
1926  * be put in box_id.  If type is NONE, then box doesn't matter.
1927  */
1928
1929 int set_pobox(q, argv, cl)
1930     struct query *q;
1931     char **argv;
1932     client *cl;
1933 {
1934     EXEC SQL BEGIN DECLARE SECTION; 
1935     int user, id;
1936     char *box, potype[9];
1937     EXEC SQL END DECLARE SECTION; 
1938     int status;
1939
1940     box = argv[2];
1941     user = *(int *)argv[0];
1942
1943     EXEC SQL REPEATED SELECT pop_id, potype INTO :id, :potype FROM users
1944       WHERE users_id = :user;
1945     if (ingres_errno) return(mr_errcode);
1946     if (!strcmp(strtrim(potype), "POP"))
1947       set_pop_usage(id, -1);
1948
1949     if (!strcmp(argv[1], "POP")) {
1950         status = name_to_id(box, "MACHINE", &id);
1951         if (status == MR_NO_MATCH)
1952           return(MR_MACHINE);
1953         else if (status)
1954           return(status);
1955         EXEC SQL REPEATED UPDATE users SET potype = 'POP', pop_id = :id
1956           WHERE users_id = :user;
1957         set_pop_usage(id, 1);
1958     } else if (!strcmp(argv[1], "SMTP")) {
1959         if (index(box, '/') || index(box, '|'))
1960           return(MR_BAD_CHAR);
1961         status = name_to_id(box, "STRING", &id);
1962         if (status == MR_NO_MATCH) {
1963             EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
1964               WHERE name='strings_id';
1965             id++;
1966             EXEC SQL REPEATED UPDATE numvalues SET value = :id
1967               WHERE name='strings_id';
1968             EXEC SQL INSERT INTO strings (string_id, string)
1969               VALUES (:id, :box);
1970         } else if (status)
1971           return(status);
1972         EXEC SQL REPEATED UPDATE users SET potype='SMTP', box_id = :id
1973           WHERE users_id = :user;
1974     } else /* argv[1] == "NONE" */ {
1975         EXEC SQL REPEATED UPDATE users SET potype='NONE'
1976           WHERE users_id = :user;
1977     }
1978
1979     set_pobox_modtime(q, argv, cl);
1980     EXEC SQL REPEATED UPDATE tblstats SET updates = updates+1, modtime='now'
1981       WHERE tblstats.table_name='users';
1982     if (ingres_errno) return(mr_errcode);
1983     return(MR_SUCCESS);
1984 }
1985
1986
1987 /* get_list_info:  passed a wildcard list name, returns lots of stuff about
1988  * each list.  This is tricky:  first build a queue of all requested
1989  * data.  Rest of processing consists of fixing gid, ace_name, and modby.
1990  */
1991
1992 get_list_info(q, aargv, cl, action, actarg)
1993     register struct query *q;
1994     char **aargv;
1995     client *cl;
1996     register int (*action)();
1997     int actarg;
1998 {
1999     char *argv[13], *malloc(), *realloc();
2000     EXEC SQL BEGIN DECLARE SECTION; 
2001     char *name, acl_type[9], listname[33], active[5], public[5], hidden[5];
2002     char maillist[5], grouplist[5], gid_str[6], acl_name[256], desc[256];
2003     char modtime[27], modby[256], modwith[9];
2004     int id, rowcount, acl_id, hid, modby_id;
2005     EXEC SQL END DECLARE SECTION; 
2006     int returned, status;
2007     struct save_queue *sq, *sq_create();
2008
2009     returned = rowcount = 0;
2010     name = aargv[0];
2011     convert_wildcards(name);
2012
2013     sq = sq_create();
2014     EXEC SQL DECLARE csr102 CURSOR FOR SELECT list_id FROM list 
2015       WHERE name LIKE :name ESCAPE '*';
2016     EXEC SQL OPEN csr102;
2017     while(1)
2018     {
2019         EXEC SQL FETCH csr102 INTO :id;
2020         if(sqlca.sqlcode!=0) break;
2021         sq_save_data(sq, id);
2022         rowcount++;
2023     }
2024     EXEC SQL CLOSE csr102;
2025
2026     if (ingres_errno) return(mr_errcode);
2027     if (rowcount == 0)
2028       return(MR_NO_MATCH);
2029
2030     argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden;
2031     argv[4] = maillist; argv[5] = grouplist; argv[6] = gid_str; 
2032     argv[7] = acl_type; argv[9] = desc; argv[10] = modtime; argv[12] = modwith;
2033
2034     while (sq_get_data(sq, &id)) {
2035         if (id == 0)
2036           continue;
2037         argv[6] = gid_str;
2038         EXEC SQL REPEATED SELECT name, CHAR(active), CHAR(publicflg),
2039             CHAR(hidden), hidden, CHAR(maillist), CHAR(grouplist), CHAR(gid),
2040             TRIM(acl_type), acl_id, description, CHAR(modtime), modby, modwith 
2041           INTO :listname, :active, :public, :hidden, :hid, :maillist,
2042             :grouplist, :gid_str, :acl_type, :acl_id, :desc, 
2043             :modtime, :modby_id, :modwith
2044           FROM list WHERE list_id = :id;
2045
2046         if (ingres_errno) return(mr_errcode);
2047
2048         if (atoi(gid_str) == -1)
2049             argv[6] = UNIQUE_GID;
2050
2051         argv[8] = malloc(0);
2052         if (!strcmp(acl_type, "LIST")) {
2053             status = id_to_name(acl_id, "LIST", &argv[8]);
2054         } else if (!strcmp(acl_type, "USER")) {
2055             status = id_to_name(acl_id, "USER", &argv[8]);
2056         } else if (!strcmp(acl_type, "KERBEROS")) {
2057             status = id_to_name(acl_id, "STRING", &argv[8]);
2058         } else if (!strcmp(acl_type, "NONE")) {
2059             status = 0;
2060             free(argv[8]);
2061             argv[8] = strsave("NONE");
2062         } else {
2063             status = 0;
2064             free(argv[8]);
2065             argv[8] = strsave("???");
2066         }
2067         if (status && status != MR_NO_MATCH) return(status);
2068
2069         argv[11] = malloc(0);
2070         if (modby_id > 0)
2071           status = id_to_name(modby_id, "USER", &argv[11]);
2072         else
2073           status = id_to_name(-modby_id, "STRING", &argv[11]);
2074         if (status && status != MR_NO_MATCH) return(status);
2075
2076         mr_trim_args(q->vcnt, argv);
2077         returned++;
2078         (*action)(q->vcnt, argv, actarg);
2079         free(argv[8]);
2080         free(argv[11]);
2081     }
2082
2083     sq_destroy(sq);
2084     if (ingres_errno) return(mr_errcode);
2085     return (MR_SUCCESS);
2086 }
2087
2088
2089 /* Add_member_to_list: do list flattening as we go!  MAXLISTDEPTH is
2090  * how many different ancestors a member is allowed to have.
2091  */
2092
2093 #define MAXLISTDEPTH    1024
2094
2095 int add_member_to_list(q, argv, cl)
2096     struct query *q;
2097     char **argv;
2098     client *cl;
2099 {
2100     EXEC SQL BEGIN DECLARE SECTION; 
2101     int id, lid, mid, error, who, ref;
2102     char *mtype, dtype[9], *entity;
2103     EXEC SQL END DECLARE SECTION; 
2104     int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
2105     int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
2106     int status;
2107     char *dtypes[MAXLISTDEPTH];
2108     char *iargv[3], *buf;
2109
2110     lid = *(int *)argv[0];
2111     mtype = argv[1];
2112     mid = *(int *)argv[2];
2113     /* if the member is already a direct member of the list, punt */
2114     EXEC SQL REPEATED SELECT list_id INTO :idummy FROM imembers 
2115       WHERE list_id = :lid AND member_id = :mid 
2116         AND member_type = :mtype AND direct = 1;
2117     if (sqlca.sqlerrd[2] > 0)
2118       return(MR_EXISTS);
2119     if (!strcasecmp(mtype, "STRING")) {
2120         buf = malloc(0);
2121         status = id_to_name(mid, "STRING", &buf);
2122         if (status) return(status);
2123         if (index(buf, '/') || index(buf, '|')) {
2124             free(buf);
2125             return(MR_BAD_CHAR);
2126         }
2127         free(buf);
2128     }
2129
2130     ancestors[0] = lid;
2131     aref[0] = 1;
2132     acount = 1;
2133     EXEC SQL DECLARE csr103 CURSOR FOR
2134       SELECT list_id, ref_count FROM imembers
2135         WHERE member_id = :lid AND member_type='LIST';
2136     EXEC SQL OPEN csr103;
2137     while(1) {
2138         EXEC SQL FETCH csr103 INTO :id, :ref;
2139         if(sqlca.sqlcode != 0) break;
2140         aref[acount] = ref;
2141         ancestors[acount++] = id;
2142         if (acount >= MAXLISTDEPTH) break; 
2143     }
2144     EXEC SQL CLOSE csr103;
2145     if (ingres_errno) return(mr_errcode);
2146     if (acount >= MAXLISTDEPTH) {
2147         return(MR_INTERNAL);
2148     }
2149     descendants[0] = mid;
2150     dtypes[0] = mtype;
2151     dref[0] = 1;
2152     dcount = 1;
2153     error = 0;
2154     if (!strcmp(mtype, "LIST")) {
2155         EXEC SQL DECLARE csr104 CURSOR FOR 
2156           SELECT member_id, member_type, ref_count
2157           FROM imembers
2158           WHERE list_id = :mid;
2159         EXEC SQL OPEN csr104;
2160         while(1) {
2161             EXEC SQL FETCH csr104 INTO :id, :dtype, :ref;
2162             if(sqlca.sqlcode != 0) break;
2163             switch (dtype[0]) {
2164             case 'L':
2165                 dtypes[dcount] = "LIST";
2166                 break;
2167             case 'U':
2168                 dtypes[dcount] = "USER";
2169                 break;
2170             case 'S':
2171                 dtypes[dcount] = "STRING";
2172                 break;
2173             case 'K':
2174                 dtypes[dcount] = "KERBEROS";
2175                 break;
2176             default:
2177                 error++;
2178                 break;
2179             }
2180             dref[dcount] = ref;
2181             descendants[dcount++] = id;
2182             if (dcount >= MAXLISTDEPTH) {
2183                 error++;
2184                 break;
2185             }
2186         }
2187         EXEC SQL CLOSE csr104;
2188         if (ingres_errno) return(mr_errcode);
2189         if (error)
2190           return(MR_INTERNAL);
2191     }
2192     for (a = 0; a < acount; a++) {
2193         lid = ancestors[a];
2194         for (d = 0; d < dcount; d++) {
2195             mid = descendants[d];
2196             mtype = dtypes[d];
2197             if (mid == lid && !strcmp(mtype, "LIST")) {
2198                 return(MR_LISTLOOP);
2199             }
2200             EXEC SQL REPEATED SELECT ref_count INTO :idummy FROM imembers
2201               WHERE list_id = :lid AND member_id = :mid 
2202                 AND member_type = :mtype;
2203             ref = aref[a] * dref[d];
2204             if (sqlca.sqlerrd[2] > 0) {
2205                 if (a == 0 && d == 0) {
2206                     EXEC SQL UPDATE imembers 
2207                       SET ref_count = ref_count+ref, direct=1
2208                       WHERE list_id = :lid AND member_id = :mid
2209                         AND member_type = :mtype;
2210                 } else {
2211                     EXEC SQL UPDATE imembers
2212                       SET ref_count = ref_count+ref
2213                       WHERE list_id = :lid AND member_id = :mid
2214                         AND member_type = :mtype;
2215                 }
2216             } else {
2217                 incremental_clear_before();
2218                 if (a == 0 && d == 0) {
2219                     EXEC SQL INSERT INTO imembers 
2220                       (list_id, member_id, direct, member_type, ref_count)
2221                       VALUES (:lid, :mid, 1, :mtype, 1);
2222                 } else {
2223                     EXEC SQL INSERT INTO imembers
2224                       (list_id, member_id, member_type, ref_count)
2225                       VALUES (:lid, :mid, :mtype, 1);
2226                 }
2227                 iargv[0] = (char *)lid;
2228                 iargv[1] = mtype;
2229                 iargv[2] = (char *)mid;
2230                 incremental_after("members", 0, iargv);
2231             }
2232         }
2233     }
2234     lid = *(int *)argv[0];
2235     entity = cl->entity;
2236     who = cl->client_id;
2237     EXEC SQL REPEATED UPDATE list 
2238       SET modtime='now', modby = :who, modwith = :entity
2239       WHERE list_id = :lid;
2240     if (ingres_errno) return(mr_errcode);
2241     return(MR_SUCCESS);
2242 }
2243
2244
2245 /* Delete_member_from_list: do list flattening as we go!
2246  */
2247
2248 int delete_member_from_list(q, argv, cl)
2249     struct query *q;
2250     char **argv;
2251     client *cl;
2252 {
2253     EXEC SQL BEGIN DECLARE SECTION; 
2254     int id, lid, mid, cnt, error, who, ref;
2255     char *mtype, dtype[9], *entity;
2256     EXEC SQL END DECLARE SECTION; 
2257     int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
2258     int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
2259     char *dtypes[MAXLISTDEPTH];
2260     char *iargv[3];
2261
2262     lid = *(int *)argv[0];
2263     mtype = argv[1];
2264     mid = *(int *)argv[2];
2265     /* if the member is not a direct member of the list, punt */
2266     EXEC SQL REPEATED SELECT list_id INTO :idummy FROM imembers
2267       WHERE list_id = :lid AND member_id = :mid 
2268         AND member_type = :mtype AND direct = 1;
2269     if (ingres_errno) return(mr_errcode);
2270     if (sqlca.sqlcode == 100)
2271       return(MR_NO_MATCH);
2272     ancestors[0] = lid;
2273     aref[0] = 1;
2274     acount = 1;
2275     EXEC SQL DECLARE csr105 CURSOR FOR 
2276       SELECT list_id, ref_count FROM imembers
2277         WHERE member_id = :lid AND member_type = 'LIST';
2278     EXEC SQL OPEN csr105;
2279     while(1) {
2280         EXEC SQL FETCH csr105 INTO :id, :ref;
2281         if(sqlca.sqlcode!=0) break;
2282         aref[acount] = ref;
2283         ancestors[acount++] = id;
2284         if (acount >= MAXLISTDEPTH) break;
2285     }
2286     EXEC SQL CLOSE csr105;
2287     if (ingres_errno) 
2288       return(mr_errcode);
2289     if (acount >= MAXLISTDEPTH)
2290       return(MR_INTERNAL);
2291     descendants[0] = mid;
2292     dtypes[0] = mtype;
2293     dref[0] = 1;
2294     dcount = 1;
2295     error = 0;
2296     if (!strcmp(mtype, "LIST")) {
2297         EXEC SQL DECLARE csr106 CURSOR FOR 
2298           SELECT member_id, member_type, ref_count FROM imembers
2299             WHERE list_id = :mid;
2300         EXEC SQL OPEN csr106;
2301         while(1) {
2302             EXEC SQL FETCH csr106 INTO :id, :dtype, :ref;
2303             if(sqlca.sqlcode!=0) break;
2304             switch (dtype[0]) {
2305             case 'L':
2306                 dtypes[dcount] = "LIST";
2307                 break;
2308             case 'U':
2309                 dtypes[dcount] = "USER";
2310                 break;
2311             case 'S':
2312                 dtypes[dcount] = "STRING";
2313                 break;
2314             case 'K':
2315                 dtypes[dcount] = "KERBEROS";
2316                 break;
2317             default:
2318                 error++;
2319                 break;
2320             }
2321             dref[dcount] = ref;
2322             descendants[dcount++] = id;
2323             if (dcount >= MAXLISTDEPTH) break;
2324         }
2325         EXEC SQL CLOSE csr106;
2326         if (ingres_errno) 
2327           return(mr_errcode);
2328         if (error)
2329           return(MR_INTERNAL);
2330     }
2331     for (a = 0; a < acount; a++) {
2332         lid = ancestors[a];
2333         for (d = 0; d < dcount; d++) {
2334             mid = descendants[d];
2335             mtype = dtypes[d];
2336             if (mid == lid && !strcmp(mtype, "LIST")) {
2337                 return(MR_LISTLOOP);
2338             }
2339             EXEC SQL REPEATED SELECT ref_count INTO :cnt FROM imembers
2340               WHERE list_id = :lid AND member_id = :mid AND member_type = :mtype;
2341             ref = aref[a] * dref[d];
2342             if (cnt <= ref) {
2343                 iargv[0] = (char *)lid;
2344                 iargv[1] = mtype;
2345                 iargv[2] = (char *)mid;
2346                 incremental_before("members", 0, iargv);
2347                 EXEC SQL DELETE FROM imembers 
2348                   WHERE list_id = :lid AND member_id = :mid 
2349                     AND member_type= :mtype;
2350                 incremental_clear_after();
2351             } else if (a == 0 && d == 0) {
2352                 EXEC SQL UPDATE imembers 
2353                   SET ref_count = refcount - :ref, direct = 0
2354                   WHERE list_id = :lid AND member_id = :mid 
2355                     AND member_type = :mtype;
2356             } else {
2357                 EXEC SQL UPDATE imembers 
2358                   SET ref_count=refcount-:ref
2359                   WHERE list_id = :lid AND member_id = :mid 
2360                     AND member_type = :mtype;
2361             }
2362         }
2363     }
2364     lid = *(int *)argv[0];
2365     entity = cl->entity;
2366     who = cl->client_id;
2367     EXEC SQL UPDATE list SET modtime = 'now', modby = :who, modwith = :entity
2368       WHERE list_id = :lid;
2369     if (ingres_errno) return(mr_errcode);
2370     return(MR_SUCCESS);
2371 }
2372
2373
2374 /* get_ace_use - given a type and a name, return a type and a name.
2375  * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0],
2376  * and argv[1] will contain the ID of the entity in question.  The R*
2377  * types mean to recursively look at every containing list, not just
2378  * when the object in question is a direct member.  On return, the
2379  * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR.
2380  */
2381
2382 int get_ace_use(q, argv, cl, action, actarg)
2383     struct query *q;
2384     char *argv[];
2385     client *cl;
2386     int (*action)();
2387     int actarg;
2388 {
2389     int found = 0;
2390     EXEC SQL BEGIN DECLARE SECTION; 
2391     char *atype;
2392     int aid, listid, id;
2393     EXEC SQL END DECLARE SECTION; 
2394     struct save_queue *sq, *sq_create();
2395
2396     atype = argv[0];
2397     aid = *(int *)argv[1];
2398     if (!strcmp(atype, "LIST") || !strcmp(atype, "USER") ||
2399         !strcmp(atype, "KERBEROS")) {
2400         return(get_ace_internal(atype, aid, action, actarg));
2401     }
2402
2403     sq = sq_create();
2404     if (!strcmp(atype, "RLIST")) {
2405         sq_save_data(sq, aid);
2406         /* get all the list_id's of containing lists */
2407         EXEC SQL DECLARE csr107 CURSOR FOR 
2408           SELECT list_id FROM imembers
2409             WHERE member_type='LIST' AND member_id = :aid;
2410         EXEC SQL OPEN csr107;
2411         while(1) {
2412             EXEC SQL FETCH csr107 INTO :listid;
2413             if(sqlca.sqlcode != 0) break;
2414             sq_save_unique_data(sq, listid);
2415         }
2416         EXEC SQL CLOSE csr107;
2417         /* now process each one */
2418         while (sq_get_data(sq, &id)) {
2419             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2420               found++;
2421         }
2422     }
2423
2424     if (!strcmp(atype, "RUSER")) {
2425         EXEC SQL DECLARE csr108 CURSOR FOR 
2426           SELECT list_id FROM imembers
2427             WHERE member_type='USER' AND member_id = :aid;
2428         EXEC SQL OPEN csr108;
2429         while(1) {
2430             EXEC SQL FETCH csr108 INTO :listid;
2431             if(sqlca.sqlcode != 0) break;
2432             sq_save_data(sq, listid);
2433         }
2434         EXEC SQL CLOSE csr108;
2435         /* now process each one */
2436         while (sq_get_data(sq, &id)) {
2437             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2438               found++;
2439         }
2440         if (get_ace_internal("USER", aid, action, actarg) == MR_SUCCESS)
2441           found++;
2442     }
2443
2444     if (!strcmp(atype, "RKERBERO")) {
2445         EXEC SQL DECLARE csr109 CURSOR FOR 
2446           SELECT list_id FROM imembers
2447             WHERE member_type='KERBEROS' AND member_id = :aid;
2448         EXEC SQL OPEN csr109;
2449         while(1) {
2450             EXEC SQL FETCH csr109 INTO :listid;
2451             if(sqlca.sqlcode != 0) break;
2452             sq_save_data(sq, listid);
2453         }
2454         EXEC SQL CLOSE csr109;
2455         /* now process each one */
2456         while (sq_get_data(sq, &id)) {
2457             if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
2458               found++;
2459         }
2460         if (get_ace_internal("KERBEROS", aid, action, actarg) == MR_SUCCESS)
2461           found++;
2462     }
2463
2464     sq_destroy(sq);     
2465     if (ingres_errno) return(mr_errcode);
2466     if (!found) return(MR_NO_MATCH);
2467     return(MR_SUCCESS);
2468 }
2469
2470
2471 /* This looks up a single list or user for ace use.  atype must be "USER"
2472  * or "LIST", and aid is the ID of the corresponding object.  This is used
2473  * by get_ace_use above.
2474  */
2475
2476 get_ace_internal(atype, aid, action, actarg)
2477     EXEC SQL BEGIN DECLARE SECTION; 
2478     char *atype;
2479     int aid;
2480     EXEC SQL END DECLARE SECTION; 
2481     int (*action)();
2482     int actarg;
2483 {
2484     char *rargv[2];
2485     int found = 0;
2486     EXEC SQL BEGIN DECLARE SECTION; 
2487     char name[33];
2488     EXEC SQL END DECLARE SECTION; 
2489
2490     rargv[1] = name;
2491     if (!strcmp(atype, "LIST")) {
2492         rargv[0] = "FILESYS";
2493         EXEC SQL DECLARE csr110 CURSOR FOR 
2494           SELECT label FROM filesys
2495             WHERE owners = :aid;
2496         EXEC SQL OPEN csr110;
2497         while(1) {
2498             EXEC SQL FETCH csr110 INTO :name;
2499             if(sqlca.sqlcode != 0) break;
2500             (*action)(2, rargv, actarg);
2501             found++;
2502         }
2503         EXEC SQL CLOSE csr110;
2504
2505         rargv[0] = "QUERY";
2506         EXEC SQL DECLARE csr111 CURSOR FOR 
2507           SELECT capability FROM capacls
2508             WHERE list_id = :aid ;
2509         EXEC SQL OPEN csr111;
2510         while(1) {
2511             EXEC SQL FETCH csr111 INTO :name ; 
2512             if(sqlca.sqlcode != 0) break;
2513             (*action)(2, rargv, actarg);
2514             found++;
2515         }
2516         EXEC SQL CLOSE csr111;
2517     } else if (!strcmp(atype, "USER")) {
2518         rargv[0] = "FILESYS";
2519         EXEC SQL DECLARE csr112 CURSOR FOR 
2520           SELECT label FROM filesys
2521             WHERE owner = :aid;
2522         EXEC SQL OPEN csr112;
2523         while(1) {
2524             EXEC SQL FETCH csr112 INTO :name ;
2525             if(sqlca.sqlcode != 0) break;
2526             (*action)(2, rargv, actarg);
2527             found++;
2528         }
2529         EXEC SQL CLOSE csr112; 
2530     }
2531     
2532     rargv[0] = "LIST";
2533     EXEC SQL DECLARE csr113 CURSOR FOR 
2534       SELECT name FROM list
2535         WHERE acl_type = :atype AND acl_id = :aid;
2536     EXEC SQL OPEN csr113;
2537     while(1) {
2538         EXEC SQL FETCH csr113 INTO :name; 
2539         if(sqlca.sqlcode != 0) break;
2540         (*action)(2, rargv, actarg);
2541         found++;
2542     }
2543     EXEC SQL CLOSE csr113;
2544     
2545     rargv[0] = "SERVICE";
2546     EXEC SQL DECLARE csr114 CURSOR FOR 
2547       SELECT name FROM servers
2548         WHERE acl_type = :atype AND acl_id = :aid;
2549     EXEC SQL OPEN csr114;
2550     while(1) {
2551         EXEC SQL FETCH csr114 INTO :name;
2552         if(sqlca.sqlcode != 0) break;
2553         (*action)(2, rargv, actarg);
2554         found++;
2555     }
2556     EXEC SQL CLOSE csr114; 
2557
2558     rargv[0] = "HOSTACCESS";
2559     EXEC SQL DECLARE csr115 CURSOR FOR 
2560       SELECT name FROM machine, hostaccess
2561         WHERE mach_id = hostaccess.mach_id AND hostaccess.acl_type = :atype
2562           AND hostaccess.acl_id = :aid;
2563     EXEC SQL OPEN csr115;
2564     while(1) {
2565         EXEC SQL FETCH csr115 INTO :name;
2566         if(sqlca.sqlcode != 0) break;
2567         (*action)(2, rargv, actarg);
2568         found++;
2569     }
2570     EXEC SQL CLOSE csr115;
2571
2572     rargv[0] = "ZEPHYR";
2573     EXEC SQL DECLARE csr116 CURSOR FOR 
2574       SELECT class FROM zephyr
2575         WHERE zephyr.xmt_type = :atype AND zephyr.xmt_id = :aid 
2576           OR zephyr.sub_type = :atype AND zephyr.sub_id = :aid 
2577           OR zephyr.iws_type = :atype AND zephyr.iws_id = :aid 
2578           OR zephyr.iui_type = :atype AND zephyr.iui_id = :aid;
2579     EXEC SQL OPEN csr116;
2580     while(1) {
2581         EXEC SQL FETCH csr116 INTO :name; 
2582         if(sqlca.sqlcode != 0) break;
2583         (*action)(2, rargv, actarg);
2584         found++;
2585     }
2586     EXEC SQL CLOSE csr116;
2587
2588     if (!found) return(MR_NO_MATCH);
2589     return(MR_SUCCESS);
2590 }
2591
2592
2593 /* get_lists_of_member - given a type and a name, return the name and flags
2594  * of all of the lists of the given member.  The member_type is one of
2595  * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0],
2596  * and argv[1] will contain the ID of the entity in question.  The R*
2597  * types mean to recursively look at every containing list, not just
2598  * when the object in question is a direct member.
2599  */
2600
2601 int get_lists_of_member(q, argv, cl, action, actarg)
2602     struct query *q;
2603     char *argv[];
2604     client *cl;
2605     int (*action)();
2606     int actarg;
2607 {
2608     int found = 0, direct = 1;
2609     char *rargv[6];
2610     EXEC SQL BEGIN DECLARE SECTION; 
2611     char *atype;
2612     int aid, listid, id;
2613     char name[33], active[5], public[5], hidden[5], maillist[5], grouplist[5];
2614     EXEC SQL END DECLARE SECTION; 
2615
2616     atype = argv[0];
2617     aid = *(int *)argv[1];
2618     if (!strcmp(atype, "RLIST")) {
2619         atype = "LIST";
2620         direct = 0;
2621     }
2622     if (!strcmp(atype, "RUSER")) {
2623         atype = "USER";
2624         direct = 0;
2625     }
2626     if (!strcmp(atype, "RSTRING")) {
2627         atype = "STRING";
2628         direct = 0;
2629     }
2630     if (!strcmp(atype, "RKERBEROS")) {
2631         atype = "KERBEROS";
2632         direct = 0;
2633     }
2634
2635     rargv[0] = name;
2636     rargv[1] = active;
2637     rargv[2] = public;
2638     rargv[3] = hidden;
2639     rargv[4] = maillist;
2640     rargv[5] = grouplist;
2641     if (direct) {
2642         EXEC SQL DECLARE csr117a CURSOR FOR 
2643           SELECT name, CHAR(active), CHAR(publicflg), CHAR(hidden), 
2644               CHAR(maillist), CHAR(grouplist)
2645             FROM list l, imembers m
2646             WHERE l.list_id = m.list_id AND m.direct = 1 
2647               AND m.member_type = :atype AND m.member_id = :aid;
2648         EXEC SQL OPEN csr117a;
2649         while(1) {
2650             EXEC SQL FETCH csr117a 
2651               INTO :name, :active, :public, :hidden, :maillist, :grouplist;
2652             if(sqlca.sqlcode != 0) break; 
2653             (*action)(6, rargv, actarg);
2654             found++;
2655         }
2656         EXEC SQL CLOSE csr117a;
2657     } else {
2658         EXEC SQL DECLARE csr117b 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 
2663               AND m.member_type = :atype AND m.member_id = :aid;
2664         EXEC SQL OPEN csr117b;
2665         while(1) {
2666             EXEC SQL FETCH csr117b 
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 csr117b;
2673     }
2674
2675     if (ingres_errno) return(mr_errcode);
2676     if (!found) return(MR_NO_MATCH);
2677     return(MR_SUCCESS);
2678 }
2679
2680
2681 /* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of
2682  * the five flags associated with each list.  It will return the name of
2683  * each list that meets the quailifications.  It does this by building a
2684  * where clause based on the arguments, then doing a retrieve.
2685  */
2686
2687 static char *lflags[5] = { "active", "publicflg", "hidden", "maillist", "group" };
2688
2689 int qualified_get_lists(q, argv, cl, action, actarg)
2690     struct query *q;
2691     char *argv[];
2692     client *cl;
2693     int (*action)();
2694     int actarg;
2695 {
2696     return(qualified_get(q, argv, action, actarg, "l.list_id != 0",
2697                          "l", "name", lflags));
2698 }
2699
2700
2701 /** get_members_of_list - optimized query for retrieval of list members
2702  **
2703  ** Inputs:
2704  **   argv[0] - list_id
2705  **
2706  ** Description:
2707  **   - retrieve USER members, then LIST members, then STRING members
2708  **/
2709
2710 get_members_of_list(q, argv, cl, action, actarg)
2711     struct query *q;
2712     char *argv[];
2713     client *cl;
2714     int (*action)();
2715     int actarg;
2716 {
2717     EXEC SQL BEGIN DECLARE SECTION; 
2718     int list_id, member_id;
2719     char member_name[129], member_type[9];
2720     EXEC SQL END DECLARE SECTION; 
2721     char *targv[2];
2722     int members;
2723     struct save_queue *sq;
2724
2725     list_id = *(int *)argv[0];
2726     members = 0;
2727     sq = sq_create();
2728
2729     EXEC SQL DECLARE csr118 CURSOR FOR 
2730       SELECT member_type, member_id FROM imembers
2731         WHERE list_id = :list_id AND direct=1;
2732     EXEC SQL OPEN csr118;
2733     while(1) {
2734         EXEC SQL FETCH csr118 INTO :member_type, :member_id; 
2735         if (sqlca.sqlcode != 0) break;
2736         if (members++ > 49) 
2737           break;
2738         sq_save_data(sq, ((int)member_type[0] << 24) | (member_id & 0xffffff));
2739     }
2740     EXEC SQL CLOSE csr118;
2741
2742     if (members <= 49) {
2743         targv[1] = malloc(0);
2744         while (sq_remove_data(sq, &member_id)) {
2745             switch (member_id >> 24) {
2746             case 'U':
2747                 targv[0] = "USER";
2748                 id_to_name(member_id & 0xffffff, "USER", &targv[1]);
2749                 (*action)(2, targv, actarg);
2750                 break;
2751             case 'L':
2752                 targv[0] = "LIST";
2753                 id_to_name(member_id & 0xffffff, "LIST", &targv[1]);
2754                 (*action)(2, targv, actarg);
2755                 break;
2756             case 'S':
2757                 targv[0] = "STRING";
2758                 id_to_name(member_id & 0xffffff, "STRING", &targv[1]);
2759                 (*action)(2, targv, actarg);
2760                 break;
2761             case 'K':
2762                 targv[0] = "KERBEROS";
2763                 id_to_name(member_id & 0xffffff, "STRING", &targv[1]);
2764                 (*action)(2, targv, actarg);
2765                 break;
2766             default:
2767                 sq_destroy(sq);
2768                 return(MR_INTERNAL);
2769             }
2770         }
2771         free(targv[1]);
2772         sq_destroy(sq);
2773         return(MR_SUCCESS);
2774     }
2775     sq_destroy(sq);
2776
2777     targv[1] = member_name;
2778     targv[0] = "USER";
2779     EXEC SQL DECLARE csr119 CURSOR FOR 
2780       SELECT users.login FROM users, imembers
2781         WHERE imembers.list_id = :list_id AND imembers.member_type = 'USER'
2782           AND imembers.member_id = users.users_id AND  imembers.direct=1
2783         ORDER BY 1;
2784     EXEC SQL OPEN csr119;
2785     while(1) {
2786         EXEC SQL FETCH csr119 INTO :member_name;
2787         if(sqlca.sqlcode != 0) break;
2788         (*action)(2, targv, actarg);
2789     }
2790     EXEC SQL CLOSE csr119;
2791     if (ingres_errno) return(mr_errcode);
2792
2793     targv[0] = "LIST";
2794     EXEC SQL DECLARE csr120 CURSOR FOR 
2795       SELECT list.name FROM list, imembers
2796         WHERE imembers.list_id = :list_id AND imembers.member_type='LIST'
2797           AND imembers.member_id = list.list_id AND imembers.direct=1
2798         ORDER BY 1;
2799     EXEC SQL OPEN csr120;
2800     while(1) {
2801         EXEC SQL FETCH csr120 INTO :member_name; 
2802         if(sqlca.sqlcode != 0) break;
2803         (*action)(2, targv, actarg);
2804     }
2805     EXEC SQL CLOSE csr120;
2806     if (ingres_errno) return(mr_errcode);
2807
2808     targv[0] = "STRING";
2809     EXEC SQL DECLARE csr121 CURSOR FOR 
2810       SELECT strings.string FROM strings, imembers
2811         WHERE imembers.list_id = :list_id AND imembers.member_type='STRING'
2812           AND imembers.member_id = strings.string_id AND imembers.direct=1
2813         ORDER BY 1;
2814     EXEC SQL OPEN csr121;
2815     while(1) {
2816         EXEC SQL FETCH csr121 INTO :member_name; 
2817         if(sqlca.sqlcode != 0) break;
2818         (*action)(2, targv, actarg);
2819     }
2820     EXEC SQL CLOSE csr121;
2821     if (ingres_errno) return(mr_errcode);
2822
2823     targv[0] = "KERBEROS";
2824     EXEC SQL DECLARE csr122 CURSOR FOR 
2825       SELECT strings.string FROM strings, imembers
2826         WHERE imembers.list_id = :list_id AND imembers.member_type='KERBEROS'
2827           AND imembers.member_id = strings.string_id AND imembers.direct=1
2828         ORDER BY 1;
2829     EXEC SQL OPEN csr122;
2830     while(1) {
2831         EXEC SQL FETCH csr122 INTO :member_name; 
2832         if(sqlca.sqlcode != 0) break;
2833         (*action)(2, targv, actarg);
2834     }
2835     EXEC SQL CLOSE csr122;
2836     if (ingres_errno) return(mr_errcode);
2837
2838     return(MR_SUCCESS);
2839 }
2840
2841
2842 /* count_members_of_list: this is a simple query, but it cannot be done
2843  * through the dispatch table.
2844  */
2845
2846 int count_members_of_list(q, argv, cl, action, actarg)
2847     struct query *q;
2848     char *argv[];
2849     client *cl;
2850     int (*action)();
2851     int actarg;
2852 {
2853     EXEC SQL BEGIN DECLARE SECTION; 
2854     int  list, ct = 0;
2855     EXEC SQL END DECLARE SECTION; 
2856     char *rargv[1], countbuf[5];
2857
2858     list = *(int *)argv[0];
2859     rargv[0] = countbuf;
2860     EXEC SQL REPEATED SELECT count (*) INTO :ct FROM imembers
2861       WHERE list_id = :list AND direct=1;
2862     if (ingres_errno) return(mr_errcode);
2863     sprintf(countbuf, "%d", ct);
2864     (*action)(1, rargv, actarg);
2865     return(MR_SUCCESS);
2866 }
2867
2868
2869 /* qualified_get_server: passed "TRUE", "FALSE", or "DONTCARE" for each of
2870  * the three flags associated with each service.  It will return the name of
2871  * each service that meets the quailifications.  It does this by building a
2872  * where clause based on the arguments, then doing a retrieve.
2873  */
2874
2875 static char *sflags[3] = { "enable", "inprogress", "harderror" };
2876
2877 int qualified_get_server(q, argv, cl, action, actarg)
2878     struct query *q;
2879     char *argv[];
2880     client *cl;
2881     int (*action)();
2882     int actarg;
2883 {
2884     return(qualified_get(q, argv, action, actarg, "s.name != ''",
2885                          "s", "name", sflags));
2886 }
2887
2888
2889 /* generic qualified get routine, used by qualified_get_lists,
2890  * qualified_get_server, and qualified_get_serverhost.
2891  *   Args:
2892  *      start - a simple where clause, must not be empty
2893  *      range - the name of the range variable
2894  *      field - the field to return
2895  *      flags - an array of strings, names of the flag variables
2896  */
2897
2898 int qualified_get(q, argv, action, actarg, start, range, field, flags)
2899     struct query *q;
2900     char *argv[];
2901     int (*action)();
2902     int actarg;
2903     char *start;
2904     char *range;
2905     char *field;
2906     char *flags[];
2907 {
2908     char name[33], qual[256];
2909     int rowcount=0, i;
2910     char *rargv[1], buf[32];
2911
2912     strcpy(qual, start);
2913     for (i = 0; i < q->argc; i++) {
2914         if (!strcmp(argv[i], "TRUE")) {
2915             sprintf(buf, " and %s.%s != 0", range, flags[i]);
2916             (void) strcat(qual, buf);
2917         } else if (!strcmp(argv[i], "FALSE")) {
2918             sprintf(buf, " and %s.%s = 0", range, flags[i]);
2919             (void) strcat(qual, buf);
2920         }
2921     }
2922       
2923     rargv[0] = SQLDA->sqlvar[0].sqldata;
2924     sprintf(stmt_buf,"SELECT %s.%s FROM %s %s WHERE %s",q->rtable,field,q->rtable,q->rvar,qual);
2925     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
2926     if(sqlca.sqlcode)
2927       return(MR_INTERNAL);
2928     EXEC SQL DECLARE csr123 CURSOR FOR stmt;
2929     EXEC SQL OPEN csr123;
2930     while(1) {
2931         EXEC SQL FETCH csr123 USING DESCRIPTOR :SQLDA;
2932         if(sqlca.sqlcode != 0) break;
2933         rowcount++;
2934         (*action)(1, rargv, actarg);
2935     }
2936     EXEC SQL CLOSE csr123;
2937     if (ingres_errno) return(mr_errcode);
2938     if (rowcount == 0)
2939       return(MR_NO_MATCH);
2940     return(MR_SUCCESS);
2941 }
2942
2943
2944 /* qualified_get_serverhost: passed "TRUE", "FALSE", or "DONTCARE" for each of
2945  * the five flags associated with each serverhost.  It will return the name of
2946  * each service and host that meets the quailifications.  It does this by 
2947  * building a where clause based on the arguments, then doing a retrieve.
2948  */
2949
2950 static char *shflags[6] = { "service", "enable", "override", "success",
2951                             "inprogress", "hosterror" };
2952
2953 int qualified_get_serverhost(q, argv, cl, action, actarg)
2954     struct query *q;
2955     char *argv[];
2956     client *cl;
2957     int (*action)();
2958     int actarg;
2959 {
2960     EXEC SQL BEGIN DECLARE SECTION; 
2961     char sname[33], mname[33], qual[256];
2962     EXEC SQL END DECLARE SECTION; 
2963     char *rargv[2], buf[32];
2964     int i, rowcount;
2965
2966     /** the uppercase() function is INGRES-specific */
2967     sprintf(qual, "machine.mach_id = serverhosts.mach_id AND \
2968 serverhosts.service = uppercase('%s')",
2969             argv[0]);
2970     for (i = 1; i < q->argc; i++) {
2971         if (!strcmp(argv[i], "TRUE")) {
2972             sprintf(buf, " AND serverhosts.%s != 0", shflags[i]);
2973             strcat(qual, buf);
2974         } else if (!strcmp(argv[i], "FALSE")) {
2975             sprintf(buf, " AND serverhosts.%s = 0", shflags[i]);
2976             strcat(qual, buf);
2977         }
2978     }
2979       
2980     rargv[0] = sname;
2981     rargv[1] = mname;
2982     EXEC SQL DECLARE csr124 CURSOR FOR 
2983       SELECT serverhosts.service, machine.name FROM serverhosts, machine 
2984         WHERE :qual; 
2985     EXEC SQL OPEN csr124;
2986     while(1) {
2987         EXEC SQL FETCH csr124 INTO :sname, :mname;
2988         if(sqlca.sqlcode != 0) break;
2989         rowcount++;
2990         (*action)(2, rargv, actarg);
2991     }
2992     EXEC SQL CLOSE csr124;
2993
2994     if (ingres_errno) return(mr_errcode);
2995     if (rowcount == 0)
2996       return(MR_NO_MATCH);
2997     return(MR_SUCCESS);
2998 }
2999
3000
3001 /* register_user - change user's login name and allocate a pobox, group,
3002  * filesystem, and quota for them.  The user's status must start out as 0,
3003  * and is left as 2.  Arguments are: user's UID, new login name, and user's
3004  * type for filesystem allocation (MR_FS_STUDENT, MR_FS_FACULTY, 
3005  * MR_FS_STAFF, MR_FS_MISC).
3006  */
3007
3008 register_user(q, argv, cl)
3009     struct query *q;
3010     char **argv;
3011     client *cl;
3012 {
3013     EXEC SQL BEGIN DECLARE SECTION; 
3014     char *login, dir[65], *entity, *directory, machname[33];
3015     int who, rowcount, mid, uid, users_id, flag, utype, nid, list_id, quota;
3016     int size, alloc, pid, m_id, ostatus, nstatus, gidval, fsidval, npidval;
3017     EXEC SQL END DECLARE SECTION; 
3018     char buffer[256], *aargv[3];
3019     int maxsize;
3020
3021     entity = cl->entity;
3022     who = cl->client_id;
3023
3024     uid = atoi(argv[0]);
3025     login = argv[1];
3026     utype = atoi(argv[2]);
3027
3028     /* find user */
3029     EXEC SQL REPEATED SELECT users_id, status INTO :users_id, :ostatus
3030       FROM users
3031       WHERE uid = :uid AND (status=0 OR status=5 OR status=6);
3032
3033     if (sqlca.sqlerrd[2] == 0)
3034       return(MR_NO_MATCH);
3035     if (sqlca.sqlerrd[2] > 1)
3036       return(MR_NOT_UNIQUE);
3037
3038     /* check new login name */
3039     EXEC SQL REPEATED SELECT login INTO :cdummy FROM users 
3040       WHERE login = LEFT(:login,SIZE(login)) AND users_id != :users_id;
3041     if (ingres_errno) return(mr_errcode);
3042     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3043     EXEC SQL REPEATED SELECT name INTO :cdummy FROM list
3044       WHERE name = LEFT(:login,SIZE(name));
3045     if (ingres_errno) return(mr_errcode);
3046     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3047     EXEC SQL REPEATED SELECT label INTO :cdummy FROM filesys
3048       WHERE label = LEFT(:login,SIZE(label));
3049     if (ingres_errno) return(mr_errcode);
3050     if (sqlca.sqlerrd[2] > 0) return(MR_IN_USE);
3051     com_err(whoami, 0, "login name OK");
3052
3053     /* choose place for pobox, put in mid */
3054     EXEC SQL DECLARE csr130 CURSOR FOR 
3055       SELECT sh.mach_id, m.name FROM serverhosts sh, machine m
3056       WHERE sh.service='POP' AND sh.mach_id=m.mach_id
3057         AND sh.value2 - sh.value1 =
3058           (SELECT MAX(value2 - value1) FROM serverhosts
3059             WHERE service = 'POP');
3060     EXEC SQL OPEN csr130;
3061     EXEC SQL FETCH csr130 INTO :mid, :machname;
3062     if (sqlca.sqlerrd[2] == 0) {
3063         EXEC SQL CLOSE csr130;
3064         if (ingres_errno) return(mr_errcode);
3065         return(MR_NO_POBOX);
3066     } else {
3067         EXEC SQL CLOSE csr130;
3068         if (ingres_errno) return(mr_errcode);
3069     }
3070
3071     /* change login name, set pobox */
3072     sprintf(buffer, "users.users_id = %d", users_id);
3073     incremental_before("users", buffer, 0);
3074     nstatus = 2;
3075     if (ostatus == 5 || ostatus == 6)
3076       nstatus = 1;
3077     EXEC SQL REPEATED UPDATE users SET login = :login, status = :nstatus,
3078         modtime='now', modby = :who, modwith = :entity, potype='POP',
3079         pop_id = :mid, pmodtime='now', pmodby = :who, pmodwith = :entity
3080       WHERE users_id = :users_id;
3081
3082     if (ingres_errno) return(mr_errcode);
3083     if (sqlca.sqlerrd[2] != 1)
3084       return(MR_INTERNAL);
3085     set_pop_usage(mid, 1);
3086     com_err(whoami, 0, "set login name to %s and pobox to %s", login,
3087             strtrim(machname));
3088     incremental_after("users", buffer, 0);
3089
3090     /* create group list */
3091     if (set_next_object_id("gid", "list", 1))
3092       return(MR_NO_ID);
3093     if (set_next_object_id("list_id", "list", 0))
3094       return(MR_NO_ID);
3095     EXEC SQL REPEATED SELECT value INTO :list_id FROM numvalues 
3096       WHERE name='list_id';
3097     if (ingres_errno) return(mr_errcode);
3098     if (sqlca.sqlerrd[2] != 1)
3099       return(MR_INTERNAL);
3100     incremental_clear_before();
3101     EXEC SQL SELECT value INTO :gidval FROM numvalues WHERE name = 'gid';
3102     EXEC SQL REPEATED INSERT INTO list
3103              (name, list_id, active, publicflg, hidden, maillist, grouplist,
3104               gid, description, acl_type, acl_id, 
3105               modtime, modby, modwith)
3106       VALUES (:login, :list_id, 1, 0, 0, 0, 1,
3107               :gidval, 'User Group', 'USER', :users_id,
3108               'now', :who, :entity);
3109     if (ingres_errno) return(mr_errcode);
3110     if (sqlca.sqlerrd[2] != 1)
3111       return(MR_INTERNAL);
3112     sprintf(buffer, "list_id = %d", list_id);
3113     incremental_after("list", buffer, 0);
3114     aargv[0] = (char *) list_id;
3115     aargv[1] = "USER";
3116     aargv[2] = (char *) users_id;
3117     incremental_clear_before();
3118     EXEC SQL REPEATED INSERT INTO imembers 
3119              (list_id, member_type, member_id, ref_count, direct)
3120       VALUES (:list_id, 'USER', :users_id, 1, 1);
3121     if (ingres_errno) return(mr_errcode);
3122     if (sqlca.sqlerrd[2] != 1)
3123       return(MR_INTERNAL);
3124     incremental_after("members", 0, aargv);
3125
3126     /* decide where to put filesystem */
3127     maxsize = 0;
3128     directory = NULL;
3129     EXEC SQL DECLARE csr125 CURSOR FOR
3130       SELECT mach_id, trim(dir), nfsphys_id, status, partsize, allocated
3131         FROM nfsphys;
3132     EXEC SQL OPEN csr125;
3133     while(1) {
3134         EXEC SQL FETCH csr125 INTO :mid, :dir, :nid, :flag, :size, :alloc;
3135         if(sqlca.sqlcode) 
3136           break;
3137         if ((flag & utype) && (size != 0) && (size - alloc > maxsize)) {
3138             maxsize = size - alloc;
3139             if (directory)
3140               free(directory);
3141             directory = strsave(dir);
3142             pid = nid;
3143             m_id = mid;
3144         }
3145     }
3146     EXEC SQL CLOSE csr125;
3147     if (ingres_errno) return(mr_errcode);
3148     if (maxsize == 0)
3149       return(MR_NO_FILESYS);
3150
3151     /* create filesystem */
3152     if (set_next_object_id("filsys_id", "filesys", 0))
3153       return(MR_NO_ID);
3154     incremental_clear_before(); 
3155     EXEC SQL SELECT value INTO :fsidval FROM numvalues
3156       WHERE numvalues.name='filsys_id';
3157     EXEC SQL REPEATED INSERT INTO filesys
3158         (filsys_id, phys_id, label, type, mach_id, name, 
3159          mount, access, comments, owner, owners, createflg, 
3160          lockertype, modtime, modby, modwith)
3161       VALUES
3162         (:fsidval, :pid, :login, 'NFS', :m_id, :directory+'/'+:login,
3163          '/mit/'+:login, 'w', 'User Locker', :users_id, :list_id, 1,
3164          'HOMEDIR', 'now', :who, :entity)        ;
3165     if (ingres_errno) return(mr_errcode);
3166     if (sqlca.sqlerrd[2]  != 1)
3167       return(MR_INTERNAL);
3168     sprintf(buffer,"fs.filsys_id = %d",fsidval);
3169     incremental_after("filesys", buffer, 0);
3170     com_err(whoami, 0, "filesys on mach %d in %s/%s", m_id,
3171             directory, login);
3172
3173     /* set quota */
3174     EXEC SQL REPEATED SELECT value INTO :quota FROM numvalues
3175       WHERE name='def_quota';
3176     if (ingres_errno) return(mr_errcode);
3177     if (sqlca.sqlerrd[2] != 1)
3178       return(MR_NO_QUOTA);
3179     incremental_clear_before();
3180     EXEC SQL REPEATED INSERT INTO quota 
3181         (entity_id, filsys_id, type, quota, phys_id, modtime, modby, modwith)
3182       VALUES
3183         (:users_id, :fsidval, 'USER', :quota, :pid, 'now', :who, :entity);
3184     if (ingres_errno) return(mr_errcode);
3185     if (sqlca.sqlerrd[2] != 1)
3186       return(MR_INTERNAL);
3187     EXEC SQL SELECT phys_id INTO :npidval FROM filesys
3188       WHERE filsys_id=:fsidval;
3189     EXEC SQL REPEATED UPDATE nfsphys SET allocated=allocated + :quota
3190       WHERE nfsphys.nfsphys_id = :npidval;
3191     if (ingres_errno) return(mr_errcode);
3192     if (sqlca.sqlerrd[2] != 1)
3193       return(MR_INTERNAL);
3194     aargv[0] = login;
3195     aargv[1] = "USER";
3196     aargv[2] = login;
3197     sprintf(buffer, "q.entity_id = %d and q.filsys_id = %d and q.type = 'USER'", users_id, fsidval);
3198     incremental_after("quota", buffer, aargv);
3199     com_err(whoami, 0, "quota of %d assigned", quota);
3200     if (ingres_errno) return(mr_errcode);
3201
3202     cache_entry(login, "USER", users_id);
3203
3204     EXEC SQL REPEATED UPDATE tblstats SET updates=updates+1, modtime='now'
3205       WHERE table_name='users';
3206     EXEC SQL REPEATED UPDATE tblstats SET appends=appends+1, modtime='now'
3207       WHERE table_name='list' OR table_name='filesys' OR table_name='quota';
3208     if (ingres_errno) return(mr_errcode);
3209     return(MR_SUCCESS);
3210 }
3211
3212
3213
3214 /** set_pop_usage - incr/decr usage count for pop server in serverhosts talbe
3215  **
3216  ** Inputs:
3217  **   id of machine
3218  **   delta (will be +/- 1)
3219  **
3220  ** Description:
3221  **   - incr/decr value field in serverhosts table for pop/mach_id
3222  **
3223  **/
3224
3225 static int set_pop_usage(id, cnt)
3226     EXEC SQL BEGIN DECLARE SECTION; 
3227     int id;
3228     int cnt;
3229     EXEC SQL END DECLARE SECTION; 
3230 {
3231     EXEC SQL REPEATED UPDATE serverhosts SET value1 = value1 + :cnt
3232       WHERE serverhosts.service = 'POP' AND serverhosts.mach_id = :id;
3233       
3234     if (ingres_errno) return(mr_errcode);
3235     return(MR_SUCCESS);
3236 }
3237
3238
3239 \f
3240 /* Validation Routines */
3241
3242 validate_row(q, argv, v)
3243     register struct query *q;
3244     char *argv[];
3245     register struct validate *v;
3246 {
3247     EXEC SQL BEGIN DECLARE SECTION; 
3248     char *name;
3249     char qual[128];
3250     int rowcount;
3251     EXEC SQL END DECLARE SECTION; 
3252
3253     /* build where clause */
3254     build_qual(v->qual, v->argc, argv, qual);
3255
3256     if (log_flags & LOG_VALID)
3257         /* tell the logfile what we're doing */
3258         com_err(whoami, 0, "validating row: %s", qual);
3259     
3260     /* look for the record */
3261     sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual);
3262     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3263     if(sqlca.sqlcode)
3264       return(MR_INTERNAL);
3265     EXEC SQL DECLARE csr126 CURSOR FOR stmt;
3266     EXEC SQL OPEN csr126;
3267     EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA;
3268     EXEC SQL CLOSE csr126;
3269     rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
3270
3271     if (ingres_errno) return(mr_errcode);
3272     if (rowcount == 0) return(MR_NO_MATCH);
3273     if (rowcount > 1) return(MR_NOT_UNIQUE);
3274     return(MR_EXISTS);
3275 }
3276
3277 validate_fields(q, argv, vo, n)
3278     struct query *q;
3279     register char *argv[];
3280     register struct valobj *vo;
3281     register int n;
3282 {
3283     register int status;
3284
3285     while (--n >= 0) {
3286         switch (vo->type) {
3287         case V_NAME:
3288             if (log_flags & LOG_VALID)
3289                 com_err(whoami, 0, "validating %s in %s: %s", 
3290                     vo->namefield, vo->table, argv[vo->index]);
3291             status = validate_name(argv, vo);
3292             break;
3293
3294         case V_ID:
3295             if (log_flags & LOG_VALID)
3296                 com_err(whoami, 0, "validating %s in %s: %s", 
3297                     vo->idfield, vo->table, argv[vo->index]);
3298             status = validate_id(q, argv, vo);
3299             break;
3300
3301         case V_DATE:
3302             if (log_flags & LOG_VALID)
3303                 com_err(whoami, 0, "validating date: %s", argv[vo->index]);
3304             status = validate_date(argv, vo);
3305             break;
3306
3307         case V_TYPE:
3308             if (log_flags & LOG_VALID)
3309                 com_err(whoami, 0, "validating %s type: %s",
3310                     vo->table, argv[vo->index]);
3311             status = validate_type(argv, vo);
3312             break;
3313
3314         case V_TYPEDATA:
3315             if (log_flags & LOG_VALID)
3316                 com_err(whoami, 0, "validating typed data (%s): %s",
3317                     argv[vo->index - 1], argv[vo->index]);
3318             status = validate_typedata(q, argv, vo);
3319             break;
3320
3321         case V_RENAME:
3322             if (log_flags & LOG_VALID)
3323                 com_err(whoami, 0, "validating rename %s in %s",
3324                         argv[vo->index], vo->table);
3325             status = validate_rename(argv, vo);
3326             break;
3327
3328         case V_CHAR:
3329             if (log_flags & LOG_VALID)
3330               com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
3331             status = validate_chars(argv[vo->index]);
3332             break;
3333
3334         case V_SORT:
3335             status = MR_EXISTS;
3336             break;
3337
3338         case V_LOCK:
3339             status = lock_table(vo);
3340             break;
3341
3342         case V_WILD:
3343             status = convert_wildcards(argv[vo->index]);
3344             break;
3345
3346         case V_UPWILD:
3347             status = convert_wildcards_uppercase(argv[vo->index]);
3348             break;
3349
3350         }
3351
3352         if (status != MR_EXISTS) return(status);
3353         vo++;
3354     }
3355
3356     if (ingres_errno) return(mr_errcode);
3357     return(MR_SUCCESS);
3358 }
3359
3360
3361 /* validate_chars: verify that there are no illegal characters in
3362  * the string.  Legal characters are printing chars other than 
3363  * ", *, ?, \, [ and ].
3364  */
3365 static int illegalchars[] = {
3366     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
3367     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
3368     0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
3369     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
3370     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
3371     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
3372     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
3373     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
3374     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3375     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3376     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3377     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3378     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
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 };
3383
3384 validate_chars(s)
3385 register char *s;
3386 {
3387     while (*s)
3388       if (illegalchars[*s++])
3389         return(MR_BAD_CHAR);
3390     return(MR_EXISTS);
3391 }
3392
3393
3394 validate_id(q, argv, vo)
3395     struct query *q;
3396     char *argv[];
3397     register struct valobj *vo;
3398 {
3399     EXEC SQL BEGIN DECLARE SECTION; 
3400     char *name, *tbl, *namefield, *idfield;
3401     int id, rowcount;
3402     EXEC SQL END DECLARE SECTION; 
3403     int status;
3404     register char *c;
3405
3406     name = argv[vo->index];
3407     tbl = vo->table;
3408     namefield = vo->namefield;
3409     idfield = vo->idfield;
3410
3411     if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) ||
3412         !strcmp(tbl, "machine") ||
3413         !strcmp(tbl, "filesys") ||
3414         !strcmp(tbl, "list") ||
3415         !strcmp(tbl, "cluster") ||
3416         !strcmp(tbl, "strings")) {
3417         if (!strcmp(tbl, "machine"))
3418           for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3419         status = name_to_id(name, tbl, &id);
3420         if (status == 0) {
3421             *(int *)argv[vo->index] = id;
3422             return(MR_EXISTS);
3423         } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") &&
3424                    (q->type == APPEND || q->type == UPDATE)) {
3425             EXEC SQL SELECT value INTO :id FROM numvalues
3426               WHERE name = 'strings_id';
3427             id++;
3428             EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'string_id';
3429             EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
3430             cache_entry(name, "STRING", id);
3431             *(int *)argv[vo->index] = id;
3432             return(MR_EXISTS);
3433         } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
3434           return(vo->error);
3435         else
3436           return(status);
3437     }
3438
3439     if (!strcmp(namefield, "uid")) {
3440         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name);
3441     } else {
3442         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name);
3443     }
3444     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3445     if(sqlca.sqlcode)
3446       return(MR_INTERNAL);
3447     EXEC SQL DECLARE csr127 CURSOR FOR stmt;
3448     EXEC SQL OPEN csr127;
3449     rowcount=0;
3450     EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
3451     if(sqlca.sqlcode == 0) {
3452         rowcount++;
3453         EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
3454         if(sqlca.sqlcode == 0) rowcount++;
3455     }
3456     EXEC SQL CLOSE csr127;
3457     if (ingres_errno) 
3458       return(mr_errcode);
3459
3460     if (rowcount != 1) return(vo->error); 
3461     bcopy(SQLDA->sqlvar[0].sqldata,argv[vo->index],sizeof(int));
3462     return(MR_EXISTS);
3463 }
3464
3465 validate_name(argv, vo)
3466     char *argv[];
3467     register struct valobj *vo;
3468 {
3469     EXEC SQL BEGIN DECLARE SECTION; 
3470     char *name, *tbl, *namefield;
3471     int rowcount;
3472     EXEC SQL END DECLARE SECTION; 
3473     register char *c;
3474
3475     name = argv[vo->index];
3476     tbl = vo->table;
3477     namefield = vo->namefield;
3478     if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) {
3479         for (c = name; *c; c++)
3480           if (islower(*c))
3481             *c = toupper(*c);
3482     }
3483     sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
3484             tbl,tbl,namefield,name);
3485     EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3486     if(sqlca.sqlcode) 
3487       return(MR_INTERNAL);
3488     EXEC SQL DECLARE csr128 CURSOR FOR stmt;
3489     EXEC SQL OPEN csr128;
3490     EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA; 
3491     rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
3492     EXEC SQL CLOSE csr128;
3493
3494     if (ingres_errno) return(mr_errcode);
3495     return ((rowcount == 1) ? MR_EXISTS : vo->error);
3496 }
3497
3498 validate_date(argv, vo)
3499     char *argv[];
3500     struct valobj *vo;
3501 {
3502     EXEC SQL BEGIN DECLARE SECTION; 
3503     char *idate;
3504     double dd;
3505     int errorno;
3506     EXEC SQL END DECLARE SECTION; 
3507
3508     idate = argv[vo->index];
3509     EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
3510
3511     if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
3512     return(MR_EXISTS);
3513 }
3514
3515
3516 validate_rename(argv, vo)
3517 char *argv[];
3518 struct valobj *vo;
3519 {
3520     EXEC SQL BEGIN DECLARE SECTION; 
3521     char *name, *tbl, *namefield, *idfield;
3522     int id;
3523     EXEC SQL END DECLARE SECTION; 
3524     int status;
3525     register char *c;
3526
3527     c = name = argv[vo->index];
3528     while (*c)
3529       if (illegalchars[*c++])
3530         return(MR_BAD_CHAR);
3531     tbl = vo->table;
3532     /* minor kludge to upcasify machine names */
3533     if (!strcmp(tbl, "machine"))
3534         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3535     namefield = vo->namefield;
3536     idfield = vo->idfield;
3537     id = -1;
3538     if (idfield == 0) {
3539         if (!strcmp(argv[vo->index], argv[vo->index - 1]))
3540           return(MR_EXISTS);
3541         sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = LEFT('%s',SIZE(%s))",
3542                 namefield,tbl,namefield,name,namefield);
3543         EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; 
3544         if(sqlca.sqlcode)
3545           return(MR_INTERNAL);
3546         EXEC SQL DECLARE csr129 CURSOR FOR stmt;
3547         EXEC SQL OPEN csr129;
3548         EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA; 
3549         if(sqlca.sqlcode == 0) id=1; else id=0;
3550         EXEC SQL CLOSE csr129;
3551         
3552         if (ingres_errno) return(mr_errcode);
3553         if (id)
3554           return(vo->error);
3555         else
3556           return(MR_EXISTS);
3557     }
3558     status = name_to_id(name, tbl, &id);
3559     if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
3560       return(MR_EXISTS);
3561     else
3562       return(vo->error);
3563 }
3564
3565
3566 validate_type(argv, vo)
3567     char *argv[];
3568     register struct valobj *vo;
3569 {
3570     EXEC SQL BEGIN DECLARE SECTION; 
3571     char *typename;
3572     char *val;
3573     EXEC SQL END DECLARE SECTION; 
3574     register char *c;
3575
3576     typename = vo->table;
3577     c = val = argv[vo->index];
3578     while (*c) {
3579         if (illegalchars[*c++])
3580           return(MR_BAD_CHAR);
3581     }
3582
3583     /* uppercase type fields */
3584     for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
3585
3586     EXEC SQL SELECT trans INTO :cdummy FROM alias
3587       WHERE name = :typename AND type='TYPE' AND trans = :val;
3588     if (ingres_errno) return(mr_errcode);
3589     return (sqlca.sqlerrd[2] ? MR_EXISTS : vo->error);
3590 }
3591
3592 /* validate member or type-specific data field */
3593
3594 validate_typedata(q, argv, vo)
3595     register struct query *q;
3596     register char *argv[];
3597     register struct valobj *vo;
3598 {
3599     EXEC SQL BEGIN DECLARE SECTION; 
3600     char *name;
3601     char *field_type;
3602     char data_type[129];
3603     int id;
3604     EXEC SQL END DECLARE SECTION; 
3605     int status;
3606     char *index();
3607     register char *c;
3608
3609     /* get named object */
3610     name = argv[vo->index];
3611
3612     /* get field type string (known to be at index-1) */
3613     field_type = argv[vo->index-1];
3614
3615     /* get corresponding data type associated with field type name */
3616     EXEC SQL SELECT trans INTO :data_type FROM alias 
3617       WHERE name = :field_type AND type='TYPEDATA';
3618     if (ingres_errno) return(mr_errcode);
3619     if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
3620
3621     /* now retrieve the record id corresponding to the named object */
3622     if (index(data_type, ' '))
3623         *index(data_type, ' ') = 0;
3624     if (!strcmp(data_type, "user")) {
3625         /* USER */
3626         status = name_to_id(name, data_type, &id);
3627         if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
3628           return(MR_USER);
3629         if (status) return(status);
3630     } else if (!strcmp(data_type, "list")) {
3631         /* LIST */
3632         status = name_to_id(name, data_type, &id);
3633         if (status && status == MR_NOT_UNIQUE)
3634           return(MR_LIST);
3635         if (status == MR_NO_MATCH) {
3636             /* if idfield is non-zero, then if argv[0] matches the string
3637              * that we're trying to resolve, we should get the value of
3638              * numvalues.[idfield] for the id.
3639              */
3640             if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
3641                 set_next_object_id(q->validate->object_id, q->rtable, 0);
3642                 name = vo->idfield;
3643                 EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
3644                   WHERE name = :name;
3645                 if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
3646             } else
3647               return(MR_LIST);
3648         } else if (status) return(status);
3649     } else if (!strcmp(data_type, "machine")) {
3650         /* MACHINE */
3651         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
3652         status = name_to_id(name, data_type, &id);
3653         if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
3654           return(MR_MACHINE);
3655         if (status) return(status);
3656     } else if (!strcmp(data_type, "string")) {
3657         /* STRING */
3658         status = name_to_id(name, data_type, &id);
3659         if (status && status == MR_NOT_UNIQUE)
3660           return(MR_STRING);
3661         if (status == MR_NO_MATCH) {
3662             if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
3663             EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
3664             id++;
3665             EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
3666             EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
3667             cache_entry(name, "STRING", id);
3668         } else if (status) return(status);
3669     } else if (!strcmp(data_type, "none")) {
3670         id = 0;
3671     } else {
3672         return(MR_TYPE);
3673     }
3674
3675     /* now set value in argv */
3676     *(int *)argv[vo->index] = id;
3677     
3678     return (MR_EXISTS);
3679 }
3680
3681
3682 /* Lock the table named by the validation object */
3683
3684 lock_table(vo)
3685 struct valobj *vo;
3686 {
3687     sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0",
3688             vo->table,vo->table,vo->idfield);
3689     EXEC SQL EXECUTE IMMEDIATE :stmt_buf; 
3690     if (ingres_errno) return(mr_errcode);
3691     if (sqlca.sqlerrd[2] != 1)
3692       return(vo->error);
3693     else
3694       return(MR_EXISTS);
3695 }
3696
3697
3698 /* Check the database at startup time.  For now this just resets the
3699  * inprogress flags that the DCM uses.
3700  */
3701
3702 sanity_check_database()
3703 {
3704 }
3705
3706
3707 /* Dynamic SQL support routines */
3708 MR_SQLDA_T *mr_alloc_SQLDA()
3709 {
3710     MR_SQLDA_T *it;
3711     short *null_indicators;
3712     register int j;
3713
3714     if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) {
3715         com_err(whoami, MR_NO_MEM, "setting up SQLDA");
3716         exit(1);
3717     }
3718
3719     if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) {
3720         com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators");
3721         exit(1);
3722     }
3723
3724     for(j=0; j<QMAXARGS; j++) {
3725         if((it->sqlvar[j].sqldata=malloc(sizeof(short)+QMAXARGSIZE))==NULL) {
3726             com_err(whoami, MR_NO_MEM, "setting up SQLDA variables");
3727             exit(1);
3728         }
3729         it->sqlvar[j].sqllen=QMAXARGSIZE;
3730         it->sqlvar[j].sqlind=null_indicators+j;
3731         null_indicators[j]=0;
3732     }
3733     it->sqln=QMAXARGS;
3734     return it;
3735 }
3736
3737
3738 /* Use this after FETCH USING DESCRIPTOR one or more 
3739  * result columns may contain NULLs.  This routine is
3740  * not currently needed, since db/schema creates all
3741  * columns with a NOT NULL WITH DEFAULT clause.
3742  *
3743  * This is currently dead flesh, since no Moira columns
3744  * allow null values; all use default values.
3745  */
3746 mr_fix_nulls_in_SQLDA(da)
3747     MR_SQLDA_T *da;
3748 {
3749     register IISQLVAR *var;
3750     register int j;
3751     int *intp;
3752
3753     for(j=0, var=da->sqlvar; j<da->sqld; j++, var++) {
3754         switch(var->sqltype) {
3755           case -IISQ_CHA_TYPE:
3756             if(*var->sqlind)
3757               *var->sqldata='\0';
3758             break;
3759           case -IISQ_INT_TYPE:
3760             if(*var->sqlind) {
3761                 intp=(int *)var->sqldata;
3762                 *intp=0;
3763             }
3764             break;
3765         } 
3766     }
3767 }
3768
3769 /* prefetch_value():
3770  * This routine fetches an appropriate value from the numvalues table.
3771  * It is a little hack to get around the fact that SQL doesn't let you
3772  * do something like INSERT INTO table (foo) VALUES (other_table.bar).
3773  *
3774  * It is called from the query table as (*v->pre_rtn)(q,Argv,cl) or
3775  * from within a setup_...() routine with the appropriate arguments.
3776  *
3777  * Correct functioning of this routine may depend on the assumption
3778  * that this query is an APPEND.
3779  */ 
3780
3781 prefetch_value(q,argv,cl)
3782     struct query *q;
3783     char **argv;
3784     client *cl;
3785 {
3786     EXEC SQL BEGIN DECLARE SECTION; 
3787     char *name = q->validate->object_id;
3788     int value;
3789     EXEC SQL END DECLARE SECTION; 
3790     int status, limit, argc;
3791
3792     /* set next object id, limiting it if necessary */
3793     if(!strcmp(name, "uid") || !strcmp(name, "gid"))  
3794       limit = 1; /* So far as I know, this isn't needed.  Just CMA. */
3795     else 
3796       limit = 0;
3797     if((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS)
3798       return(status);
3799
3800     /* fetch object id */
3801     EXEC SQL SELECT value INTO :value FROM numvalues WHERE name=:name;
3802     if(ingres_errno) return(mr_errcode);
3803     if(sqlca.sqlerrd[2] != 1) return(MR_INTERNAL);
3804
3805     argc = q->argc + q->vcnt;   /* end of Argv for APPENDs */
3806     sprintf(argv[argc],"%d",value);
3807
3808     return(MR_SUCCESS);
3809 }
3810
3811 /* prefetch_filesys():
3812  * Fetches the phys_id from filesys based on the filsys_id in argv[0].
3813  * Appends the filsys_id and the phys_id to the argv so they can be
3814  * referenced in an INSERT into a table other than filesys.  Also
3815  * see comments at prefetch_value().
3816  *
3817  * Assumes the existence of a row where filsys_id = argv[0], since a
3818  * filesys label has already been resolved to a filsys_id. 
3819  */
3820 prefetch_filesys(q,argv,cl)
3821     struct query *q;
3822     char **argv;
3823     client *cl;
3824 {
3825     EXEC SQL BEGIN DECLARE SECTION; 
3826     int fid,phid;
3827     EXEC SQL END DECLARE SECTION; 
3828     int argc;
3829
3830     fid = *(int *)argv[0];
3831     EXEC SQL SELECT phys_id INTO :phid FROM filesys WHERE filsys_id = :fid;
3832     if(ingres_errno) return(mr_errcode);
3833
3834     argc=q->argc+q->vcnt;
3835     sprintf(argv[argc++],"%d",fid);
3836     sprintf(argv[argc],"%d",phid);
3837
3838     return(MR_SUCCESS);
3839 }
3840
3841 /* Convert normal Unix-style wildcards to SQL voodoo */
3842 convert_wildcards(arg)
3843     char *arg;
3844 {
3845     static char buffer[QMAXARGSIZE];
3846     register char *s, *d;
3847
3848     for(d=buffer,s=arg;*s;s++) {
3849         switch(*s) {
3850           case '*': *d++='%'; *d++='%'; break;
3851           case '?': *d++='_'; break;
3852           case '_':
3853           case '[': 
3854           case ']': *d++='*'; *d++ = *s; break;
3855           case '%': *d++='*'; *d++='%'; *d++='%'; break;
3856           default: *d++ = *s; break;
3857         }
3858     }
3859     *d='\0';
3860
3861     /* Copy back into argv */
3862     strcpy(arg,buffer);
3863
3864     return(MR_EXISTS);
3865 }
3866
3867 /* This version includes uppercase conversion, for things like gmac.
3868  * This is necessary because "LIKE" doesn't work with "uppercase()".
3869  * Including it in a wildcard routine saves making two passes over
3870  * the argument string. 
3871  */
3872 convert_wildcards_uppercase(arg)
3873     char *arg;
3874 {
3875     static char buffer[QMAXARGSIZE];
3876     register char *s, *d;
3877
3878     for(d=buffer,s=arg;*s;s++) {
3879         switch(*s) {
3880           case '*': *d++='%'; *d++='%'; break;
3881           case '?': *d++='_'; break;
3882           case '_':
3883           case '[': 
3884           case ']': *d++='*'; *d++ = *s; break;
3885           case '%': *d++='*'; *d++='%'; *d++='%'; break;
3886           default: *d++=toupper(*s); break;       /* This is the only diff. */
3887         }
3888     }
3889     *d='\0';
3890
3891     /* Copy back into argv */
3892     strcpy(arg,buffer);
3893  
3894     return(MR_EXISTS);
3895 }
3896
3897 /* eof:qsupport.dc */
This page took 0.350332 seconds and 3 git commands to generate.