3 * Argument validation routines
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
11 #include "mr_server.h"
20 EXEC SQL INCLUDE sqlca;
21 EXEC SQL INCLUDE sqlda;
25 extern char *whoami, *table_name[], *sqlbuffer[QMAXARGS];
26 extern int dbms_errno, mr_errcode;
28 EXEC SQL BEGIN DECLARE SECTION;
29 extern char stmt_buf[];
30 EXEC SQL END DECLARE SECTION;
32 int validate_chars(char *argv[], struct valobj *vo);
33 int validate_id(struct query *, char *argv[], struct valobj *vo);
34 int validate_name(char *argv[], struct valobj *vo);
35 int validate_rename(char *argv[], struct valobj *vo);
36 int validate_type(char *argv[], struct valobj *vo);
37 int validate_typedata(struct query *, char *argv[], struct valobj *vo);
38 int validate_len(char *argv[], struct valobj *vo);
39 int lock_table(struct valobj *vo);
40 int readlock_table(struct valobj *vo);
41 int convert_wildcards_uppercase(char *arg);
43 extern SQLDA *sqlald(int, int, int);
44 SQLDA *mr_alloc_sqlda(void);
46 EXEC SQL WHENEVER SQLERROR DO dbmserr();
48 /* Validation Routines */
50 int validate_row(struct query *q, char *argv[], struct validate *v)
52 EXEC SQL BEGIN DECLARE SECTION;
55 EXEC SQL END DECLARE SECTION;
57 /* build where clause */
58 build_qual(v->qual, v->argc, argv, qual);
60 /* look for the record */
61 sprintf(stmt_buf, "SELECT COUNT (*) FROM %s WHERE %s",
62 table_name[q->rtable], qual);
67 rowcount = atoi(sqlbuffer[0]);
75 int validate_fields(struct query *q, char *argv[], struct valobj *vo, int n)
84 status = validate_name(argv, vo);
88 status = validate_id(q, argv, vo);
92 status = validate_type(argv, vo);
96 status = validate_typedata(q, argv, vo);
100 status = validate_rename(argv, vo);
104 status = validate_chars(argv, vo);
108 status = validate_len(argv, vo);
112 status = lock_table(vo);
116 status = readlock_table(vo);
120 status = convert_wildcards(argv[vo->index]);
124 status = convert_wildcards_uppercase(argv[vo->index]);
129 if (status != MR_EXISTS)
140 /* validate_chars: verify that there are no illegal characters in
141 * the string. Legal characters are printing chars other than
142 * ", *, ?, \, [ and ].
144 static int illegalchars[] = {
145 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
146 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
147 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
153 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
154 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
155 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
156 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
157 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
158 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
159 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
160 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
163 int validate_chars(char *argv[], struct valobj *vo)
165 char *s = argv[vo->index];
166 EXEC SQL BEGIN DECLARE SECTION;
169 EXEC SQL END DECLARE SECTION;
171 /* check for bad characters */
174 if (illegalchars[(int)*s++])
178 /* check for length */
179 tname = table_name[vo->table];
180 cname = vo->namefield;
181 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
182 WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
184 if ((strlen(argv[vo->index]) > len) &&
185 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
186 return MR_ARG_TOO_LONG;
192 int validate_id(struct query *q, char *argv[], struct valobj *vo)
194 EXEC SQL BEGIN DECLARE SECTION;
195 char *name, *namefield, *idfield;
196 int id, rowcount, tbl;
197 EXEC SQL END DECLARE SECTION;
201 name = argv[vo->index];
203 namefield = vo->namefield;
204 idfield = vo->idfield;
206 if ((tbl == USERS_TABLE && !strcmp(namefield, "login")) ||
207 tbl == MACHINE_TABLE || tbl == SUBNET_TABLE || tbl == FILESYS_TABLE ||
208 tbl == LIST_TABLE || tbl == CLUSTERS_TABLE || tbl == STRINGS_TABLE)
210 if (tbl == MACHINE_TABLE || tbl == SUBNET_TABLE)
212 for (c = name; *c; c++)
218 status = name_to_id(name, tbl, &id);
221 *(int *)argv[vo->index] = id;
224 else if (status == MR_NO_MATCH && tbl == STRINGS_TABLE &&
225 (q->type == APPEND || q->type == UPDATE))
227 id = add_string(name);
228 cache_entry(name, STRINGS_TABLE, id);
229 *(int *)argv[vo->index] = id;
232 else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
239 /* else, it's `dubu', which uses unix_uid from users */
240 EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
241 WHERE unix_uid = :name;
246 EXEC SQL SELECT users_id INTO :id FROM users
247 WHERE unix_uid = :name;
248 *(int *)argv[vo->index] = id;
253 int validate_name(char *argv[], struct valobj *vo)
255 char *name, *namefield;
258 name = argv[vo->index];
259 namefield = vo->namefield;
260 if (vo->table == SERVERS_TABLE && !strcmp(namefield, "name"))
262 for (c = name; *c; c++)
268 sprintf(stmt_buf, "SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
269 table_name[vo->table], table_name[vo->table], namefield, name);
274 return (atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error;
277 int validate_rename(char *argv[], struct valobj *vo)
279 EXEC SQL BEGIN DECLARE SECTION;
280 char *name, *namefield, *idfield;
282 EXEC SQL END DECLARE SECTION;
286 status = validate_chars(argv, vo);
287 if (status != MR_EXISTS)
289 name = argv[vo->index];
290 /* minor kludge to upcasify machine names */
291 if (vo->table == MACHINE_TABLE)
293 for (c = name; *c; c++)
299 namefield = vo->namefield;
300 idfield = vo->idfield;
304 if (!strcmp(argv[vo->index], argv[vo->index - 1]))
306 sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = '%s'",
307 namefield, table_name[vo->table], namefield, name);
312 if (sqlca.sqlcode == SQL_NO_MATCH)
313 return MR_EXISTS; /* how's _that_ for intuitive? */
317 status = name_to_id(name, vo->table, &id);
318 if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
325 int validate_type(char *argv[], struct valobj *vo)
327 EXEC SQL BEGIN DECLARE SECTION;
331 EXEC SQL END DECLARE SECTION;
334 typename = vo->namefield;
335 c = val = argv[vo->index];
338 if (illegalchars[(int)*c++])
342 /* uppercase type fields */
343 for (c = val; *c; c++)
349 EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
350 WHERE name = :typename AND type = 'TYPE' AND trans = :val;
353 return cnt ? MR_EXISTS : vo->error;
356 /* validate member or type-specific data field */
358 int validate_typedata(struct query *q, char *argv[], struct valobj *vo)
360 EXEC SQL BEGIN DECLARE SECTION;
365 EXEC SQL END DECLARE SECTION;
369 /* get named object */
370 name = argv[vo->index];
372 /* get field type string (known to be at index-1) */
373 field_type = argv[vo->index - 1];
375 /* get corresponding data type associated with field type name */
376 EXEC SQL SELECT trans INTO :data_type FROM alias
377 WHERE name = :field_type AND type = 'TYPEDATA';
380 if (sqlca.sqlerrd[2] != 1)
383 /* now retrieve the record id corresponding to the named object */
384 if (strchr(data_type, ' '))
385 *strchr(data_type, ' ') = '\0';
386 if (!strcmp(data_type, "user"))
389 if (strchr(name, '@'))
391 status = name_to_id(name, USERS_TABLE, &id);
392 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
397 else if (!strcmp(data_type, "list"))
400 status = name_to_id(name, LIST_TABLE, &id);
401 if (status && status == MR_NOT_UNIQUE)
403 if (status == MR_NO_MATCH)
405 /* if idfield is non-zero, then if argv[0] matches the string
406 * that we're trying to resolve, we should get the value of
407 * numvalues.[idfield] for the id.
409 if (vo->idfield && !strcmp(argv[0], argv[vo->index]))
411 set_next_object_id(q->validate->object_id, q->rtable, 0);
413 EXEC SQL SELECT value INTO :id FROM numvalues
415 if (sqlca.sqlerrd[2] != 1)
424 else if (!strcmp(data_type, "machine"))
427 for (c = name; *c; c++)
432 status = name_to_id(name, MACHINE_TABLE, &id);
433 if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
438 else if (!strcmp(data_type, "string"))
441 status = name_to_id(name, STRINGS_TABLE, &id);
442 if (status && status == MR_NOT_UNIQUE)
444 if (status == MR_NO_MATCH)
446 if (q->type != APPEND && q->type != UPDATE)
448 id = add_string(name);
449 cache_entry(name, STRINGS_TABLE, id);
454 else if (!strcmp(data_type, "none"))
459 /* now set value in argv */
460 *(int *)argv[vo->index] = id;
466 /* Make sure the data fits in the field */
467 int validate_len(char *argv[], struct valobj *vo)
469 EXEC SQL BEGIN DECLARE SECTION;
472 EXEC SQL END DECLARE SECTION;
474 tname = table_name[vo->table];
475 cname = vo->namefield;
476 EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
477 WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
479 if ((strlen(argv[vo->index]) > len) &&
480 strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
481 return MR_ARG_TOO_LONG;
486 /* Lock the table named by the validation object */
488 int lock_table(struct valobj *vo)
491 sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
492 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
501 * Get a read lock on the table by accessing the magic lock
502 * record. Certain tables are constructed so that they contain
503 * an id field whose value is zero and a modtime field. We
504 * manipulate the modtime field of the id 0 record to effect
505 * locking of the table
508 int readlock_table(struct valobj *vo)
511 sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
512 EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
519 return MR_EXISTS; /* validate_fields expects us to return
520 * this value if everything went okay
524 /* Check the database at startup time. */
526 void sanity_check_database(void)
528 EXEC SQL BEGIN DECLARE SECTION;
530 EXEC SQL END DECLARE SECTION;
532 /* Sometimes a crash can leave strings_id in numvalues in an
533 incorrect state. Check for that and fix it. */
535 EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name = 'strings_id';
537 for (id = oid + 1; sqlca.sqlcode == 0; id++)
539 EXEC SQL SELECT string_id INTO :id FROM strings
540 WHERE string_id = :id;
544 EXEC SQL UPDATE numvalues SET value = :id - 1 WHERE name = 'strings_id';
548 char *sqlbuffer[QMAXARGS];
550 /* Dynamic SQL support routines */
551 SQLDA *mr_alloc_sqlda(void)
556 it = sqlald(QMAXARGS, ARGLEN, 0);
559 com_err(whoami, MR_NO_MEM, "setting up SQLDA");
563 for (j = 0; j < QMAXARGS; j++)
565 it->V[j] = sqlbuffer[j] = xmalloc(ARGLEN);
566 it->T[j] = 97; /* 97 = CHARZ = null-terminated string */
574 /* Convert normal Unix-style wildcards to SQL voodoo */
575 int convert_wildcards(char *arg)
577 static char buffer[ARGLEN];
580 for (d = buffer, s = arg; *s; s++)
607 /* Copy back into argv */
613 /* This version includes uppercase conversion, for things like gmac.
614 * This is necessary because "LIKE" doesn't work with "uppercase()".
615 * Including it in a wildcard routine saves making two passes over
616 * the argument string.
618 int convert_wildcards_uppercase(char *arg)
620 static char buffer[ARGLEN];
623 for (d = buffer, s = arg; *s; s++)
644 *d++ = toupper(*s); /* This is the only diff. */
650 /* Copy back into argv */
657 /* Adds a string to the string table. Returns the id number.
660 int add_string(char *nm)
662 EXEC SQL BEGIN DECLARE SECTION;
665 EXEC SQL END DECLARE SECTION;
667 EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
669 EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
671 EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);