summaryrefslogtreecommitdiffstats
path: root/database/eurephiadb_mapping.c
blob: e875a9e3d081b014d62c5d8d39290a219622b790 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
/* eurephiadb_common.c  --  Database field mapping between database and the C code
 *
 *  GPLv2 only - Copyright (C) 2008 - 2012
 *               David Sommerseth <dazo@users.sourceforge.net>
 *
 *  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 <dazo@users.sourceforge.net>
 * @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 <string.h>
#include <assert.h>

#include <libxml/tree.h>

#define EUREPHIADB_MAPPING_C
#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephiadb_mapping.h>
#include <eurephia_nullsafe.h>
#include <passwd.h>

/**
 * 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
        "plugins",               // TABLE_PLUGINS
        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;

        case TABLE_PLUGINS:
                srcmap = eTblMap_plugins;
                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;
}


/**
 * Looks up a given fieldMap entry to a given field
 *
 * @param map       eDBfieldMap with the values
 * @param field_id  The field ID to retrieve the value from
 *
 * @returns the pointer to the fieldMap entry on success, otherwise NULL
 */
static eDBfieldMap *_eDBmappingGetFieldMapEntry(eDBfieldMap *map, long 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;
                }
        }
        return NULL;
}

/**
 * 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 long field_id) {
        eDBfieldMap *ptr = _eDBmappingGetFieldMapEntry(map, field_id);
        return (ptr != NULL ? ptr->value : NULL);
}


/**
 * Sets the value of a field in a eDBfieldMap pointer chain.
 *
 * @param map       eDBfieldMap with the values
 * @param field_id  The field ID to modify
 * @param value     String pointer to the value to use
 *
 * @return Returns 1 on success, otherwise 0
 */
int eDBmappingSetValue(eDBfieldMap *map, long long field_id, char *value) {
        eDBfieldMap *ptr = _eDBmappingGetFieldMapEntry(map, field_id);
        if( ptr != NULL ) {
                ptr->value = strdup_nullsafe(value);
                return 1;
        }
        return 0;
}