+ char *name;
+ char *access;
+##{
+## char dir[81];
+ char caccess;
+ register int status;
+ register char *cp1;
+ register char *cp2;
+
+ status = MR_NFS;
+## range of np is nfsphys
+## repeat retrieve (var_phys_id = np.#nfsphys_id, dir = trim(np.#dir))
+## where np.#mach_id = @mach_id sort by #dir:d {
+ cp1 = name;
+ cp2 = dir;
+ while (*cp2) {
+ if (*cp1++ != *cp2) break;
+ cp2++;
+ }
+ if (*cp2 == 0) {
+ status = MR_SUCCESS;
+## endretrieve
+ }
+## }
+ if (ingres_errno)
+ return(mr_errcode);
+ return(status);
+##}
+
+
+/* setup_dfil: free any quota records and fsgroup info associated with
+ * a filesystem when it is deleted. Also adjust the allocation numbers.
+ */
+
+setup_dfil(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int id;
+
+ id = *(int *)argv[0];
+## range of q is quota
+## range of n is nfsphys
+## repeat replace n (allocated=n.allocated-sum(q.quota where q.filsys_id=@id))
+## where n.nfsphys_id = filesys.phys_id and filesys.filsys_id = @id
+
+## repeat delete q where q.filsys_id = @id
+## repeat delete fsgroup where fsgroup.filsys_id = @id
+## repeat delete fsgroup where fsgroup.group_id = @id
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* setup_dnfp: check to see that the nfs physical partition does not have
+ * any filesystems assigned to it before allowing it to be deleted.
+ */
+
+setup_dnfp(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int id, exists;
+
+ id = *(int *)argv[0];
+## repeat retrieve (exists = any(filesys.label where filesys.phys_id = @id))
+ if (exists)
+ return(MR_IN_USE);
+ if (ingres_errno)
+ return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* setup_dqot: Remove allocation from nfsphys before deleting quota.
+ * argv[0] = filsys_id
+ * argv[1] = type if "update_quota" or "delete_quota"
+ * argv[2 or 1] = users_id or list_id
+ */
+
+setup_dqot(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int quota, fs, id;
+## char *qtype;
+
+ fs = *(int *)argv[0];
+ if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota")) {
+ qtype = argv[1];
+ id = *(int *)argv[2];
+ } else {
+ qtype = "USER";
+ id = *(int *)argv[1];
+ }
+
+## range of q is #quota
+## repeat retrieve (quota = q.#quota) where q.type = @qtype and
+## q.entity_id = @id and q.filsys_id = @fs
+## repeat replace nfsphys (allocated = nfsphys.allocated - @quota)
+## where nfsphys.nfsphys_id = filesys.#phys_id and filesys.filsys_id = @fs
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* setup_sshi: don't exclusive lock the machine table during
+ * set_server_host_internal.
+ */
+
+setup_sshi(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## set lockmode session where readlock = system
+##}
+
+
+/* setup add_kerberos_user_mapping: add the string to the string
+ * table if necessary.
+ */
+
+setup_akum(q, argv, cl)
+struct query *q;
+char **argv;
+client *cl;
+##{
+## int id, rowcount;
+## char *name;
+
+ name = argv[1];
+ if (name_to_id(name, "STRING", &id) != MR_SUCCESS) {
+ if (q->type != APPEND) return(MR_STRING);
+## range of v is values
+## retrieve (id = v.value) where v.#name = "strings_id"
+ id++;
+## replace v (value = id) where v.#name = "strings_id"
+## append to strings (string_id = id, string = name)
+ cache_entry(name, "STRING", id);
+ }
+ if (ingres_errno) return(mr_errcode);
+ *(int *)argv[1] = id;
+ return(MR_SUCCESS);
+##}
+
+
+\f
+/* FOLLOWUP ROUTINES */
+
+/* generic set_modtime routine. This takes the table name from the query,
+ * and will update the modtime, modby, and modwho fields in the entry in
+ * the table whose name field matches argv[0].
+ */
+
+set_modtime(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## char *name, *entity, *table;
+## int who;
+
+ entity = cl->entity;
+ who = cl->client_id;
+ table = q->rtable;
+ name = argv[0];
+
+## replace table (modtime = "now", modby = who, modwith = entity)
+## where table.#name = name
+ return(MR_SUCCESS);
+##}
+
+/* generic set_modtime_by_id routine. This takes the table name from
+ * the query, and the id name from the validate record,
+ * and will update the modtime, modby, and modwho fields in the entry in
+ * the table whose id matches argv[0].
+ */
+
+set_modtime_by_id(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *entity, *table, *id_name;
+## int who, id;
+
+ entity = cl->entity;
+ who = cl->client_id;
+ table = q->rtable;
+ id_name = q->validate->object_id;
+
+ id = *(int *)argv[0];
+## replace table (modtime = "now", modby = who, modwith = entity)
+## where table.id_name = id
+ return(MR_SUCCESS);
+##}
+
+
+/* Sets the finger modtime on a user record. The users_id will be in argv[0].
+ */
+
+set_finger_modtime(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## int users_id, who;
+## char *entity;
+
+ entity = cl->entity;
+ who = cl->client_id;
+ users_id = *(int *)argv[0];
+
+## repeat replace u (fmodtime = "now", fmodby = @who, fmodwith = @entity)
+## where u.#users_id = @users_id
+ return(MR_SUCCESS);
+##}
+
+
+/* Sets the pobox modtime on a user record. The users_id will be in argv[0].
+ */
+
+set_pobox_modtime(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int users_id, who;
+## char *entity;
+
+ entity = cl->entity;
+ who = cl->client_id;
+ users_id = *(int *)argv[0];
+
+## repeat replace users (pmodtime = "now", pmodby = @who, pmodwith = @entity)
+## where users.#users_id = @users_id
+ return(MR_SUCCESS);
+##}
+
+
+/* Like set_modtime, but uppercases the name first.
+ */
+
+set_uppercase_modtime(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *name, *entity, *table;
+## int who;
+
+ entity = cl->entity;
+ who = cl->client_id;
+ table = q->rtable;
+ name = argv[0];
+
+## replace table (modtime = "now", modby = who, modwith = entity)
+## where table.#name = uppercase(name)
+ return(MR_SUCCESS);
+##}
+
+
+/* Sets the modtime on the machine whose mach_id is in argv[0]. This routine
+ * is necessary for add_machine_to_cluster becuase the table that query
+ * operates on is "mcm", not "machine".
+ */
+
+set_mach_modtime_by_id(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *entity;
+## int who, id;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ id = *(int *)argv[0];
+## repeat replace machine (modtime = "now", modby = @who, modwith = @entity)
+## where machine.mach_id = @id
+ return(MR_SUCCESS);
+##}
+
+
+/* Sets the modtime on the cluster whose mach_id is in argv[0]. This routine
+ * is necessary for add_cluster_data and delete_cluster_data becuase the
+ * table that query operates on is "svc", not "cluster".
+ */
+
+set_cluster_modtime_by_id(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *entity;
+## int who, id;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ id = *(int *)argv[0];
+## repeat replace cluster (modtime = "now", modby = @who, modwith = @entity)
+## where cluster.clu_id = @id
+ return(MR_SUCCESS);
+##}
+
+
+/* sets the modtime on the serverhost where the service name is in argv[0]
+ * and the mach_id is in argv[1].
+ */
+
+set_serverhost_modtime(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *entity, *serv;
+## int who, id;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ serv = argv[0];
+ id = *(int *)argv[1];
+## repeat replace sh (modtime = "now", modby = @who, modwith = @entity)
+## where sh.service = uppercase(@serv) and sh.mach_id = @id
+ return(MR_SUCCESS);
+##}
+
+
+/* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
+ * directory name is in argv[1].
+ */
+
+set_nfsphys_modtime(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## char *entity, *dir;
+## int who, id;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ id = *(int *)argv[0];
+ dir = argv[1];
+## repeat replace np (modtime = "now", modby = @who, modwith = @entity)
+## where np.#dir = @dir and np.mach_id = @id
+ return(MR_SUCCESS);
+##}
+
+
+/* sets the modtime on a filesystem, where argv[0] contains the filesys
+ * label.
+ */
+
+set_filesys_modtime(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## char *label, *entity;
+## int who;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ label = argv[0];
+ if (!strcmp(q->shortname, "ufil"))
+ label = argv[1];
+
+## repeat replace fs (modtime = "now", modby = @who, modwith = @entity,
+## #phys_id = @var_phys_id) where fs.#label = @label
+ return(MR_SUCCESS);
+##}
+
+
+/* sets the modtime on a zephyr class, where argv[0] contains the class
+ * name.
+ */
+
+set_zephyr_modtime(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## char *class, *entity;
+## int who;
+
+ entity = cl->entity;
+ who = cl->client_id;
+
+ class = argv[0];
+
+## repeat replace z (modtime = "now", modby = @who, modwith = @entity)
+## where z.#class = @class
+ return(MR_SUCCESS);
+##}
+
+
+/* fixes the modby field. This will be the second to last thing in the
+ * argv, the argv length is determined from the query structure. It is
+ * passed as a pointer to an integer. This will either turn it into a
+ * username, or # + the users_id.
+ */
+followup_fix_modby(q, sq, v, action, actarg, cl)
+ struct query *q;
+ register struct save_queue *sq;
+ struct validate *v;
+ register int (*action)();
+ register int actarg;
+ client *cl;
+{
+ register int i, j;
+ char **argv, *malloc();
+ int id, status;
+
+ i = q->vcnt - 2;
+ while (sq_get_data(sq, &argv)) {
+ id = atoi(argv[i]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[i]);
+ else
+ status = id_to_name(-id, "STRING", &argv[i]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+ (*action)(q->vcnt, argv, actarg);
+ for (j = 0; j < q->vcnt; j++)
+ free(argv[j]);
+ free(argv);
+ }
+ sq_destroy(sq);
+ return(MR_SUCCESS);
+}
+
+
+/**
+ ** followup_ausr - add finger and pobox entries, set_user_modtime
+ **
+ ** Inputs:
+ ** argv[0] - login (add_user)
+ ** argv[3] - last name
+ ** argv[4] - first name
+ ** argv[5] - middle name
+ **
+ **/
+
+followup_ausr(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## int who;
+## char *login, *entity;
+## char fullname[129];
+
+ login = argv[0];
+ who = cl->client_id;
+ entity = cl->entity;
+
+ /* build fullname */
+ if (strlen(argv[4]) && strlen(argv[5]))
+ sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
+ else if (strlen(argv[4]))
+ sprintf(fullname, "%s %s", argv[4], argv[3]);
+ else
+ sprintf(fullname, "%s", argv[3]);
+
+ /* create finger entry, pobox & set modtime on user */
+## repeat replace u (modtime = "now", modby=@who, modwith=@entity,
+## #fullname=@fullname, mit_affil = u.mit_year,
+## fmodtime="now", fmodby=@who, fmodwith=@entity,
+## potype="NONE", pmodtime="now", pmodby=@who, pmodwith=@entity)
+## where u.#login = @login
+
+ return(MR_SUCCESS);
+##}
+
+
+/* followup_gpob: fixes argv[2] based on the IDs currently there and the
+ * type in argv[1]. Then completes the upcall to the user.
+ *
+ * argv[2] is of the form "123:234" where the first integer is the machine
+ * ID if it is a pop box, and the second is the string ID if it is an SMTP
+ * box. argv[1] should be "POP", "SMTP", or "NONE". Boxes of type NONE
+ * are skipped.
+ */
+
+followup_gpob(q, sq, v, action, actarg, cl)
+ register struct query *q;
+ register struct save_queue *sq;
+ register struct validate *v;
+ register int (*action)();
+ int actarg;
+ client *cl;
+{
+ char **argv, *index();
+ char *ptype, *p;
+ int mid, sid, status;
+
+ /* for each row */
+ while (sq_get_data(sq, &argv)) {
+ mr_trim_args(2, argv);
+ ptype = argv[1];
+ p = index(argv[2], ':');
+ *p++ = 0;
+ mid = atoi(argv[2]);
+ sid = atoi(p);
+
+ if (!strcmp(ptype, "POP")) {
+ status = id_to_name(mid, "MACHINE", &argv[2]);
+ if (status == MR_NO_MATCH)
+ return(MR_MACHINE);
+ } else if (!strcmp(ptype, "SMTP")) {
+ status = id_to_name(sid, "STRING", &argv[2]);
+ if (status == MR_NO_MATCH)
+ return(MR_STRING);
+ } else /* ptype == "NONE" */ {
+ goto skip;
+ }
+ if (status) return(status);
+
+ if (!strcmp(q->shortname, "gpob")) {
+ sid = atoi(argv[4]);
+ if (sid > 0)
+ status = id_to_name(sid, "USER", &argv[4]);
+ else
+ status = id_to_name(-sid, "STRING", &argv[4]);
+ }
+ if (status && status != MR_NO_MATCH) return(status);
+
+ (*action)(q->vcnt, argv, actarg);
+ skip:
+ /* free saved data */
+ free(argv[0]);
+ free(argv[1]);
+ free(argv[4]);
+ free(argv);
+ }
+
+ sq_destroy(sq);
+ return (MR_SUCCESS);
+}
+
+
+/* followup_glin: fix the ace_name in argv[8]. argv[7] will contain the
+ * ace_type: "LIST", "USER", or "NONE". Decode the id in argv[8] into the
+ * proper name based on the type, and repace that string in the argv.
+ * Also fixes the modby field by called followup_fix_modby.
+ */
+
+followup_glin(q, sq, v, action, actarg, cl)
+ register struct query *q;
+ register struct save_queue *sq;
+ register struct validate *v;
+ register int (*action)();
+ int actarg;
+ client *cl;
+{
+ char **argv, *malloc(), *realloc(), *type;
+ int id, i, idx, status;
+
+ idx = 8;
+ if (!strcmp(q->shortname, "gsin"))
+ idx = 12;
+
+ while (sq_get_data(sq, &argv)) {
+ mr_trim_args(q->vcnt, argv);
+
+ id = atoi(argv[i = q->vcnt - 2]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[i]);
+ else
+ status = id_to_name(-id, "STRING", &argv[i]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+
+ id = atoi(argv[idx]);
+ type = argv[idx - 1];
+
+ if (!strcmp(type, "LIST")) {
+ status = id_to_name(id, "LIST", &argv[idx]);
+ } else if (!strcmp(type, "USER")) {
+ status = id_to_name(id, "USER", &argv[idx]);
+ } else if (!strcmp(type, "KERBEROS")) {
+ status = id_to_name(id, "STRING", &argv[idx]);
+ } else if (!strcmp(type, "NONE")) {
+ status = 0;
+ free(argv[idx]);
+ argv[idx] = strsave("NONE");
+ } else {
+ status = 0;
+ free(argv[idx]);
+ argv[idx] = strsave("???");
+ }
+ if (status && status != MR_NO_MATCH)
+ return(status);
+
+ if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
+ argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
+ strcpy(argv[6], UNIQUE_GID);
+ }
+
+ /* send the data */
+ (*action)(q->vcnt, argv, actarg);
+
+ /* free saved data */
+ for (i = 0; i < q->vcnt; i++)
+ free(argv[i]);
+ free(argv);
+ }
+
+ sq_destroy(sq);
+ return (MR_SUCCESS);
+}
+
+
+/* followup_gqot: Fix the entity name, directory name & modby fields
+ * argv[0] = filsys_id
+ * argv[1] = type
+ * argv[2] = entity_id
+ * argv[3] = ascii(quota)
+ */
+
+followup_gqot(q, sq, v, action, actarg, cl)
+ struct query *q;
+ register struct save_queue *sq;
+ struct validate *v;
+ register int (*action)();
+ register int actarg;
+ client *cl;
+##{
+ register int j;
+ char **argv, *malloc();
+## int id, rowcount;
+## char *name, *label;
+ int status, idx;
+
+ if (!strcmp(q->name, "get_quota") ||
+ !strcmp(q->name, "get_quota_by_filesys"))
+ idx = 4;
+ else
+ idx = 3;
+ while (sq_get_data(sq, &argv)) {
+ if (idx == 4) {
+ switch (argv[1][0]) {
+ case 'U':
+ status = id_to_name(atoi(argv[2]), "USER", &argv[2]);
+ break;
+ case 'G':
+ case 'L':
+ status = id_to_name(atoi(argv[2]), "LIST", &argv[2]);
+ break;
+ case 'A':
+ free(argv[2]);
+ argv[2] = strsave("system:anyuser");
+ break;
+ default:
+ id = atoi(argv[2]);
+ argv[2] = malloc(8);
+ sprintf(argv[2], "%d", id);
+ }
+ }
+ id = atoi(argv[idx]);
+ free(argv[idx]);
+ argv[idx] = malloc(256);
+ name = argv[idx];
+ if (id == 0) {
+ label = argv[0];
+## repeat retrieve (name = filesys.#name) where filesys.#label = @label
+ } else {
+## repeat retrieve (name = nfsphys.dir) where nfsphys.nfsphys_id = @id
+ }
+## inquire_equel(rowcount = "rowcount")
+ if (rowcount != 1) {
+ sprintf(argv[idx], "#%d", id);
+ }
+
+ id = atoi(argv[idx+3]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[idx+3]);
+ else
+ status = id_to_name(-id, "STRING", &argv[idx+3]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+ (*action)(q->vcnt, argv, actarg);
+ for (j = 0; j < q->vcnt; j++)
+ free(argv[j]);
+ free(argv);
+ }
+ sq_destroy(sq);
+ return(MR_SUCCESS);
+##}
+
+
+/* followup_aqot: Add allocation to nfsphys after creating quota.
+ * argv[0] = filsys_id
+ * argv[1] = type if "add_quota" or "update_quota"
+ * argv[2 or 1] = id
+ * argv[3 or 2] = ascii(quota)
+ */
+
+followup_aqot(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int quota, id, fs, who;
+## char *entity, *qtype;
+
+ fs = *(int *)argv[0];
+ if (!strcmp(q->name, "add_quota") || !strcmp(q->name, "update_quota")) {
+ qtype = argv[1];
+ id = *(int *)argv[2];
+ quota = atoi(argv[3]);
+ } else {
+ qtype = "USER";
+ id = *(int *)argv[1];
+ quota = atoi(argv[2]);
+ }
+ who = cl->client_id;
+ entity = cl->entity;
+
+## repeat replace q (modtime = "now", modby = @who, modwith = @entity)
+## where q.filsys_id = @fs and q.type = @qtype and q.entity_id = @id
+## repeat replace nfsphys (allocated = nfsphys.allocated + @quota)
+## where nfsphys.nfsphys_id = filesys.#phys_id and filesys.filsys_id = @fs
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+followup_gpce(q, sq, v, action, actarg, cl)
+ struct query *q;
+ register struct save_queue *sq;
+ struct validate *v;
+ register int (*action)();
+ register int actarg;
+ client *cl;
+{
+ register int i, j;
+ char **argv, *malloc();
+ int id, status;
+
+ i = q->vcnt - 2;
+ while (sq_get_data(sq, &argv)) {
+ id = atoi(argv[PCAP_QSERVER]);
+ status = id_to_name(id, "MACHINE", &argv[PCAP_QSERVER]);
+ if (status) return (status);
+ id = atoi(argv[i]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[i]);
+ else
+ status = id_to_name(-id, "STRING", &argv[i]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+ (*action)(q->vcnt, argv, actarg);
+ for (j = 0; j < q->vcnt; j++)
+ free(argv[j]);
+ free(argv);
+ }
+ sq_destroy(sq);
+ return(MR_SUCCESS);
+}
+
+
+/* followup_gzcl:
+ */
+
+followup_gzcl(q, sq, v, action, actarg, cl)
+ register struct query *q;
+ register struct save_queue *sq;
+ register struct validate *v;
+ register int (*action)();
+ int actarg;
+ client *cl;
+{
+ int id, i, status;
+ char **argv;
+
+ while (sq_get_data(sq, &argv)) {
+ mr_trim_args(q->vcnt, argv);
+
+ id = atoi(argv[i = q->vcnt - 2]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[i]);
+ else
+ status = id_to_name(-id, "STRING", &argv[i]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+
+ for (i = 1; i < 8; i+=2) {
+ id = atoi(argv[i+1]);
+ if (!strcmp(argv[i], "LIST")) {
+ status = id_to_name(id, "LIST", &argv[i+1]);
+ } else if (!strcmp(argv[i], "USER")) {
+ status = id_to_name(id, "USER", &argv[i+1]);
+ } else if (!strcmp(argv[i], "KERBEROS")) {
+ status = id_to_name(id, "STRING", &argv[i+1]);
+ } else if (!strcmp(argv[i], "NONE")) {
+ status = 0;
+ free(argv[i+1]);
+ argv[i+1] = strsave("NONE");
+ } else {
+ status = 0;
+ free(argv[i+1]);
+ argv[i+1] = strsave("???");
+ }
+ if (status && status != MR_NO_MATCH)
+ return(status);
+ }
+
+ /* send the data */
+ (*action)(q->vcnt, argv, actarg);
+
+ /* free saved data */
+ for (i = 0; i < q->vcnt; i++)
+ free(argv[i]);
+ free(argv);
+ }
+ sq_destroy(sq);
+ return(MR_SUCCESS);
+}
+
+
+/* followup_gsha:
+ */
+
+followup_gsha(q, sq, v, action, actarg, cl)
+ register struct query *q;
+ register struct save_queue *sq;
+ register struct validate *v;
+ register int (*action)();
+ int actarg;
+ client *cl;
+{
+ char **argv;
+ int i, id, status;
+
+ while (sq_get_data(sq, &argv)) {
+ mr_trim_args(q->vcnt, argv);
+
+ id = atoi(argv[4]);
+ if (id > 0)
+ status = id_to_name(id, "USER", &argv[4]);
+ else
+ status = id_to_name(-id, "STRING", &argv[4]);
+ if (status && status != MR_NO_MATCH)
+ return(status);
+
+ id = atoi(argv[2]);
+ if (!strcmp(argv[1], "LIST")) {
+ status = id_to_name(id, "LIST", &argv[2]);
+ } else if (!strcmp(argv[1], "USER")) {
+ status = id_to_name(id, "USER", &argv[2]);
+ } else if (!strcmp(argv[1], "KERBEROS")) {
+ status = id_to_name(id, "STRING", &argv[2]);
+ } else if (!strcmp(argv[1], "NONE")) {
+ status = 0;
+ free(argv[2]);
+ argv[2] = strsave("NONE");
+ } else {
+ status = 0;
+ free(argv[2]);
+ argv[2] = strsave("???");
+ }
+ if (status && status != MR_NO_MATCH)
+ return(status);
+
+ /* send the data */
+ (*action)(q->vcnt, argv, actarg);
+
+ /* free saved data */
+ for (i = 0; i < q->vcnt; i++)
+ free(argv[i]);
+ free(argv);
+ }
+ sq_destroy(sq);
+ return(MR_SUCCESS);
+}
+
+
+\f
+/* Special query routines */
+
+/* set_pobox - this does all of the real work.
+ * argv = user_id, type, box
+ * if type is POP, then box should be a machine, and its ID should be put in
+ * pop_id. If type is SMTP, then box should be a string and its ID should
+ * be put in box_id. If type is NONE, then box doesn't matter.
+ */
+
+int set_pobox(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int user, id, rowcount;
+## char *box, potype[9];
+ int status;
+
+ box = argv[2];
+ user = *(int *)argv[0];
+
+## repeat retrieve (id = users.pop_id, potype = users.#potype)
+## where users.users_id = @user
+ if (ingres_errno) return(mr_errcode);
+ if (!strcmp(strtrim(potype), "POP"))
+ set_pop_usage(id, -1);
+
+ if (!strcmp(argv[1], "POP")) {
+ status = name_to_id(box, "MACHINE", &id);
+ if (status == MR_NO_MATCH)
+ return(MR_MACHINE);
+ else if (status)
+ return(status);
+## repeat replace users (#potype = "POP", pop_id = @id)
+## where users.users_id = @user
+ set_pop_usage(id, 1);
+ } else if (!strcmp(argv[1], "SMTP")) {
+ if (index(box, '/') || index(box, '|'))
+ return(MR_BAD_CHAR);
+ status = name_to_id(box, "STRING", &id);
+ if (status == MR_NO_MATCH) {
+## repeat retrieve (id = values.value) where values.name = "strings_id"
+ id++;
+## repeat replace values (value = @id) where values.name = "strings_id"
+## append to strings (string_id = id, string = box)
+ } else if (status)
+ return(status);
+## repeat replace users (#potype = "SMTP", box_id = @id)
+## where users.users_id = @user
+ } else /* argv[1] == "NONE" */ {
+## repeat replace users (#potype = "NONE") where users.users_id = @user
+ }
+
+ set_pobox_modtime(q, argv, cl);
+## repeat replace tblstats (updates = tblstats.updates + 1, modtime = "now")
+## where tblstats.#table = "users"
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* get_list_info: passed a wildcard list name, returns lots of stuff about
+ * each list. This is tricky: first build a queue of all requested
+ * data. Rest of processing consists of fixing gid, ace_name, and modby.
+ */
+
+get_list_info(q, aargv, cl, action, actarg)
+ register struct query *q;
+ char **aargv;
+ client *cl;
+ register int (*action)();
+ int actarg;
+##{
+ char *argv[13], *malloc(), *realloc();
+## char *name, acl_type[9], listname[33], active[5], public[5], hidden[5];
+## char maillist[5], group[5], gid[6], acl_name[256], desc[256], modtime[27];
+## char modby[256], modwith[9];
+## int id, rowcount, acl_id, hid, modby_id;
+ int returned, status;
+ struct save_queue *sq, *sq_create();
+
+ returned = rowcount = 0;
+ name = aargv[0];
+
+ sq = sq_create();
+## range of l is list
+## repeat retrieve (id = l.list_id) where l.#name = @name {
+ sq_save_data(sq, id);
+ rowcount++;
+## }
+ if (ingres_errno) return(mr_errcode);
+ if (rowcount == 0)
+ return(MR_NO_MATCH);
+
+ argv[0] = listname; argv[1] = active; argv[2] = public; argv[3] = hidden;
+ argv[4] = maillist; argv[5] = group; argv[6] = gid; argv[7] = acl_type;
+ argv[9] = desc; argv[10] = modtime; argv[12] = modwith;
+
+ while (sq_get_data(sq, &id)) {
+ if (id == 0)
+ continue;
+ argv[6] = gid;
+## repeat retrieve (listname = l.#name, active = text(l.#active),
+## public = text(l.#public), hidden = text(l.#hidden),
+## hid = l.#hidden, maillist = text(l.#maillist),
+## group = text(l.#group), gid = text(l.#gid),
+## acl_type = trim(l.#acl_type), acl_id = l.#acl_id,
+## desc = l.#desc, modtime = l.#modtime, modby_id = l.#modby,
+## modwith =l.#modwith)
+## where l.list_id = @id
+ if (ingres_errno) return(mr_errcode);
+
+ if (atoi(gid) == -1)
+ argv[6] = UNIQUE_GID;
+
+ argv[8] = malloc(0);
+ if (!strcmp(acl_type, "LIST")) {
+ status = id_to_name(acl_id, "LIST", &argv[8]);
+ } else if (!strcmp(acl_type, "USER")) {
+ status = id_to_name(acl_id, "USER", &argv[8]);
+ } else if (!strcmp(acl_type, "KERBEROS")) {
+ status = id_to_name(acl_id, "STRING", &argv[8]);
+ } else if (!strcmp(acl_type, "NONE")) {
+ status = 0;
+ free(argv[8]);
+ argv[8] = strsave("NONE");
+ } else {
+ status = 0;
+ free(argv[8]);
+ argv[8] = strsave("???");
+ }
+ if (status && status != MR_NO_MATCH) return(status);
+
+ argv[11] = malloc(0);
+ if (modby_id > 0)
+ status = id_to_name(modby_id, "USER", &argv[11]);
+ else
+ status = id_to_name(-modby_id, "STRING", &argv[11]);
+ if (status && status != MR_NO_MATCH) return(status);
+
+ mr_trim_args(q->vcnt, argv);
+ returned++;
+ (*action)(q->vcnt, argv, actarg);
+ free(argv[8]);
+ free(argv[11]);
+ }
+
+ sq_destroy(sq);
+ if (ingres_errno) return(mr_errcode);
+ return (MR_SUCCESS);
+##}
+
+
+/* Add_member_to_list: do list flattening as we go! MAXLISTDEPTH is
+ * how many different ancestors a member is allowed to have.
+ */
+
+#define MAXLISTDEPTH 1024
+
+int add_member_to_list(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int id, lid, mid, exists, error, who, ref;
+## char *mtype, dtype[9], *entity;
+ int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
+ int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
+ int status;
+ char *dtypes[MAXLISTDEPTH];
+ char *iargv[3], *buf;
+
+## range of m is imembers
+ lid = *(int *)argv[0];
+ mtype = argv[1];
+ mid = *(int *)argv[2];
+ /* if the member is already a direct member of the list, punt */
+## repeat retrieve (exists = any(m.list_id where m.list_id=@lid and
+## m.member_id = @mid and m.member_type = @mtype
+## and m.direct = 1))
+ if (exists)
+ return(MR_EXISTS);
+ if (!strcasecmp(mtype, "STRING")) {
+ buf = malloc(0);
+ status = id_to_name(mid, "STRING", &buf);
+ if (status) return(status);
+ if (index(buf, '/') || index(buf, '|')) {
+ free(buf);
+ return(MR_BAD_CHAR);
+ }
+ free(buf);
+ }
+
+ ancestors[0] = lid;
+ aref[0] = 1;
+ acount = 1;
+## repeat retrieve (id = m.list_id, ref = m.ref_count)
+## where m.member_id = @lid and m.member_type = "LIST" {
+ aref[acount] = ref;
+ ancestors[acount++] = id;
+ if (acount >= MAXLISTDEPTH) {
+## endretrieve
+ }
+## }
+ if (ingres_errno) return(mr_errcode);
+ if (acount >= MAXLISTDEPTH) {
+ return(MR_INTERNAL);
+ }
+ descendants[0] = mid;
+ dtypes[0] = mtype;
+ dref[0] = 1;
+ dcount = 1;
+ error = 0;
+ if (!strcmp(mtype, "LIST")) {
+## repeat retrieve (id = m.member_id, dtype = m.member_type,
+## ref = m.ref_count)
+## where m.list_id = @mid {
+ switch (dtype[0]) {
+ case 'L':
+ dtypes[dcount] = "LIST";
+ break;
+ case 'U':
+ dtypes[dcount] = "USER";
+ break;
+ case 'S':
+ dtypes[dcount] = "STRING";
+ break;
+ case 'K':
+ dtypes[dcount] = "KERBEROS";
+ break;
+ default:
+ error++;
+## endretrieve
+ }
+ dref[dcount] = ref;
+ descendants[dcount++] = id;
+ if (dcount >= MAXLISTDEPTH) {
+ error++;
+## endretrieve
+ }
+## }
+ if (ingres_errno) return(mr_errcode);
+ if (error)
+ return(MR_INTERNAL);
+ }
+ for (a = 0; a < acount; a++) {
+ lid = ancestors[a];
+ for (d = 0; d < dcount; d++) {
+ mid = descendants[d];
+ mtype = dtypes[d];
+ if (mid == lid && !strcmp(mtype, "LIST")) {
+ return(MR_LISTLOOP);
+ }
+## repeat retrieve (exists = any(m.ref_count where m.list_id = @lid
+## and m.member_id = @mid
+## and m.member_type = @mtype))
+ ref = aref[a] * dref[d];
+ if (exists) {
+ if (a == 0 && d == 0)
+## replace m (ref_count = m.ref_count+ref, direct = 1)
+## where m.list_id = lid and m.member_id = mid and
+## m.member_type = mtype
+ else
+## replace m (ref_count = m.ref_count+ref)
+## where m.list_id = lid and m.member_id = mid and
+## m.member_type = mtype
+ } else {
+ incremental_clear_before();
+ if (a == 0 && d == 0)
+## append imembers (list_id=lid, member_id = mid, direct = 1,
+## member_type=mtype, ref_count = 1)
+ else
+## append imembers (list_id=lid, member_id = mid,
+## member_type=mtype, ref_count = ref)
+ iargv[0] = (char *)lid;
+ iargv[1] = mtype;
+ iargv[2] = (char *)mid;
+ incremental_after("members", 0, iargv);
+ }
+ }
+ }
+ lid = *(int *)argv[0];
+ entity = cl->entity;
+ who = cl->client_id;
+## repeat replace list (modtime = "now", modby = @who, modwith = @entity)
+## where list.#list_id = @lid
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* Delete_member_from_list: do list flattening as we go!
+ */
+
+int delete_member_from_list(q, argv, cl)
+ struct query *q;
+ char **argv;
+ client *cl;
+##{
+## int id, lid, mid, cnt, exists, error, who, ref;
+## char *mtype, dtype[9], *entity;
+ int ancestors[MAXLISTDEPTH], aref[MAXLISTDEPTH], acount, a;
+ int descendants[MAXLISTDEPTH], dref[MAXLISTDEPTH], dcount, d;
+ char *dtypes[MAXLISTDEPTH];
+ char *iargv[3];
+
+## range of m is imembers
+ lid = *(int *)argv[0];
+ mtype = argv[1];
+ mid = *(int *)argv[2];
+ /* if the member is not a direct member of the list, punt */
+## repeat retrieve (exists = any(m.list_id where m.list_id=@lid and
+## m.member_id = @mid and m.member_type = @mtype
+## and m.direct = 1))
+ if (ingres_errno) return(mr_errcode);
+ if (!exists)
+ return(MR_NO_MATCH);
+ ancestors[0] = lid;
+ aref[0] = 1;
+ acount = 1;
+## repeat retrieve (id = m.list_id, ref = m.ref_count)
+## where m.member_id = @lid and m.member_type = "LIST" {
+ aref[acount] = ref;
+ ancestors[acount++] = id;
+ if (acount >= MAXLISTDEPTH)
+## endretrieve
+## }
+ if (ingres_errno) return(mr_errcode);
+ if (acount >= MAXLISTDEPTH)
+ return(MR_INTERNAL);
+ descendants[0] = mid;
+ dtypes[0] = mtype;
+ dref[0] = 1;
+ dcount = 1;
+ error = 0;
+ if (!strcmp(mtype, "LIST")) {
+## repeat retrieve (id = m.member_id, dtype = m.member_type,
+## ref = m.ref_count)
+## where m.list_id = @mid {
+ switch (dtype[0]) {
+ case 'L':
+ dtypes[dcount] = "LIST";
+ break;
+ case 'U':
+ dtypes[dcount] = "USER";
+ break;
+ case 'S':
+ dtypes[dcount] = "STRING";
+ break;
+ case 'K':
+ dtypes[dcount] = "KERBEROS";
+ break;
+ default:
+ error++;
+## endretrieve
+ }
+ dref[dcount] = ref;
+ descendants[dcount++] = id;
+ if (dcount >= MAXLISTDEPTH)
+## endretrieve
+## }
+ if (ingres_errno) return(mr_errcode);
+ if (error)
+ return(MR_INTERNAL);
+ }
+ for (a = 0; a < acount; a++) {
+ lid = ancestors[a];
+ for (d = 0; d < dcount; d++) {
+ mid = descendants[d];
+ mtype = dtypes[d];
+ if (mid == lid && !strcmp(mtype, "LIST")) {
+ return(MR_LISTLOOP);
+ }
+## repeat retrieve (cnt = m.ref_count)
+## where m.list_id = @lid and m.member_id = @mid
+## and m.member_type = @mtype
+ ref = aref[a] * dref[d];
+ if (cnt <= ref) {
+ iargv[0] = (char *)lid;
+ iargv[1] = mtype;
+ iargv[2] = (char *)mid;
+ incremental_before("members", 0, iargv);
+## delete m where m.list_id = lid and m.member_id = mid and
+## m.member_type = mtype
+ incremental_clear_after();
+ } else if (a == 0 && d == 0) {
+## replace m (ref_count = m.ref_count-ref, direct = 0)
+## where m.list_id = lid and m.member_id = mid and
+## m.member_type = mtype
+ } else {
+## replace m (ref_count = m.ref_count-ref)
+## where m.list_id = lid and m.member_id = mid and
+## m.member_type = mtype
+ }
+ }
+ }
+ lid = *(int *)argv[0];
+ entity = cl->entity;
+ who = cl->client_id;
+## repeat replace list (modtime = "now", modby = @who, modwith = @entity)
+## where list.#list_id = @lid
+ if (ingres_errno) return(mr_errcode);
+ return(MR_SUCCESS);
+##}
+
+
+/* get_ace_use - given a type and a name, return a type and a name.
+ * The ace_type is one of "LIST", "USER", "RLIST", or "RUSER" in argv[0],
+ * and argv[1] will contain the ID of the entity in question. The R*
+ * types mean to recursively look at every containing list, not just
+ * when the object in question is a direct member. On return, the
+ * usage type will be one of LIST, SERVICE, FILESYS, QUOTA, QUERY, or ZEPHYR.
+ */
+
+int get_ace_use(q, argv, cl, action, actarg)
+ struct query *q;
+ char *argv[];
+ client *cl;
+ int (*action)();
+ int actarg;
+##{
+ int found = 0;
+## char *atype;
+## int aid, listid, id;
+ struct save_queue *sq, *sq_create();
+
+## range of m is imembers
+ atype = argv[0];
+ aid = *(int *)argv[1];
+ if (!strcmp(atype, "LIST") || !strcmp(atype, "USER") ||
+ !strcmp(atype, "KERBEROS")) {
+ return(get_ace_internal(atype, aid, action, actarg));
+ }
+
+ sq = sq_create();
+ if (!strcmp(atype, "RLIST")) {
+ sq_save_data(sq, aid);
+ /* get all the list_id's of containing lists */
+## repeat retrieve (listid = m.list_id)
+## where m.member_type = "LIST" and m.member_id = @id {
+ sq_save_unique_data(sq, listid);
+## }
+ /* now process each one */
+ while (sq_get_data(sq, &id)) {
+ if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
+ found++;
+ }
+ }
+
+ if (!strcmp(atype, "RUSER")) {
+## repeat retrieve (listid = m.list_id)
+## where m.member_type = "USER" and m.member_id = @aid {
+ sq_save_data(sq, listid);
+## }
+ /* now process each one */
+ while (sq_get_data(sq, &id)) {
+ if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
+ found++;
+ }
+ if (get_ace_internal("USER", aid, action, actarg) == MR_SUCCESS)
+ found++;
+ }
+
+ if (!strcmp(atype, "RKERBERO")) {
+## repeat retrieve (listid = m.list_id)
+## where m.member_type = "KERBEROS" and m.member_id = @aid {
+ sq_save_data(sq, listid);
+## }
+ /* now process each one */
+ while (sq_get_data(sq, &id)) {
+ if (get_ace_internal("LIST", id, action, actarg) == MR_SUCCESS)
+ found++;
+ }
+ if (get_ace_internal("KERBEROS", aid, action, actarg) == MR_SUCCESS)
+ found++;
+ }
+
+ sq_destroy(sq);
+ if (ingres_errno) return(mr_errcode);
+ if (!found) return(MR_NO_MATCH);
+ return(MR_SUCCESS);
+##}
+
+
+/* This looks up a single list or user for ace use. atype must be "USER"
+ * or "LIST", and aid is the ID of the corresponding object. This is used
+ * by get_ace_use above.
+ */
+
+##get_ace_internal(atype, aid, action, actarg)
+## char *atype;
+## int aid;
+ int (*action)();
+ int actarg;
+##{
+ char *rargv[2];
+ int found = 0;
+## char name[33];
+
+ rargv[1] = name;
+ if (!strcmp(atype, "LIST")) {
+ rargv[0] = "FILESYS";
+## repeat retrieve (name = filesys.label)
+## where filesys.owners = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+
+ rargv[0] = "QUERY";
+## repeat retrieve (name = capacls.capability)
+## where capacls.list_id = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+ } else if (!strcmp(atype, "USER")) {
+ rargv[0] = "FILESYS";
+## repeat retrieve (name = filesys.label)
+## where filesys.owner = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+ }
+
+ rargv[0] = "LIST";
+## repeat retrieve (name = list.#name)
+## where list.acl_type = @atype and list.acl_id = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+
+ rargv[0] = "SERVICE";
+## repeat retrieve (name = servers.#name)
+## where servers.acl_type = @atype and servers.acl_id = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+
+ rargv[0] = "HOSTACCESS";
+## repeat retrieve (name = machine.#name)
+## where machine.mach_id = hostaccess.mach_id and
+## hostaccess.acl_type = @atype and hostaccess.acl_id = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+ rargv[0] = "ZEPHYR";
+## repeat retrieve (name = zephyr.class)
+## where zephyr.xmt_type = @atype and zephyr.xmt_id = @aid or
+## zephyr.sub_type = @atype and zephyr.sub_id = @aid or
+## zephyr.iws_type = @atype and zephyr.iws_id = @aid or
+## zephyr.iui_type = @atype and zephyr.iui_id = @aid {
+ (*action)(2, rargv, actarg);
+ found++;
+## }
+
+ if (!found) return(MR_NO_MATCH);
+ return(MR_SUCCESS);
+##}
+
+
+/* get_lists_of_member - given a type and a name, return the name and flags
+ * of all of the lists of the given member. The member_type is one of
+ * "LIST", "USER", "STRING", "RLIST", "RUSER", or "RSTRING" in argv[0],
+ * and argv[1] will contain the ID of the entity in question. The R*
+ * types mean to recursively look at every containing list, not just
+ * when the object in question is a direct member.
+ */
+
+int get_lists_of_member(q, argv, cl, action, actarg)
+ struct query *q;
+ char *argv[];
+ client *cl;
+ int (*action)();
+ int actarg;
+##{
+ int found = 0, direct = 1;
+ char *rargv[6];
+## char *atype;
+## int aid, listid, id;
+## char name[33], active[5], public[5], hidden[5], maillist[5], group[5];
+
+ atype = argv[0];
+ aid = *(int *)argv[1];
+ if (!strcmp(atype, "RLIST")) {
+ atype = "LIST";
+ direct = 0;
+ }
+ if (!strcmp(atype, "RUSER")) {
+ atype = "USER";
+ direct = 0;
+ }
+ if (!strcmp(atype, "RSTRING")) {
+ atype = "STRING";
+ direct = 0;
+ }
+ if (!strcmp(atype, "RKERBEROS")) {
+ atype = "KERBEROS";
+ direct = 0;
+ }
+
+ rargv[0] = name;
+ rargv[1] = active;
+ rargv[2] = public;
+ rargv[3] = hidden;
+ rargv[4] = maillist;
+ rargv[5] = group;
+## range of m is imembers
+ if (direct) {
+## repeat retrieve (name = list.#name, active = text(list.#active),
+## public = text(list.#public), hidden = text(list.#hidden),
+## maillist = text(list.#maillist), group = text(list.#group))
+## where list.list_id = m.list_id and m.direct = 1 and
+## m.member_type = @atype and m.member_id = @aid {
+ (*action)(6, rargv, actarg);
+ found++;
+## }
+ } else {
+## repeat retrieve (name = list.#name, active = text(list.#active),
+## public = text(list.#public), hidden = text(list.#hidden),
+## maillist = text(list.#maillist), group = text(list.#group))
+## where list.list_id = m.list_id and
+## m.member_type = @atype and m.member_id = @aid {
+ (*action)(6, rargv, actarg);
+ found++;
+## }
+ }
+
+ if (ingres_errno) return(mr_errcode);
+ if (!found) return(MR_NO_MATCH);
+ return(MR_SUCCESS);
+##}
+
+
+/* qualified_get_lists: passed "TRUE", "FALSE", or "DONTCARE" for each of
+ * the five flags associated with each list. It will return the name of
+ * each list that meets the quailifications. It does this by building a
+ * where clause based on the arguments, then doing a retrieve.
+ */