00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00038
00039 #include "asterisk/paths.h"
00040 #include "asterisk/config.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/cdr.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/lock.h"
00046
00047 #define CSV_LOG_DIR "/cdr-csv"
00048 #define CSV_MASTER "/Master.csv"
00049
00050 #define DATE_FORMAT "%Y-%m-%d %T"
00051
00052 static int usegmtime = 0;
00053 static int accountlogs;
00054 static int loguniqueid = 0;
00055 static int loguserfield = 0;
00056 static int loaded = 0;
00057 static const char config[] = "cdr.conf";
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 static char *name = "csv";
00091
00092 AST_MUTEX_DEFINE_STATIC(mf_lock);
00093 AST_MUTEX_DEFINE_STATIC(acf_lock);
00094
00095 static int load_config(int reload)
00096 {
00097 struct ast_config *cfg;
00098 struct ast_variable *var;
00099 const char *tmp;
00100 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00101
00102 if (!(cfg = ast_config_load(config, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
00103 ast_log(LOG_WARNING, "unable to load config: %s\n", config);
00104 return 0;
00105 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00106 return 1;
00107
00108 accountlogs = 1;
00109 usegmtime = 0;
00110 loguniqueid = 0;
00111 loguserfield = 0;
00112
00113 if (!(var = ast_variable_browse(cfg, "csv"))) {
00114 ast_config_destroy(cfg);
00115 return 0;
00116 }
00117
00118 if ((tmp = ast_variable_retrieve(cfg, "csv", "usegmtime"))) {
00119 usegmtime = ast_true(tmp);
00120 if (usegmtime)
00121 ast_debug(1, "logging time in GMT\n");
00122 }
00123
00124
00125 if ((tmp = ast_variable_retrieve(cfg, "csv", "accountlogs"))) {
00126 accountlogs = ast_true(tmp);
00127 if (accountlogs) {
00128 ast_debug(1, "logging in separate files per accountcode\n");
00129 }
00130 }
00131
00132 if ((tmp = ast_variable_retrieve(cfg, "csv", "loguniqueid"))) {
00133 loguniqueid = ast_true(tmp);
00134 if (loguniqueid)
00135 ast_debug(1, "logging CDR field UNIQUEID\n");
00136 }
00137
00138 if ((tmp = ast_variable_retrieve(cfg, "csv", "loguserfield"))) {
00139 loguserfield = ast_true(tmp);
00140 if (loguserfield)
00141 ast_debug(1, "logging CDR user-defined field\n");
00142 }
00143
00144 ast_config_destroy(cfg);
00145 return 1;
00146 }
00147
00148 static int append_string(char *buf, const char *s, size_t bufsize)
00149 {
00150 int pos = strlen(buf), spos = 0, error = -1;
00151
00152 if (pos >= bufsize - 4)
00153 return -1;
00154
00155 buf[pos++] = '\"';
00156
00157 while(pos < bufsize - 3) {
00158 if (!s[spos]) {
00159 error = 0;
00160 break;
00161 }
00162 if (s[spos] == '\"')
00163 buf[pos++] = '\"';
00164 buf[pos++] = s[spos];
00165 spos++;
00166 }
00167
00168 buf[pos++] = '\"';
00169 buf[pos++] = ',';
00170 buf[pos++] = '\0';
00171
00172 return error;
00173 }
00174
00175 static int append_int(char *buf, int s, size_t bufsize)
00176 {
00177 char tmp[32];
00178 int pos = strlen(buf);
00179
00180 snprintf(tmp, sizeof(tmp), "%d", s);
00181
00182 if (pos + strlen(tmp) > bufsize - 3)
00183 return -1;
00184
00185 strncat(buf, tmp, bufsize - strlen(buf) - 1);
00186 pos = strlen(buf);
00187 buf[pos++] = ',';
00188 buf[pos++] = '\0';
00189
00190 return 0;
00191 }
00192
00193 static int append_date(char *buf, struct timeval when, size_t bufsize)
00194 {
00195 char tmp[80] = "";
00196 struct ast_tm tm;
00197
00198 if (strlen(buf) > bufsize - 3)
00199 return -1;
00200
00201 if (ast_tvzero(when)) {
00202 strncat(buf, ",", bufsize - strlen(buf) - 1);
00203 return 0;
00204 }
00205
00206 ast_localtime(&when, &tm, usegmtime ? "GMT" : NULL);
00207 ast_strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);
00208
00209 return append_string(buf, tmp, bufsize);
00210 }
00211
00212 static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
00213 {
00214
00215 buf[0] = '\0';
00216
00217 append_string(buf, cdr->accountcode, bufsize);
00218
00219 append_string(buf, cdr->src, bufsize);
00220
00221 append_string(buf, cdr->dst, bufsize);
00222
00223 append_string(buf, cdr->dcontext, bufsize);
00224
00225 append_string(buf, cdr->clid, bufsize);
00226
00227 append_string(buf, cdr->channel, bufsize);
00228
00229 append_string(buf, cdr->dstchannel, bufsize);
00230
00231 append_string(buf, cdr->lastapp, bufsize);
00232
00233 append_string(buf, cdr->lastdata, bufsize);
00234
00235 append_date(buf, cdr->start, bufsize);
00236
00237 append_date(buf, cdr->answer, bufsize);
00238
00239 append_date(buf, cdr->end, bufsize);
00240
00241 append_int(buf, cdr->duration, bufsize);
00242
00243 append_int(buf, cdr->billsec, bufsize);
00244
00245 append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
00246
00247 append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
00248
00249 if (loguniqueid)
00250 append_string(buf, cdr->uniqueid, bufsize);
00251
00252 if(loguserfield)
00253 append_string(buf, cdr->userfield,bufsize);
00254
00255 if (strlen(buf) < bufsize - 5) {
00256
00257 buf[strlen(buf) - 1] = '\0';
00258 strncat(buf, "\n", bufsize - strlen(buf) - 1);
00259 return 0;
00260 }
00261 return -1;
00262 }
00263
00264 static int writefile(char *s, char *acc)
00265 {
00266 char tmp[PATH_MAX];
00267 FILE *f;
00268
00269 if (strchr(acc, '/') || (acc[0] == '.')) {
00270 ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
00271 return -1;
00272 }
00273
00274 snprintf(tmp, sizeof(tmp), "%s/%s/%s.csv", ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);
00275
00276 ast_mutex_lock(&acf_lock);
00277 if (!(f = fopen(tmp, "a"))) {
00278 ast_mutex_unlock(&acf_lock);
00279 ast_log(LOG_ERROR, "Unable to open file %s : %s\n", tmp, strerror(errno));
00280 return -1;
00281 }
00282 fputs(s, f);
00283 fflush(f);
00284 fclose(f);
00285 ast_mutex_unlock(&acf_lock);
00286
00287 return 0;
00288 }
00289
00290
00291 static int csv_log(struct ast_cdr *cdr)
00292 {
00293 FILE *mf = NULL;
00294
00295 char buf[1024];
00296 char csvmaster[PATH_MAX];
00297 snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
00298 #if 0
00299 printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
00300 #endif
00301 if (build_csv_record(buf, sizeof(buf), cdr)) {
00302 ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
00303 return 0;
00304 }
00305
00306
00307
00308
00309 ast_mutex_lock(&mf_lock);
00310 if ((mf = fopen(csvmaster, "a"))) {
00311 fputs(buf, mf);
00312 fflush(mf);
00313 fclose(mf);
00314 mf = NULL;
00315 ast_mutex_unlock(&mf_lock);
00316 } else {
00317 ast_mutex_unlock(&mf_lock);
00318 ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno));
00319 }
00320
00321 if (accountlogs && !ast_strlen_zero(cdr->accountcode)) {
00322 if (writefile(buf, cdr->accountcode))
00323 ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
00324 }
00325
00326 return 0;
00327 }
00328
00329 static int unload_module(void)
00330 {
00331 ast_cdr_unregister(name);
00332 loaded = 0;
00333 return 0;
00334 }
00335
00336 static int load_module(void)
00337 {
00338 int res;
00339
00340 if(!load_config(0))
00341 return AST_MODULE_LOAD_DECLINE;
00342
00343 if ((res = ast_cdr_register(name, ast_module_info->description, csv_log))) {
00344 ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
00345 } else {
00346 loaded = 1;
00347 }
00348 return res;
00349 }
00350
00351 static int reload(void)
00352 {
00353 if (load_config(1)) {
00354 loaded = 1;
00355 } else {
00356 loaded = 0;
00357 ast_log(LOG_WARNING, "No [csv] section in cdr.conf. Unregistering backend.\n");
00358 ast_cdr_unregister(name);
00359 }
00360
00361 return 0;
00362 }
00363
00364 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Comma Separated Values CDR Backend",
00365 .load = load_module,
00366 .unload = unload_module,
00367 .reload = reload,
00368 .load_pri = AST_MODPRI_CDR_DRIVER,
00369 );