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