/* eurephiadb_common.c -- Database field mapping between database and the C code * * GPLv2 only - Copyright (C) 2008 - 2012 * David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /** * @file eurephiadb_mapping.c * @author David Sommerseth * @date 2008-12-06 * * @brief Database field mapping between database and the C code/programs * * This feature makes it possible to let each database driver implementation to * use its own table and column/field names in the database, while having a * unified interface for table names and field names in the application. * */ #include #include #include #define EUREPHIADB_MAPPING_C #include #include #include #include #include /** * Simple mapping table for table_id to string. Must match the order of the * defined tables in eurephia_mapping.h */ const char *TABLE_NAME[] = { NULL, "users", // TABLE_USERS "certificates", // TABLE_CERTS "usercerts", // TABLE_USERCERTS "lastlog", // TABLE_LASTLOG "attemptslog", // TABLE_ATTEMPTS "blacklist", // TABLE_BLACKLIST "eurephia_adminaccess", // TABLE_EUREPHIAADMACC "firewall_profiles", // TABLE_FWPROFILES NULL}; /** * Simple mapping table for session status */ const char *SESSION_STATUS[] = { "UNKNOWN", "STARTED", "OPEN", "CLOSING", "CLOSED", "CLEANEDUP", NULL}; // Extra forward declarations to avoid compiler warnings. These functions are inline functions // in eurephia_xml.h, which would then cause multiple declarations of these functions if that // include file gets included here. char *xmlGetAttrValue(xmlAttr *properties, const char *key); char *xmlExtractContent(xmlNode *n); /** * Frees the memory used by a eDBfieldMap structure * * @param p Pointer to the eDBfieldMap to be freed */ void eDBfreeMapping(eDBfieldMap *p) { if( p == NULL ) { return; } eDBfreeMapping(p->next); // Release value and this pointer. // Do not attempt to release field_name, as it is a constant char * free_nullsafe(NULL, p->value); free_nullsafe(NULL, p->table_alias); free(p); } /** * Internal function for mapping the unified eurephia mapping table from a table index. * The table index is defined in eurephiadb_mapping.h, prefixed with TABLE_ * * @param table table index (integer) * * @return Returns a pointer to the corresponding eDBfieldMap if table index is found, otherwise * NULL is returned. * @see eurephiadb_mapping.h */ eDBfieldMap *eDBgetTableFieldMapping(int table) { eDBfieldMap *srcmap, *newmap = NULL, *ptr = NULL; int i; switch( table ) { case TABLE_USERS: srcmap = eTblMap_user; break; case TABLE_CERTS: srcmap = eTblMap_certificates; break; case TABLE_LASTLOG: srcmap = eTblMap_lastlog; break; case TABLE_ATTEMPTS: srcmap = eTblMap_attempts; break; case TABLE_BLACKLIST: srcmap = eTblMap_blacklist; break; case TABLE_USERCERTS: srcmap = eTblMap_usercerts; break; case TABLE_EUREPHIAADMACC: srcmap = eTblMap_eurephiaadmacc; break; case TABLE_FWPROFILES: srcmap = eTblMap_fwprofiles; break; default: return NULL; } // Copy the mapping into a pointer chain for( i = 0; srcmap[i].field_id != FIELD_NONE; i++ ) { ptr = (eDBfieldMap *) malloc_nullsafe(NULL, sizeof(eDBfieldMap)+2); assert(ptr != NULL); ptr->tableid = srcmap[i].tableid; ptr->table_alias = NULL; ptr->field_id = srcmap[i].field_id; ptr->field_type = srcmap[i].field_type; ptr->filter_type = srcmap[i].filter_type; ptr->field_name = srcmap[i].field_name; ptr->value = NULL; ptr->next = newmap; newmap = ptr; } return newmap; } /** * Internal function for copying over a particular field from one eDBfieldMap to another one * * @param newmap The destination map to be updated * @param dbmap The source map where to copy the data from * @param field The ID of the field which is being copied over */ inline void eDBcopyMapAttribs(eDBfieldMap *newmap, eDBfieldMap *dbmap, int field) { int i = 0; for( i = 0; dbmap[i].field_name != NULL; i++ ) { if( dbmap[i].field_id == field ) { newmap->field_name = dbmap[i].field_name; if( newmap->field_type != ft_SETNULL ) { newmap->field_type = dbmap[i].field_type; } } } } /** * Create a eDBfieldMap to be used by the database driver, with the values from an fieldMapping XML node. * This function will go through all fields in the XML tags, and associate the unified field names and * table name with the database specific field/table names. * * @param ctx eurephiaCTX * @param dbmap The database specific eDBfieldMap containing its own fields and table names * @param tblalias If the SQL needs to use an alias, this alias prefix will be used for the fields * @param fmapnode An xmlNode pointing at the fieldMapping node/tag. * * @return On success, a pointer to a eDBfieldMap specific for the database driver will be returned, * otherwise NULL. */ eDBfieldMap *eDBxmlMapping(eurephiaCTX *ctx, eDBfieldMap *dbmap, const char *tblalias, xmlNode *fmapnode) { eDBfieldMap *map = NULL, *ptr = NULL; char *fmap_table = NULL; xmlNode *nptr = NULL; // Check that the node we got is the right one if( xmlStrcmp(fmapnode->name, (xmlChar *)"fieldMapping") != 0 ) { eurephia_log(ctx, LOG_ERROR, 0, "Invalid fieldMapping XML node"); return NULL; } // Make sure the fmapnode is suitable for the given dbmap assert( TABLE_NAME[dbmap->tableid] != NULL ); fmap_table = xmlGetAttrValue(fmapnode->properties, "table"); if( fmap_table == NULL ) { eurephia_log(ctx, LOG_ERROR, 0, "Invalid fieldMapping XML document"); return NULL; } if( strcmp(fmap_table, TABLE_NAME[dbmap->tableid]) != 0 ) { eurephia_log(ctx, LOG_ERROR, 0, "Mismatch between XML fieldMapping and eDBfieldMap"); return NULL; } // Get a copy of the system map map = eDBgetTableFieldMapping(dbmap->tableid); // Loop through the XML, and register the different values to the system map fields nptr = fmapnode->children; while( nptr != NULL ) { // We are only interested in element nodes if( (nptr->type != XML_ELEMENT_NODE) ) { nptr = nptr->next; continue; } if( nptr->name != NULL ) { int setnull = 0; xmlAttr *atr; char *pwhash = NULL; eDBfieldFilterType filter = flt_NOTSET; // Check attributes given. Looking for: setnull, pwhash, filter for( atr = nptr->properties; atr != NULL; atr = atr->next ) { if( atr->name == NULL ) { // Skip nodes without name (just in case) continue; } if( xmlStrcmp(atr->name, (xmlChar *)"setnull") == 0 ) { xmlNode *n = atr->children; setnull = (((n != NULL) && (n->content != NULL) && (xmlStrcmp(n->content,(xmlChar *)"1") == 0)) ? 1 : 0); } else if( xmlStrcmp(atr->name, (xmlChar *)"pwhash") == 0 ) { pwhash = (atr->children != NULL ? (char *) atr->children->content : NULL); } else if( xmlStrcmp(atr->name, (xmlChar *)"filter") == 0 ) { if( xmlStrcmp(atr->children->content, (xmlChar *)"not-equals") == 0 ) { filter = flt_NEQ; } else if ( xmlStrcmp(atr->children->content, (xmlChar *)"less-than") == 0 ) { filter = flt_LT; } else if ( xmlStrcmp(atr->children->content, (xmlChar *)"less-than-equals") == 0 ) { filter = flt_LTE; } else if ( xmlStrcmp(atr->children->content, (xmlChar *)"greater-than") == 0 ) { filter = flt_GT; } else if ( xmlStrcmp(atr->children->content, (xmlChar *)"greater-than-equals") == 0 ) { filter = flt_GTE; } else { filter = flt_EQ; } } } // Look up field in our copy of the system map for( ptr = map; ptr != NULL; ptr = ptr->next ) { // If we find the field, copy the value into our map if( xmlStrcmp((xmlChar *)ptr->field_name, nptr->name) == 0 ) { if( setnull ) { // If flagged for being set to NULL, change the field type ptr->field_type = ft_SETNULL; } switch( ptr->field_type ) { case ft_SETNULL: // Don't set any values if we want to set it to NULL ptr->value = NULL; break; case ft_PASSWD: // If it is a password field type, hash the value if( (pwhash != NULL) && (strcmp(pwhash, "sha512") == 0) ) { ptr->value = (nptr->children != NULL ? (char *)nptr->children->content : strdup("")); } else { // Force hashing of value if it is an unknown hash type ptr->value = (nptr->children != NULL ? eurephia_pwd_crypt(ctx, (char *)nptr->children->content, NULL) : strdup("")); } break; default: ptr->value = (nptr->children != NULL ? strdup_nullsafe((char *)nptr->children->content) : strdup("")); break; } if( filter != flt_NOTSET ) { ptr->filter_type = filter; } break; } } // Go to next XML node nptr = nptr->next; } else { eurephia_log(ctx, LOG_ERROR, 0, "*** Illegal XML - unaccepted node: (%i) %s\n", nptr->type, nptr->name); return NULL; } } for( ptr = map; ptr != NULL; ptr = ptr->next ) { // Copy over field name - translated via the db mapping table eDBcopyMapAttribs(ptr, dbmap, ptr->field_id); // Set correct table alias in the new map ptr->table_alias = strdup_nullsafe(tblalias); } return map; } /** * Generates a database specific list which is comma separated for the database driver to * use in the ORDER BY section of SQL queries. The unified database fields are being translated * into the database specific field names. * * @param tfmap eDBfieldMap specific to the database driver. * @param skeys_str Comma separated string containing the field names for the ORDER BY clause * * @return Returns a comma separated string with translated field names on success, otherwise NULL. * @remark The resulting pointer is pointing at a static char pointer and should not be freed. */ const char *eDBmkSortKeyString(eDBfieldMap *tfmap, const char *skeys_str) { eDBfieldMap *sk_map = NULL, *ptr1 = NULL, *tfmp = NULL; char *cp = NULL, *tok = NULL, *delims = ","; static char sortkeys[8194]; if( skeys_str == NULL ) { return NULL; } // Make sure we have table field map assert( tfmap != NULL ); // Get the correct table mapping for user input sk_map = eDBgetTableFieldMapping(tfmap->tableid); assert( sk_map != NULL ); // Split up the skeys_str (sort keys string) and build up a map cp = strdup_nullsafe(skeys_str); tok = strtok(cp, delims); memset(&sortkeys, 0, 8194); while( tok != NULL ) { for( ptr1 = sk_map; ptr1 != NULL; ptr1 = ptr1->next) { // If we find the the field in the unified mapping table ... if( strcmp(tok, ptr1->field_name) == 0 ) { // look up the proper field name for the current database for( tfmp = tfmap; tfmp != NULL; tfmp = tfmp->next ) { if( ptr1->field_id == tfmp->field_id ) { if( tfmp->table_alias != NULL ) { append_str(sortkeys, tfmp->table_alias, 8192); append_str(sortkeys, ".", 8192); } append_str(sortkeys, tfmp->field_name, 8192); append_str(sortkeys, ",", 8192); } } } } tok = strtok(NULL, delims); } free_nullsafe(NULL, cp); sortkeys[strlen(sortkeys)-1] = '\0'; eDBfreeMapping(sk_map); return (strlen_nullsafe(sortkeys) > 0 ? sortkeys : NULL); } /** * Tells which database fields in a eDBfieldMap are set and available. * * @param map eDBfieldMap containing the database specific mapping and field values set * * @return Returns a bit-wise OR'ed value of all fields in an eDBfieldMap which have their values * set to something else than NULL. */ long int eDBmappingFieldsPresent(eDBfieldMap *map) { long int ret = 0; eDBfieldMap *p = NULL; // Loops through all elements and flags those // fields with a value set. for( p = map; p != NULL; p = p->next ) { if( p->value != NULL ) { ret |= p->field_id; } } return ret; } /** * Retrieves the value of a field in a eDBfieldMap pointer chain. * * @param map eDBfieldMap with the values * @param field_id The field ID to retrieve the value from * * @return Returns const char * to the value in the eDBfieldMap on success, or NULL if either the * value is not found or if the value is not set. */ const char *eDBmappingGetValue(eDBfieldMap *map, long field_id) { eDBfieldMap *ptr = NULL; if( map == NULL ) { return NULL; } for( ptr = map; ptr != NULL; ptr = ptr->next ) { if( ptr->field_id == field_id ) { return ptr->value; } } return NULL; }