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