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