static void format_date(char *buffer, size_t length, struct timeval *when) { struct ast_tm tm; ast_localtime(when, &tm, NULL); ast_strftime(buffer, length, DATE_FORMAT, &tm); }
struct ast_json *ast_json_timeval(const struct timeval tv, const char *zone) { char buf[AST_ISO8601_LEN]; struct ast_tm tm = {}; ast_localtime(&tv, &tm, zone); ast_strftime(buf, sizeof(buf),AST_ISO8601_FORMAT, &tm); return ast_json_string_create(buf); }
/* * Write the metadata to the log file */ static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan) { int res = 0; struct timeval t; struct ast_tm now; char *cl; char *cn; char workstring[80]; char timestamp[80]; /* Extract the caller ID location */ ast_copy_string(workstring, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring)); ast_shrink_phone_number(workstring); if (ast_strlen_zero(workstring)) { cl = "<unknown>"; } else { cl = workstring; } cn = S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"); /* Get the current time */ t = ast_tvnow(); ast_localtime(&t, &now, NULL); /* Format the time */ ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); res = fprintf(logfile, "\n\n[metadata]\n\n"); if (res >= 0) { res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type); } if (res >= 0) { res = fprintf(logfile, "CALLINGFROM=%s\n", cl); } if (res >= 0) { res = fprintf(logfile, "CALLERNAME=%s\n", cn); } if (res >= 0) { res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp); } if (res >= 0) { res = fprintf(logfile, "[events]\n\n"); } if (res < 0) { ast_verb(3, "AlarmReceiver: can't write metadata\n"); ast_debug(1,"AlarmReceiver: can't write metadata\n"); } else { res = 0; } return res; }
static void dump_datetime(char *output, int maxlen, void *value, int len) { struct ast_tm tm; unsigned long val = (unsigned long) ntohl(get_unaligned_uint32(value)); if (len == (int)sizeof(unsigned int)) { tm.tm_sec = (val & 0x1f) << 1; tm.tm_min = (val >> 5) & 0x3f; tm.tm_hour = (val >> 11) & 0x1f; tm.tm_mday = (val >> 16) & 0x1f; tm.tm_mon = ((val >> 21) & 0x0f) - 1; tm.tm_year = ((val >> 25) & 0x7f) + 100; ast_strftime(output, maxlen, "%Y-%m-%d %T", &tm); } else
static void custom_log(struct ast_event *event) { CURL *curl; CURLcode res; struct ast_tm timeresult; struct curl_slist *headers = NULL; char start_time[80] = ""; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; if (ast_cel_fill_record(event, &record)) { return; } ast_localtime(&record.event_time, &timeresult, NULL); ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult); curl = curl_easy_init(); if (curl) { headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.0.5:9200/asterisk/cel/"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); char *post_fields = malloc(sizeof(char) * 6048); sprintf(post_fields, "{\"EventName\": \"%s\", \"AccountCode\": \"%s\", \ \"CallerIDnum\": \"%s\", \"CallerIDname\": \"%s\", \ \"CallerIDani\": \"%s\", \"CallerIDrdnis\": \"%s\", \ \"CAllerIDdnid\": \"%s\", \"Exten\": \"%s\", \ \"Context\": \"%s\", \"Channel\": \"%s\", \ \"Application\": \"%s\", \"AppData\": \"%s\", \ \"EventTime\": \"%s\", \"AMAFlags\": \"%s\", \ \"UniqueID\": \"%s\", \"LinkedID\": \"%s\", \ \"Userfield\": \"%s\", \"Peer\": \"%s\", \ \"Peeraccount\": \"%s\", \"Extra\": \"%s\" }", record.event_name, record.account_code, record.caller_id_num, record.caller_id_name, record.caller_id_ani, record.caller_id_rdnis, record.caller_id_dnid, record.extension, record.context, record.channel_name, record.application_name, record.application_data, start_time, ast_channel_amaflags2string(record.amaflag), record.unique_id, record.linked_id, record.user_field, record.peer, record.peer_account, record.extra); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_fields); curl_easy_perform(curl); curl_slist_free_all(headers); curl_easy_cleanup(curl); free(post_fields); }
/*! * \brief Write metadata to log file * * \param logfile Log File Pointer * \param signalling_type Signaling Type * \param chan Asterisk Channel * \param no_checksum Expecting messages without checksum * * \retval 0 success * \retval -1 failure */ static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum) { struct timeval t; struct ast_tm now; char *cl; char *cn; char workstring[80]; char timestamp[80]; /* Extract the caller ID location */ ast_copy_string(workstring, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring)); ast_shrink_phone_number(workstring); if (ast_strlen_zero(workstring)) { cl = "<unknown>"; } else { cl = workstring; } cn = S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"); /* Get the current time */ t = ast_tvnow(); ast_localtime(&t, &now, NULL); /* Format the time */ ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n" "CHECKSUM=%s\n" "CALLINGFROM=%s\n" "CALLERNAME=%s\n" "TIMESTAMP=%s\n\n", signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) { return 0; } else if (fprintf(logfile, "\n\n[metadata]\n\n" "PROTOCOL=%s\n" "CHECKSUM=%s\n" "CALLINGFROM=%s\n" "CALLERNAME=%s\n" "TIMESTAMP=%s\n\n" "[events]\n\n", signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) { return 0; } ast_verb(3, "AlarmReceiver: can't write metadata\n"); ast_debug(1, "AlarmReceiver: can't write metadata\n"); return -1; }
static void manager_log(const struct ast_event *event, void *userdata) { struct ast_tm timeresult; char start_time[80] = ""; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; if (ast_cel_fill_record(event, &record)) { return; } if (!enablecel) { return; } ast_localtime(&record.event_time, &timeresult, NULL); ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult); manager_event(EVENT_FLAG_CALL, "CEL", "EventName: %s\r\n" "AccountCode: %s\r\n" "CallerIDnum: %s\r\n" "CallerIDname: %s\r\n" "CallerIDani: %s\r\n" "CallerIDrdnis: %s\r\n" "CallerIDdnid: %s\r\n" "Exten: %s\r\n" "Context: %s\r\n" "Channel: %s\r\n" "Application: %s\r\n" "AppData: %s\r\n" "EventTime: %s\r\n" "AMAFlags: %s\r\n" "UniqueID: %s\r\n" "LinkedID: %s\r\n" "Userfield: %s\r\n" "Peer: %s\r\n", record.event_name, record.account_code, record.caller_id_num, record.caller_id_name, record.caller_id_ani, record.caller_id_rdnis, record.caller_id_dnid, record.extension, record.context, record.channel_name, record.application_name, record.application_data, start_time, ast_cel_get_ama_flag_name(record.amaflag), record.unique_id, record.linked_id, record.user_field, record.peer); }
static int append_date(char *buf, struct timeval when, size_t bufsize) { char tmp[80] = ""; struct ast_tm tm; if (strlen(buf) > bufsize - 3) return -1; if (ast_tvzero(when)) { strncat(buf, ",", bufsize - strlen(buf) - 1); return 0; } ast_localtime(&when, &tm, usegmtime ? "GMT" : NULL); ast_strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm); return append_string(buf, tmp, bufsize); }
static char *cli_time(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct timeval date_tv; struct ast_tm tm = { 0, }; const char *zone = NULL; const char *format = "%Y-%m-%d %H:%M:%S.%q"; char buf[64]; switch (cmd) { case CLI_INIT: e->command = "lab time"; e->usage = "lab time <timezone> <format>\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc >= 3) { zone = a->argv[2]; } if (a->argc >= 4) { format = a->argv[3]; } if (a->argc >= 5) { return CLI_SHOWUSAGE; } date_tv = ast_tvnow(); ast_localtime(&date_tv, &tm, zone); tm.tm_usec = 123; ast_strftime(buf, sizeof(buf), format, &tm); ast_cli(a->fd, "%s\nus: %d\n", buf, tm.tm_usec); return CLI_SUCCESS; }
static void manager_log(const struct ast_event *event, void *userdata) { struct ast_tm timeresult; char start_time[80] = ""; char user_defined_header[160]; const char *event_name; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; if (!enablecel) { return; } if (ast_cel_fill_record(event, &record)) { return; } ast_localtime(&record.event_time, &timeresult, NULL); ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult); event_name = record.event_name; user_defined_header[0] = '\0'; if (record.event_type == AST_CEL_USER_DEFINED) { if (cel_show_user_def) { snprintf(user_defined_header, sizeof(user_defined_header), "UserDefType: %s\r\n", record.user_defined_name); } else { event_name = record.user_defined_name; } } manager_event(EVENT_FLAG_CALL, "CEL", "EventName: %s\r\n" "AccountCode: %s\r\n" "CallerIDnum: %s\r\n" "CallerIDname: %s\r\n" "CallerIDani: %s\r\n" "CallerIDrdnis: %s\r\n" "CallerIDdnid: %s\r\n" "Exten: %s\r\n" "Context: %s\r\n" "Channel: %s\r\n" "Application: %s\r\n" "AppData: %s\r\n" "EventTime: %s\r\n" "AMAFlags: %s\r\n" "UniqueID: %s\r\n" "LinkedID: %s\r\n" "Userfield: %s\r\n" "Peer: %s\r\n" "PeerAccount: %s\r\n" "%s" "Extra: %s\r\n", event_name, record.account_code, record.caller_id_num, record.caller_id_name, record.caller_id_ani, record.caller_id_rdnis, record.caller_id_dnid, record.extension, record.context, record.channel_name, record.application_name, record.application_data, start_time, ast_cel_get_ama_flag_name(record.amaflag), record.unique_id, record.linked_id, record.user_field, record.peer, record.peer_account, user_defined_header, record.extra); }
static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_iterator i; struct ast_sip_sched_task *schtd; const char *log_format = ast_logger_get_dateformat(); struct ast_tm tm; char queued[32]; char last_start[32]; char last_end[32]; int datelen; struct timeval now = ast_tvnow(); const char *separator = "======================================"; switch (cmd) { case CLI_INIT: e->command = "pjsip show scheduled_tasks"; e->usage = "Usage: pjsip show scheduled_tasks\n" " Show all scheduled tasks\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) { return CLI_SHOWUSAGE; } ast_localtime(&now, &tm, NULL); datelen = ast_strftime(queued, sizeof(queued), log_format, &tm); ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n"); ast_cli(a->fd, " %1$-24s %2$-8s %3$-9s %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", "Task Name", "Interval", "Times Run", "State", datelen, "Queued", "Last Started", "Last Ended"); ast_cli(a->fd, " %1$-24.24s %2$-8.8s %3$-9.9s %4$-7.7s %6$-*5$.*5$s %7$-*5$.*5$s %8$-*5$.*5$s\n", separator, separator, separator, separator, datelen, separator, separator, separator); ao2_ref(tasks, +1); ao2_rdlock(tasks); i = ao2_iterator_init(tasks, 0); while ((schtd = ao2_iterator_next(&i))) { ast_localtime(&schtd->when_queued, &tm, NULL); ast_strftime(queued, sizeof(queued), log_format, &tm); if (ast_tvzero(schtd->last_start)) { strcpy(last_start, "not yet started"); } else { ast_localtime(&schtd->last_start, &tm, NULL); ast_strftime(last_start, sizeof(last_start), log_format, &tm); } if (ast_tvzero(schtd->last_end)) { if (ast_tvzero(schtd->last_start)) { strcpy(last_end, "not yet started"); } else { strcpy(last_end, "running"); } } else { ast_localtime(&schtd->last_end, &tm, NULL); ast_strftime(last_end, sizeof(last_end), log_format, &tm); } ast_cli(a->fd, " %1$-24.24s %2$-8.3f %3$-9d %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", schtd->name, schtd->interval / 1000.0, schtd->run_count, schtd->is_running ? "running" : "waiting", datelen, queued, last_start, last_end); ao2_cleanup(schtd); } ao2_iterator_destroy(&i); ao2_unlock(tasks); ao2_ref(tasks, -1); ast_cli(a->fd, "\n"); return CLI_SUCCESS; }
static int beanstalk_put(struct ast_cdr *cdr) { struct ast_tm timeresult; char strAnswerTime[80] = ""; char strStartTime[80]; char strEndTime[80]; char *cdr_buffer; int bs_id; int bs_socket; struct ast_json *t_cdr_json; if (!enablecdr) { return 0; } ast_rwlock_rdlock(&config_lock); bs_socket = bs_connect(bs_host, bs_port); if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) { ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port); ast_rwlock_unlock(&config_lock); return 0; } ast_localtime(&cdr->start, &timeresult, NULL); ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult); if (cdr->answer.tv_sec) { ast_localtime(&cdr->answer, &timeresult, NULL); ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult); } ast_localtime(&cdr->end, &timeresult, NULL); ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult); ast_rwlock_unlock(&config_lock); t_cdr_json = ast_json_pack("{s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:i, s:i, s:s, s:s, s:s, s:s}", "AccountCode", S_OR(cdr->accountcode, ""), "Source", S_OR(cdr->src, ""), "Destination", S_OR(cdr->dst, ""), "DestinationContext", S_OR(cdr->dcontext, ""), "CallerID", S_OR(cdr->clid, ""), "Channel", S_OR(cdr->channel, ""), "DestinationChannel", S_OR(cdr->dstchannel, ""), "LastApplication", S_OR(cdr->lastapp, ""), "LastData", S_OR(cdr->lastdata, ""), "StartTime", S_OR(strStartTime, ""), "AnswerTime", S_OR(strAnswerTime, ""), "EndTime", S_OR(strEndTime, ""), "Duration", cdr->duration, "Billsec", cdr->billsec, "Disposition", S_OR(ast_cdr_disp2str(cdr->disposition), ""), "AMAFlags", S_OR(ast_channel_amaflags2string(cdr->amaflags), ""), "UniqueID", S_OR(cdr->uniqueid, ""), "UserField", S_OR(cdr->userfield, "")); cdr_buffer = ast_json_dump_string(t_cdr_json); ast_json_unref(t_cdr_json); bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cdr_buffer, strlen(cdr_buffer)); if (bs_id > 0) { ast_log(LOG_DEBUG, "Successfully created job %d with %s\n", bs_id, cdr_buffer); } else { ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cdr_buffer); } bs_disconnect(bs_socket); ast_json_free(cdr_buffer); return 0; }
static int manager_log(struct ast_cdr *cdr) { struct ast_tm timeresult; char strStartTime[80] = ""; char strAnswerTime[80] = ""; char strEndTime[80] = ""; char buf[CUSTOM_FIELDS_BUF_SIZE]; if (!enablecdr) return 0; ast_localtime(&cdr->start, &timeresult, NULL); ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult); if (cdr->answer.tv_sec) { ast_localtime(&cdr->answer, &timeresult, NULL); ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult); } ast_localtime(&cdr->end, &timeresult, NULL); ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult); buf[0] = '\0'; ast_rwlock_rdlock(&customfields_lock); if (customfields && ast_str_strlen(customfields)) { struct ast_channel *dummy = ast_dummy_channel_alloc(); if (!dummy) { ast_log(LOG_ERROR, "Unable to allocate channel for variable substitution.\n"); return 0; } dummy->cdr = ast_cdr_dup(cdr); pbx_substitute_variables_helper(dummy, ast_str_buffer(customfields), buf, sizeof(buf) - 1); ast_channel_unref(dummy); } ast_rwlock_unlock(&customfields_lock); manager_event(EVENT_FLAG_CDR, "Cdr", "AccountCode: %s\r\n" "Source: %s\r\n" "Destination: %s\r\n" "DestinationContext: %s\r\n" "CallerID: %s\r\n" "Channel: %s\r\n" "DestinationChannel: %s\r\n" "LastApplication: %s\r\n" "LastData: %s\r\n" "StartTime: %s\r\n" "AnswerTime: %s\r\n" "EndTime: %s\r\n" "Duration: %ld\r\n" "BillableSeconds: %ld\r\n" "Disposition: %s\r\n" "AMAFlags: %s\r\n" "UniqueID: %s\r\n" "UserField: %s\r\n" "%s", cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf); return 0; }
static int http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) { char file_name[64] = "/tmp/test-media-cache-XXXXXX"; struct ast_str *http_header = ast_str_create(128); struct ast_str *cache_control = ast_str_create(128); int fd = -1; int unmodified = 0; int send_file = options.send_file && method == AST_HTTP_GET; if (!http_header) { goto error; } if (send_file) { char buf[1024]; fd = mkstemp(file_name); if (fd == -1) { ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno); goto error; } memset(buf, 1, sizeof(buf)); if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { ast_log(LOG_ERROR, "Failed to write expected number of bytes to pipe\n"); close(fd); goto error; } close(fd); fd = open(file_name, 0); if (fd == -1) { ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno); goto error; } } if (options.cache_control.maxage) { SET_OR_APPEND_CACHE_CONTROL(cache_control); ast_str_append(&cache_control, 0, "max-age=%d", options.cache_control.maxage); } if (options.cache_control.s_maxage) { SET_OR_APPEND_CACHE_CONTROL(cache_control); ast_str_append(&cache_control, 0, "s-maxage=%d", options.cache_control.s_maxage); } if (options.cache_control.no_cache) { SET_OR_APPEND_CACHE_CONTROL(cache_control); ast_str_append(&cache_control, 0, "%s", "no-cache"); } if (options.cache_control.must_revalidate) { SET_OR_APPEND_CACHE_CONTROL(cache_control); ast_str_append(&cache_control, 0, "%s", "must-revalidate"); } if (ast_str_strlen(cache_control)) { ast_str_append(&http_header, 0, "%s\r\n", ast_str_buffer(cache_control)); } if (options.expires.tv_sec) { struct ast_tm now_time; char tmbuf[64]; ast_localtime(&options.expires, &now_time, NULL); ast_strftime(tmbuf, sizeof(tmbuf), "%a, %d %b %Y %T %z", &now_time); ast_str_append(&http_header, 0, "Expires: %s\r\n", tmbuf); } if (!ast_strlen_zero(options.etag)) { struct ast_variable *v; ast_str_append(&http_header, 0, "ETag: %s\r\n", options.etag); for (v = headers; v; v = v->next) { if (!strcasecmp(v->name, "If-None-Match") && !strcasecmp(v->value, options.etag)) { unmodified = 1; break; } } } if (!unmodified) { ast_http_send(ser, method, options.status_code, options.status_text, http_header, NULL, send_file ? fd : 0, 1); } else { ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1); } if (send_file) { close(fd); unlink(file_name); } ast_free(cache_control); return 0; error: ast_free(http_header); ast_free(cache_control); ast_http_request_close_on_completion(ser); ast_http_error(ser, 418, "I'm a Teapot", "Please don't ask me to brew coffee."); return 0; }
static int build_radius_record(VALUE_PAIR **tosend, struct ast_cdr *cdr) { int recordtype = PW_STATUS_STOP; struct ast_tm tm; char timestr[128]; char *tmp; if (!rc_avpair_add(rh, tosend, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) return -1; /* Account code */ if (!rc_avpair_add(rh, tosend, PW_AST_ACCT_CODE, &cdr->accountcode, strlen(cdr->accountcode), VENDOR_CODE)) return -1; /* Source */ if (!rc_avpair_add(rh, tosend, PW_AST_SRC, &cdr->src, strlen(cdr->src), VENDOR_CODE)) return -1; /* Destination */ if (!rc_avpair_add(rh, tosend, PW_AST_DST, &cdr->dst, strlen(cdr->dst), VENDOR_CODE)) return -1; /* Destination context */ if (!rc_avpair_add(rh, tosend, PW_AST_DST_CTX, &cdr->dcontext, strlen(cdr->dcontext), VENDOR_CODE)) return -1; /* Caller ID */ if (!rc_avpair_add(rh, tosend, PW_AST_CLID, &cdr->clid, strlen(cdr->clid), VENDOR_CODE)) return -1; /* Channel */ if (!rc_avpair_add(rh, tosend, PW_AST_CHAN, &cdr->channel, strlen(cdr->channel), VENDOR_CODE)) return -1; /* Destination Channel */ if (!rc_avpair_add(rh, tosend, PW_AST_DST_CHAN, &cdr->dstchannel, strlen(cdr->dstchannel), VENDOR_CODE)) return -1; /* Last Application */ if (!rc_avpair_add(rh, tosend, PW_AST_LAST_APP, &cdr->lastapp, strlen(cdr->lastapp), VENDOR_CODE)) return -1; /* Last Data */ if (!rc_avpair_add(rh, tosend, PW_AST_LAST_DATA, &cdr->lastdata, strlen(cdr->lastdata), VENDOR_CODE)) return -1; /* Start Time */ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, ast_localtime(&cdr->start, &tm, ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL)); if (!rc_avpair_add(rh, tosend, PW_AST_START_TIME, timestr, strlen(timestr), VENDOR_CODE)) return -1; /* Answer Time */ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, ast_localtime(&cdr->answer, &tm, ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL)); if (!rc_avpair_add(rh, tosend, PW_AST_ANSWER_TIME, timestr, strlen(timestr), VENDOR_CODE)) return -1; /* End Time */ ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, ast_localtime(&cdr->end, &tm, ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL)); if (!rc_avpair_add(rh, tosend, PW_AST_END_TIME, timestr, strlen(timestr), VENDOR_CODE)) return -1; /* Duration */ if (!rc_avpair_add(rh, tosend, PW_AST_DURATION, &cdr->duration, 0, VENDOR_CODE)) return -1; /* Billable seconds */ if (!rc_avpair_add(rh, tosend, PW_AST_BILL_SEC, &cdr->billsec, 0, VENDOR_CODE)) return -1; /* Disposition */ tmp = ast_cdr_disp2str(cdr->disposition); if (!rc_avpair_add(rh, tosend, PW_AST_DISPOSITION, tmp, strlen(tmp), VENDOR_CODE)) return -1; /* AMA Flags */ tmp = ast_cdr_flags2str(cdr->amaflags); if (!rc_avpair_add(rh, tosend, PW_AST_AMA_FLAGS, tmp, strlen(tmp), VENDOR_CODE)) return -1; if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) { /* Unique ID */ if (!rc_avpair_add(rh, tosend, PW_AST_UNIQUE_ID, &cdr->uniqueid, strlen(cdr->uniqueid), VENDOR_CODE)) return -1; } if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUSERFIELD)) { /* append the user field */ if (!rc_avpair_add(rh, tosend, PW_AST_USER_FIELD, &cdr->userfield, strlen(cdr->userfield), VENDOR_CODE)) return -1; } /* Setting Acct-Session-Id & User-Name attributes for proper generation of Acct-Unique-Session-Id on server side */ /* Channel */ if (!rc_avpair_add(rh, tosend, PW_USER_NAME, &cdr->channel, strlen(cdr->channel), 0)) return -1; /* Unique ID */ if (!rc_avpair_add(rh, tosend, PW_ACCT_SESSION_ID, &cdr->uniqueid, strlen(cdr->uniqueid), 0)) return -1; return 0; }
static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data) { struct ast_cdr *cdr = data; SQLRETURN ODBC_res; char sqlcmd[2048] = "", timestr[128]; struct ast_tm tm; SQLHSTMT stmt; ast_localtime(&cdr->start, &tm, ast_test_flag(&config, CONFIG_USEGMTIME) ? "GMT" : NULL); ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm); if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) { snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s " "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp," "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) " "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr); } else { snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s " "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata," "duration,billsec,disposition,amaflags,accountcode) " "VALUES ({ts '%s'},?,?,?,?,?,?,?,?,?,?,?,?,?)", table, timestr); } ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { ast_verb(11, "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res); SQLFreeHandle(SQL_HANDLE_STMT, stmt); return NULL; } SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL); SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL); SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL); SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL); SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL); SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL); SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL); SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL); if (ast_test_flag(&config, CONFIG_HRTIME)) { double hrbillsec = 0.0; double hrduration; if (!ast_tvzero(cdr->answer)) { hrbillsec = (double) ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0; } hrduration = (double) ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0; SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrduration, 0, NULL); SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_FLOAT, 0, 0, &hrbillsec, 0, NULL); } else { SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL); SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL); } if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING)) SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL); else SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL); SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL); SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL); if (ast_test_flag(&config, CONFIG_LOGUNIQUEID)) { SQLBindParameter(stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL); SQLBindParameter(stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL); } ODBC_res = SQLExecDirect(stmt, (unsigned char *)sqlcmd, SQL_NTS); if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { ast_verb(11, "cdr_odbc: Error in ExecDirect: %d\n", ODBC_res); SQLFreeHandle(SQL_HANDLE_STMT, stmt); return NULL; } return stmt; }
static int timeout_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { double x = 0.0; long sec = 0L; char timestr[64]; struct ast_tm myt; struct timeval when = {0,}; int res; if (!chan) return -1; if (!data) { ast_log(LOG_ERROR, "Must specify type of timeout to set.\n"); return -1; } if (!value) return -1; res = sscanf(value, "%30ld%30lf", &sec, &x); if (res == 0 || sec < 0) { when.tv_sec = 0; when.tv_usec = 0; } else if (res == 1) { when.tv_sec = sec; } else if (res == 2) { when.tv_sec = sec; when.tv_usec = x * 1000000; } switch (*data) { case 'a': case 'A': ast_channel_lock(chan); ast_channel_setwhentohangup_tv(chan, when); ast_channel_unlock(chan); if (VERBOSITY_ATLEAST(3)) { if (!ast_tvzero(*ast_channel_whentohangup(chan))) { when = ast_tvadd(when, ast_tvnow()); ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S.%3q %Z", ast_localtime(&when, &myt, NULL)); ast_verb(3, "Channel will hangup at %s.\n", timestr); } else { ast_verb(3, "Channel hangup cancelled.\n"); } } break; case 'r': case 'R': if (ast_channel_pbx(chan)) { ast_channel_pbx(chan)->rtimeoutms = when.tv_sec * 1000 + when.tv_usec / 1000; ast_verb(3, "Response timeout set to %.3f\n", ast_channel_pbx(chan)->rtimeoutms / 1000.0); } break; case 'd': case 'D': if (ast_channel_pbx(chan)) { ast_channel_pbx(chan)->dtimeoutms = when.tv_sec * 1000 + when.tv_usec / 1000; ast_verb(3, "Digit timeout set to %.3f\n", ast_channel_pbx(chan)->dtimeoutms / 1000.0); } break; default: ast_log(LOG_ERROR, "Unknown timeout type specified.\n"); break; } return 0; }
static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record) { int recordtype = PW_STATUS_STOP; struct ast_tm tm; char timestr[128]; char *amaflags; int left_len; char *left_value; int send_len; if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) { return -1; } /* Account code */ if (!ADD_VENDOR_CODE(PW_AST_ACCT_CODE, record->account_code)) { return -1; } /* Source */ if (!ADD_VENDOR_CODE(PW_AST_CIDNUM, record->caller_id_num)) { return -1; } /* Destination */ if (!ADD_VENDOR_CODE(PW_AST_EXTEN, record->extension)) { return -1; } /* Destination context */ if (!ADD_VENDOR_CODE(PW_AST_CONTEXT, record->context)) { return -1; } /* Caller ID */ if (!ADD_VENDOR_CODE(PW_AST_CIDNAME, record->caller_id_name)) { return -1; } /* Caller ID ani */ if (!ADD_VENDOR_CODE(PW_AST_CIDANI, record->caller_id_ani)) { return -1; } /* Caller ID rdnis */ if (!ADD_VENDOR_CODE(PW_AST_CIDRDNIS, record->caller_id_rdnis)) { return -1; } /* Caller ID dnid */ if (!ADD_VENDOR_CODE(PW_AST_CIDDNID, record->caller_id_dnid)) { return -1; } /* Channel */ if (!ADD_VENDOR_CODE(PW_AST_CHANNAME, record->channel_name)) { return -1; } /* Last Application */ if (!ADD_VENDOR_CODE(PW_AST_APPNAME, record->application_name)) { return -1; } /* Last Data */ if (!ADD_VENDOR_CODE(PW_AST_APPDATA, record->application_data)) { return -1; } /* Extra */ left_len = strlen(record->extra); left_value = (char *)record->extra; while(left_len > 0){ if(left_len > 240){ send_len = 240; }else{ send_len = left_len; } if(!rc_avpair_add(rh, send, PW_AST_EXTRA, left_value, send_len, VENDOR_CODE )) { return -1; } left_len-=send_len; left_value+=send_len; } /* Event Time */ ast_localtime(&record->event_time, &tm, ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL); ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm); if (!rc_avpair_add(rh, send, PW_AST_EVENT_TIME, timestr, strlen(timestr), VENDOR_CODE)) { return -1; } /* AMA Flags */ amaflags = ast_strdupa(ast_channel_amaflags2string(record->amaflag)); if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) { return -1; } if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) { /* Unique ID */ if (!ADD_VENDOR_CODE(PW_AST_UNIQUE_ID, record->unique_id)) { return -1; } } /* LinkedID */ if (!ADD_VENDOR_CODE(PW_AST_LINKED_ID, record->linked_id)) { return -1; } /* Setting Acct-Session-Id & User-Name attributes for proper generation of Acct-Unique-Session-Id on server side */ /* Channel */ if (!rc_avpair_add(rh, send, PW_USER_NAME, (void *)record->channel_name, strlen(record->channel_name), 0)) { return -1; } return 0; }
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) { struct varshead *headp; struct ast_var_t *newvariable; const char *mixed_name; char timebuf[30]; struct ast_channel *tchan; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; /* do not call ast_channel_alloc because this is not really a real channel */ if (!(tchan = ast_dummy_channel_alloc())) { return NULL; } headp = ast_channel_varshead(tchan); /* first, get the variables from the event */ if (ast_cel_fill_record(event, &record)) { ast_channel_unref(tchan); return NULL; } /* next, fill the channel with their data */ mixed_name = (record.event_type == AST_CEL_USER_DEFINED) ? record.user_defined_name : record.event_name; if ((newvariable = ast_var_assign("eventtype", mixed_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if (ast_strlen_zero(cel_dateformat)) { snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec, (long) record.event_time.tv_usec); } else { struct ast_tm tm; ast_localtime(&record.event_time, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); } if ((newvariable = ast_var_assign("eventtime", timebuf))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventenum", record.event_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventextra", record.extra))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_caller(tchan)->id.name.valid = 1; ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name); ast_channel_caller(tchan)->id.number.valid = 1; ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num); ast_channel_caller(tchan)->ani.number.valid = 1; ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani); ast_channel_redirecting(tchan)->from.number.valid = 1; ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis); ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid); ast_channel_exten_set(tchan, record.extension); ast_channel_context_set(tchan, record.context); ast_channel_name_set(tchan, record.channel_name); ast_channel_uniqueid_set(tchan, record.unique_id); ast_channel_linkedid_set(tchan, record.linked_id); ast_channel_accountcode_set(tchan, record.account_code); ast_channel_peeraccount_set(tchan, record.peer_account); ast_channel_userfield_set(tchan, record.user_field); if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_appl_set(tchan, ast_strdup(record.application_name)); ast_channel_data_set(tchan, ast_strdup(record.application_data)); ast_channel_amaflags_set(tchan, record.amaflag); return tchan; }
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) { struct varshead *headp; struct ast_var_t *newvariable; char timebuf[30]; struct ast_channel *tchan; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; /* do not call ast_channel_alloc because this is not really a real channel */ if (!(tchan = ast_dummy_channel_alloc())) { return NULL; } headp = &tchan->varshead; /* first, get the variables from the event */ if (ast_cel_fill_record(event, &record)) { ast_channel_release(tchan); return NULL; } /* next, fill the channel with their data */ if ((newvariable = ast_var_assign("eventtype", record.event_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if (ast_strlen_zero(cel_dateformat)) { snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec, (long) record.event_time.tv_usec); } else { struct ast_tm tm; ast_localtime(&record.event_time, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); } if ((newvariable = ast_var_assign("eventtime", timebuf))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventextra", record.extra))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } tchan->caller.id.name.valid = 1; tchan->caller.id.name.str = ast_strdup(record.caller_id_name); tchan->caller.id.number.valid = 1; tchan->caller.id.number.str = ast_strdup(record.caller_id_num); tchan->caller.ani.number.valid = 1; tchan->caller.ani.number.str = ast_strdup(record.caller_id_ani); tchan->redirecting.from.number.valid = 1; tchan->redirecting.from.number.str = ast_strdup(record.caller_id_rdnis); tchan->dialed.number.str = ast_strdup(record.caller_id_dnid); ast_copy_string(tchan->exten, record.extension, sizeof(tchan->exten)); ast_copy_string(tchan->context, record.context, sizeof(tchan->context)); ast_string_field_set(tchan, name, record.channel_name); ast_string_field_set(tchan, uniqueid, record.unique_id); ast_string_field_set(tchan, linkedid, record.linked_id); ast_string_field_set(tchan, accountcode, record.account_code); ast_string_field_set(tchan, peeraccount, record.peer_account); ast_string_field_set(tchan, userfield, record.user_field); pbx_builtin_setvar_helper(tchan, "BRIDGEPEER", record.peer); tchan->appl = ast_strdup(record.application_name); tchan->data = ast_strdup(record.application_data); tchan->amaflags = record.amaflag; return tchan; }
struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) { struct varshead *headp; struct ast_var_t *newvariable; const char *mixed_name; char timebuf[30]; struct ast_channel *tchan; struct ast_cel_event_record record = { .version = AST_CEL_EVENT_RECORD_VERSION, }; struct ast_datastore *datastore; char *app_data; /* do not call ast_channel_alloc because this is not really a real channel */ if (!(tchan = ast_dummy_channel_alloc())) { return NULL; } headp = ast_channel_varshead(tchan); /* first, get the variables from the event */ if (ast_cel_fill_record(event, &record)) { ast_channel_unref(tchan); return NULL; } /* next, fill the channel with their data */ mixed_name = (record.event_type == AST_CEL_USER_DEFINED) ? record.user_defined_name : record.event_name; if ((newvariable = ast_var_assign("eventtype", mixed_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if (ast_strlen_zero(cel_dateformat)) { snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec, (long) record.event_time.tv_usec); } else { struct ast_tm tm; ast_localtime(&record.event_time, &tm, NULL); ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); } if ((newvariable = ast_var_assign("eventtime", timebuf))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventenum", record.event_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } if ((newvariable = ast_var_assign("eventextra", record.extra))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_caller(tchan)->id.name.valid = 1; ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name); ast_channel_caller(tchan)->id.number.valid = 1; ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num); ast_channel_caller(tchan)->ani.number.valid = 1; ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani); ast_channel_redirecting(tchan)->from.number.valid = 1; ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis); ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid); ast_channel_exten_set(tchan, record.extension); ast_channel_context_set(tchan, record.context); ast_channel_name_set(tchan, record.channel_name); ast_channel_uniqueid_set(tchan, record.unique_id); ast_channel_linkedid_set(tchan, record.linked_id); ast_channel_accountcode_set(tchan, record.account_code); ast_channel_peeraccount_set(tchan, record.peer_account); ast_channel_userfield_set(tchan, record.user_field); if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) { AST_LIST_INSERT_HEAD(headp, newvariable, entries); } ast_channel_amaflags_set(tchan, record.amaflag); /* We need to store an 'application name' and 'application * data' on the channel for logging purposes, but the channel * structure only provides a place to store pointers, and it * expects these pointers to be pointing to data that does not * need to be freed. This means that the channel's destructor * does not attempt to free any storage that these pointers * point to. However, we can't provide data in that form directly for * these structure members. In order to ensure that these data * elements have a lifetime that matches the channel's * lifetime, we'll put them in a datastore attached to the * channel, and set's the channel's pointers to point into the * datastore. The datastore will then be automatically destroyed * when the channel is destroyed. */ if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) { ast_channel_unref(tchan); return NULL; } if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) { ast_datastore_free(datastore); ast_channel_unref(tchan); return NULL; } ast_channel_appl_set(tchan, strcpy(app_data, record.application_name)); ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1, record.application_data)); datastore->data = app_data; ast_channel_datastore_add(tchan, datastore); return tchan; }