Constify a character array;
[metze/wireshark/wip.git] / epan / uat.c
1 /*
2  *  uat.c
3  *
4  * $Id$
5  *
6  *  User Accessible Tables
7  *  Mantain an array of user accessible data strucures
8  *
9  * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 2001 Gerald Combs
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28  */
29 #include "config.h"
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37
38 #include <glib.h>
39
40 #include <wsutil/file_util.h>
41 #include <wsutil/str_util.h>
42 #include <wsutil/report_err.h>
43
44 #include <epan/emem.h>
45 #include <wsutil/filesystem.h>
46 #include <epan/packet.h>
47 #include <epan/range.h>
48
49 #include "uat-int.h"
50
51 static GPtrArray* all_uats = NULL;
52
53 void uat_init(void) {
54     all_uats = g_ptr_array_new();
55 }
56
57 uat_t* uat_new(const char* name,
58                size_t size,
59                const char* filename,
60                gboolean from_profile,
61                void** data_ptr,
62                guint* numitems_ptr,
63                guint flags,
64                const char* help,
65                uat_copy_cb_t copy_cb,
66                uat_update_cb_t update_cb,
67                uat_free_cb_t free_cb,
68                uat_post_update_cb_t post_update_cb,
69                uat_field_t* flds_array) {
70     /* Create new uat */
71     uat_t* uat = (uat_t *)g_malloc(sizeof(uat_t));
72     guint i;
73
74     /* Add to global array of uats */
75     if (!all_uats)
76         all_uats = g_ptr_array_new();
77
78     g_ptr_array_add(all_uats,uat);
79
80     /* Check params */
81     g_assert(name && size && filename && data_ptr && numitems_ptr);
82
83     /* Set uat values from inputs */
84     uat->name = g_strdup(name);
85     uat->record_size = size;
86     uat->filename = g_strdup(filename);
87     uat->from_profile = from_profile;
88     uat->user_ptr = data_ptr;
89     uat->nrows_p = numitems_ptr;
90     uat->copy_cb = copy_cb;
91     uat->update_cb = update_cb;
92     uat->free_cb = free_cb;
93     uat->post_update_cb = post_update_cb;
94     uat->fields = flds_array;
95     uat->user_data = g_array_new(FALSE,FALSE,(guint)uat->record_size);
96     uat->raw_data = g_array_new(FALSE,FALSE,(guint)uat->record_size);
97     uat->valid_data = g_array_new(FALSE,FALSE,sizeof(gboolean));
98     uat->changed = FALSE;
99     uat->loaded = FALSE;
100     uat->from_global = FALSE;
101     uat->rep = NULL;
102     uat->free_rep = NULL;
103     uat->help = help;
104     uat->flags = flags;
105
106     for (i=0;flds_array[i].title;i++) {
107         fld_data_t* f = (fld_data_t *)g_malloc(sizeof(fld_data_t));
108
109         f->colnum = i+1;
110         f->rep = NULL;
111         f->free_rep = NULL;
112
113         flds_array[i].priv = f;
114     }
115
116     uat->ncols = i;
117
118     *data_ptr = NULL;
119     *numitems_ptr = 0;
120
121     return uat;
122 }
123
124 void* uat_add_record(uat_t* uat, const void* data, gboolean valid_rec) {
125     void* rec;
126     gboolean* valid;
127
128     /* Save a copy of the raw (possibly that may contain invalid field values) data */
129     g_array_append_vals (uat->raw_data, data, 1);
130
131     rec = uat->raw_data->data + (uat->record_size * (uat->raw_data->len-1));
132
133     if (uat->copy_cb) {
134         uat->copy_cb(rec, data, (unsigned int) uat->record_size);
135     }
136
137     if (valid_rec) {
138         /* Add a "known good" record to the list to be used by the dissector */
139         g_array_append_vals (uat->user_data, data, 1);
140
141         rec = uat->user_data->data + (uat->record_size * (uat->user_data->len-1));
142
143         if (uat->copy_cb) {
144             uat->copy_cb(rec, data, (unsigned int) uat->record_size);
145         }
146
147         UAT_UPDATE(uat);
148     } else {
149         rec = NULL;
150     }
151
152     g_array_append_vals (uat->valid_data, &valid_rec, 1);
153     valid = (gboolean*)(uat->valid_data->data + (sizeof(gboolean) * (uat->valid_data->len-1)));
154     *valid = valid_rec;
155
156     return rec;
157 }
158
159 void uat_swap(uat_t* uat, guint a, guint b) {
160     size_t s = uat->record_size;
161     void* tmp = ep_alloc(s);
162     gboolean tmp_bool;
163
164     g_assert( a < uat->raw_data->len && b < uat->raw_data->len );
165
166     if (a == b) return;
167
168     memcpy(tmp, UAT_INDEX_PTR(uat,a), s);
169     memcpy(UAT_INDEX_PTR(uat,a), UAT_INDEX_PTR(uat,b), s);
170     memcpy(UAT_INDEX_PTR(uat,b), tmp, s);
171
172     tmp_bool = *(gboolean*)(uat->valid_data->data + (sizeof(gboolean) * (a)));
173     *(gboolean*)(uat->valid_data->data + (sizeof(gboolean) * (a))) = *(gboolean*)(uat->valid_data->data + (sizeof(gboolean) * (b)));
174     *(gboolean*)(uat->valid_data->data + (sizeof(gboolean) * (b))) = tmp_bool;
175
176
177 }
178
179 void uat_remove_record_idx(uat_t* uat, guint idx) {
180
181     g_assert( idx < uat->raw_data->len );
182
183     if (uat->free_cb) {
184         uat->free_cb(UAT_INDEX_PTR(uat,idx));
185     }
186
187     g_array_remove_index(uat->raw_data, idx);
188     g_array_remove_index(uat->valid_data, idx);
189 }
190
191 /* The returned filename was g_malloc()'d so the caller must free it */
192 gchar* uat_get_actual_filename(uat_t* uat, gboolean for_writing) {
193     gchar *pers_fname = NULL;
194
195     if (! uat->from_global) {
196         pers_fname =  get_persconffile_path(uat->filename, uat->from_profile);
197     }
198
199     if ((! for_writing ) && (! file_exists(pers_fname) )) {
200         gchar* data_fname = get_datafile_path(uat->filename);
201
202         if (file_exists(data_fname)) {
203             g_free(pers_fname);
204             return data_fname;
205         }
206
207         g_free(data_fname);
208         g_free(pers_fname);
209         return NULL;
210     }
211
212     return pers_fname;
213 }
214
215 uat_t* uat_get_table_by_name(const char* name) {
216     guint i;
217
218     for (i=0; i < all_uats->len; i++) {
219         uat_t* u = (uat_t *)g_ptr_array_index(all_uats,i);
220         if ( g_str_equal(u->name,name) ) {
221             return (u);
222         }
223     }
224
225     return NULL;
226 }
227
228 static void putfld(FILE* fp, void* rec, uat_field_t* f) {
229     guint fld_len;
230     const char* fld_ptr;
231
232     f->cb.tostr(rec,&fld_ptr,&fld_len,f->cbdata.tostr,f->fld_data);
233
234     switch(f->mode){
235         case PT_TXTMOD_ENUM:
236         case PT_TXTMOD_FILENAME:
237         case PT_TXTMOD_DIRECTORYNAME:
238         case PT_TXTMOD_STRING: {
239             guint i;
240
241             putc('"',fp);
242
243             for(i=0;i<fld_len;i++) {
244                 char c = fld_ptr[i];
245
246                 if (c == '"' || c == '\\' || ! isprint((guchar)c) ) {
247                     fprintf(fp,"\\x%.2x",c);
248                 } else {
249                     putc(c,fp);
250                 }
251             }
252
253             putc('"',fp);
254             return;
255         }
256         case PT_TXTMOD_HEXBYTES: {
257             guint i;
258
259             for(i=0;i<fld_len;i++) {
260                 fprintf(fp,"%.2x",((guint8*)fld_ptr)[i]);
261             }
262
263             return;
264         }
265         default:
266             g_assert_not_reached();
267     }
268 }
269
270 gboolean uat_save(uat_t* uat, const char** error) {
271     guint i;
272     gchar* fname = uat_get_actual_filename(uat,TRUE);
273     FILE* fp;
274
275     if (! fname ) return FALSE;
276
277     fp = ws_fopen(fname,"w");
278
279     if (!fp && errno == ENOENT) {
280         /* Parent directory does not exist, try creating first */
281         gchar *pf_dir_path = NULL;
282         if (create_persconffile_dir(&pf_dir_path) != 0) {
283             *error = ep_strdup_printf("uat_save: error creating '%s'", pf_dir_path);
284             g_free (pf_dir_path);
285             return FALSE;
286         }
287         fp = ws_fopen(fname,"w");
288     }
289
290     if (!fp) {
291         *error = ep_strdup_printf("uat_save: error opening '%s': %s",fname,g_strerror(errno));
292         return FALSE;
293     }
294
295     *error = NULL;
296     g_free (fname);
297
298     /* Ensure raw_data is synced with user_data and all "good" entries have been accounted for */
299
300     /* Start by clearing current user_data */
301     for ( i = 0 ; i < uat->user_data->len ; i++ ) {
302         if (uat->free_cb) {
303             uat->free_cb(UAT_USER_INDEX_PTR(uat,i));
304         }
305     }
306     g_array_set_size(uat->user_data,0);
307
308     *((uat)->user_ptr) = NULL;
309     *((uat)->nrows_p) = 0;
310
311     /* Now copy "good" raw_data entries to user_data */
312     for ( i = 0 ; i < uat->raw_data->len ; i++ ) {
313         void* rec = uat->raw_data->data + (uat->record_size * i);
314         gboolean* valid = (gboolean*)(uat->valid_data->data + sizeof(gboolean)*i);
315         if (*valid) {
316             g_array_append_vals(uat->user_data, rec, 1);
317             if (uat->copy_cb) {
318                 uat->copy_cb(UAT_USER_INDEX_PTR(uat,i), rec, (unsigned int) uat->record_size);
319             }
320
321             UAT_UPDATE(uat);
322         }
323     }
324
325
326     fprintf(fp,"# This file is automatically generated, DO NOT MODIFY.\n");
327
328     for ( i = 0 ; i < uat->user_data->len ; i++ ) {
329         void* rec = uat->user_data->data + (uat->record_size * i);
330         uat_field_t* f;
331         guint j;
332
333         f = uat->fields;
334
335
336         for( j=0 ; j < uat->ncols ; j++ ) {
337             putfld(fp, rec, &(f[j]));
338             fputs((j == uat->ncols - 1) ? "\n" : "," ,fp);
339         }
340
341     }
342
343     fclose(fp);
344
345     uat->changed = FALSE;
346
347     return TRUE;
348 }
349
350 void uat_destroy(uat_t* uat) {
351     /* XXX still missing a destructor */
352     g_ptr_array_remove(all_uats,uat);
353
354 }
355
356 uat_t *uat_find(gchar *name) {
357     guint i;
358
359     for (i=0; i < all_uats->len; i++) {
360         uat_t* u = (uat_t *)g_ptr_array_index(all_uats,i);
361
362         if (strcmp(u->name, name) == 0 || strcmp(u->filename, name) == 0) {
363             return u;
364         }
365     }
366     return NULL;
367 }
368
369 void uat_clear(uat_t* uat) {
370     guint i;
371
372     for ( i = 0 ; i < uat->user_data->len ; i++ ) {
373         if (uat->free_cb) {
374             uat->free_cb(UAT_USER_INDEX_PTR(uat,i));
375         }
376     }
377
378     for ( i = 0 ; i < uat->raw_data->len ; i++ ) {
379         if (uat->free_cb) {
380             uat->free_cb(UAT_INDEX_PTR(uat,i));
381         }
382     }
383
384     g_array_set_size(uat->raw_data,0);
385     g_array_set_size(uat->user_data,0);
386     g_array_set_size(uat->valid_data,0);
387
388     *((uat)->user_ptr) = NULL;
389     *((uat)->nrows_p) = 0;
390 }
391
392 void* uat_dup(uat_t* uat, guint* len_p) {
393     guint size = (guint) (uat->record_size * uat->user_data->len);
394     *len_p = size;
395     return size ? g_memdup(uat->user_data->data,size) : NULL ;
396 }
397
398 void* uat_se_dup(uat_t* uat, guint* len_p) {
399     guint size = (guint) (uat->record_size * uat->user_data->len);
400     *len_p = (guint) size;
401     return size ? se_memdup(uat->user_data->data,size) : NULL ;
402 }
403
404 void uat_unload_all(void) {
405     guint i;
406
407     for (i=0; i < all_uats->len; i++) {
408         uat_t* u = (uat_t *)g_ptr_array_index(all_uats,i);
409         /* Do not unload if not in profile */
410         if (u->from_profile) {
411             uat_clear(u);
412             u->loaded = FALSE;
413         }
414     }
415 }
416
417 void uat_cleanup(void) {
418     while( all_uats->len ) {
419         uat_destroy((uat_t*)all_uats->pdata);
420     }
421
422     g_ptr_array_free(all_uats,TRUE);
423 }
424
425
426 void uat_foreach_table(uat_cb_t cb,void* user_data) {
427     guint i;
428
429     for (i=0; i < all_uats->len; i++)
430         cb(g_ptr_array_index(all_uats,i), user_data);
431
432 }
433
434
435 void uat_load_all(void) {
436     guint i;
437     const gchar* err;
438
439     for (i=0; i < all_uats->len; i++) {
440         uat_t* u = (uat_t *)g_ptr_array_index(all_uats,i);
441         err = NULL;
442
443         if (!u->loaded)
444             uat_load(u, &err);
445
446         if (err) {
447             report_failure("Error loading table '%s': %s",u->name,err);
448         }
449     }
450 }
451
452
453 gboolean uat_fld_chk_str(void* u1 _U_, const char* strptr, guint len _U_, const void* u2 _U_, const void* u3 _U_, const char** err) {
454     if (strptr == NULL) {
455         *err = "NULL pointer";
456         return FALSE;
457     }
458
459     *err = NULL;
460     return TRUE;
461 }
462
463 gboolean uat_fld_chk_oid(void* u1 _U_, const char* strptr, guint len, const void* u2 _U_, const void* u3 _U_, const char** err) {
464   unsigned int i;
465     *err = NULL;
466
467     if (strptr == NULL) {
468       *err = "NULL pointer";
469       return FALSE;
470     }
471
472     for(i = 0; i < len; i++)
473       if(!(isdigit(strptr[i]) || strptr[i] == '.')) {
474         *err = "Only digits [0-9] and \".\" allowed in an OID";
475         break;
476       }
477
478     if(strptr[len-1] == '.')
479       *err = "OIDs must not be terminated with a \".\"";
480
481     if(!((*strptr == '0' || *strptr == '1' || *strptr =='2') && (len > 1 && strptr[1] == '.')))
482       *err = "OIDs must start with \"0.\" (ITU-T assigned), \"1.\" (ISO assigned) or \"2.\" (joint ISO/ITU-T assigned)";
483
484     /* should also check that the second arc is in the range 0-39 */
485
486     return *err == NULL;
487 }
488
489 gboolean uat_fld_chk_proto(void* u1 _U_, const char* strptr, guint len, const void* u2 _U_, const void* u3 _U_, const char** err) {
490     if (len) {
491         char* name = ep_strndup(strptr,len);
492         ascii_strdown_inplace(name);
493         g_strchug(name);
494
495         if (find_dissector(name)) {
496             *err = NULL;
497             return TRUE;
498         } else {
499             *err = "dissector not found";
500             return FALSE;
501         }
502     } else {
503         *err = NULL;
504         return TRUE;
505     }
506 }
507
508 gboolean uat_fld_chk_num_dec(void* u1 _U_, const char* strptr, guint len, const void* u2 _U_, const void* u3 _U_, const char** err) {
509     if (len > 0) {
510         char* str = ep_strndup(strptr,len);
511         long i = strtol(str,&str,10);
512
513         if ( ( i == 0) && (errno == ERANGE || errno == EINVAL) ) {
514             *err = g_strerror(errno);
515             return FALSE;
516         }
517     }
518
519     *err = NULL;
520     return TRUE;
521 }
522
523 gboolean uat_fld_chk_num_hex(void* u1 _U_, const char* strptr, guint len, const void* u2 _U_, const void* u3 _U_, const char** err) {
524     if (len > 0) {
525         char* str = ep_strndup(strptr,len);
526         long i = strtol(str,&str,16);
527
528         if ( ( i == 0) && (errno == ERANGE || errno == EINVAL) ) {
529             *err = g_strerror(errno);
530             return FALSE;
531         }
532     }
533
534     *err = NULL;
535     return TRUE;
536 }
537
538 gboolean uat_fld_chk_enum(void* u1 _U_, const char* strptr, guint len, const void* v, const void* u3 _U_, const char** err) {
539     char* str = ep_strndup(strptr,len);
540     guint i;
541     const value_string* vs = (const value_string *)v;
542
543     for(i=0;vs[i].strptr;i++) {
544         if (g_str_equal(vs[i].strptr,str)) {
545             *err = NULL;
546             return TRUE;
547         }
548     }
549
550     *err = ep_strdup_printf("invalid value: %s",str);
551     return FALSE;
552 }
553
554 gboolean uat_fld_chk_range(void* u1 _U_, const char* strptr, guint len, const void* v _U_, const void* u3, const char** err) {
555     char* str = ep_strndup(strptr,len);
556     range_t* r = NULL;
557     convert_ret_t ret = range_convert_str(&r, str,GPOINTER_TO_UINT(u3));
558
559     switch (  ret ) {
560         case CVT_NO_ERROR:
561             *err = NULL;
562             return TRUE;
563         case CVT_SYNTAX_ERROR:
564             *err = ep_strdup_printf("syntax error in range: %s",str);
565             return FALSE;
566         case CVT_NUMBER_TOO_BIG:
567             *err = ep_strdup_printf("value too large in range: '%s' (max = %u)",str,GPOINTER_TO_UINT(u3));
568             return FALSE;
569         default:
570             *err = "This should not happen, it is a bug in wireshark! please report to wireshark-dev@wireshark.org";
571             return FALSE;
572     }
573 }
574
575 static int xton(char d) {
576     switch(d) {
577         case '0': return 0;
578         case '1': return 1;
579         case '2': return 2;
580         case '3': return 3;
581         case '4': return 4;
582         case '5': return 5;
583         case '6': return 6;
584         case '7': return 7;
585         case '8': return 8;
586         case '9': return 9;
587         case 'a':  case 'A': return 10;
588         case 'b':  case 'B': return 11;
589         case 'c':  case 'C': return 12;
590         case 'd':  case 'D': return 13;
591         case 'e':  case 'E': return 14;
592         case 'f':  case 'F': return 15;
593         default: return -1;
594     }
595 }
596
597 char* uat_unbinstring(const char* si, guint in_len, guint* len_p) {
598     guint8* buf;
599     guint len = in_len/2;
600     int i = 0;
601     int d0, d1;
602
603     if (in_len%2) {
604         return NULL;
605     }
606
607     buf= (guint8 *)g_malloc0(len+1);
608     if (len_p) *len_p = len;
609
610     while(in_len) {
611         d1 = xton(*(si++));
612         d0 = xton(*(si++));
613
614         buf[i++] = (d1 * 16) + d0;
615
616         in_len -= 2;
617     }
618
619     return (char*)buf;
620 }
621
622 char* uat_unesc(const char* si, guint in_len, guint* len_p) {
623     char* buf = (char *)g_malloc0(in_len+1);
624     char* p = buf;
625     guint len = 0;
626     const char* s;
627     const char* in_end = si+in_len;
628
629     for (s = (const char *)si; s < in_end; s++) {
630         switch(*s) {
631             case '\\':
632                 switch(*(++s)) {
633                     case 'a': *(p++) = '\a'; len++; break;
634                     case 'b': *(p++) = '\b'; len++; break;
635                     case 'e': *(p++) = '\033' /* '\e' is non ANSI-C */; len++; break;
636                     case 'f': *(p++) = '\f'; len++; break;
637                     case 'n': *(p++) = '\n'; len++; break;
638                     case 'r': *(p++) = '\r'; len++; break;
639                     case 't': *(p++) = '\t'; len++; break;
640                     case 'v': *(p++) = '\v'; len++; break;
641
642                     case '0':
643                     case '1':
644                     case '2':
645                     case '3':
646                     case '4':
647                     case '5':
648                     case '6':
649                     case '7':
650                     {
651                         int c0 = 0;
652                         int c1 = 0;
653                         int c2 = 0;
654                         int c = 0;
655
656                         c0 = (*s) - '0';
657
658                         if ( s[1] >= '0' && s[1] <= '7' ) {
659                             c1 = c0;
660                             c0 = (*++s) - '0';
661
662                             if ( s[1] >= '0' && s[1] <= '7' ) {
663                                 c2 = c1;
664                                 c1 = c0;
665                                 c0 = (*++s) - '0';
666                             }
667                         }
668                         c = (64 * c2) + (8 * c1) + c0;
669                         *(p++) = (char) (c > 255 ? 255 : c);
670                         len++;
671                         break;
672                     }
673
674                     case 'x':
675                     {
676                         char c1 = *(s+1);
677                         char c0 = *(s+2);
678
679                         if (isxdigit((guchar)c1) && isxdigit((guchar)c0)) {
680                             *(p++) = (xton(c1) * 0x10) + xton(c0);
681                             s += 2;
682                         } else {
683                             *(p++) = *s;
684                         }
685                         len++;
686                         break;
687                     }
688                     default:
689                         *p++ = *s;
690                         break;
691                 }
692                 break;
693             default:
694                 *(p++) = *s;
695                 len++;
696                 break;
697         }
698     }
699
700     if (len_p) *len_p = len;
701     return buf;
702 }
703
704 char* uat_undquote(const char* si, guint in_len, guint* len_p) {
705     return uat_unesc(si+1,in_len-2,len_p);
706 }
707
708
709 char* uat_esc(const char* buf, guint len) {
710     const guint8* end = ((guint8*)buf)+len;
711     char* out = (char *)ep_alloc0((4*len)+1);
712     const guint8* b;
713     char* s = out;
714
715     for (b = (guint8 *)buf; b < end; b++) {
716         if (isprint(*b) ) {
717             *(s++) = (*b);
718         } else {
719             g_snprintf(s,5,"\\x%.2x",((guint)*b));
720             s+=4;
721         }
722     }
723
724     return out;
725
726 }
727
728 CHK_STR_IS_DEF(isprint)
729 CHK_STR_IS_DEF(isalpha)
730 CHK_STR_IS_DEF(isalnum)
731 CHK_STR_IS_DEF(isdigit)
732 CHK_STR_IS_DEF(isxdigit)
733
734 /*
735  * Editor modelines
736  *
737  * Local Variables:
738  * c-basic-offset: 4
739  * tab-width: 8
740  * indent-tabs-mode: nil
741  * End:
742  *
743  * ex: set shiftwidth=4 tabstop=8 expandtab:
744  * :indentSize=4:tabSize=8:noTabs=true:
745  */