]>
Commit | Line | Data |
---|---|---|
b070f8a1 | 1 | /* |
2 | * $Source$ | |
3 | * $Author$ | |
4 | * $Header$ | |
5 | * | |
6 | * Copyright (C) 1987 by the Massachusetts Institute of Technology | |
7 | * For copying and distribution information, please see the file | |
8 | * <mit-copyright.h>. | |
9 | * | |
10 | */ | |
11 | ||
12 | #ifndef lint | |
5d677c81 | 13 | static char *rcsid_qsupport_dc = "$Header$"; |
b070f8a1 | 14 | #endif lint |
15 | ||
16 | #include <mit-copyright.h> | |
17 | #include "query.h" | |
18 | #include "mr_server.h" | |
19 | #include <ctype.h> | |
1a41acb7 | 20 | #ifdef GDSS |
21 | #include "gdss.h" | |
22 | #endif /* GDSS */ | |
5d677c81 | 23 | EXEC SQL INCLUDE sqlca; |
24 | EXEC SQL INCLUDE sqlda; | |
25 | #include "qrtn.h" | |
b070f8a1 | 26 | |
27 | extern char *whoami, *strsave(); | |
28 | extern int ingres_errno, mr_errcode; | |
29 | ||
0fc7ab46 | 30 | EXEC SQL BEGIN DECLARE SECTION; |
45bf7573 | 31 | extern char stmt_buf[]; |
0fc7ab46 | 32 | EXEC SQL END DECLARE SECTION; |
5d677c81 | 33 | |
453b99fe | 34 | EXEC SQL WHENEVER SQLERROR CALL ingerr; |
35 | ||
0fc7ab46 | 36 | |
0659551f | 37 | /* Special query routines */ |
b070f8a1 | 38 | |
0659551f | 39 | /* set_pobox - this does all of the real work. |
40 | * argv = user_id, type, box | |
41 | * if type is POP, then box should be a machine, and its ID should be put in | |
42 | * pop_id. If type is SMTP, then box should be a string and its ID should | |
43 | * be put in box_id. If type is NONE, then box doesn't matter. | |
0fc7ab46 | 44 | */ |
b070f8a1 | 45 | |
0659551f | 46 | int set_pobox(q, argv, cl) |
b070f8a1 | 47 | struct query *q; |
0659551f | 48 | char **argv; |
b070f8a1 | 49 | client *cl; |
5d677c81 | 50 | { |
0fc7ab46 | 51 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 52 | int user, id; |
53 | char *box, potype[9]; | |
0fc7ab46 | 54 | EXEC SQL END DECLARE SECTION; |
0659551f | 55 | int status; |
5d677c81 | 56 | |
0659551f | 57 | box = argv[2]; |
58 | user = *(int *)argv[0]; | |
b070f8a1 | 59 | |
0659551f | 60 | EXEC SQL REPEATED SELECT pop_id, potype INTO :id, :potype FROM users |
61 | WHERE users_id = :user; | |
62 | if (ingres_errno) return(mr_errcode); | |
63 | if (!strcmp(strtrim(potype), "POP")) | |
64 | set_pop_usage(id, -1); | |
b070f8a1 | 65 | |
0659551f | 66 | if (!strcmp(argv[1], "POP")) { |
67 | status = name_to_id(box, "MACHINE", &id); | |
68 | if (status == MR_NO_MATCH) | |
69 | return(MR_MACHINE); | |
70 | else if (status) | |
71 | return(status); | |
72 | EXEC SQL REPEATED UPDATE users SET potype = 'POP', pop_id = :id | |
73 | WHERE users_id = :user; | |
74 | set_pop_usage(id, 1); | |
75 | } else if (!strcmp(argv[1], "SMTP")) { | |
76 | if (index(box, '/') || index(box, '|')) | |
77 | return(MR_BAD_CHAR); | |
78 | status = name_to_id(box, "STRING", &id); | |
79 | if (status == MR_NO_MATCH) { | |
80 | id=add_string(box); | |
81 | } else if (status) | |
82 | return(status); | |
83 | EXEC SQL REPEATED UPDATE users SET potype='SMTP', box_id = :id | |
84 | WHERE users_id = :user; | |
85 | } else /* argv[1] == "NONE" */ { | |
86 | EXEC SQL REPEATED UPDATE users SET potype='NONE' | |
87 | WHERE users_id = :user; | |
b070f8a1 | 88 | } |
89 | ||
0659551f | 90 | set_pobox_modtime(q, argv, cl); |
91 | EXEC SQL REPEATED UPDATE tblstats SET updates = updates+1, modtime='now' | |
92 | WHERE table_name='users'; | |
93 | if (ingres_errno) return(mr_errcode); | |
b070f8a1 | 94 | return(MR_SUCCESS); |
5d677c81 | 95 | } |
b070f8a1 | 96 | |
97 | ||
0659551f | 98 | /* get_list_info: passed a wildcard list name, returns lots of stuff about |
99 | * each list. This is tricky: first build a queue of all requested | |
100 | * data. Rest of processing consists of fixing gid, ace_name, and modby. | |
0fc7ab46 | 101 | */ |
b070f8a1 | 102 | |
0659551f | 103 | get_list_info(q, aargv, cl, action, actarg) |
104 | register struct query *q; | |
105 | char **aargv; | |
b070f8a1 | 106 | client *cl; |
0659551f | 107 | register int (*action)(); |
108 | int actarg; | |
5d677c81 | 109 | { |
e448f08d | 110 | char *argv[13]; |
0fc7ab46 | 111 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 112 | char *name, acl_type[9], listname[33], active[5], public[5], hidden[5]; |
113 | char maillist[5], grouplist[5], gid_str[6], acl_name[256], desc[256]; | |
114 | char modtime[27], modby[256], modwith[9]; | |
115 | int id, rowcount, acl_id, hid, modby_id; | |
116 | char qual[80]; | |
0fc7ab46 | 117 | EXEC SQL END DECLARE SECTION; |
0659551f | 118 | int returned, status; |
119 | struct save_queue *sq, *sq_create(); | |
b070f8a1 | 120 | |
0659551f | 121 | returned = rowcount = 0; |
122 | name = aargv[0]; | |
123 | convert_wildcards(name); | |
b070f8a1 | 124 | |
0659551f | 125 | sq = sq_create(); |
126 | sprintf(qual,"name LIKE '%s' ESCAPE '*'",name); | |
127 | optimize_sql_stmt(qual); | |
128 | EXEC SQL DECLARE csr102 CURSOR FOR SELECT list_id FROM list | |
129 | WHERE :qual; | |
130 | if (ingres_errno) | |
131 | return(mr_errcode); | |
132 | EXEC SQL OPEN csr102; | |
133 | if (ingres_errno) | |
134 | return(mr_errcode); | |
135 | while(1) | |
136 | { | |
137 | EXEC SQL FETCH csr102 INTO :id; | |
138 | if(sqlca.sqlcode!=0) break; | |
139 | sq_save_data(sq, id); | |
140 | rowcount++; | |
141 | } | |
142 | EXEC SQL CLOSE csr102; | |
5d677c81 | 143 | |
0659551f | 144 | if (ingres_errno) return(mr_errcode); |
1a41acb7 | 145 | if (rowcount == 0) |
146 | return(MR_NO_MATCH); | |
b070f8a1 | 147 | |
0659551f | 148 | argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden; |
149 | argv[4] = maillist; argv[5] = grouplist; argv[6] = gid_str; | |
150 | argv[7] = acl_type; argv[9] = desc; argv[10] = modtime; argv[12] = modwith; | |
b070f8a1 | 151 | |
0659551f | 152 | while (sq_get_data(sq, &id)) { |
153 | if (id == 0) | |
154 | continue; | |
155 | argv[6] = gid_str; | |
156 | EXEC SQL REPEATED SELECT name, CHAR(active), CHAR(publicflg), | |
157 | CHAR(hidden), hidden, CHAR(maillist), CHAR(grouplist), CHAR(gid), | |
158 | TRIM(acl_type), acl_id, description, CHAR(modtime), modby, modwith | |
159 | INTO :listname, :active, :public, :hidden, :hid, :maillist, | |
160 | :grouplist, :gid_str, :acl_type, :acl_id, :desc, | |
161 | :modtime, :modby_id, :modwith | |
162 | FROM list WHERE list_id = :id; | |
b070f8a1 | 163 | |
0659551f | 164 | if (ingres_errno) return(mr_errcode); |
b070f8a1 | 165 | |
0659551f | 166 | if (atoi(gid_str) == -1) |
167 | argv[6] = UNIQUE_GID; | |
b070f8a1 | 168 | |
0659551f | 169 | argv[8] = malloc(0); |
170 | if (!strcmp(acl_type, "LIST")) { | |
171 | status = id_to_name(acl_id, "LIST", &argv[8]); | |
172 | } else if (!strcmp(acl_type, "USER")) { | |
173 | status = id_to_name(acl_id, "USER", &argv[8]); | |
174 | } else if (!strcmp(acl_type, "KERBEROS")) { | |
175 | status = id_to_name(acl_id, "STRING", &argv[8]); | |
176 | } else if (!strcmp(acl_type, "NONE")) { | |
177 | status = 0; | |
178 | free(argv[8]); | |
179 | argv[8] = strsave("NONE"); | |
180 | } else { | |
181 | status = 0; | |
182 | free(argv[8]); | |
183 | argv[8] = strsave("???"); | |
184 | } | |
185 | if (status && status != MR_NO_MATCH) return(status); | |
b070f8a1 | 186 | |
0659551f | 187 | argv[11] = malloc(0); |
188 | if (modby_id > 0) | |
189 | status = id_to_name(modby_id, "USER", &argv[11]); | |
190 | else | |
191 | status = id_to_name(-modby_id, "STRING", &argv[11]); | |
192 | if (status && status != MR_NO_MATCH) return(status); | |
b070f8a1 | 193 | |
0659551f | 194 | mr_trim_args(q->vcnt, argv); |
195 | returned++; | |
196 | (*action)(q->vcnt, argv, actarg); | |
197 | free(argv[8]); | |
198 | free(argv[11]); | |
0fc7ab46 | 199 | } |
200 | ||
0659551f | 201 | sq_destroy(sq); |
202 | if (ingres_errno) return(mr_errcode); | |
203 | return (MR_SUCCESS); | |
b070f8a1 | 204 | } |
205 | ||
206 | ||
0659551f | 207 | /* Add_member_to_list: do list flattening as we go! MAXLISTDEPTH is |
208 | * how many different ancestors a member is allowed to have. | |
b070f8a1 | 209 | */ |
210 | ||
0659551f | 211 | #define MAXLISTDEPTH 1024 |
b070f8a1 | 212 | |
0659551f | 213 | int add_member_to_list(q, argv, cl) |
b070f8a1 | 214 | struct query *q; |
0659551f | 215 | char **argv; |
b070f8a1 | 216 | client *cl; |
5d677c81 | 217 | { |
0fc7ab46 | 218 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 219 | int id, lid, mid, error, who, ref, rowcnt; |
220 | char *mtype, dtype[9], *entity; | |
0fc7ab46 | 221 | EXEC SQL END DECLARE SECTION; |
0659551f | 222 | int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a; |
223 | int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d; | |
224 | int status; | |
225 | char *dtypes[MAXLISTDEPTH]; | |
226 | char *iargv[3], *buf; | |
b070f8a1 | 227 | |
0659551f | 228 | lid = *(int *)argv[0]; |
229 | mtype = argv[1]; | |
230 | mid = *(int *)argv[2]; | |
231 | /* if the member is already a direct member of the list, punt */ | |
232 | EXEC SQL REPEATED SELECT COUNT(list_id) INTO :rowcnt FROM imembers | |
233 | WHERE list_id = :lid AND member_id = :mid | |
234 | AND member_type = :mtype AND direct = 1; | |
235 | if (rowcnt > 0) | |
236 | return(MR_EXISTS); | |
237 | if (!strcasecmp(mtype, "STRING")) { | |
238 | buf = malloc(0); | |
239 | status = id_to_name(mid, "STRING", &buf); | |
240 | if (status) return(status); | |
241 | if (index(buf, '/') || index(buf, '|')) { | |
242 | free(buf); | |
243 | return(MR_BAD_CHAR); | |
244 | } | |
245 | free(buf); | |
246 | } | |
b070f8a1 | 247 | |
0659551f | 248 | ancestors[0] = lid; |
249 | aref[0] = 1; | |
250 | acount = 1; | |
251 | EXEC SQL DECLARE csr103 CURSOR FOR | |
252 | SELECT list_id, ref_count FROM imembers | |
253 | WHERE member_id = :lid AND member_type='LIST'; | |
254 | if (ingres_errno) | |
255 | return(mr_errcode); | |
256 | EXEC SQL OPEN csr103; | |
257 | if (ingres_errno) | |
258 | return(mr_errcode); | |
259 | while(1) { | |
260 | EXEC SQL FETCH csr103 INTO :id, :ref; | |
261 | if(sqlca.sqlcode != 0) break; | |
262 | aref[acount] = ref; | |
263 | ancestors[acount++] = id; | |
264 | if (acount >= MAXLISTDEPTH) break; | |
265 | } | |
266 | EXEC SQL CLOSE csr103; | |
267 | if (ingres_errno) return(mr_errcode); | |
268 | if (acount >= MAXLISTDEPTH) { | |
269 | return(MR_INTERNAL); | |
270 | } | |
271 | descendants[0] = mid; | |
272 | dtypes[0] = mtype; | |
273 | dref[0] = 1; | |
274 | dcount = 1; | |
275 | error = 0; | |
276 | if (!strcmp(mtype, "LIST")) { | |
277 | EXEC SQL DECLARE csr104 CURSOR FOR | |
278 | SELECT member_id, member_type, ref_count | |
279 | FROM imembers | |
280 | WHERE list_id = :mid; | |
281 | if (ingres_errno) | |
282 | return(mr_errcode); | |
283 | EXEC SQL OPEN csr104; | |
284 | if (ingres_errno) | |
285 | return(mr_errcode); | |
286 | while(1) { | |
287 | EXEC SQL FETCH csr104 INTO :id, :dtype, :ref; | |
288 | if(sqlca.sqlcode != 0) break; | |
289 | switch (dtype[0]) { | |
290 | case 'L': | |
291 | dtypes[dcount] = "LIST"; | |
292 | break; | |
293 | case 'U': | |
294 | dtypes[dcount] = "USER"; | |
295 | break; | |
296 | case 'S': | |
297 | dtypes[dcount] = "STRING"; | |
298 | break; | |
299 | case 'K': | |
300 | dtypes[dcount] = "KERBEROS"; | |
301 | break; | |
302 | default: | |
303 | error++; | |
304 | break; | |
305 | } | |
306 | dref[dcount] = ref; | |
307 | descendants[dcount++] = id; | |
308 | if (dcount >= MAXLISTDEPTH) { | |
309 | error++; | |
310 | break; | |
311 | } | |
312 | } | |
313 | EXEC SQL CLOSE csr104; | |
314 | if (ingres_errno) return(mr_errcode); | |
315 | if (error) | |
b070f8a1 | 316 | return(MR_INTERNAL); |
b070f8a1 | 317 | } |
0659551f | 318 | for (a = 0; a < acount; a++) { |
319 | lid = ancestors[a]; | |
320 | for (d = 0; d < dcount; d++) { | |
321 | mid = descendants[d]; | |
322 | mtype = dtypes[d]; | |
323 | if (mid == lid && !strcmp(mtype, "LIST")) { | |
324 | return(MR_LISTLOOP); | |
325 | } | |
326 | EXEC SQL REPEATED SELECT COUNT(ref_count) INTO :rowcnt | |
327 | FROM imembers | |
328 | WHERE list_id = :lid AND member_id = :mid | |
329 | AND member_type = :mtype; | |
330 | ref = aref[a] * dref[d]; | |
331 | if (rowcnt > 0) { | |
332 | if (a == 0 && d == 0) { | |
333 | EXEC SQL UPDATE imembers | |
334 | SET ref_count = ref_count+:ref, direct=1 | |
335 | WHERE list_id = :lid AND member_id = :mid | |
336 | AND member_type = :mtype; | |
337 | } else { | |
338 | EXEC SQL UPDATE imembers | |
339 | SET ref_count = ref_count+:ref | |
340 | WHERE list_id = :lid AND member_id = :mid | |
341 | AND member_type = :mtype; | |
342 | } | |
343 | } else { | |
344 | incremental_clear_before(); | |
345 | if (a == 0 && d == 0) { | |
346 | EXEC SQL INSERT INTO imembers | |
347 | (list_id, member_id, direct, member_type, ref_count) | |
348 | VALUES (:lid, :mid, 1, :mtype, 1); | |
349 | } else { | |
350 | EXEC SQL INSERT INTO imembers | |
351 | (list_id, member_id, member_type, ref_count) | |
352 | VALUES (:lid, :mid, :mtype, 1); | |
353 | } | |
354 | iargv[0] = (char *)lid; | |
355 | iargv[1] = mtype; | |
356 | iargv[2] = (char *)mid; | |
357 | incremental_after("members", 0, iargv); | |
358 | } | |
359 | } | |
b070f8a1 | 360 | } |
0659551f | 361 | lid = *(int *)argv[0]; |
362 | entity = cl->entity; | |
363 | who = cl->client_id; | |
364 | EXEC SQL REPEATED UPDATE list | |
365 | SET modtime='now', modby = :who, modwith = :entity | |
366 | WHERE list_id = :lid; | |
b070f8a1 | 367 | if (ingres_errno) return(mr_errcode); |
b070f8a1 | 368 | return(MR_SUCCESS); |
5d677c81 | 369 | } |
b070f8a1 | 370 | |
371 | ||
0659551f | 372 | /* Delete_member_from_list: do list flattening as we go! |
b070f8a1 | 373 | */ |
374 | ||
0659551f | 375 | int delete_member_from_list(q, argv, cl) |
b070f8a1 | 376 | struct query *q; |
377 | char **argv; | |
0659551f | 378 | client *cl; |
5d677c81 | 379 | { |
0fc7ab46 | 380 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 381 | int id, lid, mid, cnt, error, who, ref; |
382 | char *mtype, dtype[9], *entity; | |
5d677c81 | 383 | EXEC SQL END DECLARE SECTION; |
0659551f | 384 | int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a; |
385 | int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d; | |
386 | char *dtypes[MAXLISTDEPTH]; | |
387 | char *iargv[3]; | |
b070f8a1 | 388 | |
0659551f | 389 | lid = *(int *)argv[0]; |
390 | mtype = argv[1]; | |
391 | mid = *(int *)argv[2]; | |
392 | /* if the member is not a direct member of the list, punt */ | |
393 | EXEC SQL REPEATED SELECT COUNT(list_id) INTO :cnt FROM imembers | |
394 | WHERE list_id = :lid AND member_id = :mid | |
395 | AND member_type = :mtype AND direct = 1; | |
b070f8a1 | 396 | if (ingres_errno) return(mr_errcode); |
0659551f | 397 | if (cnt == 0) |
398 | return(MR_NO_MATCH); | |
399 | ancestors[0] = lid; | |
400 | aref[0] = 1; | |
401 | acount = 1; | |
402 | EXEC SQL DECLARE csr105 CURSOR FOR | |
403 | SELECT list_id, ref_count FROM imembers | |
404 | WHERE member_id = :lid AND member_type = 'LIST'; | |
b070f8a1 | 405 | if (ingres_errno) |
406 | return(mr_errcode); | |
0659551f | 407 | EXEC SQL OPEN csr105; |
408 | if (ingres_errno) | |
409 | return(mr_errcode); | |
410 | while(1) { | |
411 | EXEC SQL FETCH csr105 INTO :id, :ref; | |
412 | if(sqlca.sqlcode!=0) break; | |
413 | aref[acount] = ref; | |
414 | ancestors[acount++] = id; | |
415 | if (acount >= MAXLISTDEPTH) break; | |
b070f8a1 | 416 | } |
0659551f | 417 | EXEC SQL CLOSE csr105; |
418 | if (ingres_errno) | |
45bf7573 | 419 | return(mr_errcode); |
0659551f | 420 | if (acount >= MAXLISTDEPTH) |
421 | return(MR_INTERNAL); | |
422 | descendants[0] = mid; | |
423 | dtypes[0] = mtype; | |
424 | dref[0] = 1; | |
425 | dcount = 1; | |
426 | error = 0; | |
427 | if (!strcmp(mtype, "LIST")) { | |
428 | EXEC SQL DECLARE csr106 CURSOR FOR | |
429 | SELECT member_id, member_type, ref_count FROM imembers | |
430 | WHERE list_id = :mid; | |
431 | if (ingres_errno) | |
432 | return(mr_errcode); | |
433 | EXEC SQL OPEN csr106; | |
434 | if (ingres_errno) | |
435 | return(mr_errcode); | |
436 | while(1) { | |
437 | EXEC SQL FETCH csr106 INTO :id, :dtype, :ref; | |
438 | if(sqlca.sqlcode!=0) break; | |
439 | switch (dtype[0]) { | |
440 | case 'L': | |
441 | dtypes[dcount] = "LIST"; | |
442 | break; | |
443 | case 'U': | |
444 | dtypes[dcount] = "USER"; | |
445 | break; | |
446 | case 'S': | |
447 | dtypes[dcount] = "STRING"; | |
448 | break; | |
449 | case 'K': | |
450 | dtypes[dcount] = "KERBEROS"; | |
451 | break; | |
452 | default: | |
453 | error++; | |
454 | break; | |
455 | } | |
456 | dref[dcount] = ref; | |
457 | descendants[dcount++] = id; | |
458 | if (dcount >= MAXLISTDEPTH) break; | |
459 | } | |
460 | EXEC SQL CLOSE csr106; | |
461 | if (ingres_errno) | |
462 | return(mr_errcode); | |
463 | if (error) | |
464 | return(MR_INTERNAL); | |
3beadd83 | 465 | } |
0659551f | 466 | for (a = 0; a < acount; a++) { |
467 | lid = ancestors[a]; | |
468 | for (d = 0; d < dcount; d++) { | |
469 | mid = descendants[d]; | |
470 | mtype = dtypes[d]; | |
471 | if (mid == lid && !strcmp(mtype, "LIST")) { | |
472 | return(MR_LISTLOOP); | |
473 | } | |
474 | EXEC SQL REPEATED SELECT ref_count INTO :cnt FROM imembers | |
475 | WHERE list_id = :lid AND member_id = :mid AND member_type = :mtype; | |
476 | ref = aref[a] * dref[d]; | |
477 | if (cnt <= ref) { | |
478 | iargv[0] = (char *)lid; | |
479 | iargv[1] = mtype; | |
480 | iargv[2] = (char *)mid; | |
481 | incremental_before("members", 0, iargv); | |
482 | EXEC SQL DELETE FROM imembers | |
483 | WHERE list_id = :lid AND member_id = :mid | |
484 | AND member_type= :mtype; | |
485 | incremental_clear_after(); | |
486 | } else if (a == 0 && d == 0) { | |
487 | EXEC SQL UPDATE imembers | |
488 | SET ref_count = ref_count - :ref, direct = 0 | |
489 | WHERE list_id = :lid AND member_id = :mid | |
490 | AND member_type = :mtype; | |
491 | } else { | |
492 | EXEC SQL UPDATE imembers | |
493 | SET ref_count = ref_count - :ref | |
494 | WHERE list_id = :lid AND member_id = :mid | |
495 | AND member_type = :mtype; | |
496 | } | |
497 | } | |
3beadd83 | 498 | } |
0659551f | 499 | lid = *(int *)argv[0]; |
500 | entity = cl->entity; | |
501 | who = cl->client_id; | |
502 | EXEC SQL UPDATE list SET modtime = 'now', modby = :who, modwith = :entity | |
503 | WHERE list_id = :lid; | |
504 | if (ingres_errno) return(mr_errcode); | |
b070f8a1 | 505 | return(MR_SUCCESS); |
5d677c81 | 506 | } |
b070f8a1 | 507 | |
508 | ||
0659551f | 509 | /* get_ace_use - given a type and a name, return a type and a name. |
510 | * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0], | |
511 | * and argv[1] will contain the ID of the entity in question. The R* | |
512 | * types mean to recursively look at every containing list, not just | |
513 | * when the object in question is a direct member. On return, the | |
514 | * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR. | |
b070f8a1 | 515 | */ |
516 | ||
0659551f | 517 | int get_ace_use(q, argv, cl, action, actarg) |
b070f8a1 | 518 | struct query *q; |
519 | char *argv[]; | |
45bf7573 | 520 | client *cl; |
0659551f | 521 | int (*action)(); |
522 | int actarg; | |
5d677c81 | 523 | { |
0659551f | 524 | int found = 0; |
0fc7ab46 | 525 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 526 | char *atype; |
527 | int aid, listid, id; | |
0fc7ab46 | 528 | EXEC SQL END DECLARE SECTION; |
0659551f | 529 | struct save_queue *sq, *sq_create(); |
b070f8a1 | 530 | |
0659551f | 531 | atype = argv[0]; |
532 | aid = *(int *)argv[1]; | |
533 | if (!strcmp(atype, "LIST") || !strcmp(atype, "USER") || | |
534 | !strcmp(atype, "KERBEROS")) { | |
535 | return(get_ace_internal(atype, aid, action, actarg)); | |
536 | } | |
b070f8a1 | 537 | |
0659551f | 538 | sq = sq_create(); |
539 | if (!strcmp(atype, "RLIST")) { | |
540 | sq_save_data(sq, aid); | |
541 | /* get all the list_id's of containing lists */ | |
542 | EXEC SQL DECLARE csr107 CURSOR FOR | |
543 | SELECT list_id FROM imembers | |
544 | WHERE member_type='LIST' AND member_id = :aid; | |
545 | if (ingres_errno) | |
546 | return(mr_errcode); | |
547 | EXEC SQL OPEN csr107; | |
548 | if (ingres_errno) | |
549 | return(mr_errcode); | |
550 | while(1) { | |
551 | EXEC SQL FETCH csr107 INTO :listid; | |
552 | if(sqlca.sqlcode != 0) break; | |
553 | sq_save_unique_data(sq, listid); | |
554 | } | |
555 | EXEC SQL CLOSE csr107; | |
556 | /* now process each one */ | |
557 | while (sq_get_data(sq, &id)) { | |
558 | if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS) | |
559 | found++; | |
560 | } | |
561 | } | |
b070f8a1 | 562 | |
0659551f | 563 | if (!strcmp(atype, "RUSER")) { |
564 | EXEC SQL DECLARE csr108 CURSOR FOR | |
565 | SELECT list_id FROM imembers | |
566 | WHERE member_type='USER' AND member_id = :aid; | |
567 | if (ingres_errno) | |
568 | return(mr_errcode); | |
569 | EXEC SQL OPEN csr108; | |
570 | if (ingres_errno) | |
571 | return(mr_errcode); | |
572 | while(1) { | |
573 | EXEC SQL FETCH csr108 INTO :listid; | |
574 | if(sqlca.sqlcode != 0) break; | |
575 | sq_save_data(sq, listid); | |
576 | } | |
577 | EXEC SQL CLOSE csr108; | |
578 | /* now process each one */ | |
579 | while (sq_get_data(sq, &id)) { | |
580 | if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS) | |
581 | found++; | |
582 | } | |
583 | if (get_ace_internal("USER", aid, action, actarg) == MR_SUCCESS) | |
584 | found++; | |
585 | } | |
45bf7573 | 586 | |
0659551f | 587 | if (!strcmp(atype, "RKERBERO")) { |
588 | EXEC SQL DECLARE csr109 CURSOR FOR | |
589 | SELECT list_id FROM imembers | |
590 | WHERE member_type='KERBEROS' AND member_id = :aid; | |
591 | if (ingres_errno) | |
592 | return(mr_errcode); | |
593 | EXEC SQL OPEN csr109; | |
594 | if (ingres_errno) | |
595 | return(mr_errcode); | |
596 | while(1) { | |
597 | EXEC SQL FETCH csr109 INTO :listid; | |
598 | if(sqlca.sqlcode != 0) break; | |
599 | sq_save_data(sq, listid); | |
600 | } | |
601 | EXEC SQL CLOSE csr109; | |
602 | /* now process each one */ | |
603 | while (sq_get_data(sq, &id)) { | |
604 | if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS) | |
605 | found++; | |
606 | } | |
607 | if (get_ace_internal("KERBEROS", aid, action, actarg) == MR_SUCCESS) | |
608 | found++; | |
609 | } | |
b3ce33fe | 610 | |
0659551f | 611 | sq_destroy(sq); |
612 | if (ingres_errno) return(mr_errcode); | |
613 | if (!found) return(MR_NO_MATCH); | |
45bf7573 | 614 | return(MR_SUCCESS); |
5d677c81 | 615 | } |
b070f8a1 | 616 | |
617 | ||
0659551f | 618 | /* This looks up a single list or user for ace use. atype must be "USER" |
619 | * or "LIST", and aid is the ID of the corresponding object. This is used | |
620 | * by get_ace_use above. | |
b070f8a1 | 621 | */ |
622 | ||
0659551f | 623 | get_ace_internal(atype, aid, action, actarg) |
624 | EXEC SQL BEGIN DECLARE SECTION; | |
625 | char *atype; | |
626 | int aid; | |
627 | EXEC SQL END DECLARE SECTION; | |
628 | int (*action)(); | |
629 | int actarg; | |
5d677c81 | 630 | { |
0659551f | 631 | char *rargv[2]; |
632 | int found = 0; | |
0fc7ab46 | 633 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 634 | char name[33]; |
0fc7ab46 | 635 | EXEC SQL END DECLARE SECTION; |
b070f8a1 | 636 | |
0659551f | 637 | rargv[1] = name; |
638 | if (!strcmp(atype, "LIST")) { | |
639 | rargv[0] = "FILESYS"; | |
640 | EXEC SQL DECLARE csr110 CURSOR FOR | |
641 | SELECT label FROM filesys | |
642 | WHERE owners = :aid; | |
643 | if (ingres_errno) | |
644 | return(mr_errcode); | |
645 | EXEC SQL OPEN csr110; | |
646 | if (ingres_errno) | |
647 | return(mr_errcode); | |
648 | while(1) { | |
649 | EXEC SQL FETCH csr110 INTO :name; | |
650 | if(sqlca.sqlcode != 0) break; | |
651 | (*action)(2, rargv, actarg); | |
652 | found++; | |
b070f8a1 | 653 | } |
0659551f | 654 | EXEC SQL CLOSE csr110; |
b94e861b | 655 | |
0659551f | 656 | rargv[0] = "QUERY"; |
657 | EXEC SQL DECLARE csr111 CURSOR FOR | |
658 | SELECT capability FROM capacls | |
659 | WHERE list_id = :aid ; | |
660 | if (ingres_errno) | |
661 | return(mr_errcode); | |
662 | EXEC SQL OPEN csr111; | |
663 | if (ingres_errno) | |
664 | return(mr_errcode); | |
665 | while(1) { | |
666 | EXEC SQL FETCH csr111 INTO :name ; | |
667 | if(sqlca.sqlcode != 0) break; | |
668 | (*action)(2, rargv, actarg); | |
669 | found++; | |
b070f8a1 | 670 | } |
0659551f | 671 | EXEC SQL CLOSE csr111; |
672 | } else if (!strcmp(atype, "USER")) { | |
673 | rargv[0] = "FILESYS"; | |
674 | EXEC SQL DECLARE csr112 CURSOR FOR | |
675 | SELECT label FROM filesys | |
676 | WHERE owner = :aid; | |
677 | if (ingres_errno) | |
678 | return(mr_errcode); | |
679 | EXEC SQL OPEN csr112; | |
680 | if (ingres_errno) | |
681 | return(mr_errcode); | |
682 | while(1) { | |
683 | EXEC SQL FETCH csr112 INTO :name ; | |
684 | if(sqlca.sqlcode != 0) break; | |
685 | (*action)(2, rargv, actarg); | |
686 | found++; | |
b070f8a1 | 687 | } |
0659551f | 688 | EXEC SQL CLOSE csr112; |
b070f8a1 | 689 | } |
0fc7ab46 | 690 | |
0659551f | 691 | rargv[0] = "LIST"; |
692 | EXEC SQL DECLARE csr113 CURSOR FOR | |
693 | SELECT name FROM list | |
694 | WHERE acl_type = :atype AND acl_id = :aid; | |
835aabee | 695 | if (ingres_errno) |
696 | return(mr_errcode); | |
0659551f | 697 | EXEC SQL OPEN csr113; |
835aabee | 698 | if (ingres_errno) |
699 | return(mr_errcode); | |
5d677c81 | 700 | while(1) { |
0659551f | 701 | EXEC SQL FETCH csr113 INTO :name; |
5d677c81 | 702 | if(sqlca.sqlcode != 0) break; |
b070f8a1 | 703 | (*action)(2, rargv, actarg); |
0659551f | 704 | found++; |
5d677c81 | 705 | } |
0659551f | 706 | EXEC SQL CLOSE csr113; |
b070f8a1 | 707 | |
0659551f | 708 | rargv[0] = "SERVICE"; |
709 | EXEC SQL DECLARE csr114 CURSOR FOR | |
710 | SELECT name FROM servers | |
711 | WHERE acl_type = :atype AND acl_id = :aid; | |
835aabee | 712 | if (ingres_errno) |
713 | return(mr_errcode); | |
0659551f | 714 | EXEC SQL OPEN csr114; |
835aabee | 715 | if (ingres_errno) |
716 | return(mr_errcode); | |
0659551f | 717 | while(1) { |
718 | EXEC SQL FETCH csr114 INTO :name; | |
719 | if(sqlca.sqlcode != 0) break; | |
720 | (*action)(2, rargv, actarg); | |
721 | found++; | |
0fc7ab46 | 722 | } |
0659551f | 723 | EXEC SQL CLOSE csr114; |
b070f8a1 | 724 | |
0659551f | 725 | rargv[0] = "HOSTACCESS"; |
726 | EXEC SQL DECLARE csr115 CURSOR FOR | |
727 | SELECT name FROM machine m, hostaccess ha | |
728 | WHERE m.mach_id = ha.mach_id AND ha.acl_type = :atype | |
729 | AND ha.acl_id = :aid; | |
730 | if (ingres_errno) | |
731 | return(mr_errcode); | |
732 | EXEC SQL OPEN csr115; | |
733 | if (ingres_errno) | |
734 | return(mr_errcode); | |
735 | while(1) { | |
736 | EXEC SQL FETCH csr115 INTO :name; | |
737 | if(sqlca.sqlcode != 0) break; | |
738 | (*action)(2, rargv, actarg); | |
739 | found++; | |
0fc7ab46 | 740 | } |
0659551f | 741 | EXEC SQL CLOSE csr115; |
b070f8a1 | 742 | |
0659551f | 743 | rargv[0] = "ZEPHYR"; |
744 | EXEC SQL DECLARE csr116 CURSOR FOR | |
745 | SELECT class FROM zephyr z | |
746 | WHERE z.xmt_type = :atype AND z.xmt_id = :aid | |
747 | OR z.sub_type = :atype AND z.sub_id = :aid | |
748 | OR z.iws_type = :atype AND z.iws_id = :aid | |
749 | OR z.iui_type = :atype AND z.iui_id = :aid; | |
750 | if (ingres_errno) | |
751 | return(mr_errcode); | |
752 | EXEC SQL OPEN csr116; | |
753 | if (ingres_errno) | |
754 | return(mr_errcode); | |
755 | while(1) { | |
756 | EXEC SQL FETCH csr116 INTO :name; | |
757 | if(sqlca.sqlcode != 0) break; | |
758 | (*action)(2, rargv, actarg); | |
759 | found++; | |
760 | } | |
761 | EXEC SQL CLOSE csr116; | |
b070f8a1 | 762 | |
0659551f | 763 | if (!found) return(MR_NO_MATCH); |
b070f8a1 | 764 | return(MR_SUCCESS); |
5d677c81 | 765 | } |
b070f8a1 | 766 | |
767 | ||
0659551f | 768 | /* get_lists_of_member - given a type and a name, return the name and flags |
769 | * of all of the lists of the given member. The member_type is one of | |
770 | * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0], | |
771 | * and argv[1] will contain the ID of the entity in question. The R* | |
772 | * types mean to recursively look at every containing list, not just | |
773 | * when the object in question is a direct member. | |
774 | */ | |
b070f8a1 | 775 | |
0659551f | 776 | int get_lists_of_member(q, argv, cl, action, actarg) |
777 | struct query *q; | |
778 | char *argv[]; | |
779 | client *cl; | |
780 | int (*action)(); | |
781 | int actarg; | |
782 | { | |
783 | int found = 0, direct = 1; | |
784 | char *rargv[6]; | |
0fc7ab46 | 785 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 786 | char *atype; |
787 | int aid, listid, id; | |
788 | char name[33], active[5], public[5], hidden[5], maillist[5], grouplist[5]; | |
0fc7ab46 | 789 | EXEC SQL END DECLARE SECTION; |
0fc7ab46 | 790 | |
0659551f | 791 | atype = argv[0]; |
792 | aid = *(int *)argv[1]; | |
793 | if (!strcmp(atype, "RLIST")) { | |
794 | atype = "LIST"; | |
795 | direct = 0; | |
796 | } | |
797 | if (!strcmp(atype, "RUSER")) { | |
798 | atype = "USER"; | |
799 | direct = 0; | |
800 | } | |
801 | if (!strcmp(atype, "RSTRING")) { | |
802 | atype = "STRING"; | |
803 | direct = 0; | |
804 | } | |
805 | if (!strcmp(atype, "RKERBEROS")) { | |
806 | atype = "KERBEROS"; | |
807 | direct = 0; | |
808 | } | |
b070f8a1 | 809 | |
0659551f | 810 | rargv[0] = name; |
811 | rargv[1] = active; | |
812 | rargv[2] = public; | |
813 | rargv[3] = hidden; | |
814 | rargv[4] = maillist; | |
815 | rargv[5] = grouplist; | |
816 | if (direct) { | |
817 | EXEC SQL DECLARE csr117a CURSOR FOR | |
818 | SELECT l.name, CHAR(l.active), CHAR(l.publicflg), CHAR(l.hidden), | |
819 | CHAR(l.maillist), CHAR(l.grouplist) | |
820 | FROM list l, imembers im | |
821 | WHERE l.list_id = im.list_id AND im.direct = 1 | |
822 | AND im.member_type = :atype AND im.member_id = :aid; | |
823 | if (ingres_errno) | |
824 | return(mr_errcode); | |
825 | EXEC SQL OPEN csr117a; | |
826 | if (ingres_errno) | |
827 | return(mr_errcode); | |
828 | while(1) { | |
829 | EXEC SQL FETCH csr117a | |
830 | INTO :name, :active, :public, :hidden, :maillist, :grouplist; | |
831 | if(sqlca.sqlcode != 0) break; | |
832 | (*action)(6, rargv, actarg); | |
833 | found++; | |
834 | } | |
835 | EXEC SQL CLOSE csr117a; | |
038fd8a4 | 836 | } else { |
0659551f | 837 | EXEC SQL DECLARE csr117b CURSOR FOR |
838 | SELECT l.name, CHAR(l.active), CHAR(l.publicflg), CHAR(l.hidden), | |
839 | CHAR(l.maillist), CHAR(l.grouplist) | |
840 | FROM list l, imembers im | |
841 | WHERE l.list_id = im.list_id | |
842 | AND im.member_type = :atype AND im.member_id = :aid; | |
843 | if (ingres_errno) | |
844 | return(mr_errcode); | |
845 | EXEC SQL OPEN csr117b; | |
846 | if (ingres_errno) | |
847 | return(mr_errcode); | |
848 | while(1) { | |
849 | EXEC SQL FETCH csr117b | |
850 | INTO :name, :active, :public, :hidden, :maillist, :grouplist; | |
851 | if(sqlca.sqlcode != 0) break; | |
852 | (*action)(6, rargv, actarg); | |
853 | found++; | |
854 | } | |
855 | EXEC SQL CLOSE csr117b; | |
038fd8a4 | 856 | } |
0659551f | 857 | |
858 | if (ingres_errno) return(mr_errcode); | |
859 | if (!found) return(MR_NO_MATCH); | |
3afb5524 | 860 | return(MR_SUCCESS); |
038fd8a4 | 861 | } |
862 | ||
b070f8a1 | 863 | |
0659551f | 864 | /* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of |
865 | * the five flags associated with each list. It will return the name of | |
866 | * each list that meets the quailifications. It does this by building a | |
867 | * where clause based on the arguments, then doing a retrieve. | |
868 | */ | |
869 | ||
870 | static char *lflags[5] = { "active", "publicflg", "hidden", "maillist", "grouplist" }; | |
b070f8a1 | 871 | |
0659551f | 872 | int qualified_get_lists(q, argv, cl, action, actarg) |
873 | struct query *q; | |
b070f8a1 | 874 | char *argv[]; |
0659551f | 875 | client *cl; |
876 | int (*action)(); | |
877 | int actarg; | |
5d677c81 | 878 | { |
0659551f | 879 | return(qualified_get(q, argv, action, actarg, "l.list_id != 0", |
880 | "l", "name", lflags)); | |
881 | } | |
0fc7ab46 | 882 | |
5d677c81 | 883 | |
0659551f | 884 | /* get_members_of_list - this gets only direct members */ |
885 | ||
886 | get_members_of_list(q, argv, cl, action, actarg) | |
887 | struct query *q; | |
888 | char *argv[]; | |
889 | client *cl; | |
890 | int (*action)(); | |
891 | int actarg; | |
892 | { | |
893 | return(gmol_internal(q, argv, cl, action, actarg, 1)); | |
5d677c81 | 894 | } |
0659551f | 895 | |
896 | /* get_end_members_of_list - this gets direct or indirect members */ | |
897 | ||
898 | get_end_members_of_list(q, argv, cl, action, actarg) | |
b070f8a1 | 899 | struct query *q; |
0659551f | 900 | char *argv[]; |
901 | client *cl; | |
902 | int (*action)(); | |
903 | int actarg; | |
b070f8a1 | 904 | { |
0659551f | 905 | return(gmol_internal(q, argv, cl, action, actarg, 0)); |
906 | } | |
907 | ||
908 | /** gmol_internal - optimized query for retrieval of list members | |
909 | ** used by both get_members_of_list and get_end_members_of_list | |
910 | ** | |
911 | ** Inputs: | |
912 | ** argv[0] - list_id | |
913 | ** | |
914 | ** Description: | |
915 | ** - retrieve USER members, then LIST members, then STRING members | |
916 | **/ | |
b070f8a1 | 917 | |
0659551f | 918 | gmol_internal(q, argv, cl, action, actarg, flag) |
919 | struct query *q; | |
920 | char *argv[]; | |
921 | client *cl; | |
922 | int (*action)(); | |
923 | int actarg; | |
924 | int flag; | |
925 | { | |
926 | EXEC SQL BEGIN DECLARE SECTION; | |
927 | int list_id, member_id, direct; | |
928 | char member_name[129], member_type[9]; | |
929 | EXEC SQL END DECLARE SECTION; | |
930 | char *targv[2]; | |
931 | int members; | |
932 | struct save_queue *sq; | |
b070f8a1 | 933 | |
0659551f | 934 | /* true/false flag indicates whether to display only direct members. */ |
935 | if (flag) | |
936 | direct = 0; | |
937 | else | |
938 | direct = -1; | |
99e09b48 | 939 | |
0659551f | 940 | list_id = *(int *)argv[0]; |
941 | members = 0; | |
942 | sq = sq_create(); | |
99e09b48 | 943 | |
0659551f | 944 | EXEC SQL DECLARE csr118 CURSOR FOR |
945 | SELECT member_type, member_id FROM imembers | |
946 | WHERE list_id = :list_id AND direct > :direct; | |
947 | if (ingres_errno) | |
948 | return(mr_errcode); | |
949 | EXEC SQL OPEN csr118; | |
950 | if (ingres_errno) | |
951 | return(mr_errcode); | |
952 | while(1) { | |
953 | EXEC SQL FETCH csr118 INTO :member_type, :member_id; | |
954 | if (sqlca.sqlcode != 0) break; | |
955 | if (members++ > 49) | |
956 | break; | |
957 | sq_save_data(sq, ((int)member_type[0] << 24) | (member_id & 0xffffff)); | |
958 | } | |
959 | EXEC SQL CLOSE csr118; | |
99e09b48 | 960 | |
0659551f | 961 | if (members <= 49) { |
962 | targv[1] = malloc(0); | |
963 | while (sq_remove_data(sq, &member_id)) { | |
964 | switch (member_id >> 24) { | |
965 | case 'U': | |
966 | targv[0] = "USER"; | |
967 | id_to_name(member_id & 0xffffff, "USER", &targv[1]); | |
968 | (*action)(2, targv, actarg); | |
969 | break; | |
970 | case 'L': | |
971 | targv[0] = "LIST"; | |
972 | id_to_name(member_id & 0xffffff, "LIST", &targv[1]); | |
973 | (*action)(2, targv, actarg); | |
974 | break; | |
975 | case 'S': | |
976 | targv[0] = "STRING"; | |
977 | id_to_name(member_id & 0xffffff, "STRING", &targv[1]); | |
978 | (*action)(2, targv, actarg); | |
979 | break; | |
980 | case 'K': | |
981 | targv[0] = "KERBEROS"; | |
982 | id_to_name(member_id & 0xffffff, "STRING", &targv[1]); | |
983 | (*action)(2, targv, actarg); | |
984 | break; | |
985 | default: | |
986 | sq_destroy(sq); | |
987 | return(MR_INTERNAL); | |
988 | } | |
b070f8a1 | 989 | } |
0659551f | 990 | free(targv[1]); |
991 | sq_destroy(sq); | |
992 | return(MR_SUCCESS); | |
993 | } | |
994 | sq_destroy(sq); | |
b070f8a1 | 995 | |
0659551f | 996 | targv[1] = member_name; |
997 | targv[0] = "USER"; | |
998 | EXEC SQL DECLARE csr119 CURSOR FOR | |
999 | SELECT u.login FROM users u, imembers im | |
1000 | WHERE im.list_id = :list_id AND im.member_type = 'USER' | |
1001 | AND im.member_id = u.users_id AND im.direct > :direct | |
1002 | ORDER BY 1; | |
1003 | if (ingres_errno) | |
1004 | return(mr_errcode); | |
1005 | EXEC SQL OPEN csr119; | |
1006 | if (ingres_errno) | |
1007 | return(mr_errcode); | |
1008 | while(1) { | |
1009 | EXEC SQL FETCH csr119 INTO :member_name; | |
1010 | if(sqlca.sqlcode != 0) break; | |
1011 | (*action)(2, targv, actarg); | |
b070f8a1 | 1012 | } |
0659551f | 1013 | EXEC SQL CLOSE csr119; |
b070f8a1 | 1014 | if (ingres_errno) return(mr_errcode); |
b070f8a1 | 1015 | |
0659551f | 1016 | targv[0] = "LIST"; |
1017 | EXEC SQL DECLARE csr120 CURSOR FOR | |
1018 | SELECT l.name FROM list l, imembers im | |
1019 | WHERE im.list_id = :list_id AND im.member_type='LIST' | |
1020 | AND im.member_id = l.list_id AND im.direct > :direct | |
1021 | ORDER BY 1; | |
1022 | if (ingres_errno) | |
1023 | return(mr_errcode); | |
1024 | EXEC SQL OPEN csr120; | |
1025 | if (ingres_errno) | |
1026 | return(mr_errcode); | |
1027 | while(1) { | |
1028 | EXEC SQL FETCH csr120 INTO :member_name; | |
1029 | if(sqlca.sqlcode != 0) break; | |
1030 | (*action)(2, targv, actarg); | |
b070f8a1 | 1031 | } |
0659551f | 1032 | EXEC SQL CLOSE csr120; |
1033 | if (ingres_errno) return(mr_errcode); | |
b070f8a1 | 1034 | |
0659551f | 1035 | targv[0] = "STRING"; |
1036 | EXEC SQL DECLARE csr121 CURSOR FOR | |
1037 | SELECT CHAR(str.string) FROM strings str, imembers im | |
1038 | WHERE im.list_id = :list_id AND im.member_type='STRING' | |
1039 | AND im.member_id = str.string_id AND im.direct > :direct | |
1040 | ORDER BY 1; | |
0fc7ab46 | 1041 | if (ingres_errno) |
0659551f | 1042 | return(mr_errcode); |
1043 | EXEC SQL OPEN csr121; | |
1044 | if (ingres_errno) | |
1045 | return(mr_errcode); | |
1046 | while(1) { | |
1047 | EXEC SQL FETCH csr121 INTO :member_name; | |
1048 | if(sqlca.sqlcode != 0) break; | |
1049 | (*action)(2, targv, actarg); | |
b070f8a1 | 1050 | } |
0659551f | 1051 | EXEC SQL CLOSE csr121; |
b070f8a1 | 1052 | if (ingres_errno) return(mr_errcode); |
b070f8a1 | 1053 | |
0659551f | 1054 | targv[0] = "KERBEROS"; |
1055 | EXEC SQL DECLARE csr122 CURSOR FOR | |
1056 | SELECT CHAR(str.string) FROM strings str, imembers im | |
1057 | WHERE im.list_id = :list_id AND im.member_type='KERBEROS' | |
1058 | AND im.member_id = str.string_id | |
1059 | AND im.direct > :direct | |
1060 | ORDER BY 1; | |
1061 | if (ingres_errno) | |
1062 | return(mr_errcode); | |
1063 | EXEC SQL OPEN csr122; | |
1064 | if (ingres_errno) | |
1065 | return(mr_errcode); | |
1066 | while(1) { | |
1067 | EXEC SQL FETCH csr122 INTO :member_name; | |
1068 | if(sqlca.sqlcode != 0) break; | |
1069 | (*action)(2, targv, actarg); | |
1070 | } | |
1071 | EXEC SQL CLOSE csr122; | |
1072 | if (ingres_errno) return(mr_errcode); | |
b070f8a1 | 1073 | |
0659551f | 1074 | return(MR_SUCCESS); |
5d677c81 | 1075 | } |
b070f8a1 | 1076 | |
1077 | ||
0659551f | 1078 | /* count_members_of_list: this is a simple query, but it cannot be done |
1079 | * through the dispatch table. | |
1080 | */ | |
b070f8a1 | 1081 | |
0659551f | 1082 | int count_members_of_list(q, argv, cl, action, actarg) |
1083 | struct query *q; | |
b070f8a1 | 1084 | char *argv[]; |
0659551f | 1085 | client *cl; |
1086 | int (*action)(); | |
1087 | int actarg; | |
5d677c81 | 1088 | { |
0fc7ab46 | 1089 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 1090 | int list, ct = 0; |
0fc7ab46 | 1091 | EXEC SQL END DECLARE SECTION; |
0659551f | 1092 | char *rargv[1], countbuf[5]; |
b070f8a1 | 1093 | |
0659551f | 1094 | list = *(int *)argv[0]; |
1095 | rargv[0] = countbuf; | |
1096 | EXEC SQL REPEATED SELECT count (*) INTO :ct FROM imembers | |
1097 | WHERE list_id = :list AND direct=1; | |
b070f8a1 | 1098 | if (ingres_errno) return(mr_errcode); |
0659551f | 1099 | sprintf(countbuf, "%d", ct); |
1100 | (*action)(1, rargv, actarg); | |
1101 | return(MR_SUCCESS); | |
5d677c81 | 1102 | } |
b070f8a1 | 1103 | |
b070f8a1 | 1104 | |
0659551f | 1105 | /* qualified_get_server: passed "TRUE", "FALSE", or "DONTCARE" for each of |
1106 | * the three flags associated with each service. It will return the name of | |
1107 | * each service that meets the quailifications. It does this by building a | |
1108 | * where clause based on the arguments, then doing a retrieve. | |
1109 | */ | |
b070f8a1 | 1110 | |
0659551f | 1111 | static char *sflags[3] = { "enable", "inprogress", "harderror" }; |
b070f8a1 | 1112 | |
0659551f | 1113 | int qualified_get_server(q, argv, cl, action, actarg) |
1114 | struct query *q; | |
1115 | char *argv[]; | |
1116 | client *cl; | |
1117 | int (*action)(); | |
1118 | int actarg; | |
5d677c81 | 1119 | { |
0659551f | 1120 | return(qualified_get(q, argv, action, actarg, "s.name != ''", |
1121 | "s", "name", sflags)); | |
5d677c81 | 1122 | } |
b070f8a1 | 1123 | |
1124 | ||
0659551f | 1125 | /* generic qualified get routine, used by qualified_get_lists, |
1126 | * qualified_get_server, and qualified_get_serverhost. | |
1127 | * Args: | |
1128 | * start - a simple where clause, must not be empty | |
1129 | * range - the name of the range variable | |
1130 | * field - the field to return | |
1131 | * flags - an array of strings, names of the flag variables | |
b070f8a1 | 1132 | */ |
1133 | ||
0659551f | 1134 | int qualified_get(q, argv, action, actarg, start, range, field, flags) |
1135 | struct query *q; | |
1136 | char *argv[]; | |
1137 | int (*action)(); | |
1138 | int actarg; | |
1139 | char *start; | |
1140 | char *range; | |
1141 | char *field; | |
1142 | char *flags[]; | |
453b99fe | 1143 | { |
0659551f | 1144 | char name[33], qual[256]; |
1145 | int rowcount=0, i; | |
1146 | char *rargv[1], buf[32]; | |
453b99fe | 1147 | |
0659551f | 1148 | strcpy(qual, start); |
1149 | for (i = 0; i < q->argc; i++) { | |
1150 | if (!strcmp(argv[i], "TRUE")) { | |
1151 | sprintf(buf, " AND %s.%s != 0", range, flags[i]); | |
1152 | (void) strcat(qual, buf); | |
1153 | } else if (!strcmp(argv[i], "FALSE")) { | |
1154 | sprintf(buf, " AND %s.%s = 0", range, flags[i]); | |
1155 | (void) strcat(qual, buf); | |
1156 | } | |
453b99fe | 1157 | } |
1158 | ||
0659551f | 1159 | rargv[0] = SQLDA->sqlvar[0].sqldata; |
1160 | sprintf(stmt_buf,"SELECT CHAR(%s.%s) FROM %s %s WHERE %s",range,field,q->rtable,range,qual); | |
1161 | EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf; | |
1162 | if(sqlca.sqlcode) | |
1163 | return(MR_INTERNAL); | |
1164 | EXEC SQL DECLARE csr123 CURSOR FOR stmt; | |
1165 | EXEC SQL OPEN csr123; | |
1166 | while(1) { | |
1167 | EXEC SQL FETCH csr123 USING DESCRIPTOR :SQLDA; | |
1168 | if(sqlca.sqlcode != 0) break; | |
1169 | rowcount++; | |
1170 | (*action)(1, rargv, actarg); | |
453b99fe | 1171 | } |
0659551f | 1172 | EXEC SQL CLOSE csr123; |
1173 | if (ingres_errno) return(mr_errcode); | |
1174 | if (rowcount == 0) | |
1175 | return(MR_NO_MATCH); | |
1176 | return(MR_SUCCESS); | |
453b99fe | 1177 | } |
1178 | ||
1179 | ||
0659551f | 1180 | /* qualified_get_serverhost: passed "TRUE", "FALSE", or "DONTCARE" for each of |
1181 | * the five flags associated with each serverhost. It will return the name of | |
1182 | * each service and host that meets the quailifications. It does this by | |
1183 | * building a where clause based on the arguments, then doing a retrieve. | |
45bf7573 | 1184 | */ |
45bf7573 | 1185 | |
0659551f | 1186 | static char *shflags[6] = { "service", "enable", "override", "success", |
1187 | "inprogress", "hosterror" }; | |
45bf7573 | 1188 | |
0659551f | 1189 | int qualified_get_serverhost(q, argv, cl, action, actarg) |
45bf7573 | 1190 | struct query *q; |
0659551f | 1191 | char *argv[]; |
45bf7573 | 1192 | client *cl; |
0659551f | 1193 | int (*action)(); |
1194 | int actarg; | |
45bf7573 | 1195 | { |
0fc7ab46 | 1196 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 1197 | char sname[33], mname[33], qual[256]; |
0fc7ab46 | 1198 | EXEC SQL END DECLARE SECTION; |
0659551f | 1199 | char *rargv[2], buf[32]; |
1200 | int i, rowcount; | |
45bf7573 | 1201 | |
0659551f | 1202 | sprintf(qual, "m.mach_id = sh.mach_id AND sh.service = uppercase('%s')", |
1203 | argv[0]); | |
1204 | for (i = 1; i < q->argc; i++) { | |
1205 | if (!strcmp(argv[i], "TRUE")) { | |
1206 | sprintf(buf, " AND sh.%s != 0", shflags[i]); | |
1207 | strcat(qual, buf); | |
1208 | } else if (!strcmp(argv[i], "FALSE")) { | |
1209 | sprintf(buf, " AND sh.%s = 0", shflags[i]); | |
1210 | strcat(qual, buf); | |
1211 | } | |
1212 | } | |
45bf7573 | 1213 | |
0659551f | 1214 | rargv[0] = sname; |
1215 | rargv[1] = mname; | |
1216 | EXEC SQL DECLARE csr124 CURSOR FOR | |
1217 | SELECT sh.service, m.name FROM serverhosts sh, machine m | |
1218 | WHERE :qual; | |
1219 | if (ingres_errno) | |
1220 | return(mr_errcode); | |
1221 | EXEC SQL OPEN csr124; | |
1222 | if (ingres_errno) | |
1223 | return(mr_errcode); | |
1224 | while(1) { | |
1225 | EXEC SQL FETCH csr124 INTO :sname, :mname; | |
1226 | if(sqlca.sqlcode != 0) break; | |
1227 | rowcount++; | |
1228 | (*action)(2, rargv, actarg); | |
1229 | } | |
1230 | EXEC SQL CLOSE csr124; | |
45bf7573 | 1231 | |
0659551f | 1232 | if (ingres_errno) return(mr_errcode); |
1233 | if (rowcount == 0) | |
1234 | return(MR_NO_MATCH); | |
45bf7573 | 1235 | return(MR_SUCCESS); |
1236 | } | |
1237 | ||
0659551f | 1238 | |
1239 | /* register_user - change user's login name and allocate a pobox, group, | |
1240 | * filesystem, and quota for them. The user's status must start out as 0, | |
1241 | * and is left as 2. Arguments are: user's UID, new login name, and user's | |
1242 | * type for filesystem allocation (MR_FS_STUDENT, MR_FS_FACULTY, | |
1243 | * MR_FS_STAFF, MR_FS_MISC). | |
99e09b48 | 1244 | */ |
0659551f | 1245 | |
1246 | register_user(q, argv, cl) | |
99e09b48 | 1247 | struct query *q; |
1248 | char **argv; | |
1249 | client *cl; | |
1250 | { | |
0fc7ab46 | 1251 | EXEC SQL BEGIN DECLARE SECTION; |
0659551f | 1252 | char *login, dir[65], *entity, directory[129], machname[33]; |
1253 | int who, rowcount, mid, uid, users_id, flag, utype, nid, list_id, quota; | |
1254 | int size, alloc, pid, ostatus, nstatus, gidval, fsidval, npidval; | |
1255 | static int m_id = 0, def_quota = 0; | |
0fc7ab46 | 1256 | EXEC SQL END DECLARE SECTION; |
0659551f | 1257 | char buffer[256], *aargv[3]; |
99e09b48 | 1258 | |
0659551f | 1259 | entity = cl->entity; |
1260 | who = cl->client_id; | |
99e09b48 | 1261 | |
0659551f | 1262 | uid = atoi(argv[0]); |
1263 | login = argv[1]; | |
1264 | utype = atoi(argv[2]); | |
99e09b48 | 1265 | |
0659551f | 1266 | /* find user */ |
1267 | EXEC SQL REPEATED SELECT users_id, status INTO :users_id, :ostatus | |
1268 | FROM users | |
1269 | WHERE uid = :uid AND (status=0 OR status=5 OR status=6); | |
99e09b48 | 1270 | |
0659551f | 1271 | if (sqlca.sqlerrd[2] == 0) |
1272 | return(MR_NO_MATCH); | |
1273 | if (sqlca.sqlerrd[2] > 1) | |
1274 | return(MR_NOT_UNIQUE); | |
99e09b48 | 1275 | |
0659551f | 1276 | /* check new login name */ |
1277 | EXEC SQL REPEATED SELECT COUNT(login) INTO :rowcount FROM users | |
1278 | WHERE login = :login AND users_id != :users_id; | |
1279 | if (ingres_errno) return(mr_errcode); | |
1280 | if (rowcount > 0) return(MR_IN_USE); | |
1281 | EXEC SQL REPEATED SELECT COUNT(name) INTO :rowcount FROM list | |
1282 | WHERE name = :login; | |
1283 | if (ingres_errno) return(mr_errcode); | |
1284 | if (rowcount > 0) return(MR_IN_USE); | |
1285 | EXEC SQL REPEATED SELECT COUNT(label) INTO :rowcount FROM filesys | |
1286 | WHERE label = :login; | |
1287 | if (ingres_errno) return(mr_errcode); | |
1288 | if (rowcount > 0) return(MR_IN_USE); | |
1289 | com_err(whoami, 0, "login name OK"); | |
99e09b48 | 1290 | |
0659551f | 1291 | /* choose place for pobox, put in mid */ |
1292 | EXEC SQL DECLARE csr130 CURSOR FOR | |
1293 | SELECT sh.mach_id, m.name FROM serverhosts sh, machine m | |
1294 | WHERE sh.service='POP' AND sh.mach_id=m.mach_id | |
1295 | AND sh.value2 - sh.value1 = | |
1296 | (SELECT MAX(value2 - value1) FROM serverhosts | |
1297 | WHERE service = 'POP'); | |
1298 | if (ingres_errno) | |
1299 | return(mr_errcode); | |
1300 | EXEC SQL OPEN csr130; | |
1301 | if (ingres_errno) | |
1302 | return(mr_errcode); | |
1303 | EXEC SQL FETCH csr130 INTO :mid, :machname; | |
1304 | if (sqlca.sqlerrd[2] == 0) { | |
1305 | EXEC SQL CLOSE csr130; | |
1306 | if (ingres_errno) return(mr_errcode); | |
1307 | return(MR_NO_POBOX); | |
1308 | } else { | |
1309 | EXEC SQL CLOSE csr130; | |
1310 | if (ingres_errno) return(mr_errcode); | |
99e09b48 | 1311 | } |
99e09b48 | 1312 | |
0659551f | 1313 | /* change login name, set pobox */ |
1314 | sprintf(buffer, "u.users_id = %d", users_id); | |
1315 | incremental_before("users", buffer, 0); | |
1316 | nstatus = 2; | |
1317 | if (ostatus == 5 || ostatus == 6) | |
1318 | nstatus = 1; | |
1319 | EXEC SQL REPEATED UPDATE users SET login = :login, status = :nstatus, | |
1320 | modtime='now', modby = :who, modwith = :entity, potype='POP', | |
1321 | pop_id = :mid, pmodtime='now', pmodby = :who, pmodwith = :entity | |
1322 | WHERE users_id = :users_id; | |
0fc7ab46 | 1323 | |
0659551f | 1324 | if (ingres_errno) return(mr_errcode); |
1325 | if (sqlca.sqlerrd[2] != 1) | |
1326 | return(MR_INTERNAL); | |
1327 | set_pop_usage(mid, 1); | |
1328 | com_err(whoami, 0, "set login name to %s and pobox to %s", login, | |
1329 | strtrim(machname)); | |
1330 | incremental_after("users", buffer, 0); | |
99e09b48 | 1331 | |
0659551f | 1332 | /* create group list */ |
1333 | if (set_next_object_id("gid", "list", 1)) | |
1334 | return(MR_NO_ID); | |
1335 | if (set_next_object_id("list_id", "list", 0)) | |
1336 | return(MR_NO_ID); | |
1337 | EXEC SQL REPEATED SELECT value INTO :list_id FROM numvalues | |
1338 | WHERE name='list_id'; | |
1339 | if (ingres_errno) return(mr_errcode); | |
1340 | if (sqlca.sqlerrd[2] != 1) | |
1341 | return(MR_INTERNAL); | |
1342 | incremental_clear_before(); | |
1343 | EXEC SQL SELECT value INTO :gidval FROM numvalues WHERE name = 'gid'; | |
1344 | EXEC SQL REPEATED INSERT INTO list | |
1345 | (name, list_id, active, publicflg, hidden, maillist, grouplist, | |
1346 | gid, description, acl_type, acl_id, | |
1347 | modtime, modby, modwith) | |
1348 | VALUES (:login, :list_id, 1, 0, 0, 0, 1, | |
1349 | :gidval, 'User Group', 'USER', :users_id, | |
1350 | 'now', :who, :entity); | |
1351 | if (ingres_errno) return(mr_errcode); | |
1352 | if (sqlca.sqlerrd[2] != 1) | |
1353 | return(MR_INTERNAL); | |
1354 | sprintf(buffer, "l.list_id = %d", list_id); | |
1355 | incremental_after("list", buffer, 0); | |
1356 | aargv[0] = (char *) list_id; | |
1357 | aargv[1] = "USER"; | |
1358 | aargv[2] = (char *) users_id; | |
1359 | incremental_clear_before(); | |
1360 | EXEC SQL REPEATED INSERT INTO imembers | |
1361 | (list_id, member_type, member_id, ref_count, direct) | |
1362 | VALUES (:list_id, 'USER', :users_id, 1, 1); | |
1363 | if (ingres_errno) return(mr_errcode); | |
1364 | if (sqlca.sqlerrd[2] != 1) | |
1365 | return(MR_INTERNAL); | |
1366 | incremental_after("members", 0, aargv); | |
3beadd83 | 1367 | |
0659551f | 1368 | if (m_id == 0) { |
1369 | /* Cell Name (I know, it shouldn't be hard coded...) */ | |
1370 | strcpy(machname, "ATHENA.MIT.EDU"); | |
1371 | EXEC SQL SELECT mach_id INTO :m_id FROM machine | |
1372 | WHERE name = :machname; | |
1373 | } | |
3beadd83 | 1374 | |
0659551f | 1375 | /* create filesystem */ |
1376 | if (set_next_object_id("filsys_id", "filesys", 0)) | |
1377 | return(MR_NO_ID); | |
1378 | incremental_clear_before(); | |
1379 | if (islower(login[0]) && islower(login[1])) { | |
1380 | sprintf(directory, "/afs/athena.mit.edu/user/%c/%c/%s", | |
1381 | login[0], login[1], login); | |
1382 | } else { | |
1383 | sprintf(directory, "/afs/athena.mit.edu/user/other/%s", login); | |
1384 | } | |
1385 | ||
1386 | EXEC SQL SELECT value INTO :fsidval FROM numvalues | |
1387 | WHERE numvalues.name='filsys_id'; | |
1388 | EXEC SQL REPEATED INSERT INTO filesys | |
1389 | (filsys_id, phys_id, label, type, mach_id, name, | |
1390 | mount, access, comments, owner, owners, createflg, | |
1391 | lockertype, modtime, modby, modwith) | |
1392 | VALUES | |
1393 | (:fsidval, 0, :login, 'AFS', :m_id, :directory, | |
1394 | '/mit/'+:login, 'w', 'User Locker', :users_id, :list_id, 1, | |
1395 | 'HOMEDIR', 'now', :who, :entity); | |
3beadd83 | 1396 | |
0659551f | 1397 | if (ingres_errno) return(mr_errcode); |
1398 | if (sqlca.sqlerrd[2] != 1) | |
1399 | return(MR_INTERNAL); | |
1400 | sprintf(buffer,"fs.filsys_id = %d",fsidval); | |
1401 | incremental_after("filesys", buffer, 0); | |
3beadd83 | 1402 | |
0659551f | 1403 | /* set quota */ |
1404 | if (def_quota == 0) { | |
1405 | EXEC SQL REPEATED SELECT value INTO :def_quota FROM numvalues | |
1406 | WHERE name='def_quota'; | |
1407 | if (ingres_errno) return(mr_errcode); | |
1408 | if (sqlca.sqlerrd[2] != 1) | |
1409 | return(MR_NO_QUOTA); | |
1410 | ||
1411 | } | |
1412 | incremental_clear_before(); | |
1413 | EXEC SQL REPEATED INSERT INTO quota | |
1414 | (entity_id, filsys_id, type, quota, phys_id, modtime, modby, modwith) | |
1415 | VALUES | |
1416 | (0, :fsidval, 'ANY', :def_quota, 0, 'now', :who, :entity); | |
1417 | if (ingres_errno) return(mr_errcode); | |
1418 | if (sqlca.sqlerrd[2] != 1) | |
1419 | return(MR_INTERNAL); | |
1420 | aargv[0] = login; | |
1421 | aargv[1] = "ANY"; | |
1422 | aargv[2] = login; | |
1423 | sprintf(buffer, "q.entity_id = 0 and q.filsys_id = %d and q.type = 'ANY'", fsidval); | |
1424 | incremental_after("quota", buffer, aargv); | |
1425 | com_err(whoami, 0, "quota of %d assigned", def_quota); | |
1426 | if (ingres_errno) return(mr_errcode); | |
3afb5524 | 1427 | |
0659551f | 1428 | cache_entry(login, "USER", users_id); |
3afb5524 | 1429 | |
0659551f | 1430 | EXEC SQL REPEATED UPDATE tblstats SET updates=updates+1, modtime='now' |
1431 | WHERE table_name='users'; | |
1432 | EXEC SQL REPEATED UPDATE tblstats SET appends=appends+1, modtime='now' | |
1433 | WHERE table_name='list' OR table_name='filesys' OR table_name='quota'; | |
1434 | if (ingres_errno) return(mr_errcode); | |
1435 | return(MR_SUCCESS); | |
3afb5524 | 1436 | } |
1437 | ||
835aabee | 1438 | |
1439 | ||
0659551f | 1440 | /** set_pop_usage - incr/decr usage count for pop server in serverhosts talbe |
1441 | ** | |
1442 | ** Inputs: | |
1443 | ** id of machine | |
1444 | ** delta (will be +/- 1) | |
1445 | ** | |
1446 | ** Description: | |
1447 | ** - incr/decr value field in serverhosts table for pop/mach_id | |
1448 | ** | |
1449 | **/ | |
1450 | ||
1451 | int set_pop_usage(id, cnt) | |
835aabee | 1452 | EXEC SQL BEGIN DECLARE SECTION; |
835aabee | 1453 | int id; |
0659551f | 1454 | int cnt; |
1455 | EXEC SQL END DECLARE SECTION; | |
1456 | { | |
1457 | EXEC SQL REPEATED UPDATE serverhosts SET value1 = value1 + :cnt | |
1458 | WHERE serverhosts.service = 'POP' AND serverhosts.mach_id = :id; | |
835aabee | 1459 | |
0659551f | 1460 | if (ingres_errno) return(mr_errcode); |
1461 | return(MR_SUCCESS); | |
835aabee | 1462 | } |
1463 | ||
4872dc73 | 1464 | |
1465 | /* BEGIN KLUDGE | |
1466 | special-case `ghal "*" "name"' since Ingres does it slowly */ | |
1467 | ||
1468 | int get_hostalias(q, argv, cl, action, actarg) | |
1469 | struct query *q; | |
1470 | char *argv[]; | |
1471 | client *cl; | |
1472 | int (*action)(); | |
1473 | int actarg; | |
1474 | { | |
1475 | EXEC SQL BEGIN DECLARE SECTION; | |
1476 | char *alias=argv[0], *machine=argv[1], qual[BUFSIZ], *p; | |
1477 | int id; | |
1478 | EXEC SQL END DECLARE SECTION; | |
1479 | ||
1480 | for(p=machine; *p; p++) { | |
1481 | if(*p=='%' || *p=='_') break; | |
1482 | if(*p=='*') { | |
1483 | p++; | |
1484 | if(*p=='%') p++; | |
1485 | } | |
1486 | } | |
1487 | if(!*p) { | |
1488 | /* machine has no wildcards, so we can do it the fast way */ | |
1489 | EXEC SQL REPEATED SELECT mach_id INTO :id FROM machine | |
1490 | WHERE name=:machine; | |
1491 | if(ingres_errno) return(mr_errcode); | |
1492 | ||
1493 | sprintf(qual, "a.mach_id = %d AND m.mach_id = %d AND a.name LIKE '%s' ESCAPE '*'", id, id, alias); | |
1494 | return do_retrieve(q, qual, 0, action, actarg); | |
1495 | } | |
1496 | ||
1497 | /* not the special case... do the normal query */ | |
1498 | build_qual(q->qual, q->argc, argv, qual); | |
1499 | return do_retrieve(q, qual, 0, action, actarg); | |
1500 | } | |
1501 | ||
1502 | /* END KLUDGE */ |