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