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