]> andersk Git - moira.git/blob - server/qvalidate.pc
Move query-result-sorting info from struct validate to struct query.
[moira.git] / server / qvalidate.pc
1 /* $Id$
2  *
3  * Argument validation routines
4  *
5  * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6  * For copying and distribution information, please see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include "mr_server.h"
12 #include "query.h"
13 #include "qrtn.h"
14
15 #include <ctype.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 EXEC SQL INCLUDE sqlca;
21 EXEC SQL INCLUDE sqlda;
22
23 RCSID("$Header$");
24
25 extern char *whoami, *table_name[], *sqlbuffer[QMAXARGS];
26 extern int dbms_errno, mr_errcode;
27
28 EXEC SQL BEGIN DECLARE SECTION;
29 extern char stmt_buf[];
30 EXEC SQL END DECLARE SECTION;
31
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);
42
43 extern SQLDA *sqlald(int, int, int);
44 SQLDA *mr_alloc_sqlda(void);
45
46 EXEC SQL WHENEVER SQLERROR DO dbmserr();
47
48 /* Validation Routines */
49
50 int validate_row(struct query *q, char *argv[], struct validate *v)
51 {
52   EXEC SQL BEGIN DECLARE SECTION;
53   char qual[128];
54   int rowcount;
55   EXEC SQL END DECLARE SECTION;
56
57   /* build where clause */
58   build_qual(v->qual, v->argc, argv, qual);
59
60   /* look for the record */
61   sprintf(stmt_buf, "SELECT COUNT (*) FROM %s WHERE %s",
62           table_name[q->rtable], qual);
63   dosql(sqlbuffer);
64   if (dbms_errno)
65     return mr_errcode;
66
67   rowcount = atoi(sqlbuffer[0]);
68   if (rowcount == 0)
69     return MR_NO_MATCH;
70   if (rowcount > 1)
71     return MR_NOT_UNIQUE;
72   return MR_EXISTS;
73 }
74
75 int validate_fields(struct query *q, char *argv[], struct valobj *vo, int n)
76 {
77   int status;
78
79   while (--n >= 0)
80     {
81       switch (vo->type)
82         {
83         case V_NAME:
84           status = validate_name(argv, vo);
85           break;
86
87         case V_ID:
88           status = validate_id(q, argv, vo);
89           break;
90
91         case V_TYPE:
92           status = validate_type(argv, vo);
93           break;
94
95         case V_TYPEDATA:
96           status = validate_typedata(q, argv, vo);
97           break;
98
99         case V_RENAME:
100           status = validate_rename(argv, vo);
101           break;
102
103         case V_CHAR:
104           status = validate_chars(argv, vo);
105           break;
106
107         case V_LEN:
108           status = validate_len(argv, vo);
109           break;
110
111         case V_LOCK:
112           status = lock_table(vo);
113           break;
114
115         case V_RLOCK:
116           status = readlock_table(vo);
117           break;
118
119         case V_WILD:
120           status = convert_wildcards(argv[vo->index]);
121           break;
122
123         case V_UPWILD:
124           status = convert_wildcards_uppercase(argv[vo->index]);
125           break;
126
127         }
128
129       if (status != MR_EXISTS)
130         return status;
131       vo++;
132     }
133
134   if (dbms_errno)
135     return mr_errcode;
136   return MR_SUCCESS;
137 }
138
139
140 /* validate_chars: verify that there are no illegal characters in
141  * the string.  Legal characters are printing chars other than
142  * ", *, ?, \, [ and ].
143  */
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,
161 };
162
163 int validate_chars(char *argv[], struct valobj *vo)
164 {
165   char *s = argv[vo->index];
166   EXEC SQL BEGIN DECLARE SECTION;
167   int len;
168   char *tname, *cname;
169   EXEC SQL END DECLARE SECTION;
170
171   /* check for bad characters */
172   while (*s)
173     {
174       if (illegalchars[(int)*s++])
175         return MR_BAD_CHAR;
176     }
177
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);
183
184   if ((strlen(argv[vo->index]) > len) &&
185       strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
186     return MR_ARG_TOO_LONG;
187
188   return MR_EXISTS;
189 }
190
191
192 int validate_id(struct query *q, char *argv[], struct valobj *vo)
193 {
194   EXEC SQL BEGIN DECLARE SECTION;
195   char *name, *namefield, *idfield;
196   int id, rowcount, tbl;
197   EXEC SQL END DECLARE SECTION;
198   int status;
199   char *c;
200
201   name = argv[vo->index];
202   tbl = vo->table;
203   namefield = vo->namefield;
204   idfield = vo->idfield;
205
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)
209     {
210       if (tbl == MACHINE_TABLE || tbl == SUBNET_TABLE)
211         {
212           for (c = name; *c; c++)
213             {
214               if (islower(*c))
215                 *c = toupper(*c);
216             }
217         }
218       status = name_to_id(name, tbl, &id);
219       if (status == 0)
220         {
221           *(int *)argv[vo->index] = id;
222           return MR_EXISTS;
223         }
224       else if (status == MR_NO_MATCH && tbl == STRINGS_TABLE &&
225                (q->type == APPEND || q->type == UPDATE))
226         {
227           id = add_string(name);
228           cache_entry(name, STRINGS_TABLE, id);
229           *(int *)argv[vo->index] = id;
230           return MR_EXISTS;
231         }
232       else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
233         return vo->error;
234       else
235         return status;
236     }
237   else
238     {
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;
242       if (dbms_errno)
243         return mr_errcode;
244       if (rowcount != 1)
245         return vo->error;
246       EXEC SQL SELECT users_id INTO :id FROM users
247         WHERE unix_uid = :name;
248       *(int *)argv[vo->index] = id;
249       return MR_EXISTS;
250     }
251 }
252
253 int validate_name(char *argv[], struct valobj *vo)
254 {
255   char *name, *namefield;
256   char *c;
257
258   name = argv[vo->index];
259   namefield = vo->namefield;
260   if (vo->table == SERVERS_TABLE && !strcmp(namefield, "name"))
261     {
262       for (c = name; *c; c++)
263         {
264           if (islower(*c))
265             *c = toupper(*c);
266         }
267     }
268   sprintf(stmt_buf, "SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
269           table_name[vo->table], table_name[vo->table], namefield, name);
270   dosql(sqlbuffer);
271
272   if (dbms_errno)
273     return mr_errcode;
274   return (atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error;
275 }
276
277 int validate_rename(char *argv[], struct valobj *vo)
278 {
279   EXEC SQL BEGIN DECLARE SECTION;
280   char *name, *namefield, *idfield;
281   int id;
282   EXEC SQL END DECLARE SECTION;
283   int status;
284   char *c;
285
286   status = validate_chars(argv, vo);
287   if (status != MR_EXISTS)
288     return status;
289   name = argv[vo->index];
290   /* minor kludge to upcasify machine names */
291   if (vo->table == MACHINE_TABLE)
292     {
293       for (c = name; *c; c++)
294         {
295           if (islower(*c))
296             *c = toupper(*c);
297         }
298     }
299   namefield = vo->namefield;
300   idfield = vo->idfield;
301   id = -1;
302   if (idfield == 0)
303     {
304       if (!strcmp(argv[vo->index], argv[vo->index - 1]))
305         return MR_EXISTS;
306       sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = '%s'",
307               namefield, table_name[vo->table], namefield, name);
308       dosql(sqlbuffer);
309
310       if (dbms_errno)
311         return mr_errcode;
312       if (sqlca.sqlcode == SQL_NO_MATCH)
313         return MR_EXISTS; /* how's _that_ for intuitive? */
314       else
315         return vo->error;
316     }
317   status = name_to_id(name, vo->table, &id);
318   if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
319     return MR_EXISTS;
320   else
321     return vo->error;
322 }
323
324
325 int validate_type(char *argv[], struct valobj *vo)
326 {
327   EXEC SQL BEGIN DECLARE SECTION;
328   char *typename;
329   char *val;
330   int cnt;
331   EXEC SQL END DECLARE SECTION;
332   char *c;
333
334   typename = vo->namefield;
335   c = val = argv[vo->index];
336   while (*c)
337     {
338       if (illegalchars[(int)*c++])
339         return MR_BAD_CHAR;
340     }
341
342   /* uppercase type fields */
343   for (c = val; *c; c++)
344     {
345       if (islower(*c))
346         *c = toupper(*c);
347     }
348
349   EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
350     WHERE name = :typename AND type = 'TYPE' AND trans = :val;
351   if (dbms_errno)
352     return mr_errcode;
353   return cnt ? MR_EXISTS : vo->error;
354 }
355
356 /* validate member or type-specific data field */
357
358 int validate_typedata(struct query *q, char *argv[], struct valobj *vo)
359 {
360   EXEC SQL BEGIN DECLARE SECTION;
361   char *name;
362   char *field_type;
363   char data_type[129];
364   int id;
365   EXEC SQL END DECLARE SECTION;
366   int status;
367   char *c;
368
369   /* get named object */
370   name = argv[vo->index];
371
372   /* get field type string (known to be at index-1) */
373   field_type = argv[vo->index - 1];
374
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';
378   if (dbms_errno)
379     return mr_errcode;
380   if (sqlca.sqlerrd[2] != 1)
381     return MR_TYPE;
382
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"))
387     {
388       /* USER */
389       if (strchr(name, '@'))
390         return MR_USER;
391       status = name_to_id(name, USERS_TABLE, &id);
392       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
393         return MR_USER;
394       if (status)
395         return status;
396     }
397   else if (!strcmp(data_type, "list"))
398     {
399       /* LIST */
400       status = name_to_id(name, LIST_TABLE, &id);
401       if (status && status == MR_NOT_UNIQUE)
402         return MR_LIST;
403       if (status == MR_NO_MATCH)
404         {
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.
408            */
409           if (vo->idfield && !strcmp(argv[0], argv[vo->index]))
410             {
411               set_next_object_id(q->validate->object_id, q->rtable, 0);
412               name = vo->idfield;
413               EXEC SQL SELECT value INTO :id FROM numvalues
414                 WHERE name = :name;
415               if (sqlca.sqlerrd[2] != 1)
416                 return MR_LIST;
417             }
418           else
419             return MR_LIST;
420         }
421       else if (status)
422         return status;
423     }
424   else if (!strcmp(data_type, "machine"))
425     {
426       /* MACHINE */
427       for (c = name; *c; c++)
428         {
429           if (islower(*c))
430             *c = toupper(*c);
431         }
432       status = name_to_id(name, MACHINE_TABLE, &id);
433       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
434         return MR_MACHINE;
435       if (status)
436         return status;
437     }
438   else if (!strcmp(data_type, "string"))
439     {
440       /* STRING */
441       status = name_to_id(name, STRINGS_TABLE, &id);
442       if (status && status == MR_NOT_UNIQUE)
443         return MR_STRING;
444       if (status == MR_NO_MATCH)
445         {
446           if (q->type != APPEND && q->type != UPDATE)
447             return MR_STRING;
448           id = add_string(name);
449           cache_entry(name, STRINGS_TABLE, id);
450         }
451       else if (status)
452         return status;
453     }
454   else if (!strcmp(data_type, "none"))
455     id = 0;
456   else
457     return MR_TYPE;
458
459   /* now set value in argv */
460   *(int *)argv[vo->index] = id;
461
462   return MR_EXISTS;
463 }
464
465
466 /* Make sure the data fits in the field */
467 int validate_len(char *argv[], struct valobj *vo)
468 {
469   EXEC SQL BEGIN DECLARE SECTION;
470   int len;
471   char *tname, *cname;
472   EXEC SQL END DECLARE SECTION;
473
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);
478
479   if ((strlen(argv[vo->index]) > len) &&
480       strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
481     return MR_ARG_TOO_LONG;
482
483   return MR_EXISTS;
484 }
485
486 /* Lock the table named by the validation object */
487
488 int lock_table(struct valobj *vo)
489 {
490 #ifdef DO_LOCKING
491   sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
492   EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
493   if (dbms_errno)
494     return mr_errcode;
495   else
496 #endif
497     return MR_EXISTS;
498 }
499
500 /*
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
506  */
507
508 int readlock_table(struct valobj *vo)
509 {
510 #ifdef DO_LOCKING
511   sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
512   EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
513
514   if (dbms_errno)
515     return mr_errcode;
516   if (sqlca.sqlcode)
517     return vo->error;
518 #endif
519   return MR_EXISTS;  /* validate_fields expects us to return
520                       * this value if everything went okay
521                       */
522 }
523
524 /* Check the database at startup time. */
525
526 void sanity_check_database(void)
527 {
528   EXEC SQL BEGIN DECLARE SECTION;
529   int oid, id;
530   EXEC SQL END DECLARE SECTION;
531
532   /* Sometimes a crash can leave strings_id in numvalues in an
533      incorrect state. Check for that and fix it. */
534
535   EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name = 'strings_id';
536
537   for (id = oid + 1; sqlca.sqlcode == 0; id++)
538     {
539       EXEC SQL SELECT string_id INTO :id FROM strings
540         WHERE string_id = :id;
541     }
542
543   if (id != oid + 1)
544     EXEC SQL UPDATE numvalues SET value = :id - 1 WHERE name = 'strings_id';
545 }
546
547
548 char *sqlbuffer[QMAXARGS];
549
550 /* Dynamic SQL support routines */
551 SQLDA *mr_alloc_sqlda(void)
552 {
553   SQLDA *it;
554   int j;
555
556   it = sqlald(QMAXARGS, ARGLEN, 0);
557   if (!it)
558     {
559       com_err(whoami, MR_NO_MEM, "setting up SQLDA");
560       exit(1);
561     }
562
563   for (j = 0; j < QMAXARGS; j++)
564     {
565       it->V[j] = sqlbuffer[j] = xmalloc(ARGLEN);
566       it->T[j] = 97; /* 97 = CHARZ = null-terminated string */
567       it->L[j] = ARGLEN;
568     }
569
570   return it;
571 }
572
573
574 /* Convert normal Unix-style wildcards to SQL voodoo */
575 int convert_wildcards(char *arg)
576 {
577   static char buffer[ARGLEN];
578   char *s, *d;
579
580   for (d = buffer, s = arg; *s; s++)
581     {
582       switch (*s)
583         {
584         case '*':
585           *d++ = '%';
586           *d++ = '%';
587           break;
588         case '?':
589           *d++ = '_';
590           break;
591         case '_':
592           *d++ = '*';
593           *d++ = *s;
594           break;
595         case '%':
596           *d++ = '*';
597           *d++ = '%';
598           *d++ = '%';
599           break;
600         default:
601           *d++ = *s;
602           break;
603         }
604     }
605   *d = '\0';
606
607   /* Copy back into argv */
608   strcpy(arg, buffer);
609
610   return MR_EXISTS;
611 }
612
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.
617  */
618 int convert_wildcards_uppercase(char *arg)
619 {
620   static char buffer[ARGLEN];
621   char *s, *d;
622
623   for (d = buffer, s = arg; *s; s++)
624     {
625       switch (*s)
626         {
627         case '*':
628           *d++ = '%';
629           *d++ = '%';
630           break;
631         case '?':
632           *d++ = '_';
633           break;
634         case '_':
635           *d++ = '*';
636           *d++ = *s;
637           break;
638         case '%':
639           *d++ = '*';
640           *d++ = '%';
641           *d++ = '%';
642           break;
643         default:
644           *d++ = toupper(*s);       /* This is the only diff. */
645           break;
646         }
647     }
648   *d = '\0';
649
650   /* Copy back into argv */
651   strcpy(arg, buffer);
652
653   return MR_EXISTS;
654 }
655
656
657 /*  Adds a string to the string table.  Returns the id number.
658  *
659  */
660 int add_string(char *nm)
661 {
662   EXEC SQL BEGIN DECLARE SECTION;
663   char *name = nm;
664   int id;
665   EXEC SQL END DECLARE SECTION;
666
667   EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
668   id++;
669   EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
670
671   EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
672
673   return id;
674 }
This page took 0.094859 seconds and 5 git commands to generate.