diff --git a/epan/Makefile.am b/epan/Makefile.am index 8103543c75..f2b35d3ae0 100644 --- a/epan/Makefile.am +++ b/epan/Makefile.am @@ -116,6 +116,9 @@ radius_dict.c: radius_dict.l load_snmp_users_file.c: load_snmp_users_file.l $(LEX) -oload_snmp_users_file.c $(srcdir)/load_snmp_users_file.l + +uat_load.c: uat_load.l + $(LEX) -ouat_load.c $(srcdir)/uat_load.l dtd_parse.c : dtd_parse.l $(LEX) -odtd_parse.c $(srcdir)/dtd_parse.l diff --git a/epan/Makefile.common b/epan/Makefile.common index 3f1e6dd094..666bd0f364 100644 --- a/epan/Makefile.common +++ b/epan/Makefile.common @@ -88,6 +88,8 @@ LIBWIRESHARK_SRC = \ to_str.c \ tvbparse.c \ tvbuff.c \ + uat.c \ + uat_load.c \ unicode-utils.c \ value_string.c \ xdlc.c \ @@ -185,6 +187,8 @@ LIBWIRESHARK_INCLUDES = \ to_str.h \ tvbparse.h \ tvbuff.h \ + uat.h \ + uat-int.h \ unicode-utils.h \ value_string.h \ x264_prt_id.h \ diff --git a/epan/Makefile.nmake b/epan/Makefile.nmake index fcf9622516..f8c771e0d7 100644 --- a/epan/Makefile.nmake +++ b/epan/Makefile.nmake @@ -210,6 +210,9 @@ dtd_preparse.c : dtd_preparse.l load_snmp_users_file.c : load_snmp_users_file.l $(LEX) -oload_snmp_users_file.c load_snmp_users_file.l +uat_load.c : uat_load.l + $(LEX) -ouat_load.c uat_load.l + dtd_grammar.h: dtd_grammar.c LEMON=..\tools\lemon diff --git a/epan/uat-int.h b/epan/uat-int.h new file mode 100644 index 0000000000..e3eb29ce47 --- /dev/null +++ b/epan/uat-int.h @@ -0,0 +1,67 @@ +/* + * uat-int.h + * + * User Accessible Tables + * Mantain an array of user accessible data strucures + * Internal interface + * + * (c) 2007, Luis E. Garcia Ontanon + * + */ +#ifndef _UAT_INT_H_ +#define _UAT_INT_H_ + +#include "uat.h" + +typedef struct _uat_fld_rep_t uat_fld_rep_t; +typedef struct _uat_rep_t uat_rep_t; + +typedef void (*uat_rep_fld_free_cb_t)(uat_fld_rep_t*); +typedef void (*uat_rep_free_cb_t)(uat_rep_t*); + +typedef struct _uat_fld_t { + char* name; + uat_text_mode_t mode; + uat_fld_chk_cb_t chk_cb; + uat_fld_set_cb_t set_cb; + uat_fld_tostr_cb_t tostr_cb; + + guint colnum; + uat_fld_rep_t* rep; + uat_rep_fld_free_cb_t free_rep; + + struct _uat_fld_t* next; +} uat_fld_t; + +struct _uat_t { + char* name; + size_t record_size; + char* filename; + void** user_ptr; + guint* nrows_p; + uat_copy_cb_t copy_cb; + uat_update_cb_t update_cb; + uat_free_cb_t free_cb; + + uat_fld_t* fields; + guint ncols; + GArray* user_data; + gboolean finalized; + gboolean locked; + + uat_rep_t* rep; + uat_rep_free_cb_t free_rep; +}; + +gchar* uat_get_actual_filename(uat_t* uat, gboolean for_writing); +void uat_init(void); +void uat_reset(void); +void* uat_add_record(uat_t*, const void* orig_rec_ptr); +void uat_remove_record_idx(uat_t*, guint rec_idx); +void uat_destroy(uat_t*); +gboolean uat_save(uat_t* dt, char** error); +gboolean uat_load(uat_t* dt, char** error); + +#define UAT_UPDATE(uat) do { *((uat)->user_ptr) = (void*)((uat)->user_data->data); *((uat)->nrows_p) = (uat)->user_data->len; } while(0) + +#endif diff --git a/epan/uat.c b/epan/uat.c new file mode 100644 index 0000000000..41c911b90e --- /dev/null +++ b/epan/uat.c @@ -0,0 +1,285 @@ +/* + * uat.c + * + * User Accessible Tables + * Mantain an array of user accessible data strucures + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "uat-int.h" + +static GPtrArray* all_uats = NULL; + +void uat_init(void) { + all_uats = g_ptr_array_new(); +} + +uat_t* uat_start(const char* name, + size_t size, + char* filename, + void** data_ptr, + guint* num_items_ptr, + uat_copy_cb_t copy_cb, + uat_update_cb_t update_cb, + uat_free_cb_t free_cb) { + + uat_t* uat = g_malloc(sizeof(uat_t)); + g_ptr_array_add(all_uats,uat); + + g_assert(name && size && filename && data_ptr && num_items_ptr); + + uat->name = g_strdup(name); + uat->record_size = size; + uat->filename = g_strdup(filename); + uat->user_ptr = data_ptr; + uat->nrows_p = num_items_ptr; + uat->copy_cb = copy_cb; + uat->update_cb = update_cb; + uat->free_cb = free_cb; + + uat->fields = NULL; + uat->ncols = 0; + uat->user_data = g_array_new(FALSE,FALSE,uat->record_size); + uat->finalized = FALSE; + uat->rep = NULL; + uat->free_rep = NULL; + + return uat; +} + +void uat_add_field(uat_t* uat, + const char* name, + uat_text_mode_t mode, + uat_fld_chk_cb_t chk_cb, + uat_fld_set_cb_t set_cb, + uat_fld_tostr_cb_t tostr_cb) { + + uat_fld_t* f = g_malloc(sizeof(uat_fld_t)); + + g_assert( name && set_cb && tostr_cb && (! uat->finalized ) + && (mode == PT_TXTMOD_STRING || mode == PT_TXTMOD_HEXBYTES) ); + + f->name = g_strdup(name); + f->mode = mode; + f->chk_cb = chk_cb; + f->set_cb = set_cb; + f->tostr_cb = tostr_cb; + + f->rep = NULL; + f->free_rep = NULL; + f->colnum = uat->ncols; + f->next = NULL; + + uat->ncols++; + + if (uat->fields) { + uat_fld_t* c; + for (c = uat->fields; c->next; c = c->next) ; + c->next = f; + } else { + uat->fields = f; + } +} + +void uat_finalize(uat_t* uat) { + UAT_UPDATE(uat); + uat->finalized = TRUE; +} + +uat_t* uat_new(const char* uat_name, + size_t size, + char* filename, + void** data_ptr, + guint* numitems_ptr, + uat_copy_cb_t copy_cb, + uat_update_cb_t update_cb, + uat_free_cb_t free_cb, + ...) { + uat_t* uat = uat_start(uat_name, size, filename, data_ptr, numitems_ptr, copy_cb, update_cb, free_cb); + va_list ap; + char* name; + uat_text_mode_t mode; + uat_fld_chk_cb_t chk_cb; + uat_fld_set_cb_t set_cb; + uat_fld_tostr_cb_t tostr_cb; + va_start(ap,free_cb); + + name = va_arg(ap,char*); + + do { + mode = va_arg(ap,uat_text_mode_t); + chk_cb = va_arg(ap,uat_fld_chk_cb_t); + set_cb = va_arg(ap,uat_fld_set_cb_t); + tostr_cb = va_arg(ap,uat_fld_tostr_cb_t); + + uat_add_field(uat, name, mode, chk_cb, set_cb, tostr_cb); + + name = va_arg(ap,char*); + } while (name); + + va_end(ap); + + uat_finalize(uat); + + return uat; +} + +void* uat_add_record(uat_t* uat, const void* data) { + void* rec; + + g_assert( uat->finalized ); + + g_array_append_vals (uat->user_data, data, 1); + + rec = uat->user_data->data + (uat->record_size * (uat->user_data->len-1)); + + if (uat->copy_cb) { + uat->copy_cb(rec, data, uat->record_size); + } + + + UAT_UPDATE(uat); + + return rec; +} + +void uat_remove_record_idx(uat_t* uat, guint idx) { + + g_assert( uat->finalized && idx < uat->user_data->len); + + g_array_remove_index(uat->user_data, idx); + + UAT_UPDATE(uat); + +} + + +gchar* uat_get_actual_filename(uat_t* uat, gboolean for_writing) { + gchar* pers_fname = get_persconffile_path(uat->filename,for_writing); + + if (! file_exists(pers_fname)) { + gchar* data_fname = get_datafile_path(uat->filename); + + if (file_exists(data_fname)) { + return data_fname; + } + } + + return pers_fname; +} + +static void putfld(FILE* fp, void* rec, uat_fld_t* f) { + guint fld_len; + char* fld_ptr; + + f->tostr_cb(rec,&fld_ptr,&fld_len); + + switch(f->mode){ + case PT_TXTMOD_STRING: { + guint i; + + putc('"',fp); + + for(i=0;iuser_data->len - 1 ; i++ ) { + void* rec = uat->user_data->data + (uat->record_size * (uat->user_data->len-1)); + uat_fld_t* f; + + f = uat->fields; + + putfld(fp, rec, f); + + while (( f = f->next )) { + fputs(",",fp); + putfld(fp, rec, f); + } + + fputs("\n",fp); + } + + fclose(fp); + + return TRUE; +} + +void uat_destroy(uat_t* uat) { + g_ptr_array_remove(all_uats,uat); + +} + +void* uat_dup(uat_t* uat, guint* len_p) { + guint size = (uat->record_size * uat->user_data->len); + *len_p = size; + return size ? g_memdup(uat->user_data->data,size) : NULL ; +} + +void* uat_se_dup(uat_t* uat, guint* len_p) { + guint size = (uat->record_size * uat->user_data->len); + *len_p = size; + return size ? se_memdup(uat->user_data->data,size) : NULL ; +} + +void uat_cleanup(void) { + + while( all_uats->len ) { + uat_destroy((uat_t*)all_uats->pdata); + } + + g_ptr_array_free(all_uats,TRUE); +} + diff --git a/epan/uat.h b/epan/uat.h new file mode 100644 index 0000000000..54308940c9 --- /dev/null +++ b/epan/uat.h @@ -0,0 +1,247 @@ +/* + * uat.h + * + * User Accessible Tables + * Mantain an array of user accessible data strucures + * + * (c) 2007, Luis E. Garcia Ontanon + * + */ + +#ifndef _UAT_H_ +#define _UAT_H_ + +/* + * uat mantains a dynamically allocated table accessible to the user + * via a file and/or gui tables. + * + * the file is located either in userdir(when first read or when writen) or + * in datadir for defaults (read only , it will be always written to userdir). + * + * the behaviour of the table is controlled by a series of callbacks + * the caller must provide. + * + * BEWARE that the user can change an uat at (almost) any time, + * That is pointers to records in an uat are valid only during the call + * to the function that obtains them (do not store them). + * + * UATs are meant for short tables of user data (passwords and such) there's + * no quick access, you must iterate through them each time to fetch the record + * you are looking for. Use uat_dup() or uat_se_dup() if necessary. + * + * Only users via gui or editing the file can add/remove records your code cannot. + */ + +/* obscure data type to handle an uat */ +typedef struct _uat_t uat_t; + +/******************************************** + * Callbacks: + * these instruct uat on how to deal with user info and data in records + ********************************************/ + +/******** + * Callbacks for the entire table (these deal with entire records) + ********/ + +/* + * Copy CB + * used to copy a record + * optional, memcpy will be used if not given + * copy(dest,orig,len) + */ +typedef void* (*uat_copy_cb_t)(void*, const void*, unsigned); + +/* + * + * Free CB + * + * destroy a record's child data + * (do not free the container, it will be handled by uat) + * it is optional, no child data will be freed if no present + * free(record) + */ +typedef void (*uat_free_cb_t)(void*); + +/* + * Update CB + * + * to be called after all record fields has been updated + * optional, record will be updated always if not given + * update(record,&error) + */ +typedef void (*uat_update_cb_t)(void* , char** ); + + +/******* + * Callbacks for single fields (these deal with single values) + * the caller should provide one of these for every field! + ********/ + +/* + * given an input string (ptr, len) checks if the value is OK for a field in the record. + * it will return TRUE if OK or else + * it will return FALSE and may set *error to inform the user on what's + * wrong with the given input + * optional, if not given any input is considered OK and the set cb will be called + * chk(record, ptr, len, &error) + */ +typedef gboolean (*uat_fld_chk_cb_t)(void*, const char*, unsigned, char**); + +/* + * Set Field CB + * + * given an input string (ptr, len) sets the value of a field in the record, + * it will return TRUE if OK or else + * it will return FALSE and may set *error to inform the user on what's + * wrong with the given input + * it is mandatory + * set(record, ptr, len) + */ +typedef void (*uat_fld_set_cb_t)(void*, const char*, unsigned); + +/* + * given a record returns a string representation of the field + * mandatory + * tostr(record, &ptr, &len) + */ +typedef void (*uat_fld_tostr_cb_t)(void*, char**, unsigned*); + + + + +/*********** + * Text Mode + * + * used for file and dialog representation of fileds in columns, + * when the file is read it modifies the way the value is passed back to the fld_set_cb + * (see definition bellow for description) + ***********/ + +typedef enum _uat_text_mode_t { + PT_TXTMOD_NONE, + /* not used */ + + PT_TXTMOD_STRING, + /* + file: + reads: + ,"\x20\x00\x30", as " \00",3 + ,"", as "",0 + ,, as NULL,0 + writes: + ,"\x20\x30\x00\x20", for " 0\0 ",4 + ,"", for *, 0 + ,, for NULL, * + dialog: + accepts \x?? and other escapes + gets "",0 on empty string + */ + PT_TXTMOD_HEXBYTES, + /* + file: + reads: + ,A1b2C3d4, as "\001\002\003\004",4 + ,, as NULL,0 + writes: + ,, on NULL, * + ,a1b2c3d4, on "\001\002\003\004",4 + dialog: + "a1b2c3d4" as "\001\002\003\004",4 + "a1 b2:c3d4" as "\001\002\003\004",4 + "" as NULL,0 + "invalid" as NULL,3 + "a1b" as NULL, 1 + */ +} uat_text_mode_t; + + +/* + * uat_new() + * + * creates a new uat + * + * name: the name of the table + * + * data_ptr: a pointer to a null terminated array of pointers to the data + * + * default_data: a pinter to a struct containing default values + * + * size: the size of the structure + * + * filename: the filename to be used (either in userdir or datadir) + * + * copy_cb: a function that copies the data in the struct + * + * update_cb: will be called when a record is updated + * + * free_cb: will be called to destroy a struct in the dataset + * + * + * followed by a list of N quintuplets terminated by a NULL, each quituplet has: + * + * field_name: a string with the name of the field ([a-zA-Z0-9_-]+) + * + * field_mode: see comments for enum _uat_text_mode_t below + * + * field_chk_cb: a function that given a string will check the given value + * + * field_set_cb: a function that given a string will set the value in the data structure + * + * field_tostr_cb: a function that given a record generates a string,len pair representing this file + * + */ +uat_t* uat_new(const char* name, + size_t size, + char* filename, + void** data_ptr, + guint* num_items, + uat_copy_cb_t copy_cb, + uat_update_cb_t update_cb, + uat_free_cb_t free_cb, + ...); + + +/* + * uat_start() + * as uat_new() but leaves the dyntable without fields + */ +uat_t* uat_start(const char* name, + size_t size, + char* filename, + void** data_ptr, + guint* num_items, + uat_copy_cb_t copy_cb, + uat_update_cb_t update_cb, + uat_free_cb_t free_cb); + +/* + * uat_add_field() + * adds a field to a uat created with uat_start(), + * see uat_new() for description of arguments + */ +void uat_add_field(uat_t*, + const char* name, + uat_text_mode_t mode, + uat_fld_chk_cb_t chk_cb, + uat_fld_set_cb_t set_cb, + uat_fld_tostr_cb_t tostr_cb); + +/* + * uat_finalize() + * once fields have been added it makes the uat usable, leaves it locked. + */ +void uat_finalize(uat_t*); + +/* + * uat_dup() + * uat_se_dup() + * make a reliable copy of an uat for internal use, + * so that pointers to records can be kept through calls. + * return NULL on zero len. + */ +void* uat_dup(uat_t*, guint* len_p); /* to be freed */ +void* uat_se_dup(uat_t*, guint* len_p); + +#endif + diff --git a/epan/uat_load.l b/epan/uat_load.l new file mode 100644 index 0000000000..1bd2474e80 --- /dev/null +++ b/epan/uat_load.l @@ -0,0 +1,284 @@ +%option noyywrap +%option nounput +%option prefix="uat_load_" +%option never-interactive + +%{ + /* + * one parser to fit them all + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include "uat-int.h" + + static uat_t* uat; + static uat_fld_t* uat_fld; + static gchar* ptr; + static guint len; + static gchar* error; + static void* record; + + static char* unbinstring(const char* si, guint in_len, guint* len_p); + static char* undquote(const char* si, guint in_len, guint* len_p); + +#define ERROR(txt) do { error = txt; yyterminate(); } while(0) + +#define SET_FIELD() \ + { gchar* err; \ + if (uat_fld->chk_cb) { \ + if ( ! uat_fld->chk_cb(record, ptr, len, &err) ) { \ + ERROR(err); \ + }\ + }\ + uat_fld->set_cb(record, ptr, len);\ + g_free(ptr);\ + } while(0) + + +%} + +quoted_string \042([^\042]|\134\042)*\042 +binstring ([0-9a-zA-Z][0-9a-zA-Z])+ +separator , +newline [ \t]*[\r]?\n + +%x START_OF_LINE NEXT_FIELD SEPARATOR END_OF_RECORD +%% + +{quoted_string} { + ptr = undquote(yytext,yyleng,&len); + + if (( uat_fld = uat_fld->next )) { + BEGIN SEPARATOR; + } else { + BEGIN END_OF_RECORD; + } +} + +{binstring} { + ptr = unbinstring(yytext,yyleng,&len); + + if (!ptr) { + ERROR("uneven hexstring"); + } + + if (( uat_fld = uat_fld->next )) { + BEGIN SEPARATOR; + } else { + BEGIN END_OF_RECORD; + } +} + +{separator} { + SET_FIELD(); + uat_fld = uat_fld->next; + if (! uat_fld ) { + ERROR("more fields than required"); + } +} + +{newline} { + void* rec; + gchar* err = NULL; + + SET_FIELD(); + + rec = uat_add_record(uat, record); + + if (uat->update_cb) + uat->update_cb(rec,&err); + + if (err) { + ERROR(err); + } + + uat_fld = uat->fields; + ptr = NULL; + len = 0; + memset(record,0,uat->record_size); + + BEGIN START_OF_LINE; + } + +{newline} { ERROR("incomplete record"); } +. { ERROR("unexpected input"); } +%% + +static int xton(char d) { + switch(d) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: return -1; + } +} + +static char* unbinstring(const char* si, guint in_len, guint* len_p) { + char* buf; + guint len = in_len/2; + int i = 0; + + if (in_len%2) { + return NULL; + } + + buf= g_malloc(len); /* wastes one byte for every '\\' in text */ + *len_p = len; + + while(in_len) { + int d1 = xton(*(si++)); + int d0 = xton(*(si++)); + + buf[i++] = (d1 * 16) + d0; + + in_len -= 2; + } + + return buf; +} + +static char* undquote(const char* si, guint in_len, guint* len_p) { + char* buf = g_malloc(in_len); /* wastes one byte for every '\\' in text */ + char* p = buf; + guint len = 0; + char* end = buf+in_len; + const guint8* s = (void*)si; + + for (s++; p < end; s++) { + switch(*s) { + case '\0': + *(p-1) = '\0'; + goto done; + case '\\': + switch(*(++s)) { + case 'a': *(p++) = '\a'; len++; break; + case 'b': *(p++) = '\b'; len++; break; + case 'e': *(p++) = '\e'; len++; break; + case 'f': *(p++) = '\f'; len++; break; + case 'n': *(p++) = '\n'; len++; break; + case 'r': *(p++) = '\r'; len++; break; + case 't': *(p++) = '\t'; len++; break; + case 'v': *(p++) = '\v'; len++; break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int c0 = 0; + int c1 = 0; + int c2 = 0; + int c = 0; + + c0 = (*s) - '0'; + + if ( s[1] >= '0' && s[1] <= '7' ) { + c1 = c0; + c0 = (*++s) - '0'; + + if ( s[1] >= '0' && s[1] <= '7' ) { + c2 = c1; + c1 = c0; + c0 = (*++s) - '0'; + } + } + c = (64 * c2) + (8 * c1) + c0; + *(p++) = (char) (c > 255 ? 255 : c); + len++; + break; + } + + case 'x': + { + char c1 = *(s+1); + char c0 = *(s+2); + + if (isxdigit(c1) && isxdigit(c0)) { + *(p++) = (xton(c1) * 0x10) + xton(c0); + s += 2; + } else { + *(p++) = *s; + } + len++; + break; + } + default: + *p++ = *s; + break; + } + break; + default: + *(p++) = *s; + len++; + break; + } + } + +done: + + while ( p < end ) *(p++) = '\0'; + buf[len] = '\0'; + len--; + if (len_p) *len_p = len; + return buf; +} + +gboolean uat_load(uat_t* dt_in, char** err) { + gchar* fname = uat_get_actual_filename(uat, FALSE); + + g_assert(uat->finalized && uat->locked); + + uat = dt_in; + + ; + if (!(yyin = fopen(fname,"r"))) { + *err = strerror(errno); + return FALSE; + } + + error = NULL; + uat_fld = uat->fields; + record = g_malloc0(uat->record_size); + + BEGIN START_OF_LINE; + + yylex(); + + if (error) { + UAT_UPDATE(uat); + *err = ep_strdup(error); + return FALSE; + } else { + UAT_UPDATE(uat); + *err = NULL; + return TRUE; + } +}