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