+/*
+ * $Source$
+ * $Author$
+ * $Header$
+ *
+ * Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *
+ * $Log$
+ * Revision 1.1 1987-07-29 15:13:57 wesommer
+ * Initial revision
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_qc = "$Header$";
+#endif lint
+
+#include "query.h"
+#include "sms_server.h"
+#include <ctype.h>
+
+#define SMS_SUCCESS 0
+
+extern char *whoami;
+
+/* Specialized Access Routines */
+
+/**
+ ** access_user - verify that client name equals specified login name
+ **
+ ** Used by: update_user_shell
+ ** update_finger_by_login
+ **
+ ** - since field validation routines are called first, a users_id is
+ ** now in argv[0] instead of the login name. Therefore, we must
+ ** convert the client name to a users_id.
+ **/
+
+access_user(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+ register struct krbname *krb;
+## int client_id;
+## char *client_name;
+## int rowcount;
+
+ client_name = cl->kname.name;
+## repeat retrieve (client_id = users.users_id)
+## where users.login = @client_name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(SMS_PERM);
+ if (client_id != *(int *)argv[0]) return(SMS_PERM);
+
+ return(SMS_SUCCESS);
+##}
+
+/**
+ ** access_list - check access for adding or deleting list members
+ **
+ ** Inputs: argv[0] - list_id
+ ** cl->krb.name - client name
+ **
+ ** - check that client is a member of the access control list
+ ** - OR, if q->shortname == {amtl | dfml} and
+ ** if list.flags & LF_PUBLIC, allow access if client = member
+ **
+ **/
+
+access_list(q, argv, cl)
+ struct query *q;
+ char *argv[];
+ client *cl;
+##{
+## int list_id;
+## int acl_id;
+## int flags;
+ int member_id;
+ char *client_type;
+ int client_id;
+ int status;
+ int exists;
+
+ list_id = *(int *)argv[0];
+## repeat retrieve (acl_id = list.#acl_id, flags = list.#flags)
+## where list.#list_id = @list_id
+
+ /* parse client structure */
+ status = get_client(cl, &client_type, &client_id);
+ if (status != SMS_SUCCESS) return(status);
+
+ /* if amtl or dmfl and list is public allow client to add or delete self */
+ if (!bcmp("amtl", q->shortname, 4) || !bcmp("dmfl", q->shortname, 4)) {
+ if ((flags & LF_PUBLIC) && !bcmp("USER", argv[1], 4)) {
+ member_id = *(int *)argv[2];
+ if (member_id == client_id) return(SMS_SUCCESS);
+ }
+ }
+
+ /* check for client in access control list */
+ exists = find_member(acl_id, client_type, client_id, 0);
+ if (!exists) return(SMS_PERM);
+
+ return(SMS_SUCCESS);
+##}
+\f
+/**
+ ** Setup routine for add_user
+ **
+ ** Inputs: argv[0] - login
+ ** argv[1] - uid
+ **
+ ** Description:
+ **
+ ** - if argv[1] == "#" then set argv[1] = next(uid)
+ ** - if argv[0] == "#" then set argv[0] = "#<uid>"
+ **
+ **/
+
+setup_add_user(q, argv, cl, access_check)
+ struct query *q;
+ register char *argv[];
+ client *cl;
+ int access_check;
+##{
+## int nuid;
+## int exists;
+
+ if (access_check) return(SMS_SUCCESS);
+
+ if (!bcmp(argv[1], "#", 2)) {
+## range of u is users
+## range of v is values
+## repeat retrieve (nuid = v.value) where v.name = "uid"
+ exists = 1;
+ while (exists) {
+ nuid++;
+## repeat retrieve (exists = any(u.#uid where u.#uid = @nuid))
+ }
+## repeat replace v (value = @nuid) where v.name = "uid"
+ sprintf(argv[1], "%d", nuid);
+ }
+
+ if (!bcmp(argv[0], "#", 2)) {
+ sprintf(argv[0], "#%s", argv[1]);
+ }
+
+ return(SMS_SUCCESS);
+##}
+
+/**
+ ** Setup routine for add_group
+ **
+ ** Inputs: none
+ **
+ ** Description: allocate next gid and store in values table
+ **
+ **/
+
+setup_add_group(q, argv, cl, access_check)
+ struct query *q;
+ char *argv[];
+ client *cl;
+ int access_check;
+##{
+## int ngid;
+## int exists;
+
+ if (access_check) return(SMS_SUCCESS);
+
+## range of g is groups
+## range of v is values
+## repeat retrieve (ngid = v.value) where v.name = "gid"
+ exists = 1;
+ while (exists) {
+ ngid++;
+## repeat retrieve (exists = any(g.#gid where g.#gid = @ngid))
+ }
+
+## repeat replace v (value = @ngid) where v.name = "gid"
+ return(SMS_SUCCESS);
+##}
+\f
+/* Followup Routines */
+
+set_user_modtime(q, argv)
+ struct query *q;
+ char *argv[];
+##{
+## char *login;
+
+ login = argv[0];
+## repeat replace u (modtime = "now") where u.#login = @login
+ return(SMS_SUCCESS);
+##}
+
+set_user_modtime_by_id(q, argv)
+ struct query *q;
+ char *argv[];
+##{
+## int users_id;
+
+ users_id = *(int *)argv[0];
+## repeat replace users (modtime = "now") where users.#users_id = @users_id
+ return(SMS_SUCCESS);
+##}
+
+set_list_modtime(q, argv)
+ struct query *q;
+ char *argv[];
+##{
+## char *list_name;
+
+ list_name = argv[0];
+## repeat replace list (modtime = "now") where list.name = @list_name
+ return(SMS_SUCCESS);
+##}
+
+set_list_modtime_by_id(q, argv)
+ struct query *q;
+ char *argv[];
+##{
+## int list_id;
+
+ list_id = *(int *)argv[0];
+## repeat replace list (modtime = "now") where list.#list_id = @list_id
+ return(SMS_SUCCESS);
+##}
+
+set_finger_modtime(q, argv)
+ struct query *q;
+ char *argv[];
+##{
+## int users_id;
+
+ users_id = *(int *)argv[0];
+## repeat replace f (modtime = "now") where f.#users_id = @users_id
+ return(SMS_SUCCESS);
+##}
+
+/**
+ ** delete_list_members - called after the delete_list query to clean up
+ ** members table.
+ **
+ ** Inputs: argv[0] - list_id
+ **
+ ** Description:
+ ** - foreach string member: decr string refc; ifzero, delete string
+ ** - delete all members entries for this list_id
+ **
+ **/
+
+delete_list_members(q, argv)
+ struct query *q;
+ register char *argv[];
+##{
+## int list_id;
+## int string_id;
+## int refc;
+## int rowcount;
+ struct save_queue *sq;
+ struct save_queue *sq_create();
+
+ list_id = *(int *)argv[0];
+ sq = sq_create();
+
+## range of m is members
+## repeat retrieve (string_id = m.member_id)
+## where m.#list_id = @list_id and m.member_type = "STRING"
+## {
+ sq_save_data(sq, string_id);
+## }
+
+ while (sq_get_data(sq, &string_id)) {
+## range of s is strings
+## repeat retrieve (refc = s.#refc) where s.#string_id = @string_id
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount == 0) continue;
+ if (--refc == 0) {
+## repeat delete s where s.#string_id = @string_id
+ } else {
+## repeat replace s (#refc = @refc) where s.#string_id = @string_id
+ }
+ }
+ sq_destroy(sq);
+
+## repeat delete m where m.#list_id = @list_id
+
+ return(SMS_SUCCESS);
+##}
+
+/**
+ ** grvd_support - Support routine for get_rvd_servers query
+ **
+ ** Inputs:
+ ** q - grvd query structure
+ ** sq - save_queue struture: contains list of {machine, oper_acl_id,
+ ** admin_acl_id, shutdown_acl_id} records.
+ ** v - validate structure (not used)
+ ** action - action routine
+ ** actarg - action routine argument
+ **
+ ** Description:
+ ** - translate acl_ids to list names
+ **
+ **/
+
+grvd_support(q, sq, v, action, actarg)
+ struct query *q;
+ struct save_queue *sq;
+ struct validate *v;
+ int (*action)();
+ int actarg;
+##{
+ char **argv;
+ char *targv[4];
+## char oper[33];
+## char admin[33];
+## char shutdown[33];
+## int list_id;
+
+ targv[1] = oper;
+ targv[2] = admin;
+ targv[3] = shutdown;
+
+## range of l is list
+
+ while (sq_get_data(sq, &argv)) {
+ sscanf(argv[1], "%d", &list_id);
+## repeat retrieve (oper = l.name) where l.#list_id = @list_id
+ sscanf(argv[2], "%d", &list_id);
+## repeat retrieve (admin = l.name) where l.#list_id = @list_id
+ sscanf(argv[3], "%d", &list_id);
+## repeat retrieve (shutdown = l.name) where l.#list_id = @list_id
+
+ targv[0] = argv[0];
+ (*action)(4, targv, actarg);
+ free(argv[0]);
+ free(argv[1]);
+ free(argv[2]);
+ free(argv[3]);
+ }
+
+ sq_destroy(sq);
+ return(SMS_SUCCESS);
+##}
+
+/**
+ ** set_next_object_id - set next object id in values table
+ **
+ ** Inputs: object - object name in values table
+ **
+ ** - called before an APPEND operation to set the next object id to
+ ** be used for the new record
+ **
+ **/
+
+set_next_object_id(object)
+ char *object;
+##{
+## char *name;
+## int id;
+
+ name = object;
+## range of v is values
+## repeat retrieve (id = v.value) where v.#name = @name
+ id++;
+## repeat replace v (value = @id) where v.#name = @name
+ return(SMS_SUCCESS);
+##}
+
+\f
+/**
+ ** add_locker - special query routine for creating a user locker
+ **
+ ** Inputs:
+ ** argv[0] - users_id
+ ** argv[1] - machine_id
+ ** argv[2] - device
+ ** argv[3] - initial quota
+ **
+ ** Description:
+ ** - get prefix directory (dir) for mount point on specified machine/device
+ ** - create filesys entry (label=<login>, type=NFS, machine=<machine>,
+ ** mount=<dir>/<login>, access=w, acl=dbadmin)
+ ** - increment allocated in nfsphys by quota
+ ** - create nfsquota entry
+ **
+ ** Errors:
+ ** - SMS_NFSPHYS - machine/device does not exist in nfsphys
+ ** - SMS_FILESYS_EXISTS - file system already exists
+ **
+ **/
+
+add_locker(q, argv)
+ register struct query *q;
+ char *argv[];
+##{
+## int users_id;
+## int mach_id;
+## char *device;
+## int quota;
+## int rowcount;
+## char *login;
+## char dir[32];
+## int allocated;
+## char locker[64];
+## char mount[64];
+## int user_acl;
+
+ /* copy arguments */
+ users_id = *(int *)argv[0];
+ mach_id = *(int *)argv[1];
+ device = argv[2];
+ sscanf(argv[3], "%d", "a);
+
+## range of u is users
+## range of f is filesys
+## range of np is nfsphys
+
+ /* get login name */
+## repeat retrieve (login = u.#login) where u.#users_id = @users_id
+
+ /* get user's acl id */
+## repeat retrieve (user_acl = list.list_id) where list.name = @login
+
+ /* get filesystem directory prefix; give error if machine/device
+ pair not in nfsphys table */
+## repeat retrieve (dir = np.#dir, allocated = np.#allocated)
+## where np.#mach_id = @mach_id and np.#device = device
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount == 0) return(SMS_NFSPHYS);
+
+ /* make sure a filesys with user's name does not already exist */
+## repeat retrieve (rowcount = any(f.label where f.label = @login))
+ if (rowcount != 0) return(SMS_FILESYS_EXISTS);
+
+ /* create a new filesys */
+ sprintf(locker, "%s/%s", dir, login);
+ sprintf(mount, "/mit/%s", login);
+## repeat append filesys
+## (#users_id = @users_id, type = "NFS", #mach_id = @mach_id,
+## name = @locker, access = "w", order = 1, #mount = @mount,
+## acl_id = @user_acl)
+
+ /* increment usage count in nfsphys table */
+ allocated += quota;
+## replace np (#allocated = allocated)
+## where np.#mach_id = mach_id and np.#device = device
+
+ /* create nfsquota entry */
+## append nfsquota (#users_id = users_id, #mach_id = mach_id,
+## device = #device, #quota = quota)
+
+ return(SMS_SUCCESS);
+##}
+\f
+/* Validation Routines */
+
+validate_row(q, argv, v)
+ register struct query *q;
+ char *argv[];
+ register struct validate *v;
+##{
+## char *rvar;
+## char *table;
+## char *name;
+## char qual[128];
+## int rowcount;
+
+ /* build where clause */
+ build_qual(v->qual, v->argc, argv, qual);
+
+ /* setup ingres variables */
+ rvar = q->rvar;
+ table = q->rtable;
+ name = v->field;
+
+ /* tell the logfile what we're doing */
+ com_err(whoami, 0, "validating row");
+ com_err(whoami, 0, qual);
+
+ /* look for the record */
+## range of rvar is table
+## retrieve (rowcount = count(rvar.name where qual))
+ com_err(whoami, 0, "row validated");
+ if (rowcount == 0) return(SMS_NO_MATCH);
+ if (rowcount > 1) return(SMS_NOT_UNIQUE);
+ return(SMS_EXISTS);
+##}
+
+validate_fields(q, argv, vo, n)
+ struct query *q;
+ register char *argv[];
+ register struct valobj *vo;
+ register int n;
+{
+ register int status;
+ char buf[64];
+
+ while (--n >= 0) {
+ switch (vo->type) {
+ case V_NAME:
+ sprintf(buf, "validating %s in %s: %s",
+ vo->namefield, vo->table, argv[vo->index]);
+ com_err(whoami, 0, buf);
+ status = validate_name(argv, vo);
+ break;
+
+ case V_ID:
+ sprintf(buf, "validating %s in %s: %s",
+ vo->idfield, vo->table, argv[vo->index]);
+ com_err(whoami, 0, buf);
+ status = validate_id(argv, vo);
+ break;
+
+ case V_TYPE:
+ sprintf(buf, "validating %s type: %s",
+ vo->table, argv[vo->index]);
+ com_err(whoami, 0, buf);
+ status = validate_type(argv, vo);
+ break;
+
+ case V_TYPEDATA:
+ sprintf(buf, "validating type-specific data: %s",
+ argv[vo->index]);
+ com_err(whoami, 0, buf);
+ status = validate_typedata(q, argv, vo);
+ break;
+
+ case V_FOLLOWUP:
+ status = SMS_EXISTS;
+ break;
+
+ }
+
+ if (status != SMS_EXISTS) return(status);
+ vo++;
+ }
+
+ return(SMS_SUCCESS);
+}
+
+validate_id(argv, vo)
+ char *argv[];
+ register struct valobj *vo;
+##{
+## char *name;
+## char *table;
+## char *namefield;
+## char *idfield;
+## int id;
+## int rowcount;
+
+ name = argv[vo->index];
+ table = vo->table;
+ namefield = vo->namefield;
+ idfield = vo->idfield;
+## retrieve (id = table.idfield) where table.namefield = name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(vo->error);
+ *(int *)argv[vo->index] = id;
+ return(SMS_EXISTS);
+##}
+
+validate_name(argv, vo)
+ char *argv[];
+ register struct valobj *vo;
+##{
+## char *name;
+## char *table;
+## char *namefield;
+## int rowcount;
+
+ name = argv[vo->index];
+ table = vo->table;
+ namefield = vo->namefield;
+## retrieve (rowcount = countu(table.namefield
+## where table.namefield = name))
+ return ((rowcount == 1) ? SMS_EXISTS : vo->error);
+##}
+
+validate_type(argv, vo)
+ char *argv[];
+ register struct valobj *vo;
+##{
+## char *typename;
+## char *value;
+## int rowcount;
+ register char *c;
+
+ typename = vo->table;
+ value = argv[vo->index];
+
+ /* uppercase type fields */
+ for (c = value; *c; c++) if (islower(*c)) *c = toupper(*c);
+
+## range of a is alias
+## repeat retrieve (rowcount = count(a.trans where a.name = @typename and
+## a.type = "TYPE" and
+## a.trans = @value))
+ return ((rowcount == 1) ? SMS_EXISTS : vo->error);
+##}
+
+/* validate member or type-specific data field */
+
+validate_typedata(q, argv, vo)
+ register struct query *q;
+ register char *argv[];
+ register struct valobj *vo;
+##{
+## char *name;
+## char *field_type;
+## char data_type[17];
+## int id;
+## int refc;
+## int rowcount;
+
+ /* get named object */
+ name = argv[vo->index];
+
+ /* get field type string (known to be at index-1) */
+ field_type = argv[vo->index-1];
+
+ /* get corresponding data type associated with field type name */
+## repeat retrieve (data_type = alias.trans)
+## where alias.#name = @field_type and alias.type = "TYPEDATA"
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(SMS_TYPE);
+
+ /* now retrieve the record id corresponding to the named object */
+
+ if (!strcmp(data_type, "user")) {
+ /* USER */
+## repeat retrieve (id = users.users_id) where users.login = @name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(SMS_USER);
+
+ } else if (!strcmp(data_type, "list")) {
+ /* LIST */
+## repeat retrieve (id = list.list_id) where list.#name = @name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(SMS_LIST);
+
+ } else if (!strcmp(data_type, "machine")) {
+ /* MACHINE */
+## repeat retrieve (id = machine.mach_id) where machine.#name = @name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) return(SMS_MACHINE);
+
+ } else if (!strcmp(data_type, "string")) {
+ /* STRING */
+## range of s is strings
+## repeat retrieve (id = s.string_id, refc = s.#refc)
+## where s.string = @name
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount == 0) {
+ if (q->type != APPEND) return(SMS_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, #refc = 1)
+ } else if (rowcount == 1) {
+ if (q->type == APPEND || q->type == DELETE) {
+ refc += (q->type == APPEND) ? 1 : -1;
+ if (refc > 0) {
+## replace s (#refc = refc) where s.string_id = id
+ } else {
+## delete s where s.string_id = id
+ }
+ }
+ }
+ } else {
+ return(SMS_TYPE);
+ }
+
+ /* now set value in argv */
+ *(int *)argv[vo->index] = id;
+
+ return (SMS_EXISTS);
+##}
+
+\f
+translate_ids(q, sq, v, action, actarg)
+ register struct query *q;
+ register struct save_queue *sq;
+ register struct validate *v;
+ register int (*action)();
+ int actarg;
+##{
+## char *name;
+## char *field_type;
+## char data_type[17];
+## int id;
+## int rowcount;
+ register int i;
+ struct valobj *vo;
+ char **argv;
+
+ for (i = 0; i < v->objcnt; i++) {
+ vo = &v->valobj[i];
+ if (vo->type == V_FOLLOWUP) break;
+ }
+
+ /* for each row */
+ while (sq_get_data(sq, &argv)) {
+
+ /* get object id */
+ i = vo->index;
+ sscanf(argv[i], "%d", &id);
+ free(argv[i]);
+ name = (char *)malloc(129);
+ argv[i] = name;
+
+ /* get field type string (known to be at index-1) */
+ field_type = argv[vo->index-1];
+
+ /* get corresponding data type associated with field type name */
+## repeat retrieve (data_type = alias.trans)
+## where alias.#name = @field_type and alias.type = "TYPEDATA"
+## inquire_equel (rowcount = "rowcount")
+ if (rowcount != 1) {
+ sprintf(name, "%d", id);
+ (*action)(q->vcnt, argv, actarg);
+ continue;
+ }
+
+ /* retrieve object name */
+
+ if (!strcmp(data_type, "user")) {
+ /* USER */
+## repeat retrieve (name = users.login) where users.users_id = @id
+## inquire_equel (rowcount = "rowcount")
+
+ } else if (!strcmp(data_type, "list")) {
+ /* LIST */
+## repeat retrieve (name = list.#name) where list.list_id = @id
+## inquire_equel (rowcount = "rowcount")
+
+ } else if (!strcmp(data_type, "machine")) {
+ /* MACHINE */
+## repeat retrieve (name = machine.#name) where machine.mach_id = @id
+## inquire_equel (rowcount = "rowcount")
+
+ } else if (!strcmp(data_type, "string")) {
+ /* STRING */
+## repeat retrieve (name = strings.string)
+## where strings.string_id = @id
+## inquire_equel (rowcount = "rowcount")
+
+ } else {
+ rowcount = 0;
+ }
+
+ /* if there wasn't a corresponding object name, then use the id */
+ if (rowcount != 1) sprintf(name, "%d", id);
+
+ /* 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 (SMS_SUCCESS);
+##}
+\f
+/*
+ * Local Variables:
+ * mode: c
+ * c-indent-level: 4
+ * c-continued-statement-offset: 4
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * End:
+ */