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