static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static unsigned long long_ret; static u_char bits_ret[2]; static char string_ret[256]; struct ast_channel *chan, *bridge; struct timeval tval; u_char *ret = NULL; int i, bit; struct ast_str *out = ast_str_alloca(2048); if (header_simple_table(vp, name, length, exact, var_len, write_method, ast_active_channels())) return NULL; i = name[*length - 1] - 1; for (chan = ast_channel_walk_locked(NULL); chan && i; chan = ast_channel_walk_locked(chan), i--) ast_channel_unlock(chan); if (chan == NULL) return NULL; *var_len = sizeof(long_ret); switch (vp->magic) { case ASTCHANINDEX: long_ret = name[*length - 1]; ret = (u_char *)&long_ret; break; case ASTCHANNAME: if (!ast_strlen_zero(chan->name)) { strncpy(string_ret, chan->name, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANLANGUAGE: if (!ast_strlen_zero(chan->language)) { strncpy(string_ret, chan->language, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANTYPE: strncpy(string_ret, chan->tech->type, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMUSICCLASS: if (!ast_strlen_zero(chan->musicclass)) { strncpy(string_ret, chan->musicclass, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANBRIDGE: if ((bridge = ast_bridged_channel(chan)) != NULL) { strncpy(string_ret, bridge->name, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQ: if (chan->masq && !ast_strlen_zero(chan->masq->name)) { strncpy(string_ret, chan->masq->name, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQR: if (chan->masqr && !ast_strlen_zero(chan->masqr->name)) { strncpy(string_ret, chan->masqr->name, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANWHENHANGUP: if (!ast_tvzero(chan->whentohangup)) { gettimeofday(&tval, NULL); long_ret = difftime(chan->whentohangup.tv_sec, tval.tv_sec) * 100 - tval.tv_usec / 10000; ret= (u_char *)&long_ret; } break; case ASTCHANAPP: if (chan->appl) { strncpy(string_ret, chan->appl, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANDATA: if (chan->data) { strncpy(string_ret, chan->data, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCONTEXT: strncpy(string_ret, chan->context, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROCONTEXT: strncpy(string_ret, chan->macrocontext, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROEXTEN: strncpy(string_ret, chan->macroexten, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROPRI: long_ret = chan->macropriority; ret = (u_char *)&long_ret; break; case ASTCHANEXTEN: strncpy(string_ret, chan->exten, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANPRI: long_ret = chan->priority; ret = (u_char *)&long_ret; break; case ASTCHANACCOUNTCODE: if (!ast_strlen_zero(chan->accountcode)) { strncpy(string_ret, chan->accountcode, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANFORWARDTO: if (!ast_strlen_zero(chan->call_forward)) { strncpy(string_ret, chan->call_forward, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANUNIQUEID: strncpy(string_ret, chan->uniqueid, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANCALLGROUP: long_ret = chan->callgroup; ret = (u_char *)&long_ret; break; case ASTCHANPICKUPGROUP: long_ret = chan->pickupgroup; ret = (u_char *)&long_ret; break; case ASTCHANSTATE: long_ret = chan->_state & 0xffff; ret = (u_char *)&long_ret; break; case ASTCHANMUTED: long_ret = chan->_state & AST_STATE_MUTE ? 1 : 2; ret = (u_char *)&long_ret; break; case ASTCHANRINGS: long_ret = chan->rings; ret = (u_char *)&long_ret; break; case ASTCHANCIDDNID: if (chan->cid.cid_dnid) { strncpy(string_ret, chan->cid.cid_dnid, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNUM: if (chan->cid.cid_num) { strncpy(string_ret, chan->cid.cid_num, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNAME: if (chan->cid.cid_name) { strncpy(string_ret, chan->cid.cid_name, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDANI: if (chan->cid.cid_ani) { strncpy(string_ret, chan->cid.cid_ani, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDRDNIS: if (chan->cid.cid_rdnis) { strncpy(string_ret, chan->cid.cid_rdnis, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDPRES: long_ret = chan->cid.cid_pres; ret = (u_char *)&long_ret; break; case ASTCHANCIDANI2: long_ret = chan->cid.cid_ani2; ret = (u_char *)&long_ret; break; case ASTCHANCIDTON: long_ret = chan->cid.cid_ton; ret = (u_char *)&long_ret; break; case ASTCHANCIDTNS: long_ret = chan->cid.cid_tns; ret = (u_char *)&long_ret; break; case ASTCHANAMAFLAGS: long_ret = chan->amaflags; ret = (u_char *)&long_ret; break; case ASTCHANADSI: long_ret = chan->adsicpe; ret = (u_char *)&long_ret; break; case ASTCHANTONEZONE: if (chan->zone) { strncpy(string_ret, chan->zone->country, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANHANGUPCAUSE: long_ret = chan->hangupcause; ret = (u_char *)&long_ret; break; case ASTCHANVARIABLES: if (pbx_builtin_serialize_variables(chan, &out)) { *var_len = ast_str_strlen(out); ret = (u_char *)ast_str_buffer(out); } break; case ASTCHANFLAGS: bits_ret[0] = 0; for (bit = 0; bit < 8; bit++) bits_ret[0] |= ((chan->flags & (1 << bit)) >> bit) << (7 - bit); bits_ret[1] = 0; for (bit = 0; bit < 8; bit++) bits_ret[1] |= (((chan->flags >> 8) & (1 << bit)) >> bit) << (7 - bit); *var_len = 2; ret = bits_ret; break; case ASTCHANTRANSFERCAP: long_ret = chan->transfercapability; ret = (u_char *)&long_ret; default: break; } ast_channel_unlock(chan); return ret; }
/*! \brief Function which passes through an aliased CLI command to the real one */ static char *cli_alias_passthrough(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct cli_alias *alias; struct cli_alias tmp = { .cli_entry.command = e->command, }; char *generator; const char *line; /* Try to find the alias based on the CLI entry */ if (!(alias = ao2_find(cli_aliases, &tmp, OBJ_POINTER))) { return 0; } switch (cmd) { case CLI_INIT: ao2_ref(alias, -1); return NULL; case CLI_GENERATE: line = a->line; line += (strlen(alias->alias)); if (!strncasecmp(alias->alias, alias->real_cmd, strlen(alias->alias))) { generator = NULL; } else if (!ast_strlen_zero(a->word)) { struct ast_str *real_cmd = ast_str_alloca(strlen(alias->real_cmd) + strlen(line) + 1); ast_str_append(&real_cmd, 0, "%s%s", alias->real_cmd, line); generator = ast_cli_generator(ast_str_buffer(real_cmd), a->word, a->n); } else { generator = ast_cli_generator(alias->real_cmd, a->word, a->n); } ao2_ref(alias, -1); return generator; } /* If they gave us extra arguments we need to construct a string to pass in */ if (a->argc != e->args) { struct ast_str *real_cmd = ast_str_alloca(2048); int i; ast_str_append(&real_cmd, 0, "%s", alias->real_cmd); /* Add the additional arguments that have been passed in */ for (i = e->args + 1; i <= a->argc; i++) { ast_str_append(&real_cmd, 0, " %s", a->argv[i - 1]); } ast_cli_command(a->fd, ast_str_buffer(real_cmd)); } else { ast_cli_command(a->fd, alias->real_cmd); } ao2_ref(alias, -1); return CLI_SUCCESS; } /*! \brief CLI Command to display CLI Aliases */ static char *alias_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { #define FORMAT "%-50.50s %-50.50s\n" struct cli_alias *alias; struct ao2_iterator i; switch (cmd) { case CLI_INIT: e->command = "cli show aliases"; e->usage = "Usage: cli show aliases\n" " Displays a list of aliased CLI commands.\n"; return NULL; case CLI_GENERATE: return NULL; } ast_cli(a->fd, FORMAT, "Alias Command", "Real Command"); i = ao2_iterator_init(cli_aliases, 0); for (; (alias = ao2_iterator_next(&i)); ao2_ref(alias, -1)) { ast_cli(a->fd, FORMAT, alias->alias, alias->real_cmd); } ao2_iterator_destroy(&i); return CLI_SUCCESS; #undef FORMAT } /*! \brief CLI commands to interact with things */ static struct ast_cli_entry cli_alias[] = { AST_CLI_DEFINE(alias_show, "Show CLI command aliases"), }; /*! \brief Function called to load or reload the configuration file */ static void load_config(int reload) { struct ast_config *cfg = NULL; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; struct cli_alias *alias; struct ast_variable *v, *v1; if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { ast_log(LOG_ERROR, "res_clialiases configuration file '%s' not found\n", config_file); return; } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { return; } /* Destroy any existing CLI aliases */ if (reload) { ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, alias_unregister_cb, NULL); } for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { if (strcmp(v->name, "template")) { ast_log(LOG_WARNING, "%s is not a correct option in [%s]\n", v->name, "general"); continue; } /* Read in those there CLI aliases */ for (v1 = ast_variable_browse(cfg, v->value); v1; v1 = v1->next) { struct cli_alias *existing = ao2_callback(cli_aliases, 0, alias_name_cb, (char*)v1->name); if (existing) { ast_log(LOG_WARNING, "Alias '%s' could not be unregistered and has been retained\n", existing->alias); ao2_ref(existing, -1); continue; } if (!(alias = ao2_alloc((sizeof(*alias) + strlen(v1->name) + strlen(v1->value) + 2), NULL))) { continue; } alias->alias = ((char *) alias) + sizeof(*alias); alias->real_cmd = ((char *) alias->alias) + strlen(v1->name) + 1; strcpy(alias->alias, v1->name); strcpy(alias->real_cmd, v1->value); alias->cli_entry.handler = cli_alias_passthrough; alias->cli_entry.command = alias->alias; alias->cli_entry.usage = "Aliased CLI Command\n"; if (ast_cli_register(&alias->cli_entry)) { ao2_ref(alias, -1); continue; } ao2_link(cli_aliases, alias); ast_verb(2, "Aliased CLI command '%s' to '%s'\n", v1->name, v1->value); ao2_ref(alias, -1); } } ast_config_destroy(cfg); return; } /*! \brief Function called to reload the module */ static int reload_module(void) { load_config(1); return 0; }
/*! * \brief Handle OPTIONS request, mainly for CORS preflight requests. * * Some browsers will send this prior to non-simple methods (i.e. DELETE). * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2. */ static void handle_options(struct stasis_rest_handlers *handler, struct ast_variable *headers, struct ast_ari_response *response) { struct ast_variable *header; char const *acr_method = NULL; char const *acr_headers = NULL; char const *origin = NULL; RAII_VAR(struct ast_str *, allow, NULL, ast_free); enum ast_http_method m; int allowed = 0; /* Regular OPTIONS response */ add_allow_header(handler, response); ast_ari_response_no_content(response); /* Parse CORS headers */ for (header = headers; header != NULL; header = header->next) { if (strcmp(ACR_METHOD, header->name) == 0) { acr_method = header->value; } else if (strcmp(ACR_HEADERS, header->name) == 0) { acr_headers = header->value; } else if (strcmp("Origin", header->name) == 0) { origin = header->value; } } /* CORS 6.2, #1 - "If the Origin header is not present terminate this * set of steps." */ if (origin == NULL) { return; } /* CORS 6.2, #2 - "If the value of the Origin header is not a * case-sensitive match for any of the values in list of origins do not * set any additional headers and terminate this set of steps. * * Always matching is acceptable since the list of origins can be * unbounded. * * The Origin header can only contain a single origin as the user agent * will not follow redirects." */ if (!origin_allowed(origin)) { ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin); return; } /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header * or if parsing failed, do not set any additional headers and terminate * this set of steps." */ if (acr_method == NULL) { return; } /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers * headers let header field-names be the empty list." */ if (acr_headers == NULL) { acr_headers = ""; } /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of * the values in list of methods do not set any additional headers and * terminate this set of steps." */ allow = ast_str_create(20); if (!allow) { ast_ari_response_alloc_failed(response); return; } /* Go ahead and build the ACA_METHODS header at the same time */ for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) { if (handler->callbacks[m] != NULL) { char const *m_str = ast_get_http_method(m); if (strcmp(m_str, acr_method) == 0) { allowed = 1; } ast_str_append(&allow, 0, ",%s", m_str); } } if (!allowed) { return; } /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII * case-insensitive match for any of the values in list of headers do * not set any additional headers and terminate this set of steps. * * Note: Always matching is acceptable since the list of headers can be * unbounded." */ /* CORS 6.2 #7 - "If the resource supports credentials add a single * Access-Control-Allow-Origin header, with the value of the Origin * header as value, and add a single Access-Control-Allow-Credentials * header with the case-sensitive string "true" as value." * * Added by process_cors_request() earlier in the request. */ /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age * header..." */ /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers * consisting of (a subset of) the list of methods." */ ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n", ACA_METHODS, ast_str_buffer(allow)); /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers * consisting of (a subset of) the list of headers. * * Since the list of headers can be unbounded simply returning headers * can be enough." */ if (!ast_strlen_zero(acr_headers)) { ast_str_append(&response->headers, 0, "%s: %s\r\n", ACA_HEADERS, acr_headers); } }
static void ari_channels_handle_originate_with_id(const char *args_endpoint, const char *args_extension, const char *args_context, long args_priority, const char *args_app, const char *args_app_args, const char *args_caller_id, int args_timeout, struct ast_variable *variables, const char *args_channel_id, const char *args_other_channel_id, struct ast_ari_response *response) { char *dialtech; char dialdevice[AST_CHANNEL_NAME]; char *caller_id = NULL; char *cid_num = NULL; char *cid_name = NULL; int timeout = 30000; RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy); struct ast_format tmp_fmt; char *stuff; struct ast_channel *chan; RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); struct ast_assigned_ids assignedids = { .uniqueid = args_channel_id, .uniqueid2 = args_other_channel_id, }; if (!cap) { ast_ari_response_alloc_failed(response); return; } ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { ast_ari_response_error(response, 400, "Bad Request", "Uniqueid length exceeds maximum of %d\n", AST_MAX_PUBLIC_UNIQUEID); return; } if (ast_strlen_zero(args_endpoint)) { ast_ari_response_error(response, 400, "Bad Request", "Endpoint must be specified"); return; } dialtech = ast_strdupa(args_endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; ast_copy_string(dialdevice, stuff, sizeof(dialdevice)); } if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) { ast_ari_response_error(response, 400, "Bad Request", "Invalid endpoint specified"); return; } if (args_timeout > 0) { timeout = args_timeout * 1000; } else if (args_timeout == -1) { timeout = -1; } if (!ast_strlen_zero(args_caller_id)) { caller_id = ast_strdupa(args_caller_id); ast_callerid_parse(caller_id, &cid_name, &cid_num); if (ast_is_shrinkable_phonenumber(cid_num)) { ast_shrink_phone_number(cid_num); } } if (!ast_strlen_zero(args_app)) { const char *app = "Stasis"; RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free); if (!appdata) { ast_ari_response_alloc_failed(response); return; } ast_str_set(&appdata, 0, "%s", args_app); if (!ast_strlen_zero(args_app_args)) { ast_str_append(&appdata, 0, ",%s", args_app_args); } /* originate a channel, putting it into an application */ if (ast_pbx_outgoing_app(dialtech, cap, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, variables, NULL, &chan, &assignedids)) { ast_ari_response_alloc_failed(response); return; } } else if (!ast_strlen_zero(args_extension)) {
static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive) { const char *s; char *tmp; char *cur, *rest; char *macro; char fullmacro[80]; char varname[80]; char runningapp[80], runningdata[1024]; char *oldargs[MAX_ARGS + 1] = { NULL, }; int argc, x; int res=0; char oldexten[256]=""; int oldpriority, gosub_level = 0; char pc[80], depthc[12]; char oldcontext[AST_MAX_CONTEXT] = ""; const char *inhangupc; int offset, depth = 0, maxdepth = 7; int setmacrocontext=0; int autoloopflag, inhangup = 0; struct ast_str *tmp_subst = NULL; char *save_macro_exten; char *save_macro_context; char *save_macro_priority; char *save_macro_offset; struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL); if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n"); return -1; } do { if (macro_store) { break; } if (!(macro_store = ast_datastore_alloc(¯o_ds_info, NULL))) { ast_log(LOG_WARNING, "Unable to allocate new datastore.\n"); break; } /* Just the existence of this datastore is enough. */ macro_store->inheritance = DATASTORE_INHERIT_FOREVER; ast_channel_datastore_add(chan, macro_store); } while (0); /* does the user want a deeper rabbit hole? */ ast_channel_lock(chan); if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) { sscanf(s, "%30d", &maxdepth); } /* Count how many levels deep the rabbit hole goes */ if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) { sscanf(s, "%30d", &depth); } /* Used for detecting whether to return when a Macro is called from another Macro after hangup */ if (strcmp(ast_channel_exten(chan), "h") == 0) pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1"); if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) { sscanf(inhangupc, "%30d", &inhangup); } ast_channel_unlock(chan); if (depth >= maxdepth) { ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n"); return 0; } snprintf(depthc, sizeof(depthc), "%d", depth + 1); tmp = ast_strdupa(data); rest = tmp; macro = strsep(&rest, ","); if (ast_strlen_zero(macro)) { ast_log(LOG_WARNING, "Invalid macro name specified\n"); return 0; } snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro); if (!ast_exists_extension(chan, fullmacro, "s", 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { if (!ast_context_find(fullmacro)) ast_log(LOG_WARNING, "No such context '%s' for macro '%s'. Was called by %s@%s\n", fullmacro, macro, ast_channel_exten(chan), ast_channel_context(chan)); else ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro); return 0; } /* If we are to run the macro exclusively, take the mutex */ if (exclusive) { ast_debug(1, "Locking macrolock for '%s'\n", fullmacro); ast_autoservice_start(chan); if (ast_context_lockmacro(fullmacro)) { ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro); ast_autoservice_stop(chan); return 0; } ast_autoservice_stop(chan); } if (!(tmp_subst = ast_str_create(16))) { return -1; } /* Save old info */ oldpriority = ast_channel_priority(chan); ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten)); ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext)); if (ast_strlen_zero(ast_channel_macrocontext(chan))) { ast_channel_macrocontext_set(chan, ast_channel_context(chan)); ast_channel_macroexten_set(chan, ast_channel_exten(chan)); ast_channel_macropriority_set(chan, ast_channel_priority(chan)); setmacrocontext=1; } argc = 1; /* Save old macro variables */ save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN")); pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten); save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT")); pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext); save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY")); snprintf(pc, sizeof(pc), "%d", oldpriority); pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc); save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET")); pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL); pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); /* Setup environment for new run */ ast_channel_exten_set(chan, "s"); ast_channel_context_set(chan, fullmacro); ast_channel_priority_set(chan, 1); ast_channel_lock(chan); while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) { const char *argp; /* Save copy of old arguments if we're overwriting some, otherwise let them pass through to the other macro */ snprintf(varname, sizeof(varname), "ARG%d", argc); if ((argp = pbx_builtin_getvar_helper(chan, varname))) { oldargs[argc] = ast_strdup(argp); } pbx_builtin_setvar_helper(chan, varname, cur); argc++; } ast_channel_unlock(chan); autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { struct ast_context *c; struct ast_exten *e; int foundx; runningapp[0] = '\0'; runningdata[0] = '\0'; /* What application will execute? */ if (ast_rdlock_contexts()) { ast_log(LOG_WARNING, "Failed to lock contexts list\n"); } else { for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) { if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) { if (ast_rdlock_context(c)) { ast_log(LOG_WARNING, "Unable to lock context?\n"); } else { e = find_matching_priority(c, ast_channel_exten(chan), ast_channel_priority(chan), S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)); if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */ ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp)); ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata)); } ast_unlock_context(c); } break; } } } ast_unlock_contexts(); /* Reset the macro depth, if it was changed in the last iteration */ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); res = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL), &foundx, 1); if (res) { /* Something bad happened, or a hangup has been requested. */ if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) || (res == '*') || (res == '#')) { /* Just return result as to the previous application as if it had been dialed */ ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res); break; } switch(res) { case MACRO_EXIT_RESULT: res = 0; goto out; default: ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro); ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro); goto out; } } ast_debug(1, "Executed application: %s\n", runningapp); if (!strcasecmp(runningapp, "GOSUB")) { gosub_level++; ast_debug(1, "Incrementing gosub_level\n"); } else if (!strcasecmp(runningapp, "GOSUBIF")) { char *cond, *app_arg; char *app2; ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata); app2 = ast_str_buffer(tmp_subst); cond = strsep(&app2, "?"); app_arg = strsep(&app2, ":"); if (pbx_checkcondition(cond)) { if (!ast_strlen_zero(app_arg)) { gosub_level++; ast_debug(1, "Incrementing gosub_level\n"); } } else { if (!ast_strlen_zero(app2)) { gosub_level++; ast_debug(1, "Incrementing gosub_level\n"); } } } else if (!strcasecmp(runningapp, "RETURN")) { gosub_level--; ast_debug(1, "Decrementing gosub_level\n"); } else if (!strcasecmp(runningapp, "STACKPOP")) { gosub_level--; ast_debug(1, "Decrementing gosub_level\n"); } else if (!strncasecmp(runningapp, "EXEC", 4)) { /* Must evaluate args to find actual app */ char *tmp2, *tmp3 = NULL; ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata); tmp2 = ast_str_buffer(tmp_subst); if (!strcasecmp(runningapp, "EXECIF")) { if ((tmp3 = strchr(tmp2, '|'))) { *tmp3++ = '\0'; } if (!pbx_checkcondition(tmp2)) { tmp3 = NULL; } } else { tmp3 = tmp2; } if (tmp3) { ast_debug(1, "Last app: %s\n", tmp3); } if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) { gosub_level++; ast_debug(1, "Incrementing gosub_level\n"); } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) { gosub_level--; ast_debug(1, "Decrementing gosub_level\n"); } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) { gosub_level--; ast_debug(1, "Decrementing gosub_level\n"); } } if (gosub_level == 0 && strcasecmp(ast_channel_context(chan), fullmacro)) { ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", ast_channel_name(chan), macro); break; } /* don't stop executing extensions when we're in "h" */ if (ast_check_hangup(chan) && !inhangup) { ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", ast_channel_exten(chan), ast_channel_macroexten(chan), ast_channel_priority(chan)); goto out; } ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); } out: /* Don't let the channel change now. */ ast_channel_lock(chan); /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */ snprintf(depthc, sizeof(depthc), "%d", depth); pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP); for (x = 1; x < argc; x++) { /* Restore old arguments and delete ours */ snprintf(varname, sizeof(varname), "ARG%d", x); if (oldargs[x]) { pbx_builtin_setvar_helper(chan, varname, oldargs[x]); ast_free(oldargs[x]); } else { pbx_builtin_setvar_helper(chan, varname, NULL); } } /* Restore macro variables */ pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten); pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context); pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority); if (save_macro_exten) ast_free(save_macro_exten); if (save_macro_context) ast_free(save_macro_context); if (save_macro_priority) ast_free(save_macro_priority); if (setmacrocontext) { ast_channel_macrocontext_set(chan, ""); ast_channel_macroexten_set(chan, ""); ast_channel_macropriority_set(chan, 0); } if (!strcasecmp(ast_channel_context(chan), fullmacro)) { const char *offsets; /* If we're leaving the macro normally, restore original information */ ast_channel_priority_set(chan, oldpriority); ast_channel_context_set(chan, oldcontext); ast_channel_exten_set(chan, oldexten); if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) { /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue normally if there is any problem */ if (sscanf(offsets, "%30d", &offset) == 1) { if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + offset + 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { ast_channel_priority_set(chan, ast_channel_priority(chan) + offset); } } } } pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset); if (save_macro_offset) ast_free(save_macro_offset); /* Unlock the macro */ if (exclusive) { ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro); if (ast_context_unlockmacro(fullmacro)) { ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro); res = 0; } } ast_channel_unlock(chan); ast_free(tmp_subst); return res; }
static void ari_channels_handle_originate_with_id(const char *args_endpoint, const char *args_extension, const char *args_context, long args_priority, const char *args_app, const char *args_app_args, const char *args_caller_id, int args_timeout, struct ast_variable *variables, const char *args_channel_id, const char *args_other_channel_id, const char *args_originator, struct ast_ari_response *response) { char *dialtech; char dialdevice[AST_CHANNEL_NAME]; struct ast_dial *dial; char *caller_id = NULL; char *cid_num = NULL; char *cid_name = NULL; char *stuff; struct ast_channel *other = NULL; struct ast_channel *chan; RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); struct ast_assigned_ids assignedids = { .uniqueid = args_channel_id, .uniqueid2 = args_other_channel_id, }; struct ari_origination *origination; pthread_t thread; if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid)) || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) { ast_ari_response_error(response, 400, "Bad Request", "Uniqueid length exceeds maximum of %d", AST_MAX_PUBLIC_UNIQUEID); return; } if (ast_strlen_zero(args_endpoint)) { ast_ari_response_error(response, 400, "Bad Request", "Endpoint must be specified"); return; } dialtech = ast_strdupa(args_endpoint); if ((stuff = strchr(dialtech, '/'))) { *stuff++ = '\0'; ast_copy_string(dialdevice, stuff, sizeof(dialdevice)); } if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) { ast_ari_response_error(response, 400, "Bad Request", "Invalid endpoint specified"); return; } if (!ast_strlen_zero(args_app)) { RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free); if (!appdata) { ast_ari_response_alloc_failed(response); return; } ast_str_set(&appdata, 0, "%s", args_app); if (!ast_strlen_zero(args_app_args)) { ast_str_append(&appdata, 0, ",%s", args_app_args); } origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1); if (!origination) { ast_ari_response_alloc_failed(response); return; } strcpy(origination->appdata, ast_str_buffer(appdata)); } else if (!ast_strlen_zero(args_extension)) {
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) { RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup); struct rx_task_data *task_data; RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); pjsip_sip_uri *uri; char *domain_name; char *configured_aors, *aor_name; RAII_VAR(struct ast_str *, id, NULL, ast_free); if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { return PJ_FALSE; } if (ast_strlen_zero(endpoint->aors)) { /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors"); ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received"); ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); domain_name = ast_alloca(uri->host.slen + 1); ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1); if (endpoint->bypass_auth) { uri->user = pj_str((char *) ast_sorcery_object_get_id(endpoint)); } configured_aors = ast_strdupa(endpoint->aors); /* Iterate the configured AORs to see if the user or the user+domain match */ while ((aor_name = strsep(&configured_aors, ","))) { struct ast_sip_domain_alias *alias = NULL; if (!pj_strcmp2(&uri->user, aor_name)) { break; } if (!id && !(id = ast_str_create(uri->user.slen + uri->host.slen + 2))) { return PJ_TRUE; } ast_str_set(&id, 0, "%.*s@", (int)uri->user.slen, uri->user.ptr); if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { ast_str_append(&id, 0, "%s", alias->domain); ao2_cleanup(alias); } else { ast_str_append(&id, 0, "%s", domain_name); } if (!strcmp(aor_name, ast_str_buffer(id))) { ast_free(id); break; } } if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); ast_log(LOG_WARNING, "AOR '%.*s' not found for endpoint '%s'\n", (int)uri->user.slen, uri->user.ptr, ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!aor->max_contacts) { /* Registration is not permitted for this AOR */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted"); ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n", ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!(ser = serializer_find_or_create(aor_name))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (ast_sip_push_task(ser->serializer, rx_task, task_data)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); ao2_ref(task_data, -1); } return PJ_TRUE; }
static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, struct ast_sip_message_accumulator *counter) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", sub->id), ao2_cleanup); char *endpoint_aors; char *aor_name; struct ast_sip_body body; struct ast_str *body_text; struct ast_sip_body_data body_data = { .body_type = AST_SIP_MESSAGE_ACCUMULATOR, .body_data = counter, }; if (!endpoint) { ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n", sub->id); return; } if (ast_strlen_zero(endpoint->aors)) { ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because the endpoint has no" " configured AORs\n", sub->id); return; } body.type = MWI_TYPE; body.subtype = MWI_SUBTYPE; body_text = ast_str_create(64); if (!body_text) { return; } if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) { ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n"); ast_free(body_text); return; } body.body_text = ast_str_buffer(body_text); endpoint_aors = ast_strdupa(endpoint->aors); ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n", sub->id, counter->new_msgs, counter->old_msgs); while ((aor_name = strsep(&endpoint_aors, ","))) { RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); struct unsolicited_mwi_data mwi_data = { .sub = sub, .endpoint = endpoint, .body = &body, }; if (!aor) { ast_log(LOG_WARNING, "Unable to locate AOR %s for unsolicited MWI\n", aor_name); continue; } contacts = ast_sip_location_retrieve_aor_contacts(aor); if (!contacts || (ao2_container_count(contacts) == 0)) { ast_log(LOG_NOTICE, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name); continue; } ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data); } ast_free(body_text); }
static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request) { pjsip_auth_clt_sess auth_sess; pjsip_cseq_hdr *cseq; pj_status_t status; struct ast_sip_endpoint *endpoint; char *id = NULL; const char *id_type; pjsip_www_authenticate_hdr *auth_hdr; struct ast_str *realms; pjsip_dialog *dlg; dlg = pjsip_rdata_get_dlg(challenge); if (dlg) { endpoint = ast_sip_dialog_get_endpoint(dlg); id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL; ao2_cleanup(endpoint); id_type = "Endpoint"; } /* If there was no dialog, then this is probably a REGISTER so no endpoint */ if (!id) { id = ast_alloca(strlen(challenge->pkt_info.src_name) + 7 /* ':' + port + NULL */); sprintf(id, "%s:%d", challenge->pkt_info.src_name, challenge->pkt_info.src_port); id_type = "Host"; } auth_hdr = get_auth_header(challenge, NULL); if (auth_hdr == NULL) { ast_log(LOG_ERROR, "%s: '%s': Unable to find authenticate header in challenge.\n", id_type, id); return -1; } if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(), old_request->pool, 0) != PJ_SUCCESS) { ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n", id_type, id); return -1; } if (set_outbound_authentication_credentials(&auth_sess, auths, challenge, auth_hdr)) { ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n", id_type, id); #if defined(HAVE_PJSIP_AUTH_CLT_DEINIT) /* In case it is not a noop here in the future. */ pjsip_auth_clt_deinit(&auth_sess); #endif return -1; } status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request); #if defined(HAVE_PJSIP_AUTH_CLT_DEINIT) /* Release any cached auths */ pjsip_auth_clt_deinit(&auth_sess); #endif switch (status) { case PJ_SUCCESS: /* PJSIP creates a new transaction for new_request (meaning it creates a new * branch). However, it recycles the Call-ID, from-tag, and CSeq from the * original request. Some SIP implementations will not process the new request * since the CSeq is the same as the original request. Incrementing it here * fixes the interop issue */ cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL); ast_assert(cseq != NULL); ++cseq->cseq; return 0; case PJSIP_ENOCREDENTIAL: realms = ast_str_create(32); if (realms) { ast_str_append(&realms, 0, "%.*s", (int)auth_hdr->challenge.common.realm.slen, auth_hdr->challenge.common.realm.ptr); while((auth_hdr = get_auth_header(challenge, auth_hdr->next))) { ast_str_append(&realms, 0, ",%.*s", (int)auth_hdr->challenge.common.realm.slen, auth_hdr->challenge.common.realm.ptr); } } ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. " "No auth credentials for realm(s) '%s' in challenge.\n", id_type, id, realms ? ast_str_buffer(realms) : "<unknown>"); ast_free(realms); break; case PJSIP_EAUTHSTALECOUNT: ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. Number of stale retries exceeded.\n", id_type, id); break; case PJSIP_EFAILEDCREDENTIAL: ast_log(LOG_WARNING, "%s: '%s': Authentication credentials not accepted by server.\n", id_type, id); break; default: ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. Unknown failure.\n", id_type, id); break; } return -1; }
static int rx_task(void *data) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; const char *aor_name = ast_sorcery_object_get_id(task_data->aor); RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; /* Retrieve the current contacts, we'll need to know whether to update or not */ contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* So we don't count static contacts against max_contacts we prune them out from the container */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if (registrar_validate_path(task_data, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 420, NULL, NULL, NULL); ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts); return PJ_TRUE; } if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } user_agent_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { size_t alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; char contact_uri[PJSIP_MAX_URL_SIZE]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } if (ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, task_data->endpoint)) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = task_data->aor->qualify_frequency; contact_update->authenticate_qualify = task_data->aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sorcery_delete(ast_sip_get_sorcery(), contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_cleanup(contact_update); } else { /* We want to report the user agent that was actually in the removed contact */ user_agent = ast_strdupa(contact->user_agent); ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER * do so */ if (task_data->aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Update the contacts as things will probably have changed */ ao2_cleanup(contacts); contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(task_data->rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); return PJ_TRUE; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(task_data->aor, NULL, task_data->rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint); return PJ_TRUE; }
static void register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts, struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0; int updated = 0; int deleted = 0; int permanent = 0; int contact_count; struct ao2_container *existing_contacts = NULL; pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* We create a single pool and use it throughout this function where we need one */ details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 1024, 256); if (!details.pool) { response->code = 500; return; } /* If there are any permanent contacts configured on the AOR we need to take them * into account when counting contacts. */ if (aor->permanent_contacts) { permanent = ao2_container_count(aor->permanent_contacts); } if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); response->code = 400; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); response->code = 420; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (aor->remove_existing) { /* Cumulative number of contacts affected by this registration */ contact_count = MAX(updated + added - deleted, 0); /* We need to keep track of only existing contacts so we can later * remove them if need be. */ existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, ast_sorcery_object_id_compare); if (!existing_contacts) { response->code = 500; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts); } else { /* Total contacts after this registration */ contact_count = ao2_container_count(contacts) - permanent + added - deleted; } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); response->code = 403; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); ao2_cleanup(existing_contacts); return; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); /* If we are keeping track of existing contacts for removal then, well, there is * absolutely nothing left so no need to try to remove any. */ if (existing_contacts) { ao2_ref(existing_contacts, -1); existing_contacts = NULL; } break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); /* If a contact was returned and we need to keep track of existing contacts then it * should be removed. */ if (contact && existing_contacts) { ao2_unlink(existing_contacts, contact); } if (!contact) { int prune_on_boot; /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata); contact = ast_sip_location_create_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint); if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } if (prune_on_boot) { const char *contact_name; struct contact_transport_monitor *monitor; /* * Monitor the transport in case it gets disconnected because * the contact won't be valid anymore if that happens. */ contact_name = ast_sorcery_object_get_id(contact); monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (monitor) { strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_register(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor); ao2_ref(monitor, -1); } } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); ao2_link(contacts, contact); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_link(contacts, contact_update); ao2_cleanup(contact_update); } else { if (contact->prune_on_boot) { struct contact_transport_monitor *monitor; const char *contact_name = ast_sorcery_object_get_id(contact); monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name)); strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_unregister(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher); } /* We want to report the user agent that was actually in the removed contact */ ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* * If the AOR is configured to remove any contacts over max_contacts * that have not been updated/added/deleted as a result of this * REGISTER do so. * * The existing contacts container holds all contacts that were not * involved in this REGISTER. * The contacts container holds the current contacts of the AOR. */ if (aor->remove_existing && existing_contacts) { /* Total contacts after this registration */ contact_count = ao2_container_count(existing_contacts) + updated + added; if (contact_count > aor->max_contacts) { /* Remove excess existing contacts that expire the soonest */ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts); } ao2_ref(existing_contacts, -1); } response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); response->code = 500; return; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } response->tdata = tdata; }
/*! * \internal * \brief ARI HTTP handler. * * This handler takes the HTTP request and turns it into the appropriate * RESTful request (conversion to JSON, routing, etc.) * * \param ser TCP session. * \param urih URI handler. * \param uri URI requested. * \param method HTTP method. * \param get_params HTTP \c GET params. * \param headers HTTP headers. */ static int ast_ari_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) { RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, response_headers, ast_str_create(40), ast_free); RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free); RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); struct ast_ari_response response = {}; int ret = 0; if (!response_headers || !response_body) { return -1; } response.headers = ast_str_create(40); if (!response.headers) { return -1; } conf = ast_ari_config_get(); if (!conf || !conf->general) { return -1; } process_cors_request(headers, &response); user = authenticate_user(get_params, headers); if (!user) { /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response * message is used by an origin server to challenge the * authorization of a user agent. This response MUST include a * WWW-Authenticate header field containing at least one * challenge applicable to the requested resource. */ response.response_code = 401; response.response_text = "Unauthorized"; /* Section 1.2: * realm = "realm" "=" realm-value * realm-value = quoted-string * Section 2: * challenge = "Basic" realm */ ast_str_append(&response.headers, 0, "WWW-Authenticate: Basic realm=\"%s\"\r\n", conf->general->auth_realm); response.message = ast_json_pack("{s: s}", "error", "Authentication required"); } else if (!ast_fully_booted) { response.response_code = 503; response.response_text = "Service Unavailable"; response.message = ast_json_pack("{s: s}", "error", "Asterisk not booted"); } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) { response.message = ast_json_pack("{s: s}", "error", "Write access denied"); response.response_code = 403; response.response_text = "Forbidden"; } else if (ast_ends_with(uri, "/")) { remove_trailing_slash(uri, &response); } else if (ast_begins_with(uri, "api-docs/")) { /* Serving up API docs */ if (method != AST_HTTP_GET) { response.message = ast_json_pack("{s: s}", "message", "Unsupported method"); response.response_code = 405; response.response_text = "Method Not Allowed"; } else { /* Skip the api-docs prefix */ ast_ari_get_docs(strchr(uri, '/') + 1, headers, &response); } } else { /* Other RESTful resources */ ast_ari_invoke(ser, uri, method, get_params, headers, &response); } if (response.no_response) { /* The handler indicates no further response is necessary. * Probably because it already handled it */ return 0; } /* If you explicitly want to have no content, set message to * ast_json_null(). */ ast_assert(response.message != NULL); ast_assert(response.response_code > 0); ast_str_append(&response_headers, 0, "%s", ast_str_buffer(response.headers)); /* response.message could be NULL, in which case the empty response_body * is correct */ if (response.message && !ast_json_is_null(response.message)) { ast_str_append(&response_headers, 0, "Content-type: application/json\r\n"); if (ast_json_dump_str_format(response.message, &response_body, conf->general->format) != 0) { /* Error encoding response */ response.response_code = 500; response.response_text = "Internal Server Error"; ast_str_set(&response_body, 0, "%s", ""); ast_str_set(&response_headers, 0, "%s", ""); ret = -1; } } ast_http_send(ser, method, response.response_code, response.response_text, response_headers, response_body, 0, 0); /* ast_http_send takes ownership, so we don't have to free them */ response_headers = NULL; response_body = NULL; ast_json_unref(response.message); return ret; }
static int http_post_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_vars, struct ast_variable *headers) { struct ast_variable *var, *cookies; unsigned long ident = 0; FILE *f; int content_len = 0; struct ast_str *post_dir; GMimeMessage *message; int message_count = 0; char * boundary_marker = NULL; if (method != AST_HTTP_POST) { ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); return -1; } if (!astman_is_authed(ast_http_manid_from_vars(headers))) { ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave."); return -1; } if (!urih) { ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request"); return -1; } cookies = ast_http_get_cookies(headers); for (var = cookies; var; var = var->next) { if (!strcasecmp(var->name, "mansession_id")) { sscanf(var->value, "%30lx", &ident); break; } } if (cookies) { ast_variables_destroy(cookies); } if (ident == 0) { ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request."); return -1; } if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) { ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request."); return -1; } if (!(f = tmpfile())) { ast_log(LOG_ERROR, "Could not create temp file.\n"); ast_http_error(ser, 500, "Internal server error", "Could not create temp file."); return -1; } for (var = headers; var; var = var->next) { fprintf(f, "%s: %s\r\n", var->name, var->value); if (!strcasecmp(var->name, "Content-Length")) { if ((sscanf(var->value, "%30u", &content_len)) != 1) { ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n"); fclose(f); ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!"); return -1; } ast_debug(1, "Got a Content-Length of %d\n", content_len); } else if (!strcasecmp(var->name, "Content-Type")) { boundary_marker = strstr(var->value, "boundary="); if (boundary_marker) { boundary_marker += strlen("boundary="); } } } fprintf(f, "\r\n"); if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) { if (option_debug) { ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n"); } fclose(f); return -1; } if (fseek(f, SEEK_SET, 0)) { ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n"); fclose(f); ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning."); return -1; } post_dir = urih->data; message = parse_message(f); /* Takes ownership and will close f */ if (!message) { ast_log(LOG_ERROR, "Error parsing MIME data\n"); ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request."); return -1; } if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) { ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n"); g_object_unref(message); ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request."); return -1; } g_object_unref(message); ast_http_error(ser, 200, "OK", "File successfully uploaded."); return 0; }
/*! * \internal * \brief ARI HTTP handler. * * This handler takes the HTTP request and turns it into the appropriate * RESTful request (conversion to JSON, routing, etc.) * * \param ser TCP session. * \param urih URI handler. * \param uri URI requested. * \param method HTTP method. * \param get_params HTTP \c GET params. * \param headers HTTP headers. */ static int ast_ari_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) { RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free); RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); struct ast_ari_response response = { .fd = -1, 0 }; RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy); if (!response_body) { ast_http_request_close_on_completion(ser); ast_http_error(ser, 500, "Server Error", "Out of memory"); return 0; } response.headers = ast_str_create(40); if (!response.headers) { ast_http_request_close_on_completion(ser); ast_http_error(ser, 500, "Server Error", "Out of memory"); return 0; } conf = ast_ari_config_get(); if (!conf || !conf->general) { ast_free(response.headers); ast_http_request_close_on_completion(ser); ast_http_error(ser, 500, "Server Error", "URI handler config missing"); return 0; } process_cors_request(headers, &response); /* Process form data from a POST. It could be mixed with query * parameters, which seems a bit odd. But it's allowed, so that's okay * with us. */ post_vars = ast_http_get_post_vars(ser, headers); if (!post_vars) { switch (errno) { case EFBIG: ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large"); goto request_failed; case ENOMEM: ast_http_request_close_on_completion(ser); ast_ari_response_error(&response, 500, "Internal Server Error", "Out of memory"); goto request_failed; case EIO: ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body"); goto request_failed; } } if (get_params == NULL) { get_params = post_vars; } else if (get_params && post_vars) { /* Has both post_vars and get_params */ struct ast_variable *last_var = post_vars; while (last_var->next) { last_var = last_var->next; } /* The duped get_params will get freed when post_vars gets * ast_variables_destroyed. */ last_var->next = ast_variables_dup(get_params); get_params = post_vars; } user = authenticate_user(get_params, headers); if (response.response_code > 0) { /* POST parameter processing error. Do nothing. */ } else if (!user) { /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response * message is used by an origin server to challenge the * authorization of a user agent. This response MUST include a * WWW-Authenticate header field containing at least one * challenge applicable to the requested resource. */ ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required"); /* Section 1.2: * realm = "realm" "=" realm-value * realm-value = quoted-string * Section 2: * challenge = "Basic" realm */ ast_str_append(&response.headers, 0, "WWW-Authenticate: Basic realm=\"%s\"\r\n", conf->general->auth_realm); } else if (!ast_fully_booted) { ast_http_request_close_on_completion(ser); ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted"); } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) { ast_ari_response_error(&response, 403, "Forbidden", "Write access denied"); } else if (ast_ends_with(uri, "/")) { remove_trailing_slash(uri, &response); } else if (ast_begins_with(uri, "api-docs/")) { /* Serving up API docs */ if (method != AST_HTTP_GET) { ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method"); } else { /* Skip the api-docs prefix */ ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, &response); } } else { /* Other RESTful resources */ ast_ari_invoke(ser, uri, method, get_params, headers, &response); } if (response.no_response) { /* The handler indicates no further response is necessary. * Probably because it already handled it */ ast_free(response.headers); return 0; } request_failed: /* If you explicitly want to have no content, set message to * ast_json_null(). */ ast_assert(response.message != NULL); ast_assert(response.response_code > 0); /* response.message could be NULL, in which case the empty response_body * is correct */ if (response.message && !ast_json_is_null(response.message)) { ast_str_append(&response.headers, 0, "Content-type: application/json\r\n"); if (ast_json_dump_str_format(response.message, &response_body, conf->general->format) != 0) { /* Error encoding response */ response.response_code = 500; response.response_text = "Internal Server Error"; ast_str_set(&response_body, 0, "%s", ""); ast_str_set(&response.headers, 0, "%s", ""); } } ast_debug(3, "Examining ARI response:\n%d %s\n%s\n%s\n", response.response_code, response.response_text, ast_str_buffer(response.headers), ast_str_buffer(response_body)); ast_http_send(ser, method, response.response_code, response.response_text, response.headers, response_body, response.fd != -1 ? response.fd : 0, 0); /* ast_http_send takes ownership, so we don't have to free them */ response_body = NULL; ast_json_unref(response.message); if (response.fd >= 0) { close(response.fd); } return 0; } static struct ast_http_uri http_uri = { .callback = ast_ari_callback, .description = "Asterisk RESTful API", .uri = "ari", .has_subtree = 1, .data = NULL, .key = __FILE__, .no_decode_uri = 1, }; static int load_module(void) { ast_mutex_init(&root_handler_lock); /* root_handler may have been built during a declined load */ if (!root_handler) { root_handler = root_handler_create(); } if (!root_handler) { return AST_MODULE_LOAD_FAILURE; } /* oom_json may have been built during a declined load */ if (!oom_json) { oom_json = ast_json_pack( "{s: s}", "error", "Allocation failed"); } if (!oom_json) { /* Ironic */ return AST_MODULE_LOAD_FAILURE; } if (ast_ari_config_init() != 0) { return AST_MODULE_LOAD_DECLINE; } if (is_enabled()) { ast_debug(3, "ARI enabled\n"); ast_http_uri_link(&http_uri); } else { ast_debug(3, "ARI disabled\n"); } if (ast_ari_cli_register() != 0) { return AST_MODULE_LOAD_FAILURE; } return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { ast_ari_cli_unregister(); if (is_enabled()) { ast_debug(3, "Disabling ARI\n"); ast_http_uri_unlink(&http_uri); } ast_ari_config_destroy(); ao2_cleanup(root_handler); root_handler = NULL; ast_mutex_destroy(&root_handler_lock); ast_json_unref(oom_json); oom_json = NULL; return 0; }
static void mwi_to_string(void *body, struct ast_str **str) { struct ast_str **mwi = body; ast_str_set(str, 0, "%s", ast_str_buffer(*mwi)); }
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 void dump_str_and_free(int fd, struct ast_str *buf) { ast_cli(fd, "%s", ast_str_buffer(buf)); ast_free(buf); }
static int add_crypto_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media) { pj_str_t stmp; pjmedia_sdp_attr *attr; enum ast_rtp_dtls_hash hash; const char *crypto_attribute; struct ast_rtp_engine_dtls *dtls; static const pj_str_t STR_NEW = { "new", 3 }; static const pj_str_t STR_EXISTING = { "existing", 8 }; static const pj_str_t STR_ACTIVE = { "active", 6 }; static const pj_str_t STR_PASSIVE = { "passive", 7 }; static const pj_str_t STR_ACTPASS = { "actpass", 7 }; static const pj_str_t STR_HOLDCONN = { "holdconn", 8 }; switch (session->endpoint->media.rtp.encryption) { case AST_SIP_MEDIA_ENCRYPT_NONE: case AST_SIP_MEDIA_TRANSPORT_INVALID: break; case AST_SIP_MEDIA_ENCRYPT_SDES: if (!session_media->srtp) { session_media->srtp = ast_sdp_srtp_alloc(); if (!session_media->srtp) { return -1; } } crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp, 0 /* DTLS running? No */, session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */); if (!crypto_attribute) { /* No crypto attribute to add, bad news */ return -1; } attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute)); media->attr[media->attr_count++] = attr; break; case AST_SIP_MEDIA_ENCRYPT_DTLS: if (setup_dtls_srtp(session, session_media)) { return -1; } dtls = ast_rtp_instance_get_dtls(session_media->rtp); if (!dtls) { return -1; } switch (dtls->get_connection(session_media->rtp)) { case AST_RTP_DTLS_CONNECTION_NEW: attr = pjmedia_sdp_attr_create(pool, "connection", &STR_NEW); media->attr[media->attr_count++] = attr; break; case AST_RTP_DTLS_CONNECTION_EXISTING: attr = pjmedia_sdp_attr_create(pool, "connection", &STR_EXISTING); media->attr[media->attr_count++] = attr; break; default: break; } switch (dtls->get_setup(session_media->rtp)) { case AST_RTP_DTLS_SETUP_ACTIVE: attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTIVE); media->attr[media->attr_count++] = attr; break; case AST_RTP_DTLS_SETUP_PASSIVE: attr = pjmedia_sdp_attr_create(pool, "setup", &STR_PASSIVE); media->attr[media->attr_count++] = attr; break; case AST_RTP_DTLS_SETUP_ACTPASS: attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTPASS); media->attr[media->attr_count++] = attr; break; case AST_RTP_DTLS_SETUP_HOLDCONN: attr = pjmedia_sdp_attr_create(pool, "setup", &STR_HOLDCONN); media->attr[media->attr_count++] = attr; break; default: break; } hash = dtls->get_fingerprint_hash(session_media->rtp); crypto_attribute = dtls->get_fingerprint(session_media->rtp); if (crypto_attribute && (hash == AST_RTP_DTLS_HASH_SHA1 || hash == AST_RTP_DTLS_HASH_SHA256)) { RAII_VAR(struct ast_str *, fingerprint, ast_str_create(64), ast_free); if (!fingerprint) { return -1; } if (hash == AST_RTP_DTLS_HASH_SHA1) { ast_str_set(&fingerprint, 0, "SHA-1 %s", crypto_attribute); } else { ast_str_set(&fingerprint, 0, "SHA-256 %s", crypto_attribute); } attr = pjmedia_sdp_attr_create(pool, "fingerprint", pj_cstr(&stmp, ast_str_buffer(fingerprint))); media->attr[media->attr_count++] = attr; } break; }
static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static unsigned long long_ret; static u_char bits_ret[2]; static char string_ret[256]; struct ast_channel *chan, *bridge; struct timeval tval; u_char *ret = NULL; int i, bit; struct ast_str *out = ast_str_alloca(2048); struct ast_channel_iterator *iter; if (header_simple_table(vp, name, length, exact, var_len, write_method, ast_active_channels())) return NULL; i = name[*length - 1] - 1; if (!(iter = ast_channel_iterator_all_new())) { return NULL; } while ((chan = ast_channel_iterator_next(iter)) && i) { ast_channel_unref(chan); i--; } iter = ast_channel_iterator_destroy(iter); if (chan == NULL) { return NULL; } *var_len = sizeof(long_ret); ast_channel_lock(chan); switch (vp->magic) { case ASTCHANINDEX: long_ret = name[*length - 1]; ret = (u_char *)&long_ret; break; case ASTCHANNAME: if (!ast_strlen_zero(ast_channel_name(chan))) { strncpy(string_ret, ast_channel_name(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANLANGUAGE: if (!ast_strlen_zero(ast_channel_language(chan))) { strncpy(string_ret, ast_channel_language(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANTYPE: strncpy(string_ret, ast_channel_tech(chan)->type, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMUSICCLASS: if (!ast_strlen_zero(ast_channel_musicclass(chan))) { strncpy(string_ret, ast_channel_musicclass(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANBRIDGE: if ((bridge = ast_bridged_channel(chan)) != NULL) { strncpy(string_ret, ast_channel_name(bridge), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQ: if (ast_channel_masq(chan) && !ast_strlen_zero(ast_channel_name(ast_channel_masq(chan)))) { strncpy(string_ret, ast_channel_name(ast_channel_masq(chan)), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANMASQR: if (ast_channel_masqr(chan) && !ast_strlen_zero(ast_channel_name(ast_channel_masqr(chan)))) { strncpy(string_ret, ast_channel_name(ast_channel_masqr(chan)), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANWHENHANGUP: if (!ast_tvzero(*ast_channel_whentohangup(chan))) { gettimeofday(&tval, NULL); long_ret = difftime(ast_channel_whentohangup(chan)->tv_sec, tval.tv_sec) * 100 - tval.tv_usec / 10000; ret= (u_char *)&long_ret; } break; case ASTCHANAPP: if (ast_channel_appl(chan)) { strncpy(string_ret, ast_channel_appl(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANDATA: if (ast_channel_data(chan)) { strncpy(string_ret, ast_channel_data(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCONTEXT: strncpy(string_ret, ast_channel_context(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROCONTEXT: strncpy(string_ret, ast_channel_macrocontext(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROEXTEN: strncpy(string_ret, ast_channel_macroexten(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANMACROPRI: long_ret = ast_channel_macropriority(chan); ret = (u_char *)&long_ret; break; case ASTCHANEXTEN: strncpy(string_ret, ast_channel_exten(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANPRI: long_ret = ast_channel_priority(chan); ret = (u_char *)&long_ret; break; case ASTCHANACCOUNTCODE: if (!ast_strlen_zero(ast_channel_accountcode(chan))) { strncpy(string_ret, ast_channel_accountcode(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANFORWARDTO: if (!ast_strlen_zero(ast_channel_call_forward(chan))) { strncpy(string_ret, ast_channel_call_forward(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANUNIQUEID: strncpy(string_ret, ast_channel_uniqueid(chan), sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; break; case ASTCHANCALLGROUP: long_ret = ast_channel_callgroup(chan); ret = (u_char *)&long_ret; break; case ASTCHANPICKUPGROUP: long_ret = ast_channel_pickupgroup(chan); ret = (u_char *)&long_ret; break; case ASTCHANSTATE: long_ret = ast_channel_state(chan) & 0xffff; ret = (u_char *)&long_ret; break; case ASTCHANMUTED: long_ret = ast_channel_state(chan) & AST_STATE_MUTE ? 1 : 2; ret = (u_char *)&long_ret; break; case ASTCHANRINGS: long_ret = ast_channel_rings(chan); ret = (u_char *)&long_ret; break; case ASTCHANCIDDNID: if (ast_channel_dialed(chan)->number.str) { strncpy(string_ret, ast_channel_dialed(chan)->number.str, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNUM: if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) { strncpy(string_ret, ast_channel_caller(chan)->id.number.str, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDNAME: if (ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) { strncpy(string_ret, ast_channel_caller(chan)->id.name.str, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDANI: if (ast_channel_caller(chan)->ani.number.valid && ast_channel_caller(chan)->ani.number.str) { strncpy(string_ret, ast_channel_caller(chan)->ani.number.str, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDRDNIS: if (ast_channel_redirecting(chan)->from.number.valid && ast_channel_redirecting(chan)->from.number.str) { strncpy(string_ret, ast_channel_redirecting(chan)->from.number.str, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANCIDPRES: long_ret = ast_party_id_presentation(&ast_channel_caller(chan)->id); ret = (u_char *)&long_ret; break; case ASTCHANCIDANI2: long_ret = ast_channel_caller(chan)->ani2; ret = (u_char *)&long_ret; break; case ASTCHANCIDTON: long_ret = ast_channel_caller(chan)->id.number.plan; ret = (u_char *)&long_ret; break; case ASTCHANCIDTNS: long_ret = ast_channel_dialed(chan)->transit_network_select; ret = (u_char *)&long_ret; break; case ASTCHANAMAFLAGS: long_ret = ast_channel_amaflags(chan); ret = (u_char *)&long_ret; break; case ASTCHANADSI: long_ret = ast_channel_adsicpe(chan); ret = (u_char *)&long_ret; break; case ASTCHANTONEZONE: if (ast_channel_zone(chan)) { strncpy(string_ret, ast_channel_zone(chan)->country, sizeof(string_ret)); string_ret[sizeof(string_ret) - 1] = '\0'; *var_len = strlen(string_ret); ret = (u_char *)string_ret; } break; case ASTCHANHANGUPCAUSE: long_ret = ast_channel_hangupcause(chan); ret = (u_char *)&long_ret; break; case ASTCHANVARIABLES: if (pbx_builtin_serialize_variables(chan, &out)) { *var_len = ast_str_strlen(out); ret = (u_char *)ast_str_buffer(out); } break; case ASTCHANFLAGS: bits_ret[0] = 0; for (bit = 0; bit < 8; bit++) bits_ret[0] |= ((ast_channel_flags(chan)->flags & (1 << bit)) >> bit) << (7 - bit); bits_ret[1] = 0; for (bit = 0; bit < 8; bit++) bits_ret[1] |= (((ast_channel_flags(chan)->flags >> 8) & (1 << bit)) >> bit) << (7 - bit); *var_len = 2; ret = bits_ret; break; case ASTCHANTRANSFERCAP: long_ret = ast_channel_transfercapability(chan); ret = (u_char *)&long_ret; default: break; } ast_channel_unlock(chan); chan = ast_channel_unref(chan); return ret; }
static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *message) { struct ast_multi_channel_blob *obj = stasis_message_data(message); struct ast_json *blob = ast_multi_channel_blob_get_json(obj); struct ast_channel_snapshot *local_snapshot_one; struct ast_channel_snapshot *local_snapshot_two; RAII_VAR(struct ast_str *, local_channel_one, NULL, ast_free); RAII_VAR(struct ast_str *, local_channel_two, NULL, ast_free); RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free); const char *event; local_snapshot_one = ast_multi_channel_blob_get_channel(obj, "1"); local_snapshot_two = ast_multi_channel_blob_get_channel(obj, "2"); if (!local_snapshot_one || !local_snapshot_two) { return NULL; } event_buffer = ast_str_create(1024); local_channel_one = ast_manager_build_channel_state_string_prefix(local_snapshot_one, "LocalOne"); local_channel_two = ast_manager_build_channel_state_string_prefix(local_snapshot_two, "LocalTwo"); if (!event_buffer || !local_channel_one || !local_channel_two) { return NULL; } if (stasis_message_type(message) == ast_local_optimization_begin_type()) { struct ast_channel_snapshot *source_snapshot; RAII_VAR(struct ast_str *, source_str, NULL, ast_free); const char *dest_uniqueid; source_snapshot = ast_multi_channel_blob_get_channel(obj, "source"); if (source_snapshot) { source_str = ast_manager_build_channel_state_string_prefix(source_snapshot, "Source"); if (!source_str) { return NULL; } } dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ? local_snapshot_one->uniqueid : local_snapshot_two->uniqueid; event = "LocalOptimizationBegin"; if (source_str) { ast_str_append(&event_buffer, 0, "%s", ast_str_buffer(source_str)); } ast_str_append(&event_buffer, 0, "DestUniqueId: %s\r\n", dest_uniqueid); ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id"))); } else if (stasis_message_type(message) == ast_local_optimization_end_type()) { event = "LocalOptimizationEnd"; ast_str_append(&event_buffer, 0, "Success: %s\r\n", ast_json_integer_get(ast_json_object_get(blob, "success")) ? "Yes" : "No"); ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id"))); } else if (stasis_message_type(message) == ast_local_bridge_type()) { event = "LocalBridge"; ast_str_append(&event_buffer, 0, "Context: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "context"))); ast_str_append(&event_buffer, 0, "Exten: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "exten"))); ast_str_append(&event_buffer, 0, "LocalOptimization: %s\r\n", ast_json_is_true(ast_json_object_get(blob, "can_optimize")) ? "Yes" : "No"); } else { return NULL; } return ast_manager_event_blob_create(EVENT_FLAG_CALL, event, "%s" "%s" "%s", ast_str_buffer(local_channel_one), ast_str_buffer(local_channel_two), ast_str_buffer(event_buffer)); }
char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format) { struct ast_sockaddr sa_ipv4; const struct ast_sockaddr *sa_tmp; char host[NI_MAXHOST]; char port[NI_MAXSERV]; struct ast_str *str; int e; static const size_t size = sizeof(host) - 1 + sizeof(port) - 1 + 4; if (ast_sockaddr_isnull(sa)) { return "(null)"; } if (!(str = ast_str_thread_get(&ast_sockaddr_stringify_buf, size))) { return ""; } if (ast_sockaddr_ipv4_mapped(sa, &sa_ipv4)) { sa_tmp = &sa_ipv4; } else { sa_tmp = sa; } if ((e = getnameinfo((struct sockaddr *)&sa_tmp->ss, sa_tmp->len, format & AST_SOCKADDR_STR_ADDR ? host : NULL, format & AST_SOCKADDR_STR_ADDR ? sizeof(host) : 0, format & AST_SOCKADDR_STR_PORT ? port : 0, format & AST_SOCKADDR_STR_PORT ? sizeof(port): 0, NI_NUMERICHOST | NI_NUMERICSERV))) { ast_log(LOG_ERROR, "getnameinfo(): %s\n", gai_strerror(e)); return ""; } if ((format & AST_SOCKADDR_STR_REMOTE) == AST_SOCKADDR_STR_REMOTE) { char *p; if (ast_sockaddr_is_ipv6_link_local(sa) && (p = strchr(host, '%'))) { *p = '\0'; } } switch ((format & AST_SOCKADDR_STR_FORMAT_MASK)) { case AST_SOCKADDR_STR_DEFAULT: ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ? "[%s]:%s" : "%s:%s", host, port); break; case AST_SOCKADDR_STR_ADDR: ast_str_set(&str, 0, "%s", host); break; case AST_SOCKADDR_STR_HOST: ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ? "[%s]" : "%s", host); break; case AST_SOCKADDR_STR_PORT: ast_str_set(&str, 0, "%s", port); break; default: ast_log(LOG_ERROR, "Invalid format\n"); return ""; } return ast_str_buffer(str); }
struct ast_json *ast_json_load_str(const struct ast_str *input, struct ast_json_error *error) { return ast_json_load_string(ast_str_buffer(input), error); }
/*! * \internal * \brief Get the requested mailboxes. * \since 12.1.0 * * \param s AMI session. * \param m AMI message. * * \retval 0 to keep AMI connection. * \retval -1 to disconnect AMI connection. */ static int mwi_mailbox_get(struct mansession *s, const struct message *m) { char id_text[256]; const char *id; const char *mailbox_id = astman_get_header(m, "Mailbox"); const struct ast_mwi_mailbox_object *mailbox; struct ao2_container *mailboxes; unsigned count; struct ao2_iterator iter; if (ast_strlen_zero(mailbox_id)) { astman_send_error(s, m, "Missing mailbox parameter in request"); return 0; } if (*mailbox_id == '/') { struct ast_str *regex_string; regex_string = ast_str_create(strlen(mailbox_id) + 1); if (!regex_string) { astman_send_error(s, m, "Memory Allocation Failure"); return 0; } /* Make "/regex/" into "regex" */ if (ast_regex_string_to_regex_pattern(mailbox_id, ®ex_string) != 0) { astman_send_error_va(s, m, "Mailbox regex format invalid in: %s", mailbox_id); ast_free(regex_string); return 0; } mailboxes = ast_mwi_mailbox_get_by_regex(ast_str_buffer(regex_string)); ast_free(regex_string); } else { mailboxes = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL); if (mailboxes) { mailbox = ast_mwi_mailbox_get(mailbox_id); if (mailbox) { if (!ao2_link(mailboxes, (void *) mailbox)) { ao2_ref(mailboxes, -1); mailboxes = NULL; } ast_mwi_mailbox_unref(mailbox); } } } if (!mailboxes) { astman_send_error(s, m, "Mailbox container creation failure"); return 0; } astman_send_listack(s, m, "Mailboxes will follow", "start"); id = astman_get_header(m, "ActionID"); if (!ast_strlen_zero(id)) { snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id); } else { id_text[0] = '\0'; } /* Output mailbox list. */ count = 0; iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK); for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) { ++count; astman_append(s, "Event: MWIGet\r\n" "Mailbox: %s\r\n" "OldMessages: %u\r\n" "NewMessages: %u\r\n" "%s" "\r\n", ast_mwi_mailbox_get_id(mailbox), ast_mwi_mailbox_get_msgs_old(mailbox), ast_mwi_mailbox_get_msgs_new(mailbox), id_text); } ao2_iterator_destroy(&iter); ao2_ref(mailboxes, -1); astman_send_list_complete_start(s, m, "MWIGetComplete", count); astman_send_list_complete_end(s); return 0; }
void ast_ari_get_docs(const char *uri, struct ast_variable *headers, struct ast_ari_response *response) { RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free); RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free); RAII_VAR(char *, absolute_filename, NULL, ast_std_free); struct ast_json *obj = NULL; struct ast_variable *host = NULL; struct ast_json_error error = {}; struct stat file_stat; ast_debug(3, "%s(%s)\n", __func__, uri); absolute_path_builder = ast_str_create(80); if (absolute_path_builder == NULL) { ast_ari_response_alloc_failed(response); return; } /* absolute path to the rest-api directory */ ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR); ast_str_append(&absolute_path_builder, 0, "/rest-api/"); absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL); if (absolute_api_dirname == NULL) { ast_log(LOG_ERROR, "Error determining real directory for rest-api\n"); ast_ari_response_error( response, 500, "Internal Server Error", "Cannot find rest-api directory"); return; } /* absolute path to the requested file */ ast_str_append(&absolute_path_builder, 0, "%s", uri); absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL); if (absolute_filename == NULL) { switch (errno) { case ENAMETOOLONG: case ENOENT: case ENOTDIR: ast_ari_response_error( response, 404, "Not Found", "Resource not found"); break; case EACCES: ast_ari_response_error( response, 403, "Forbidden", "Permission denied"); break; default: ast_log(LOG_ERROR, "Error determining real path for uri '%s': %s\n", uri, strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Cannot find file"); break; } return; } if (!ast_begins_with(absolute_filename, absolute_api_dirname)) { /* HACKERZ! */ ast_log(LOG_ERROR, "Invalid attempt to access '%s' (not in %s)\n", absolute_filename, absolute_api_dirname); ast_ari_response_error( response, 404, "Not Found", "Resource not found"); return; } if (stat(absolute_filename, &file_stat) == 0) { if (!(file_stat.st_mode & S_IFREG)) { /* Not a file */ ast_ari_response_error( response, 403, "Forbidden", "Invalid access"); return; } } else { /* Does not exist */ ast_ari_response_error( response, 404, "Not Found", "Resource not found"); return; } /* Load resource object from file */ obj = ast_json_load_new_file(absolute_filename, &error); if (obj == NULL) { ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n", error.source, error.line, error.column, error.text); ast_ari_response_error( response, 500, "Internal Server Error", "Yikes! Cannot parse resource"); return; } /* Update the basePath properly */ if (ast_json_object_get(obj, "basePath") != NULL) { for (host = headers; host; host = host->next) { if (strcasecmp(host->name, "Host") == 0) { break; } } if (host != NULL) { ast_json_object_set( obj, "basePath", ast_json_stringf("http://%s/ari", host->value)); } else { /* Without the host, we don't have the basePath */ ast_json_object_del(obj, "basePath"); } } ast_ari_response_ok(response, obj); }
static int manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text) { struct parked_user *curuser; struct ao2_container *lot_container; struct ao2_iterator iter_lots; struct ao2_iterator iter_users; struct parking_lot *curlot; int total = 0; lot_container = get_parking_lot_container(); if (!lot_container) { ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n"); astman_send_error(s, m, "Could not create parking lot list"); return RESULT_SUCCESS; } iter_lots = ao2_iterator_init(lot_container, 0); astman_send_ack(s, m, "Parked calls will follow"); while ((curlot = ao2_iterator_next(&iter_lots))) { iter_users = ao2_iterator_init(curlot->parked_users, 0); while ((curuser = ao2_iterator_next(&iter_users))) { RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free); payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL); if (!payload) { return RESULT_FAILURE; } parked_call_string = manager_build_parked_call_string(payload); if (!payload) { return RESULT_FAILURE; } total++; astman_append(s, "Event: ParkedCall\r\n" "%s" /* The parked call string */ "%s" /* The action ID */ "\r\n", ast_str_buffer(parked_call_string), id_text); ao2_ref(curuser, -1); } ao2_iterator_destroy(&iter_users); ao2_ref(curlot, -1); } ao2_iterator_destroy(&iter_lots); astman_append(s, "Event: ParkedCallsComplete\r\n" "Total: %d\r\n" "%s" "\r\n", total, id_text); return RESULT_SUCCESS; }