]> andersk Git - moira.git/blob - server/qvalidate.pc
fix RCS Id strings
[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   if (log_flags & LOG_VALID)
61     /* tell the logfile what we're doing */
62     com_err(whoami, 0, "validating row: %s", qual);
63
64   /* look for the record */
65   sprintf(stmt_buf, "SELECT COUNT (*) FROM %s WHERE %s",
66           table_name[q->rtable], qual);
67   dosql(sqlbuffer);
68   if (dbms_errno)
69     return mr_errcode;
70
71   rowcount = atoi(sqlbuffer[0]);
72   if (rowcount == 0)
73     return MR_NO_MATCH;
74   if (rowcount > 1)
75     return MR_NOT_UNIQUE;
76   return MR_EXISTS;
77 }
78
79 int validate_fields(struct query *q, char *argv[], struct valobj *vo, int n)
80 {
81   int status;
82
83   while (--n >= 0)
84     {
85       switch (vo->type)
86         {
87         case V_NAME:
88           if (log_flags & LOG_VALID)
89             {
90               com_err(whoami, 0, "validating %s in %s: %s",
91                       vo->namefield, table_name[vo->table], argv[vo->index]);
92             }
93           status = validate_name(argv, vo);
94           break;
95
96         case V_ID:
97           if (log_flags & LOG_VALID)
98             {
99               com_err(whoami, 0, "validating %s in %s: %s",
100                       vo->idfield, table_name[vo->table], argv[vo->index]);
101             }
102           status = validate_id(q, argv, vo);
103           break;
104
105         case V_TYPE:
106           if (log_flags & LOG_VALID)
107             {
108               com_err(whoami, 0, "validating %s type: %s",
109                       table_name[vo->table], argv[vo->index]);
110             }
111           status = validate_type(argv, vo);
112           break;
113
114         case V_TYPEDATA:
115           if (log_flags & LOG_VALID)
116             {
117               com_err(whoami, 0, "validating typed data (%s): %s",
118                       argv[vo->index - 1], argv[vo->index]);
119             }
120           status = validate_typedata(q, argv, vo);
121           break;
122
123         case V_RENAME:
124           if (log_flags & LOG_VALID)
125             {
126               com_err(whoami, 0, "validating rename %s in %s",
127                       argv[vo->index], table_name[vo->table]);
128             }
129           status = validate_rename(argv, vo);
130           break;
131
132         case V_CHAR:
133           if (log_flags & LOG_VALID)
134             com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
135           status = validate_chars(argv, vo);
136           break;
137
138         case V_LEN:
139           if (log_flags & LOG_VALID)
140             com_err(whoami, 0, "validating length: %s", argv[vo->index]);
141           status = validate_len(argv, vo);
142           break;
143
144         case V_SORT:
145           status = MR_EXISTS;
146           break;
147
148         case V_LOCK:
149           status = lock_table(vo);
150           break;
151
152         case V_RLOCK:
153           status = readlock_table(vo);
154           break;
155
156         case V_WILD:
157           status = convert_wildcards(argv[vo->index]);
158           break;
159
160         case V_UPWILD:
161           status = convert_wildcards_uppercase(argv[vo->index]);
162           break;
163
164         }
165
166       if (status != MR_EXISTS)
167         return status;
168       vo++;
169     }
170
171   if (dbms_errno)
172     return mr_errcode;
173   return MR_SUCCESS;
174 }
175
176
177 /* validate_chars: verify that there are no illegal characters in
178  * the string.  Legal characters are printing chars other than
179  * ", *, ?, \, [ and ].
180  */
181 static int illegalchars[] = {
182   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
183   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
184   0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
185   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
186   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
187   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
188   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
189   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
190   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
191   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
192   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
193   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
194   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
195   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
196   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
197   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
198 };
199
200 int validate_chars(char *argv[], struct valobj *vo)
201 {
202   char *s = argv[vo->index];
203   EXEC SQL BEGIN DECLARE SECTION;
204   int len;
205   char *tname, *cname;
206   EXEC SQL END DECLARE SECTION;
207
208   /* check for bad characters */
209   while (*s)
210     {
211       if (illegalchars[(int)*s++])
212         return MR_BAD_CHAR;
213     }
214
215   /* check for length */
216   tname = table_name[vo->table];
217   cname = vo->namefield;
218   EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
219     WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
220
221   if ((strlen(argv[vo->index]) > len) &&
222       strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
223     return MR_ARG_TOO_LONG;
224
225   return MR_EXISTS;
226 }
227
228
229 int validate_id(struct query *q, char *argv[], struct valobj *vo)
230 {
231   EXEC SQL BEGIN DECLARE SECTION;
232   char *name, *namefield, *idfield;
233   int id, rowcount, tbl;
234   EXEC SQL END DECLARE SECTION;
235   int status;
236   char *c;
237
238   name = argv[vo->index];
239   tbl = vo->table;
240   namefield = vo->namefield;
241   idfield = vo->idfield;
242
243   if ((tbl == USERS_TABLE && !strcmp(namefield, "login")) ||
244       tbl == MACHINE_TABLE || tbl == SUBNET_TABLE || tbl == FILESYS_TABLE ||
245       tbl == LIST_TABLE || tbl == CLUSTER_TABLE || tbl == STRINGS_TABLE)
246     {
247       if (tbl == MACHINE_TABLE || tbl == SUBNET_TABLE)
248         {
249           for (c = name; *c; c++)
250             {
251               if (islower(*c))
252                 *c = toupper(*c);
253             }
254         }
255       status = name_to_id(name, tbl, &id);
256       if (status == 0)
257         {
258           *(int *)argv[vo->index] = id;
259           return MR_EXISTS;
260         }
261       else if (status == MR_NO_MATCH && tbl == STRINGS_TABLE &&
262                (q->type == APPEND || q->type == UPDATE))
263         {
264           id = add_string(name);
265           cache_entry(name, STRINGS_TABLE, id);
266           *(int *)argv[vo->index] = id;
267           return MR_EXISTS;
268         }
269       else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
270         return vo->error;
271       else
272         return status;
273     }
274   else
275     {
276       /* else, it's `dubu', which uses unix_uid from users */
277       EXEC SQL SELECT COUNT(*) INTO :rowcount FROM users
278         WHERE unix_uid = :name;
279       if (dbms_errno)
280         return mr_errcode;
281       if (rowcount != 1)
282         return vo->error;
283       EXEC SQL SELECT users_id INTO :id FROM users
284         WHERE unix_uid = :name;
285       *(int *)argv[vo->index] = id;
286       return MR_EXISTS;
287     }
288 }
289
290 int validate_name(char *argv[], struct valobj *vo)
291 {
292   char *name, *namefield;
293   char *c;
294
295   name = argv[vo->index];
296   namefield = vo->namefield;
297   if (vo->table == SERVERS_TABLE && !strcmp(namefield, "name"))
298     {
299       for (c = name; *c; c++)
300         {
301           if (islower(*c))
302             *c = toupper(*c);
303         }
304     }
305   sprintf(stmt_buf, "SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
306           table_name[vo->table], table_name[vo->table], namefield, name);
307   dosql(sqlbuffer);
308
309   if (dbms_errno)
310     return mr_errcode;
311   return (atoi(sqlbuffer[0]) == 1) ? MR_EXISTS : vo->error;
312 }
313
314 int validate_rename(char *argv[], struct valobj *vo)
315 {
316   EXEC SQL BEGIN DECLARE SECTION;
317   char *name, *namefield, *idfield;
318   int id;
319   EXEC SQL END DECLARE SECTION;
320   int status;
321   char *c;
322
323   status = validate_chars(argv, vo);
324   if (status != MR_EXISTS)
325     return status;
326   name = argv[vo->index];
327   /* minor kludge to upcasify machine names */
328   if (vo->table == MACHINE_TABLE)
329     {
330       for (c = name; *c; c++)
331         {
332           if (islower(*c))
333             *c = toupper(*c);
334         }
335     }
336   namefield = vo->namefield;
337   idfield = vo->idfield;
338   id = -1;
339   if (idfield == 0)
340     {
341       if (!strcmp(argv[vo->index], argv[vo->index - 1]))
342         return MR_EXISTS;
343       sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = '%s'",
344               namefield, table_name[vo->table], namefield, name);
345       dosql(sqlbuffer);
346
347       if (dbms_errno)
348         return mr_errcode;
349       if (sqlca.sqlcode == SQL_NO_MATCH)
350         return MR_EXISTS; /* how's _that_ for intuitive? */
351       else
352         return vo->error;
353     }
354   status = name_to_id(name, vo->table, &id);
355   if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
356     return MR_EXISTS;
357   else
358     return vo->error;
359 }
360
361
362 int validate_type(char *argv[], struct valobj *vo)
363 {
364   EXEC SQL BEGIN DECLARE SECTION;
365   char *typename;
366   char *val;
367   int cnt;
368   EXEC SQL END DECLARE SECTION;
369   char *c;
370
371   typename = vo->namefield;
372   c = val = argv[vo->index];
373   while (*c)
374     {
375       if (illegalchars[(int)*c++])
376         return MR_BAD_CHAR;
377     }
378
379   /* uppercase type fields */
380   for (c = val; *c; c++)
381     {
382       if (islower(*c))
383         *c = toupper(*c);
384     }
385
386   EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
387     WHERE name = :typename AND type = 'TYPE' AND trans = :val;
388   if (dbms_errno)
389     return mr_errcode;
390   return cnt ? MR_EXISTS : vo->error;
391 }
392
393 /* validate member or type-specific data field */
394
395 int validate_typedata(struct query *q, char *argv[], struct valobj *vo)
396 {
397   EXEC SQL BEGIN DECLARE SECTION;
398   char *name;
399   char *field_type;
400   char data_type[129];
401   int id;
402   EXEC SQL END DECLARE SECTION;
403   int status;
404   char *c;
405
406   /* get named object */
407   name = argv[vo->index];
408
409   /* get field type string (known to be at index-1) */
410   field_type = argv[vo->index - 1];
411
412   /* get corresponding data type associated with field type name */
413   EXEC SQL SELECT trans INTO :data_type FROM alias
414     WHERE name = :field_type AND type = 'TYPEDATA';
415   if (dbms_errno)
416     return mr_errcode;
417   if (sqlca.sqlerrd[2] != 1)
418     return MR_TYPE;
419
420   /* now retrieve the record id corresponding to the named object */
421   if (strchr(data_type, ' '))
422     *strchr(data_type, ' ') = '\0';
423   if (!strcmp(data_type, "user"))
424     {
425       /* USER */
426       if (strchr(name, '@'))
427         return MR_USER;
428       status = name_to_id(name, USERS_TABLE, &id);
429       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
430         return MR_USER;
431       if (status)
432         return status;
433     }
434   else if (!strcmp(data_type, "list"))
435     {
436       /* LIST */
437       status = name_to_id(name, LIST_TABLE, &id);
438       if (status && status == MR_NOT_UNIQUE)
439         return MR_LIST;
440       if (status == MR_NO_MATCH)
441         {
442           /* if idfield is non-zero, then if argv[0] matches the string
443            * that we're trying to resolve, we should get the value of
444            * numvalues.[idfield] for the id.
445            */
446           if (vo->idfield && !strcmp(argv[0], argv[vo->index]))
447             {
448               set_next_object_id(q->validate->object_id, q->rtable, 0);
449               name = vo->idfield;
450               EXEC SQL SELECT value INTO :id FROM numvalues
451                 WHERE name = :name;
452               if (sqlca.sqlerrd[2] != 1)
453                 return MR_LIST;
454             }
455           else
456             return MR_LIST;
457         }
458       else if (status)
459         return status;
460     }
461   else if (!strcmp(data_type, "machine"))
462     {
463       /* MACHINE */
464       for (c = name; *c; c++)
465         {
466           if (islower(*c))
467             *c = toupper(*c);
468         }
469       status = name_to_id(name, MACHINE_TABLE, &id);
470       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
471         return MR_MACHINE;
472       if (status)
473         return status;
474     }
475   else if (!strcmp(data_type, "string"))
476     {
477       /* STRING */
478       status = name_to_id(name, STRINGS_TABLE, &id);
479       if (status && status == MR_NOT_UNIQUE)
480         return MR_STRING;
481       if (status == MR_NO_MATCH)
482         {
483           if (q->type != APPEND && q->type != UPDATE)
484             return MR_STRING;
485           id = add_string(name);
486           cache_entry(name, STRINGS_TABLE, id);
487         }
488       else if (status)
489         return status;
490     }
491   else if (!strcmp(data_type, "none"))
492     id = 0;
493   else
494     return MR_TYPE;
495
496   /* now set value in argv */
497   *(int *)argv[vo->index] = id;
498
499   return MR_EXISTS;
500 }
501
502
503 /* Make sure the data fits in the field */
504 int validate_len(char *argv[], struct valobj *vo)
505 {
506   EXEC SQL BEGIN DECLARE SECTION;
507   int len;
508   char *tname, *cname;
509   EXEC SQL END DECLARE SECTION;
510
511   tname = table_name[vo->table];
512   cname = vo->namefield;
513   EXEC SQL SELECT data_length INTO :len FROM user_tab_columns
514     WHERE table_name = UPPER(:tname) AND column_name = UPPER(:cname);
515
516   if ((strlen(argv[vo->index]) > len) &&
517       strcmp(argv[vo->index], UNIQUE_LOGIN)) /* kludge... sigh */
518     return MR_ARG_TOO_LONG;
519
520   return MR_EXISTS;
521 }
522
523 /* Lock the table named by the validation object */
524
525 int lock_table(struct valobj *vo)
526 {
527 #ifdef DO_LOCKING
528   sprintf(stmt_buf, "LOCK TABLE %s IN EXCLUSIVE MODE", table_name[vo->table]);
529   EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
530   if (dbms_errno)
531     return mr_errcode;
532   else
533 #endif
534     return MR_EXISTS;
535 }
536
537 /*
538  * Get a read lock on the table by accessing the magic lock
539  * record.  Certain tables are constructed so that they contain
540  * an id field whose value is zero and a modtime field.  We
541  * manipulate the modtime field of the id 0 record to effect
542  * locking of the table
543  */
544
545 int readlock_table(struct valobj *vo)
546 {
547 #ifdef DO_LOCKING
548   sprintf(stmt_buf, "LOCK TABLE %s IN SHARE MODE", table_name[vo->table]);
549   EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
550
551   if (dbms_errno)
552     return mr_errcode;
553   if (sqlca.sqlcode)
554     return vo->error;
555 #endif
556   return MR_EXISTS;  /* validate_fields expects us to return
557                       * this value if everything went okay
558                       */
559 }
560
561 /* Check the database at startup time. */
562
563 void sanity_check_database(void)
564 {
565   EXEC SQL BEGIN DECLARE SECTION;
566   int oid, id;
567   EXEC SQL END DECLARE SECTION;
568
569   /* Sometimes a crash can leave strings_id in numvalues in an
570      incorrect state. Check for that and fix it. */
571
572   EXEC SQL SELECT value INTO :oid FROM numvalues WHERE name = 'strings_id';
573
574   for (id = oid + 1; sqlca.sqlcode == 0; id++)
575     {
576       EXEC SQL SELECT string_id INTO :id FROM strings
577         WHERE string_id = :id;
578     }
579
580   if (id != oid + 1)
581     EXEC SQL UPDATE numvalues SET value = :id - 1 WHERE name = 'strings_id';
582 }
583
584
585 char *sqlbuffer[QMAXARGS];
586
587 /* Dynamic SQL support routines */
588 SQLDA *mr_alloc_sqlda(void)
589 {
590   SQLDA *it;
591   int j;
592
593   it = sqlald(QMAXARGS, ARGLEN, 0);
594   if (!it)
595     {
596       com_err(whoami, MR_NO_MEM, "setting up SQLDA");
597       exit(1);
598     }
599
600   for (j = 0; j < QMAXARGS; j++)
601     {
602       it->V[j] = sqlbuffer[j] = malloc(ARGLEN);
603       it->T[j] = 97; /* 97 = CHARZ = null-terminated string */
604       it->L[j] = ARGLEN;
605     }
606
607   return it;
608 }
609
610
611 /* Convert normal Unix-style wildcards to SQL voodoo */
612 int convert_wildcards(char *arg)
613 {
614   static char buffer[ARGLEN];
615   char *s, *d;
616
617   for (d = buffer, s = arg; *s; s++)
618     {
619       switch (*s)
620         {
621         case '*':
622           *d++ = '%';
623           *d++ = '%';
624           break;
625         case '?':
626           *d++ = '_';
627           break;
628         case '_':
629           *d++ = '*';
630           *d++ = *s;
631           break;
632         case '%':
633           *d++ = '*';
634           *d++ = '%';
635           *d++ = '%';
636           break;
637         default:
638           *d++ = *s;
639           break;
640         }
641     }
642   *d = '\0';
643
644   /* Copy back into argv */
645   strcpy(arg, buffer);
646
647   return MR_EXISTS;
648 }
649
650 /* This version includes uppercase conversion, for things like gmac.
651  * This is necessary because "LIKE" doesn't work with "uppercase()".
652  * Including it in a wildcard routine saves making two passes over
653  * the argument string.
654  */
655 int convert_wildcards_uppercase(char *arg)
656 {
657   static char buffer[ARGLEN];
658   char *s, *d;
659
660   for (d = buffer, s = arg; *s; s++)
661     {
662       switch (*s)
663         {
664         case '*':
665           *d++ = '%';
666           *d++ = '%';
667           break;
668         case '?':
669           *d++ = '_';
670           break;
671         case '_':
672           *d++ = '*';
673           *d++ = *s;
674           break;
675         case '%':
676           *d++ = '*';
677           *d++ = '%';
678           *d++ = '%';
679           break;
680         default:
681           *d++ = toupper(*s);       /* This is the only diff. */
682           break;
683         }
684     }
685   *d = '\0';
686
687   /* Copy back into argv */
688   strcpy(arg, buffer);
689
690   return MR_EXISTS;
691 }
692
693
694 /*  Adds a string to the string table.  Returns the id number.
695  *
696  */
697 int add_string(char *nm)
698 {
699   EXEC SQL BEGIN DECLARE SECTION;
700   char *name = nm;
701   int id;
702   EXEC SQL END DECLARE SECTION;
703
704   EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
705   id++;
706   EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
707
708   EXEC SQL INSERT INTO strings (string_id, string) VALUES (:id, :name);
709
710   return id;
711 }
This page took 0.109205 seconds and 5 git commands to generate.