Merge in changes from my cdr-tds-conversion branch. This changes the internal
[asterisk/asterisk.git] / cdr / cdr_sqlite3_custom.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2007, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com> and others.
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Custom SQLite3 CDR records.
22  *
23  * \author Adapted by Alejandro Rios <alejandro.rios@avatar.com.co> and
24  *  Russell Bryant <russell@digium.com> from 
25  *  cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
26  *      and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
27  *      
28  *
29  * \arg See also \ref AstCDR
30  *
31  *
32  * \ingroup cdr_drivers
33  */
34
35 /*** MODULEINFO
36         <depend>sqlite3</depend>
37  ***/
38
39 #include "asterisk.h"
40
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42
43 #include <time.h>
44 #include <sqlite3.h>
45
46 #include "asterisk/paths.h"     /* use ast_config_AST_LOG_DIR */
47 #include "asterisk/channel.h"
48 #include "asterisk/cdr.h"
49 #include "asterisk/module.h"
50 #include "asterisk/config.h"
51 #include "asterisk/pbx.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/cli.h"
54
55 AST_MUTEX_DEFINE_STATIC(lock);
56
57 static const char config_file[] = "cdr_sqlite3_custom.conf";
58
59 static char *desc = "Customizable SQLite3 CDR Backend";
60 static char *name = "cdr_sqlite3_custom";
61 static sqlite3 *db = NULL;
62
63 static char table[80];
64 static char *columns;
65
66 struct values {
67         char *expression;
68         AST_LIST_ENTRY(values) list;
69 };
70
71 static AST_LIST_HEAD_STATIC(sql_values, values);
72
73 static int free_config(void);
74
75 static int load_column_config(const char *tmp)
76 {
77         char *col = NULL;
78         char *cols = NULL;
79         char *escaped = NULL;
80         struct ast_str *column_string = NULL;
81
82         if (ast_strlen_zero(tmp)) {
83                 ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
84                 return -1;
85         }
86         if (!(column_string = ast_str_create(1024))) {
87                 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
88                 return -1;
89         }
90         if (!(cols = ast_strdup(tmp))) {
91                 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
92                 ast_free(column_string);
93                 return -1;
94         }
95         while ((col = strsep(&cols, ","))) {
96                 col = ast_strip(col);
97                 escaped = sqlite3_mprintf("%q", col);
98                 if (!escaped) {
99                         ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
100                         ast_free(column_string);
101                         ast_free(cols);
102                         return -1;
103                 }
104                 if (!column_string->used)
105                         ast_str_set(&column_string, 0, "%s", escaped);
106                 else
107                         ast_str_append(&column_string, 0, ",%s", escaped);
108                 sqlite3_free(escaped);
109         }
110         if (!(columns = ast_strdup(column_string->str))) {
111                 ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
112                 ast_free(column_string);
113                 ast_free(cols);
114                 return -1;
115         }
116         ast_free(column_string);
117         ast_free(cols);
118
119         return 0;
120 }
121
122 static int load_values_config(const char *tmp)
123 {
124         char *val = NULL;
125         char *vals = NULL;
126         struct values *value = NULL;
127
128         if (ast_strlen_zero(tmp)) {
129                 ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
130                 return -1;
131         }
132         if (!(vals = ast_strdup(tmp))) {
133                 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
134                 return -1;
135         }
136         while ((val = strsep(&vals, ","))) {
137                 /* Strip the single quotes off if they are there */
138                 val = ast_strip_quoted(val, "'", "'");
139                 value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1);
140                 if (!value) {
141                         ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val);
142                         ast_free(vals);
143                         return -1;
144                 }
145                 value->expression = (char *) value + sizeof(*value);
146                 ast_copy_string(value->expression, val, strlen(val) + 1);
147                 AST_LIST_INSERT_TAIL(&sql_values, value, list);
148         }
149         ast_free(vals);
150
151         return 0;
152 }
153
154 static int load_config(int reload)
155 {
156         struct ast_config *cfg;
157         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
158         struct ast_variable *mappingvar;
159         const char *tmp;
160
161         if (!(cfg = ast_config_load(config_file, config_flags))) {
162                 if (reload)
163                         ast_log(LOG_WARNING, "Failed to reload configuration file.\n");
164                 else
165                         ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
166                 return -1;
167         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
168                 return 0;
169
170         if (reload)
171                 free_config();
172
173         ast_mutex_lock(&lock);
174
175         if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
176                 /* Nothing configured */
177                 ast_mutex_unlock(&lock);
178                 ast_config_destroy(cfg);
179                 return 0;
180         }
181         
182         /* Mapping must have a table name */
183         tmp = ast_variable_retrieve(cfg, "master", "table");
184         if (!ast_strlen_zero(tmp))
185                 ast_copy_string(table, tmp, sizeof(table));
186         else {
187                 ast_log(LOG_WARNING, "Table name not specified.  Assuming cdr.\n");
188                 strcpy(table, "cdr");
189         }
190
191         /* Columns */
192         tmp = ast_variable_retrieve(cfg, "master", "columns");
193         if (load_column_config(tmp)) {
194                 ast_mutex_unlock(&lock);
195                 ast_config_destroy(cfg);
196                 free_config();
197                 return -1;
198         }
199
200         /* Values */
201         tmp = ast_variable_retrieve(cfg, "master", "values");
202         if (load_values_config(tmp)) {
203                 ast_mutex_unlock(&lock);
204                 ast_config_destroy(cfg);
205                 free_config();
206                 return -1;
207         }
208
209         ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
210
211         ast_mutex_unlock(&lock);
212         ast_config_destroy(cfg);
213
214         return 0;
215 }
216
217 static int free_config(void)
218 {
219         struct values *value;
220
221         ast_mutex_lock(&lock);
222
223         if (db) {
224                 sqlite3_close(db);
225                 db = NULL;
226         }
227
228         if (columns) {
229                 ast_free(columns);
230                 columns = NULL;
231         }
232
233         while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list)))
234                 ast_free(value);
235
236         ast_mutex_unlock(&lock);
237
238         return 0;
239 }
240
241 static int sqlite3_log(struct ast_cdr *cdr)
242 {
243         int res = 0;
244         char *error = NULL;
245         char *sql = NULL;
246         struct ast_channel dummy = { 0, };
247         int count = 0;
248
249         { /* Make it obvious that only sql should be used outside of this block */
250                 char *escaped;
251                 char subst_buf[2048];
252                 struct values *value;
253                 struct ast_str *value_string = ast_str_create(1024);
254                 dummy.cdr = cdr;
255                 AST_LIST_TRAVERSE(&sql_values, value, list) {
256                         memset(subst_buf, 0, sizeof(subst_buf));
257                         pbx_substitute_variables_helper(&dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
258                         escaped = sqlite3_mprintf("%q", subst_buf);
259                         if (!value_string->used)
260                                 ast_str_append(&value_string, 0, "'%s'", escaped);
261                         else
262                                 ast_str_append(&value_string, 0, ",'%s'", escaped);
263                         sqlite3_free(escaped);
264                 }
265                 sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, value_string->str);
266                 ast_debug(1, "About to log: %s\n", sql);
267                 ast_free(value_string);
268         }
269
270         ast_mutex_lock(&lock);
271
272         /* XXX This seems awful arbitrary... */
273         for (count = 0; count < 5; count++) {
274                 res = sqlite3_exec(db, sql, NULL, NULL, &error);
275                 if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
276                         break;
277                 usleep(200);
278         }
279
280         if (error) {
281                 ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
282                 sqlite3_free(error);
283         }
284
285         if (sql)
286                 sqlite3_free(sql);
287
288         ast_mutex_unlock(&lock);
289
290         return res;
291 }
292
293 static int unload_module(void)
294 {
295         free_config();
296
297         ast_cdr_unregister(name);
298
299         return 0;
300 }
301
302 static int load_module(void)
303 {
304         char *error;
305         char filename[PATH_MAX];
306         int res;
307         char *sql;
308
309         if (!load_config(0)) {
310                 res = ast_cdr_register(name, desc, sqlite3_log);
311                 if (res) {
312                         ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
313                         free_config();
314                         return AST_MODULE_LOAD_DECLINE;
315                 }
316         } else
317                 return AST_MODULE_LOAD_DECLINE;
318
319         /* is the database there? */
320         snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
321         res = sqlite3_open(filename, &db);
322         if (res != SQLITE_OK) {
323                 ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
324                 free_config();
325                 return AST_MODULE_LOAD_DECLINE;
326         }
327
328         /* is the table there? */
329         sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
330         res = sqlite3_exec(db, sql, NULL, NULL, NULL);
331         sqlite3_free(sql);
332         if (res != SQLITE_OK) {
333                 /* We don't use %q for the column list here since we already escaped when building it */
334                 sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
335                 res = sqlite3_exec(db, sql, NULL, NULL, &error);
336                 sqlite3_free(sql);
337                 if (res != SQLITE_OK) {
338                         ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
339                         sqlite3_free(error);
340                         free_config();
341                         return AST_MODULE_LOAD_DECLINE;
342                 }
343         }
344
345         return 0;
346 }
347
348 static int reload(void)
349 {
350         return load_config(1);
351 }
352
353 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CDR Module",
354         .load = load_module,
355         .unload = unload_module,
356         .reload = reload,
357 );