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