From aa9db707c56fa673560e25663f581954a66f3974 Mon Sep 17 00:00:00 2001 From: Jonathan Rose Date: Tue, 14 Jan 2014 23:44:57 +0000 Subject: [PATCH] ARI: Add mailboxes resource for controlling and polling external MWI Adds the following AMI commands: PUT mailboxes/mailboxName modifies mailbox state and implicitly creates new mailboxes GET mailboxes/mailboxName retrieves a JSON representation of a single mailbox if it exists GET mailboxes retrieves a JSON array of all mailboxes DELETE mailbox/mailboxName deletes a mailbox Note that res_mwi_external must be loaded for these functions to actually do anything. Review: https://reviewboard.asterisk.org/r/3117/ ........ Merged revisions 405553 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@405554 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/stasis_app_mailbox.h | 91 +++++++ res/ari.make | 4 + res/ari/ari_model_validators.c | 70 +++++ res/ari/ari_model_validators.h | 22 ++ res/ari/resource_mailboxes.c | 93 +++++++ res/ari/resource_mailboxes.h | 97 +++++++ res/res_ari_mailboxes.c | 358 ++++++++++++++++++++++++++ res/res_stasis_mailbox.c | 165 ++++++++++++ res/res_stasis_mailbox.exports.in | 6 + rest-api/api-docs/mailboxes.json | 134 ++++++++++ rest-api/resources.json | 4 + 11 files changed, 1044 insertions(+) create mode 100644 include/asterisk/stasis_app_mailbox.h create mode 100644 res/ari/resource_mailboxes.c create mode 100644 res/ari/resource_mailboxes.h create mode 100644 res/res_ari_mailboxes.c create mode 100644 res/res_stasis_mailbox.c create mode 100644 res/res_stasis_mailbox.exports.in create mode 100644 rest-api/api-docs/mailboxes.json diff --git a/include/asterisk/stasis_app_mailbox.h b/include/asterisk/stasis_app_mailbox.h new file mode 100644 index 0000000000..f2a0a567bf --- /dev/null +++ b/include/asterisk/stasis_app_mailbox.h @@ -0,0 +1,91 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Jonathan Rose + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _ASTERISK_STASIS_APP_MAILBOX_H +#define _ASTERISK_STASIS_APP_MAILBOX_H + +/*! \file + * + * \brief Stasis Application Mailbox API. See \ref res_stasis "Stasis + * Application API" for detailed documentation. + * + * \author Jonathan Rose + * \since 12 + */ + +#include "asterisk/app.h" +#include "asterisk/stasis_app.h" + +/*! @{ */ + +/*! Stasis mailbox operation result codes */ +enum stasis_mailbox_result { + /*! Mailbox operation completed successfully */ + STASIS_MAILBOX_OK, + /*! Mailbox of the requested name does not exist */ + STASIS_MAILBOX_MISSING, + /*! Mailbox operation failed internally */ + STASIS_MAILBOX_ERROR, +}; + +/*! + * \brief Convert mailbox to JSON + * + * \param name the name of the mailbox + * \param json If the query is successful, this pointer at this address will + * be set to the JSON representation of the mailbox + * + * \return stasis mailbox result code indicating success or failure and cause + * \return \c NULL on error. + */ +enum stasis_mailbox_result stasis_app_mailbox_to_json(const char *name, struct ast_json **json); + +/*! + * brief Convert mailboxes to json array + * + * \return JSON representation of the mailboxes + * \return \c NULL on error. + */ +struct ast_json *stasis_app_mailboxes_to_json(void); + +/*! + * \brief Changes the state of a mailbox. + * + * \note Implicitly creates the mailbox. + * + * \param name The name of the ARI controlled mailbox + * \param old_messages count of old (read) messages in the mailbox + * \param new_messages count of new (unread) messages in the mailbox + * + * \return 0 if successful + * \return -1 on internal error. + */ +int stasis_app_mailbox_update( + const char *name, int old_messages, int new_messages); + +/*! + * \brief Delete a mailbox controlled by ARI. + * + * \param name the name of the ARI controlled mailbox + * + * \return a stasis mailbox application result + */ +enum stasis_mailbox_result stasis_app_mailbox_delete( + const char *name); + +#endif /* _ASTERISK_STASIS_APP_MAILBOX_H */ diff --git a/res/ari.make b/res/ari.make index c5dac2d3c0..f9a87d3fbc 100644 --- a/res/ari.make +++ b/res/ari.make @@ -49,6 +49,10 @@ res_ari_device_states.so: ari/resource_device_states.o ari/resource_device_states.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_device_states) +res_ari_mailboxes.so: ari/resource_mailboxes.o + +ari/resource_mailboxes.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_mailboxes) + res_ari_events.so: ari/resource_events.o ari/resource_events.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_events) diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index a04818045c..b1482fa878 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -1419,6 +1419,76 @@ ari_validator ast_ari_validate_device_state_fn(void) return ast_ari_validate_device_state; } +int ast_ari_validate_mailbox(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_name = 0; + int has_new_messages = 0; + int has_old_messages = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("name", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_name = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Mailbox field name failed validation\n"); + res = 0; + } + } else + if (strcmp("new_messages", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_new_messages = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Mailbox field new_messages failed validation\n"); + res = 0; + } + } else + if (strcmp("old_messages", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_old_messages = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Mailbox field old_messages failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Mailbox has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_name) { + ast_log(LOG_ERROR, "ARI Mailbox missing required field name\n"); + res = 0; + } + + if (!has_new_messages) { + ast_log(LOG_ERROR, "ARI Mailbox missing required field new_messages\n"); + res = 0; + } + + if (!has_old_messages) { + ast_log(LOG_ERROR, "ARI Mailbox missing required field old_messages\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_mailbox_fn(void) +{ + return ast_ari_validate_mailbox; +} + int ast_ari_validate_application_replaced(struct ast_json *json) { int res = 1; diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index 3266939508..ffe0039039 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -498,6 +498,24 @@ int ast_ari_validate_device_state(struct ast_json *json); */ ari_validator ast_ari_validate_device_state_fn(void); +/*! + * \brief Validator for Mailbox. + * + * Represents the state of a mailbox. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_mailbox(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_mailbox(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_mailbox_fn(void); + /*! * \brief Validator for ApplicationReplaced. * @@ -1111,6 +1129,10 @@ ari_validator ast_ari_validate_application_fn(void); * DeviceState * - name: string (required) * - state: string (required) + * Mailbox + * - name: string (required) + * - new_messages: int (required) + * - old_messages: int (required) * ApplicationReplaced * - type: string (required) * - application: string (required) diff --git a/res/ari/resource_mailboxes.c b/res/ari/resource_mailboxes.c new file mode 100644 index 0000000000..0d9bac7040 --- /dev/null +++ b/res/ari/resource_mailboxes.c @@ -0,0 +1,93 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Jonathan Rose + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief /api-docs/mailboxes.{format} implementation- Mailboxes resources + * + * \author Jonathan Rose + */ + +#include "asterisk.h" +#include "asterisk/stasis_app_mailbox.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "resource_mailboxes.h" + +void ast_ari_mailboxes_list(struct ast_variable *headers, + struct ast_ari_mailboxes_list_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + + if (!(json = stasis_app_mailboxes_to_json())) { + ast_ari_response_error(response, 500, + "Internal Server Error", "Error building response"); + return; + } + + ast_ari_response_ok(response, json); +} +void ast_ari_mailboxes_get(struct ast_variable *headers, + struct ast_ari_mailboxes_get_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + + switch (stasis_app_mailbox_to_json(args->mailbox_name, &json)) { + case STASIS_MAILBOX_MISSING: + ast_ari_response_error(response, 404, + "Not Found", "Mailbox is does not exist"); + return; + case STASIS_MAILBOX_ERROR: + ast_ari_response_error(response, 500, + "Internal Server Error", "Error building response"); + return; + case STASIS_MAILBOX_OK: + ast_ari_response_ok(response, json); + } +} +void ast_ari_mailboxes_update(struct ast_variable *headers, + struct ast_ari_mailboxes_update_args *args, + struct ast_ari_response *response) +{ + if (stasis_app_mailbox_update(args->mailbox_name, args->old_messages, args->new_messages)) { + ast_ari_response_error(response, 500, "Internal Server Error", "Error updating mailbox"); + return; + } + + ast_ari_response_no_content(response); +} +void ast_ari_mailboxes_delete(struct ast_variable *headers, + struct ast_ari_mailboxes_delete_args *args, + struct ast_ari_response *response) +{ + switch (stasis_app_mailbox_delete(args->mailbox_name)) { + case STASIS_MAILBOX_MISSING: + ast_ari_response_error(response, 404, + "Not Found", "Mailbox does not exist"); + return; + case STASIS_MAILBOX_ERROR: + ast_ari_response_error(response, 500, + "INternal Server Error", "Failed to delete the mailbox"); + return; + case STASIS_MAILBOX_OK: + ast_ari_response_no_content(response); + } +} diff --git a/res/ari/resource_mailboxes.h b/res/ari/resource_mailboxes.h new file mode 100644 index 0000000000..33c69682dd --- /dev/null +++ b/res/ari/resource_mailboxes.h @@ -0,0 +1,97 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Jonathan Rose + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Generated file - declares stubs to be implemented in + * res/ari/resource_mailboxes.c + * + * Mailboxes resources + * + * \author Jonathan Rose + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_MAILBOXES_H +#define _ASTERISK_RESOURCE_MAILBOXES_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_mailboxes_list() */ +struct ast_ari_mailboxes_list_args { +}; +/*! + * \brief List all mailboxes. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mailboxes_list(struct ast_variable *headers, struct ast_ari_mailboxes_list_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_mailboxes_get() */ +struct ast_ari_mailboxes_get_args { + /*! \brief Name of the mailbox */ + const char *mailbox_name; +}; +/*! + * \brief Retrieve the current state of a mailbox. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mailboxes_get(struct ast_variable *headers, struct ast_ari_mailboxes_get_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_mailboxes_update() */ +struct ast_ari_mailboxes_update_args { + /*! \brief Name of the mailbox */ + const char *mailbox_name; + /*! \brief Count of old messages in the mailbox */ + int old_messages; + /*! \brief Count of new messages in the mailbox */ + int new_messages; +}; +/*! + * \brief Change the state of a mailbox. (Note - implicitly creates the mailbox). + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mailboxes_update(struct ast_variable *headers, struct ast_ari_mailboxes_update_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_mailboxes_delete() */ +struct ast_ari_mailboxes_delete_args { + /*! \brief Name of the mailbox */ + const char *mailbox_name; +}; +/*! + * \brief Destroy a mailbox. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mailboxes_delete(struct ast_variable *headers, struct ast_ari_mailboxes_delete_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_MAILBOXES_H */ diff --git a/res/res_ari_mailboxes.c b/res/res_ari_mailboxes.c new file mode 100644 index 0000000000..842ff68134 --- /dev/null +++ b/res/res_ari_mailboxes.c @@ -0,0 +1,358 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Jonathan Rose + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/res_ari_resource.c.mustache + */ + +/*! \file + * + * \brief Mailboxes resources + * + * \author Jonathan Rose + */ + +/*** MODULEINFO + res_ari + res_stasis + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/app.h" +#include "asterisk/module.h" +#include "asterisk/stasis_app.h" +#include "ari/resource_mailboxes.h" +#if defined(AST_DEVMODE) +#include "ari/ari_model_validators.h" +#endif + +#define MAX_VALS 128 + +/*! + * \brief Parameter parsing callback for /mailboxes. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_mailboxes_list_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_mailboxes_list_args args = {}; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + ast_ari_mailboxes_list(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_list(response->message, + ast_ari_validate_mailbox_fn()); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /mailboxes\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /mailboxes\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /mailboxes/{mailboxName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_mailboxes_get_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_mailboxes_get_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "mailboxName") == 0) { + args.mailbox_name = (i->value); + } else + {} + } + ast_ari_mailboxes_get(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Mailbox not found */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_mailbox( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /mailboxes/{mailboxName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /mailboxes/{mailboxName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /mailboxes/{mailboxName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_mailboxes_update_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_mailboxes_update_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); + struct ast_json *field; +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = get_params; i; i = i->next) { + if (strcmp(i->name, "oldMessages") == 0) { + args.old_messages = atoi(i->value); + } else + if (strcmp(i->name, "newMessages") == 0) { + args.new_messages = atoi(i->value); + } else + {} + } + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "mailboxName") == 0) { + args.mailbox_name = (i->value); + } else + {} + } + /* Look for a JSON request entity */ + body = ast_http_get_json(ser, headers); + if (!body) { + switch (errno) { + case EFBIG: + ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large"); + goto fin; + case ENOMEM: + ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request"); + goto fin; + case EIO: + ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body"); + goto fin; + } + } + /* Parse query parameters out of it */ + field = ast_json_object_get(body, "oldMessages"); + if (field) { + args.old_messages = ast_json_integer_get(field); + } + field = ast_json_object_get(body, "newMessages"); + if (field) { + args.new_messages = ast_json_integer_get(field); + } + ast_ari_mailboxes_update(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Mailbox not found */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /mailboxes/{mailboxName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /mailboxes/{mailboxName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} +/*! + * \brief Parameter parsing callback for /mailboxes/{mailboxName}. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_mailboxes_delete_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_mailboxes_delete_args args = {}; + struct ast_variable *i; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + for (i = path_vars; i; i = i->next) { + if (strcmp(i->name, "mailboxName") == 0) { + args.mailbox_name = (i->value); + } else + {} + } + ast_ari_mailboxes_delete(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + case 404: /* Mailbox not found */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_void( + response->message); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /mailboxes/{mailboxName}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /mailboxes/{mailboxName}\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} + +/*! \brief REST handler for /api-docs/mailboxes.{format} */ +static struct stasis_rest_handlers mailboxes_mailboxName = { + .path_segment = "mailboxName", + .is_wildcard = 1, + .callbacks = { + [AST_HTTP_GET] = ast_ari_mailboxes_get_cb, + [AST_HTTP_PUT] = ast_ari_mailboxes_update_cb, + [AST_HTTP_DELETE] = ast_ari_mailboxes_delete_cb, + }, + .num_children = 0, + .children = { } +}; +/*! \brief REST handler for /api-docs/mailboxes.{format} */ +static struct stasis_rest_handlers mailboxes = { + .path_segment = "mailboxes", + .callbacks = { + [AST_HTTP_GET] = ast_ari_mailboxes_list_cb, + }, + .num_children = 1, + .children = { &mailboxes_mailboxName, } +}; + +static int load_module(void) +{ + int res = 0; + stasis_app_ref(); + res |= ast_ari_add_handler(&mailboxes); + return res; +} + +static int unload_module(void) +{ + ast_ari_remove_handler(&mailboxes); + stasis_app_unref(); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Mailboxes resources", + .load = load_module, + .unload = unload_module, + .nonoptreq = "res_ari,res_stasis", + ); diff --git a/res/res_stasis_mailbox.c b/res/res_stasis_mailbox.c new file mode 100644 index 0000000000..62f19f5554 --- /dev/null +++ b/res/res_stasis_mailbox.c @@ -0,0 +1,165 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Digium, Inc. + * + * Jonathan Rose + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + res_stasis + res_mwi_external + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/astdb.h" +#include "asterisk/astobj2.h" +#include "asterisk/module.h" +#include "asterisk/stasis_app_impl.h" +#include "asterisk/stasis_app_mailbox.h" +#include "asterisk/res_mwi_external.h" + +/*! Number of hash buckets for mailboxes */ +#define MAILBOX_BUCKETS 37 + +static struct ast_json *mailbox_to_json( + const struct ast_mwi_mailbox_object *mailbox) +{ + return ast_json_pack("{s: s, s: i, s: i}", + "name", ast_mwi_mailbox_get_id(mailbox), + "old_messages", ast_mwi_mailbox_get_msgs_old(mailbox), + "new_messages", ast_mwi_mailbox_get_msgs_new(mailbox)); +} + +enum stasis_mailbox_result stasis_app_mailbox_to_json( + const char *name, struct ast_json **json) +{ + struct ast_json *mailbox_json; + const struct ast_mwi_mailbox_object *mailbox; + + mailbox = ast_mwi_mailbox_get(name); + if (!mailbox) { + return STASIS_MAILBOX_MISSING; + } + + mailbox_json = mailbox_to_json(mailbox); + if (!mailbox_json) { + ast_mwi_mailbox_unref(mailbox); + return STASIS_MAILBOX_ERROR; + } + + *json = mailbox_json; + + return STASIS_MAILBOX_OK; +} + +struct ast_json *stasis_app_mailboxes_to_json() +{ + struct ast_json *array = ast_json_array_create(); + struct ao2_container *mailboxes; + struct ao2_iterator iter; + const struct ast_mwi_mailbox_object *mailbox; + + if (!array) { + return NULL; + } + + mailboxes = ast_mwi_mailbox_get_all(); + if (!mailboxes) { + ast_json_unref(array); + return NULL; + } + + iter = ao2_iterator_init(mailboxes, 0); + for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) { + struct ast_json *appending = mailbox_to_json(mailbox); + if (!appending || ast_json_array_append(array, appending)) { + /* Failed to append individual mailbox to the array. Abort. */ + ast_json_unref(array); + array = NULL; + break; + } + } + ao2_iterator_destroy(&iter); + + return array; +} + +int stasis_app_mailbox_update( + const char *name, int old_messages, int new_messages) +{ + struct ast_mwi_mailbox_object *mailbox; + int res = 0; + + mailbox = ast_mwi_mailbox_alloc(name); + if (!mailbox) { + return -1; + } + ast_mwi_mailbox_set_msgs_new(mailbox, new_messages); + ast_mwi_mailbox_set_msgs_old(mailbox, old_messages); + if (ast_mwi_mailbox_update(mailbox)) { + res = -1; + } + + ast_mwi_mailbox_unref(mailbox); + + return res; +} + +enum stasis_mailbox_result stasis_app_mailbox_delete( + const char *name) +{ + const struct ast_mwi_mailbox_object *mailbox; + + /* Make sure the mailbox actually exists before we delete it */ + mailbox = ast_mwi_mailbox_get(name); + if (!mailbox) { + return STASIS_MAILBOX_MISSING; + } + + ast_mwi_mailbox_unref(mailbox); + mailbox = NULL; + + /* Now delete the mailbox */ + if (ast_mwi_mailbox_delete(name)) { + return STASIS_MAILBOX_ERROR; + } + + return STASIS_MAILBOX_OK; +} + +static int load_module(void) +{ + /* Must be done first */ + ast_mwi_external_ref(); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + /* Must be done last */ + ast_mwi_external_unref(); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application mailbox support", + .load = load_module, + .unload = unload_module, + .nonoptreq = "res_stasis,res_mwi_external" + ); diff --git a/res/res_stasis_mailbox.exports.in b/res/res_stasis_mailbox.exports.in new file mode 100644 index 0000000000..0ad493c49e --- /dev/null +++ b/res/res_stasis_mailbox.exports.in @@ -0,0 +1,6 @@ +{ + global: + LINKER_SYMBOL_PREFIXstasis_app_*; + local: + *; +}; diff --git a/rest-api/api-docs/mailboxes.json b/rest-api/api-docs/mailboxes.json new file mode 100644 index 0000000000..40c83b0a9a --- /dev/null +++ b/rest-api/api-docs/mailboxes.json @@ -0,0 +1,134 @@ +{ + "_copyright": "Copyright (C) 2013, Digium, Inc.", + "_author": "Jonathan Rose ", + "_svn_revision": "$Revision$", + "apiVersion": "1.0.0", + "swaggerVersion": "1.1", + "basePath": "http://localhost:8088/stasis", + "resourcePath": "/api-docs/mailboxes.{format}", + "apis": [ + { + "path": "/mailboxes", + "description": "Mailboxes", + "operations": [ + { + "httpMethod": "GET", + "summary": "List all mailboxes.", + "nickname": "list", + "responseClass": "List[Mailbox]" + } + ] + }, + { + "path": "/mailboxes/{mailboxName}", + "description": "Mailbox state", + "operations": [ + { + "httpMethod": "GET", + "summary": "Retrieve the current state of a mailbox.", + "nickname": "get", + "responseClass": "Mailbox", + "parameters": [ + { + "name": "mailboxName", + "description": "Name of the mailbox", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Mailbox not found" + } + ] + }, + { + "httpMethod": "PUT", + "summary": "Change the state of a mailbox. (Note - implicitly creates the mailbox).", + "nickname": "update", + "responseClass": "void", + "parameters": [ + { + "name": "mailboxName", + "description": "Name of the mailbox", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + }, + { + "name": "oldMessages", + "description": "Count of old messages in the mailbox", + "paramType": "query", + "required": true, + "allowMultiple": false, + "dataType": "int" + }, + { + "name": "newMessages", + "description": "Count of new messages in the mailbox", + "paramType": "query", + "required": true, + "allowMultiple": false, + "dataType": "int" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Mailbox not found" + } + ] + }, + { + "httpMethod": "DELETE", + "summary": "Destroy a mailbox.", + "nickname": "delete", + "responseClass": "void", + "parameters": [ + { + "name": "mailboxName", + "description": "Name of the mailbox", + "paramType": "path", + "required": true, + "allowMultiple": false, + "dataType": "string" + } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Mailbox not found" + } + ] + } + ] + } + ], + "models": { + "Mailbox": { + "id": "Mailbox", + "description": "Represents the state of a mailbox.", + "properties": { + "name": { + "type": "string", + "description": "Name of the mailbox.", + "required": true + }, + "old_messages": { + "type": "int", + "description": "Count of old messages in the mailbox.", + "required": true + }, + "new_messages": { + "type": "int", + "description": "Count of new messages in the mailbox.", + "required": true + } + } + } + } +} diff --git a/rest-api/resources.json b/rest-api/resources.json index c753ec0186..3b197c369a 100644 --- a/rest-api/resources.json +++ b/rest-api/resources.json @@ -38,6 +38,10 @@ "path": "/api-docs/deviceStates.{format}", "description": "Device state resources" }, + { + "path": "/api-docs/mailboxes.{format}", + "description": "Mailboxes resources" + }, { "path": "/api-docs/events.{format}", "description": "WebSocket resource"