Exemplo n.º 1
0
char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
	RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
	RAII_VAR(void *, object, NULL, ao2_cleanup);
	int is_container = 0;
	const char *cmd1;
	const char *cmd2;
	const char *object_id;
	char formatter_type[64];

	struct ast_sip_cli_context context = {
		.indent_level = 0,
		.show_details = 0,
		.show_details_only_level_0 = 0,
		.recurse = 0,
	};

	if (cmd == CLI_INIT) {
		return NULL;
	}

	cmd1 = e->cmda[1];
	cmd2 = e->cmda[2];
	object_id = a->argv[3];

	if (!ast_ends_with(cmd2, "s")) {
		ast_copy_string(formatter_type, cmd2, sizeof(formatter_type));
		is_container = 0;
	} else if (ast_ends_with(cmd2, "ies")) {
		/* Take the plural "ies" off of the object name and re[place with "y". */
		int l = strlen(cmd2);
		snprintf(formatter_type, 64, "%*.*sy", l - 3, l - 3, cmd2);
		is_container = 1;
	} else {
		/* Take the plural "s" off of the object name. */
		ast_copy_string(formatter_type, cmd2, strlen(cmd2));
		is_container = 1;
	}

	if (!strcmp(cmd1, "show")) {
		context.show_details_only_level_0 = !is_container;
		context.recurse = 1;
	} else {
		is_container = 1;
	}

	if (cmd == CLI_GENERATE
		&& (is_container
			|| a->argc > 4
			|| (a->argc == 4 && ast_strlen_zero(a->word)))) {
		return CLI_SUCCESS;
	}

	context.output_buffer = ast_str_create(256);
	if (!context.output_buffer) {
		return CLI_FAILURE;
	}

	formatter_entry = ast_sip_lookup_cli_formatter(formatter_type);
	if (!formatter_entry) {
		ast_log(LOG_ERROR, "No formatter registered for object type %s.\n",
			formatter_type);
		ast_free(context.output_buffer);
		return CLI_FAILURE;
	}
	ast_str_append(&context.output_buffer, 0, "\n");
	formatter_entry->print_header(NULL, &context, 0);
	ast_str_append(&context.output_buffer, 0,
		" =========================================================================================\n\n");

	if (is_container || cmd == CLI_GENERATE) {
		container = formatter_entry->get_container();
		if (!container) {
			ast_cli(a->fd, "No container returned for object type %s.\n",
				formatter_type);
			ast_free(context.output_buffer);
			return CLI_FAILURE;
		}
	}

	if (cmd == CLI_GENERATE) {
		ast_free(context.output_buffer);
		return complete_show_sorcery_object(container, formatter_entry, a->word, a->n);
	}

	if (is_container) {
		if (!ao2_container_count(container)) {
			ast_free(context.output_buffer);
			ast_cli(a->fd, "No objects found.\n\n");
			return CLI_SUCCESS;
		}
		ao2_callback(container, OBJ_NODATA, formatter_entry->print_body, &context);
	} else {
		if (ast_strlen_zero(object_id)) {
			ast_free(context.output_buffer);
			ast_cli(a->fd, "No object specified.\n");
			return CLI_FAILURE;
		}

		object = formatter_entry->retrieve_by_id(object_id);
		if (!object) {
			ast_free(context.output_buffer);
			ast_cli(a->fd, "Unable to find object %s.\n\n", object_id);
			return CLI_SUCCESS;
		}
		formatter_entry->print_body(object, &context, 0);
	}

	ast_str_append(&context.output_buffer, 0, "\n");
	dump_str_and_free(a->fd, context.output_buffer);
	return CLI_SUCCESS;
}

static int formatter_sort(const void *obj, const void *arg, int flags)
{
	const struct ast_sip_cli_formatter_entry *left_obj = obj;
	const struct ast_sip_cli_formatter_entry *right_obj = arg;
	const char *right_key = arg;
	int cmp = 0;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_OBJECT:
		right_key = right_obj->name;
		/* Fall through */
	case OBJ_SEARCH_KEY:
		cmp = strcmp(left_obj->name, right_key);
		break;
	case OBJ_SEARCH_PARTIAL_KEY:
		cmp = strncmp(left_obj->name, right_key, strlen(right_key));
		break;
	default:
		cmp = 0;
		break;
	}

	return cmp;
}

static int formatter_compare(void *obj, void *arg, int flags)
{
	const struct ast_sip_cli_formatter_entry *left_obj = obj;
	const struct ast_sip_cli_formatter_entry *right_obj = arg;
	const char *right_key = arg;
	int cmp = 0;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_OBJECT:
		right_key = right_obj->name;
		/* Fall through */
	case OBJ_SEARCH_KEY:
		if (strcmp(left_obj->name, right_key) == 0) {;
			cmp = CMP_MATCH | CMP_STOP;
		}
		break;
	case OBJ_SEARCH_PARTIAL_KEY:
		if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
			cmp = CMP_MATCH;
		}
		break;
	default:
		cmp = 0;
		break;
	}

	return cmp;
}

static int formatter_hash(const void *obj, int flags)
{
	const struct ast_sip_cli_formatter_entry *left_obj = obj;
	if (flags & OBJ_SEARCH_OBJECT) {
		return ast_str_hash(left_obj->name);
	} else if (flags & OBJ_SEARCH_KEY) {
		return ast_str_hash(obj);
	}

	return -1;
}

struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
{
	return ao2_find(formatter_registry, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
}

int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
{
	ast_assert(formatter != NULL);
	ast_assert(formatter->name != NULL);
	ast_assert(formatter->print_body != NULL);
	ast_assert(formatter->print_header != NULL);
	ast_assert(formatter->get_container != NULL);
	ast_assert(formatter->iterate != NULL);
	ast_assert(formatter->get_id != NULL);
	ast_assert(formatter->retrieve_by_id != NULL);

	ao2_link(formatter_registry, formatter);

	return 0;
}
Exemplo n.º 2
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 = {};
    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, 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,
                  0, 0);
    /* ast_http_send takes ownership, so we don't have to free them */
    response_body = NULL;

    ast_json_unref(response.message);
    return 0;
}
Exemplo n.º 3
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_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;
}