diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h index a0f06df57b..d1773b6b16 100644 --- a/include/asterisk/acl.h +++ b/include/asterisk/acl.h @@ -134,6 +134,13 @@ void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to); */ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error); +/*! + * \brief Convert HAs to a comma separated string value + * \param ha the starting ha head + * \param buf string buffer to convert data to + */ +void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf); + /*! * \brief Add a rule to an ACL struct * diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 8c78720841..b6dd441d4d 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -1559,5 +1559,154 @@ void *ast_sip_dict_set(pj_pool_t* pool, void *ht, #define ast_sip_mod_data_set(pool, mod_data, id, key, val) \ mod_data[id] = ast_sip_dict_set(pool, mod_data[id], key, val) +/*! + * \brief Function pointer for contact callbacks. + */ +typedef int (*on_contact_t)(const struct ast_sip_aor *aor, + const struct ast_sip_contact *contact, + int last, void *arg); + +/*! + * \brief For every contact on an AOR call the given 'on_contact' handler. + * + * \param aor the aor containing a list of contacts to iterate + * \param on_contact callback on each contact on an AOR + * \param arg user data passed to handler + * \retval 0 Success, non-zero on failure + */ +int ast_sip_for_each_contact(const struct ast_sip_aor *aor, + on_contact_t on_contact, void *arg); + +/*! + * \brief Handler used to convert a contact to a string. + * + * \param aor the aor containing a list of contacts to iterate + * \param contact the contact to convert + * \param last is this the last contact + * \param arg user data passed to handler + * \retval 0 Success, non-zero on failure + */ +int ast_sip_contact_to_str(const struct ast_sip_aor *aor, + const struct ast_sip_contact *contact, + int last, void *arg); + +/*! + * \brief For every aor in the comma separated aors string call the + * given 'on_aor' handler. + * + * \param aors a comma separated list of aors + * \param on_aor callback for each aor + * \param arg user data passed to handler + * \retval 0 Success, non-zero on failure + */ +int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg); + +/*! + * \brief For every auth in the array call the given 'on_auth' handler. + * + * \param array an array of auths + * \param on_auth callback for each auth + * \param arg user data passed to handler + * \retval 0 Success, non-zero on failure + */ +int ast_sip_for_each_auth(const struct ast_sip_auth_array *array, + ao2_callback_fn on_auth, void *arg); + +/*! + * \brief Converts the given auth type to a string + * + * \param type the auth type to convert + * \retval a string representative of the auth type + */ +const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type); + +/*! + * \brief Converts an auths array to a string of comma separated values + * + * \param auths an auth array + * \param buf the string buffer to write the object data + * \retval 0 Success, non-zero on failure + */ +int ast_sip_auths_to_str(const struct ast_sip_auth_array *auths, char **buf); + +/* + * \brief AMI variable container + */ +struct ast_sip_ami { + /*! Manager session */ + struct mansession *s; + /*! Manager message */ + const struct message *m; + /*! user specified argument data */ + void *arg; +}; + +/*! + * \brief Creates a string to store AMI event data in. + * + * \param event the event to set + * \param ami AMI session and message container + * \retval an initialized ast_str or NULL on error. + */ +struct ast_str *ast_sip_create_ami_event(const char *event, + struct ast_sip_ami *ami); + +/*! + * \brief An entity responsible formatting endpoint information. + */ +struct ast_sip_endpoint_formatter { + /*! + * \brief Callback used to format endpoint information over AMI. + */ + int (*format_ami)(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami); + AST_RWLIST_ENTRY(ast_sip_endpoint_formatter) next; +}; + +/*! + * \brief Register an endpoint formatter. + * + * \param obj the formatter to register + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj); + +/*! + * \brief Unregister an endpoint formatter. + * + * \param obj the formatter to unregister + */ +void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj); + +/*! + * \brief Converts a sorcery object to a string of object properties. + * + * \param obj the sorcery object to convert + * \param str the string buffer to write the object data + * \retval 0 Success, non-zero on failure + */ +int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf); + +/*! + * \brief Formats the endpoint and sends over AMI. + * + * \param endpoint the endpoint to format and send + * \param endpoint ami AMI variable container + * \param count the number of formatters operated on + * \retval 0 Success, otherwise non-zero on error + */ +int ast_sip_format_endpoint_ami(struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami, int *count); + +/*! + * \brief Format auth details for AMI. + * + * \param auths an auth array + * \param ami ami variable container + * \retval 0 Success, non-zero on failure + */ +int ast_sip_format_auths_ami(const struct ast_sip_auth_array *auths, + struct ast_sip_ami *ami); #endif /* _RES_PJSIP_H */ diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h index d985b9019c..2f676f1938 100644 --- a/include/asterisk/res_pjsip_pubsub.h +++ b/include/asterisk/res_pjsip_pubsub.h @@ -252,7 +252,7 @@ struct ast_sip_subscription_handler { * during this callback. The handler MUST, however, begin the destruction * process for the subscription during this callback. */ - void (*subscription_shutdown)(struct ast_sip_subscription *subscription); + void (*subscription_shutdown)(struct ast_sip_subscription *subscription); /*! * \brief Called when a SUBSCRIBE arrives in order to create a new subscription @@ -366,6 +366,16 @@ struct ast_sip_subscription_handler { * \retval non-zero Failure */ int (*refresh_subscription)(struct ast_sip_subscription *sub); + + /*! + * \brief Converts the subscriber to AMI + * + * This is a subscriber callback. + * + * \param sub The subscription + * \param buf The string to write AMI data + */ + void (*to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf); AST_LIST_ENTRY(ast_sip_subscription_handler) next; }; diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index dad0c439c1..8ce3eccf3f 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -102,6 +102,9 @@ extern "C" { /*! \brief Maximum size of an object type */ #define MAX_OBJECT_TYPE 64 +/*! \brief Maximum length of an object field name */ +#define MAX_OBJECT_FIELD 128 + /*! * \brief Retrieval flags */ diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index fc92d4889f..5dbebba95a 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -332,7 +332,23 @@ int attribute_pure ast_true(const char *val); int attribute_pure ast_false(const char *val); /* - * \brief Join an array of strings into a single string. + * \brief Join an array of strings into a single string. + * \param s the resulting string buffer + * \param len the length of the result buffer, s + * \param w an array of strings to join. + * \param size the number of elements to join + * \param delim delimiter between elements + * + * This function will join all of the strings in the array 'w' into a single + * string. It will also place 'delim' in the result buffer in between each + * string from 'w'. + * \since 12 +*/ +void ast_join_delim(char *s, size_t len, const char * const w[], + unsigned int size, char delim); + +/* + * \brief Join an array of strings into a single string. * \param s the resulting string buffer * \param len the length of the result buffer, s * \param w an array of strings to join. @@ -341,7 +357,33 @@ int attribute_pure ast_false(const char *val); * string. It will also place a space in the result buffer in between each * string from 'w'. */ -void ast_join(char *s, size_t len, const char * const w[]); +#define ast_join(s, len, w) ast_join_delim(s, len, w, -1, ' ') + +/* + * \brief Attempts to convert the given string to camel case using + * the specified delimiter. + * + * note - returned string needs to be freed + * + * \param s the string to convert + * \param delim delimiter to parse out + * + * \retval The string converted to "CamelCase" + * \since 12 +*/ +char *ast_to_camel_case_delim(const char *s, const char *delim); + +/* + * \brief Attempts to convert the given string to camel case using + * an underscore as the specified delimiter. + * + * note - returned string needs to be freed + * + * \param s the string to convert + * + * \retval The string converted to "CamelCase" +*/ +#define ast_to_camel_case(s) ast_to_camel_case_delim(s, "_") /* \brief Parse a time (integer) string. diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index e24dc02d89..e441ba0558 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -755,6 +755,24 @@ int ast_safe_mkdir(const char *base_path, const char *path, int mode); #define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a])) +/*! + * \brief Checks to see if value is within the given bounds + * + * \param v the value to check + * \param min minimum lower bound (inclusive) + * \param max maximum upper bound (inclusive) + * \return 0 if value out of bounds, otherwise true (non-zero) + */ +#define IN_BOUNDS(v, min, max) ((v) >= (min)) && ((v) <= (max)) + +/*! + * \brief Checks to see if value is within the bounds of the given array + * + * \param v the value to check + * \param a the array to bound check + * \return 0 if value out of bounds, otherwise true (non-zero) + */ +#define ARRAY_IN_BOUNDS(v, a) IN_BOUNDS(v, 0, ARRAY_LEN(a) - 1) /* Definition for Digest authorization */ struct ast_http_digest { diff --git a/main/acl.c b/main/acl.c index a4567beda9..c341125fd6 100644 --- a/main/acl.c +++ b/main/acl.c @@ -664,6 +664,19 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha return ret; } +void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf) +{ + for (; ha; ha = ha->next) { + const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr)); + ast_str_append(buf, 0, "%s%s/%s", + ha->sense == AST_SENSE_ALLOW ? "!" : "", + addr, ast_sockaddr_stringify_addr(&ha->netmask)); + if (ha->next) { + ast_str_append(buf, 0, ","); + } + } +} + enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose) { struct ast_acl *acl; diff --git a/main/sorcery.c b/main/sorcery.c index 3b58188dac..99ee2a90eb 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -52,9 +52,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*! \brief Number of buckets for types (should be prime for performance reasons) */ #define TYPE_BUCKETS 53 -/*! \brief Maximum length of an object field name */ -#define MAX_OBJECT_FIELD 128 - /*! \brief Thread pool for observers */ static struct ast_threadpool *threadpool; diff --git a/main/utils.c b/main/utils.c index 8252488a9e..b1a95425c5 100644 --- a/main/utils.c +++ b/main/utils.c @@ -1707,7 +1707,7 @@ char *ast_process_quotes_and_slashes(char *start, char find, char replace_with) return dataPut; } -void ast_join(char *s, size_t len, const char * const w[]) +void ast_join_delim(char *s, size_t len, const char * const w[], unsigned int size, char delim) { int x, ofs = 0; const char *src; @@ -1715,9 +1715,9 @@ void ast_join(char *s, size_t len, const char * const w[]) /* Join words into a string */ if (!s) return; - for (x = 0; ofs < len && w[x]; x++) { + for (x = 0; ofs < len && w[x] && x < size; x++) { if (x > 0) - s[ofs++] = ' '; + s[ofs++] = delim; for (src = w[x]; *src && ofs < len; src++) s[ofs++] = *src; } @@ -1726,6 +1726,25 @@ void ast_join(char *s, size_t len, const char * const w[]) s[ofs] = '\0'; } +char *ast_to_camel_case_delim(const char *s, const char *delim) +{ + char *res = ast_strdup(s); + char *front, *back, *buf = res; + int size; + + front = strtok_r(buf, delim, &back); + + while (front) { + size = strlen(front); + *front = toupper(*front); + ast_copy_string(buf, front, size + 1); + buf += size; + front = strtok_r(NULL, delim, &back); + } + + return res; +} + /* * stringfields support routines. */ diff --git a/res/res_pjsip.c b/res/res_pjsip.c index cda22a3f55..db4f32f2cb 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1021,6 +1021,41 @@ Qualify a chan_pjsip endpoint. + + + Lists PJSIP endpoints. + + + + + Provides a listing of all endpoints. For each endpoint an EndpointList event + is raised that contains relevant attributes and status information. Once all + endpoints have been listed an EndpointListComplete event is issued. + + + + + + Detail listing of an endpoint and its objects. + + + + + The endpoint to list. + + + + + Provides a detailed listing of options for a given endpoint. Events are issued + showing the configuration and status of the endpoint and associated objects. These + events include EndpointDetail, AorDetail, + AuthDetail, TransportDetail, and + IdentifyDetail. Some events may be listed multiple times if multiple objects are + associated (for instance AoRs). Once all detail events have been raised a final + EndpointDetailComplete event is issued. + + + ***/ @@ -1205,6 +1240,49 @@ struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata) return endpoint; } +AST_RWLIST_HEAD_STATIC(endpoint_formatters, ast_sip_endpoint_formatter); + +int ast_sip_register_endpoint_formatter(struct ast_sip_endpoint_formatter *obj) +{ + SCOPED_LOCK(lock, &endpoint_formatters, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_INSERT_TAIL(&endpoint_formatters, obj, next); + ast_module_ref(ast_module_info->self); + return 0; +} + +void ast_sip_unregister_endpoint_formatter(struct ast_sip_endpoint_formatter *obj) +{ + struct ast_sip_endpoint_formatter *i; + SCOPED_LOCK(lock, &endpoint_formatters, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&endpoint_formatters, i, next) { + if (i == obj) { + AST_RWLIST_REMOVE_CURRENT(next); + ast_module_unref(ast_module_info->self); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; +} + +int ast_sip_format_endpoint_ami(struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami, int *count) +{ + int res = 0; + struct ast_sip_endpoint_formatter *i; + SCOPED_LOCK(lock, &endpoint_formatters, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + *count = 0; + AST_RWLIST_TRAVERSE(&endpoint_formatters, i, next) { + if (i->format_ami && ((res = i->format_ami(endpoint, ami)) < 0)) { + return res; + } + + if (!res) { + (*count)++; + } + } + return 0; +} + pjsip_endpoint *ast_sip_get_pjsip_endpoint(void) { return ast_pjsip_endpoint; @@ -1964,7 +2042,7 @@ static int load_module(void) ast_sip_initialize_global_headers(); - if (ast_res_pjsip_initialize_configuration()) { + if (ast_res_pjsip_initialize_configuration(ast_module_info)) { ast_log(LOG_ERROR, "Failed to initialize SIP configuration. Aborting load\n"); ast_sip_destroy_global_headers(); stop_monitor_thread(); diff --git a/res/res_pjsip.exports.in b/res/res_pjsip.exports.in index 5afafc481e..8b62abbfe4 100644 --- a/res/res_pjsip.exports.in +++ b/res/res_pjsip.exports.in @@ -1,77 +1,8 @@ { global: - LINKER_SYMBOL_PREFIXast_sip_register_service; - LINKER_SYMBOL_PREFIXast_sip_unregister_service; - LINKER_SYMBOL_PREFIXast_sip_register_authenticator; - LINKER_SYMBOL_PREFIXast_sip_unregister_authenticator; - LINKER_SYMBOL_PREFIXast_sip_register_outbound_authenticator; - LINKER_SYMBOL_PREFIXast_sip_unregister_outbound_authenticator; - LINKER_SYMBOL_PREFIXast_sip_register_endpoint_identifier; - LINKER_SYMBOL_PREFIXast_sip_unregister_endpoint_identifier; - LINKER_SYMBOL_PREFIXast_sip_create_serializer; - LINKER_SYMBOL_PREFIXast_sip_push_task; - LINKER_SYMBOL_PREFIXast_sip_push_task_synchronous; - LINKER_SYMBOL_PREFIXast_sip_create_request; - LINKER_SYMBOL_PREFIXast_sip_create_request_with_auth; - LINKER_SYMBOL_PREFIXast_sip_send_request; - LINKER_SYMBOL_PREFIXast_sip_requires_authentication; - LINKER_SYMBOL_PREFIXast_sip_authenticate_request; - LINKER_SYMBOL_PREFIXast_sip_get_authentication_credentials; - LINKER_SYMBOL_PREFIXast_sip_check_authentication; - LINKER_SYMBOL_PREFIXast_sip_create_auth_challenge_response; - LINKER_SYMBOL_PREFIXast_sip_set_outbound_authentication_credentials; - LINKER_SYMBOL_PREFIXast_sip_dialog_setup_outbound_authentication; - LINKER_SYMBOL_PREFIXast_sip_add_digest_to_challenge; - LINKER_SYMBOL_PREFIXast_sip_identify_endpoint; - LINKER_SYMBOL_PREFIXast_sip_add_header; - LINKER_SYMBOL_PREFIXast_sip_add_body; - LINKER_SYMBOL_PREFIXast_sip_add_body_multipart; - LINKER_SYMBOL_PREFIXast_sip_append_body; - LINKER_SYMBOL_PREFIXast_sip_get_pjsip_endpoint; - LINKER_SYMBOL_PREFIXast_sip_endpoint_alloc; - LINKER_SYMBOL_PREFIXast_sip_get_endpoints; + LINKER_SYMBOL_PREFIXast_sip_*; LINKER_SYMBOL_PREFIXast_copy_pj_str; - LINKER_SYMBOL_PREFIXast_sip_get_sorcery; - LINKER_SYMBOL_PREFIXast_sip_create_dialog_uac; - LINKER_SYMBOL_PREFIXast_sip_create_dialog_uas; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_aor; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_first_aor_contact; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact_from_aor_list; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_aor_contacts; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact; - LINKER_SYMBOL_PREFIXast_sip_location_add_contact_transport; - LINKER_SYMBOL_PREFIXast_sip_location_delete_contact_transport; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact_transport_by_uri; - LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact_transport_by_transport; - LINKER_SYMBOL_PREFIXast_sip_location_add_contact; - LINKER_SYMBOL_PREFIXast_sip_location_update_contact; - LINKER_SYMBOL_PREFIXast_sip_location_delete_contact; LINKER_SYMBOL_PREFIXast_pjsip_rdata_get_endpoint; - LINKER_SYMBOL_PREFIXast_sip_thread_is_servant; - LINKER_SYMBOL_PREFIXast_sip_dialog_set_serializer; - LINKER_SYMBOL_PREFIXast_sip_dialog_set_endpoint; - LINKER_SYMBOL_PREFIXast_sip_dialog_get_endpoint; - LINKER_SYMBOL_PREFIXast_sip_retrieve_auths; - LINKER_SYMBOL_PREFIXast_sip_cleanup_auths; - LINKER_SYMBOL_PREFIXast_sip_is_content_type; - LINKER_SYMBOL_PREFIXast_sip_get_artificial_endpoint; - LINKER_SYMBOL_PREFIXast_sip_get_artificial_auth; - LINKER_SYMBOL_PREFIXast_sip_report_invalid_endpoint; - LINKER_SYMBOL_PREFIXast_sip_report_failed_acl; - LINKER_SYMBOL_PREFIXast_sip_report_auth_failed_challenge_response; - LINKER_SYMBOL_PREFIXast_sip_report_auth_success; - LINKER_SYMBOL_PREFIXast_sip_report_auth_challenge_sent; - LINKER_SYMBOL_PREFIXast_sip_report_req_no_support; - LINKER_SYMBOL_PREFIXast_sip_report_mem_limit; - LINKER_SYMBOL_PREFIXast_sip_initialize_global_headers; - LINKER_SYMBOL_PREFIXast_sip_destroy_global_headers; - LINKER_SYMBOL_PREFIXast_sip_add_global_request_header; - LINKER_SYMBOL_PREFIXast_sip_add_global_response_header; - LINKER_SYMBOL_PREFIXast_sip_initialize_sorcery_global; - LINKER_SYMBOL_PREFIXast_sip_auth_array_init; - LINKER_SYMBOL_PREFIXast_sip_auth_array_destroy; - LINKER_SYMBOL_PREFIXast_sip_dict_get; - LINKER_SYMBOL_PREFIXast_sip_dict_set; local: *; }; diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c index 860f33b564..afa26867e3 100644 --- a/res/res_pjsip/config_auth.c +++ b/res/res_pjsip/config_auth.c @@ -23,6 +23,7 @@ #include "asterisk/res_pjsip.h" #include "asterisk/logger.h" #include "asterisk/sorcery.h" +#include "include/res_pjsip_private.h" static void auth_destroy(void *obj) { @@ -61,6 +62,24 @@ static int auth_type_handler(const struct aco_option *opt, struct ast_variable * return 0; } +static const char *auth_types_map[] = { + [AST_SIP_AUTH_TYPE_USER_PASS] = "userpass", + [AST_SIP_AUTH_TYPE_MD5] = "md5" +}; + +const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type) +{ + return ARRAY_IN_BOUNDS(type, auth_types_map) ? + auth_types_map[type] : ""; +} + +static int auth_type_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_auth *auth = obj; + *buf = ast_strdup(ast_sip_auth_type_to_str(auth->type)); + return 0; +} + static int auth_apply(const struct ast_sorcery *sorcery, void *obj) { struct ast_sip_auth *auth = obj; @@ -99,6 +118,84 @@ static int auth_apply(const struct ast_sorcery *sorcery, void *obj) return res; } +int ast_sip_for_each_auth(const struct ast_sip_auth_array *array, + ao2_callback_fn on_auth, void *arg) +{ + int i; + + if (!array || !array->num) { + return 0; + } + + for (i = 0; i < array->num; ++i) { + RAII_VAR(struct ast_sip_auth *, auth, ast_sorcery_retrieve_by_id( + ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, + array->names[i]), ao2_cleanup); + + if (!auth) { + continue; + } + + if (on_auth(auth, arg, 0)) { + return -1; + } + } + + return 0; +} + +static int sip_auth_to_ami(const struct ast_sip_auth *auth, + struct ast_str **buf) +{ + return ast_sip_sorcery_object_to_ami(auth, buf); +} + +static int format_ami_auth_handler(void *obj, void *arg, int flags) +{ + const struct ast_sip_auth *auth = obj; + struct ast_sip_ami *ami = arg; + const struct ast_sip_endpoint *endpoint = ami->arg; + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("AuthDetail", ami), ast_free); + + if (!buf) { + return -1; + } + + if (sip_auth_to_ami(auth, &buf)) { + return -1; + } + + if (endpoint) { + ast_str_append(&buf, 0, "EndpointName: %s\r\n", + ast_sorcery_object_get_id(endpoint)); + } + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +int ast_sip_format_auths_ami(const struct ast_sip_auth_array *auths, + struct ast_sip_ami *ami) +{ + return ast_sip_for_each_auth(auths, format_ami_auth_handler, ami); +} + +static int format_ami_endpoint_auth(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami) +{ + ami->arg = (void *)endpoint; + if (ast_sip_format_auths_ami(&endpoint->inbound_auths, ami)) { + return -1; + } + + return ast_sip_format_auths_ami(&endpoint->outbound_auths, ami); +} + +static struct ast_sip_endpoint_formatter endpoint_auth_formatter = { + .format_ami = format_ami_endpoint_auth +}; + /*! \brief Initialize sorcery with auth support */ int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery) { @@ -121,7 +218,8 @@ int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery) ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime", "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime)); ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type", - "userpass", auth_type_handler, NULL, 0, 0); + "userpass", auth_type_handler, auth_type_to_str, 0, 0); + ast_sip_register_endpoint_formatter(&endpoint_auth_formatter); return 0; } diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index 4ace1821b0..370e49cb0f 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -26,6 +26,45 @@ #include "asterisk/astobj2.h" #include "asterisk/sorcery.h" #include "asterisk/acl.h" +#include "include/res_pjsip_private.h" + +static int sip_transport_to_ami(const struct ast_sip_transport *transport, + struct ast_str **buf) +{ + return ast_sip_sorcery_object_to_ami(transport, buf); +} + +static int format_ami_endpoint_transport(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami) +{ + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("TransportDetail", ami), ast_free); + RAII_VAR(struct ast_sip_transport *, + transport, ast_sorcery_retrieve_by_id( + ast_sip_get_sorcery(), "transport", + endpoint->transport), ao2_cleanup); + if (!buf) { + return -1; + } + + if (!transport) { + astman_send_error_va(ami->s, ami->m, "Unable to retrieve " + "transport %s\n", endpoint->transport); + return -1; + } + + sip_transport_to_ami(transport, &buf); + + ast_str_append(&buf, 0, "EndpointName: %s\r\n", + ast_sorcery_object_get_id(endpoint)); + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +struct ast_sip_endpoint_formatter endpoint_transport_formatter = { + .format_ami = format_ami_endpoint_transport +}; static int destroy_transport_state(void *data) { @@ -213,6 +252,25 @@ static int transport_protocol_handler(const struct aco_option *opt, struct ast_v return 0; } +static const char *transport_types[] = { + [AST_TRANSPORT_UDP] = "udp", + [AST_TRANSPORT_TCP] = "tcp", + [AST_TRANSPORT_TLS] = "tls", + [AST_TRANSPORT_WS] = "ws", + [AST_TRANSPORT_WSS] = "wss" +}; + +static int transport_protocol_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + + if (ARRAY_IN_BOUNDS(transport->type, transport_types)) { + *buf = ast_strdup(transport_types[transport->type]); + } + + return 0; +} + /*! \brief Custom handler for turning a string bind into a pj_sockaddr */ static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -222,6 +280,20 @@ static int transport_bind_handler(const struct aco_option *opt, struct ast_varia return (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &transport->host) != PJ_SUCCESS) ? -1 : 0; } +static int transport_bind_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + + if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) { + return -1; + } + + /* include port as well as brackets if IPv6 */ + pj_sockaddr_print(&transport->host, *buf, MAX_OBJECT_FIELD, 1 | 2); + + return 0; +} + /*! \brief Custom handler for TLS boolean settings */ static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -240,6 +312,27 @@ static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_v return 0; } +static int verify_server_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + *buf = ast_strdup(AST_YESNO(transport->tls.verify_server)); + return 0; +} + +static int verify_client_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + *buf = ast_strdup(AST_YESNO(transport->tls.verify_client)); + return 0; +} + +static int require_client_cert_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + *buf = ast_strdup(AST_YESNO(transport->tls.require_client_cert)); + return 0; +} + /*! \brief Custom handler for TLS method setting */ static int transport_tls_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -264,6 +357,24 @@ static int transport_tls_method_handler(const struct aco_option *opt, struct ast return 0; } +static const char *tls_method_map[] = { + [PJSIP_SSL_DEFAULT_METHOD] = "default", + [PJSIP_SSL_UNSPECIFIED_METHOD] = "unspecified", + [PJSIP_TLSV1_METHOD] = "tlsv1", + [PJSIP_SSLV2_METHOD] = "sslv2", + [PJSIP_SSLV3_METHOD] = "sslv3", + [PJSIP_SSLV23_METHOD] = "sslv23", +}; + +static int tls_method_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + if (ARRAY_IN_BOUNDS(transport->tls.method, tls_method_map)) { + *buf = ast_strdup(tls_method_map[transport->tls.method]); + } + return 0; +} + /*! \brief Custom handler for TLS cipher setting */ static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -291,6 +402,27 @@ static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast } } +static int transport_tls_cipher_to_str(const void *obj, const intptr_t *args, char **buf) +{ + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + const struct ast_sip_transport *transport = obj; + int i; + + if (!str) { + return -1; + } + + for (i = 0; i < transport->tls.ciphers_num; ++i) { + ast_str_append(&str, 0, "%s", pj_ssl_cipher_name(transport->ciphers[i])); + if (i < transport->tls.ciphers_num - 1) { + ast_str_append(&str, 0, ","); + } + } + + *buf = ast_strdup(ast_str_buffer(str)); + return 0; +} + /*! \brief Custom handler for localnet setting */ static int transport_localnet_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -304,6 +436,16 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v return error; } +static int localnet_to_str(const void *obj, const intptr_t *args, char **buf) +{ + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + const struct ast_sip_transport *transport = obj; + + ast_ha_join(transport->localnet, &str); + *buf = ast_strdup(ast_str_buffer(str)); + return 0; +} + /*! \brief Initialize sorcery with transport support */ int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery) { @@ -314,8 +456,8 @@ int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery) } ast_sorcery_object_field_register(sorcery, "transport", "type", "", OPT_NOOP_T, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations)); ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file)); ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file)); @@ -325,14 +467,15 @@ int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery) ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535); ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address)); ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain)); - ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sorcery, "transport", "local_net", "", transport_localnet_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, verify_server_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, verify_client_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, require_client_cert_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, tls_method_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, transport_tls_cipher_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "local_net", "", transport_localnet_handler, localnet_to_str, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "tos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, tos)); ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos)); + ast_sip_register_endpoint_formatter(&endpoint_transport_formatter); return 0; } diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index c3e6c21924..0ee62529f6 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -8,13 +8,17 @@ #ifndef RES_PJSIP_PRIVATE_H_ #define RES_PJSIP_PRIVATE_H_ +#include "asterisk/module.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/stasis_endpoints.h" + struct ao2_container; struct ast_threadpool_options; /*! * \brief Initialize the configuration for res_pjsip */ -int ast_res_pjsip_initialize_configuration(void); +int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info); /*! * \brief Annihilate the configuration objects @@ -82,4 +86,23 @@ void ast_res_pjsip_cleanup_options_handling(void); */ void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options); +/*! + * \brief Function pointer for channel snapshot callbacks. + */ +typedef int (*on_channel_snapshot_t)( + const struct ast_channel_snapshot *snapshot, int last, void *arg); + +/*! + * \brief For every channel snapshot on an endpoint snapshot call the given + * 'on_channel_snapshot' handler. + * + * \param endpoint_snapshot snapshot of an endpoint + * \param on_channel_snapshot callback for each channel snapshot + * \param arg user data passed to handler + * \retval 0 Success, non-zero on failure + */ +int ast_sip_for_each_channel_snapshot(const struct ast_endpoint_snapshot *endpoint_snapshot, + on_channel_snapshot_t on_channel_snapshot, + void *arg); + #endif /* RES_PJSIP_PRIVATE_H_ */ diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 8569e7a383..7f4889fe88 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -300,6 +300,123 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab return 0; } +int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg) +{ + char *copy, *name; + + if (!on_aor || ast_strlen_zero(aors)) { + return 0; + } + + copy = ast_strdupa(aors); + while ((name = strsep(©, ","))) { + RAII_VAR(struct ast_sip_aor *, aor, + ast_sip_location_retrieve_aor(name), ao2_cleanup); + + if (!aor) { + continue; + } + + if (on_aor(aor, arg, 0)) { + return -1; + } + } + ast_free(copy); + return 0; +} + +int ast_sip_for_each_contact(const struct ast_sip_aor *aor, + on_contact_t on_contact, void *arg) +{ + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + struct ast_sip_contact *contact; + int num; + struct ao2_iterator i; + + if (!on_contact || + !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) { + return 0; + } + + num = ao2_container_count(contacts); + i = ao2_iterator_init(contacts, 0); + while ((contact = ao2_iterator_next(&i))) { + int res = on_contact(aor, contact, --num == 0, arg); + + ao2_ref(contact, -1); + if (res) { + return -1; + } + } + ao2_iterator_destroy(&i); + return 0; +} + +int ast_sip_contact_to_str(const struct ast_sip_aor *aor, + const struct ast_sip_contact *contact, + int last, void *arg) +{ + struct ast_str **buf = arg; + + ast_str_append(buf, 0, "%s/%s", + ast_sorcery_object_get_id(aor), contact->uri); + + if (!last) { + ast_str_append(buf, 0, ","); + } + + return 0; +} + +static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf) +{ + return ast_sip_sorcery_object_to_ami(aor, buf); +} + +static int format_ami_aor_handler(void *obj, void *arg, int flags) +{ + const struct ast_sip_aor *aor = obj; + struct ast_sip_ami *ami = arg; + const struct ast_sip_endpoint *endpoint = ami->arg; + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("AorDetail", ami), ast_free); + + int num; + RAII_VAR(struct ao2_container *, contacts, + ast_sip_location_retrieve_aor_contacts(aor), ao2_cleanup); + + if (!buf) { + return -1; + } + + sip_aor_to_ami(aor, &buf); + ast_str_append(&buf, 0, "Contacts: "); + ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &buf); + ast_str_append(&buf, 0, "\r\n"); + + num = ao2_container_count(contacts); + ast_str_append(&buf, 0, "TotalContacts: %d\r\n", num); + ast_str_append(&buf, 0, "ContactsRegistered: %d\r\n", + num - ao2_container_count(aor->permanent_contacts)); + ast_str_append(&buf, 0, "EndpointName: %s\r\n", + ast_sorcery_object_get_id(endpoint)); + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +static int format_ami_endpoint_aor(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami) +{ + ami->arg = (void *)endpoint; + return ast_sip_for_each_aor(endpoint->aors, + format_ami_aor_handler, ami); +} + +struct ast_sip_endpoint_formatter endpoint_aor_formatter = { + .format_ami = format_ami_endpoint_aor +}; + /*! \brief Initialize sorcery with location support */ int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery) { @@ -328,6 +445,7 @@ int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery) ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes)); + ast_sip_register_endpoint_formatter(&endpoint_aor_formatter); return 0; } diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index d8e781a561..24c53e8a99 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -13,11 +13,11 @@ #include "asterisk/res_pjsip.h" #include "include/res_pjsip_private.h" #include "asterisk/cli.h" +#include "asterisk/manager.h" #include "asterisk/astobj2.h" #include "asterisk/utils.h" #include "asterisk/sorcery.h" #include "asterisk/callerid.h" -#include "asterisk/stasis_endpoints.h" /*! \brief Number of buckets for persistent endpoint information */ #define PERSISTENT_BUCKETS 53 @@ -242,6 +242,25 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, return 0; } +static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + switch (endpoint->dtmf) { + case AST_SIP_DTMF_RFC_4733 : + *buf = "rfc4733"; break; + case AST_SIP_DTMF_INBAND : + *buf = "inband"; break; + case AST_SIP_DTMF_INFO : + *buf = "info"; break; + default: + *buf = "none"; + } + + *buf = ast_strdup(*buf); + return 0; +} + static int prack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -259,6 +278,22 @@ static int prack_handler(const struct aco_option *opt, struct ast_variable *var, return 0; } +static int prack_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + if (endpoint->extensions.flags & PJSIP_INV_REQUIRE_100REL) { + *buf = "required"; + } else if (endpoint->extensions.flags & PJSIP_INV_SUPPORT_100REL) { + *buf = "yes"; + } else { + *buf = "no"; + } + + *buf = ast_strdup(*buf); + return 0; +} + static int timers_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -278,6 +313,24 @@ static int timers_handler(const struct aco_option *opt, struct ast_variable *var return 0; } +static int timers_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + if (endpoint->extensions.flags & PJSIP_INV_ALWAYS_USE_TIMER) { + *buf = "always"; + } else if (endpoint->extensions.flags & PJSIP_INV_REQUIRE_TIMER) { + *buf = "required"; + } else if (endpoint->extensions.flags & PJSIP_INV_SUPPORT_TIMER) { + *buf = "yes"; + } else { + *buf = "no"; + } + + *buf = ast_strdup(*buf); + return 0; +} + void ast_sip_auth_array_destroy(struct ast_sip_auth_array *auths) { int i; @@ -344,6 +397,32 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab return ast_sip_auth_array_init(&endpoint->outbound_auths, var->value); } +int ast_sip_auths_to_str(const struct ast_sip_auth_array *auths, char **buf) +{ + if (!auths || !auths->num) { + return 0; + } + + if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) { + return -1; + } + + ast_join_delim(*buf, MAX_OBJECT_FIELD, auths->names, auths->num, ','); + return 0; +} + +static int inbound_auths_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + return ast_sip_auths_to_str(&endpoint->inbound_auths, buf); +} + +static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + return ast_sip_auths_to_str(&endpoint->outbound_auths, buf); +} + static int ident_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -362,6 +441,20 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var, return 0; } +static int ident_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + switch (endpoint->ident_method) { + case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME : + *buf = "username"; break; + default: + return 0; + } + + *buf = ast_strdup(*buf); + return 0; +} + static int direct_media_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -378,6 +471,20 @@ static int direct_media_method_handler(const struct aco_option *opt, struct ast_ return 0; } +static const char *id_configuration_refresh_methods[] = { + [AST_SIP_SESSION_REFRESH_METHOD_INVITE] = "invite", + [AST_SIP_SESSION_REFRESH_METHOD_UPDATE] = "update" +}; + +static int direct_media_method_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (ARRAY_IN_BOUNDS(endpoint->id.refresh_method, id_configuration_refresh_methods)) { + *buf = ast_strdup(id_configuration_refresh_methods[endpoint->id.refresh_method]); + } + return 0; +} + static int connected_line_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -394,6 +501,13 @@ static int connected_line_method_handler(const struct aco_option *opt, struct as return 0; } +static int connected_line_method_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(id_configuration_refresh_methods[endpoint->id.refresh_method]); + return 0; +} + static int direct_media_glare_mitigation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -413,6 +527,22 @@ static int direct_media_glare_mitigation_handler(const struct aco_option *opt, s return 0; } +static const char *direct_media_glare_mitigation_map[] = { + [AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE] = "none", + [AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING] = "outgoing", + [AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING] = "incoming" +}; + +static int direct_media_glare_mitigation_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (ARRAY_IN_BOUNDS(endpoint->media.direct_media.glare_mitigation, direct_media_glare_mitigation_map)) { + *buf = ast_strdup(direct_media_glare_mitigation_map[endpoint->media.direct_media.glare_mitigation]); + } + + return 0; +} + static int caller_id_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -437,6 +567,29 @@ static int caller_id_handler(const struct aco_option *opt, struct ast_variable * return 0; } +static int caller_id_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + const char *name = S_COR(endpoint->id.self.name.valid, + endpoint->id.self.name.str, NULL); + const char *number = S_COR(endpoint->id.self.number.valid, + endpoint->id.self.number.str, NULL); + + /* make sure size is at least 10 - that should cover the "" + case as well as any additional formatting characters added in + the name and/or number case. */ + int size = 10; + size += name ? strlen(name) : 0; + size += number ? strlen(number) : 0; + + if (!(*buf = ast_calloc(size + 1, sizeof(char)))) { + return -1; + } + + ast_callerid_merge(*buf, size + 1, name, number, NULL); + return 0; +} + static int caller_id_privacy_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -449,6 +602,16 @@ static int caller_id_privacy_handler(const struct aco_option *opt, struct ast_va return 0; } +static int caller_id_privacy_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + const char *presentation = ast_named_caller_presentation( + endpoint->id.self.name.presentation); + + *buf = ast_strdup(presentation); + return 0; +} + static int caller_id_tag_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -456,6 +619,13 @@ static int caller_id_tag_handler(const struct aco_option *opt, struct ast_variab return endpoint->id.self.tag ? 0 : -1; } +static int caller_id_tag_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->id.self.tag); + return 0; +} + static int media_encryption_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; @@ -474,6 +644,23 @@ static int media_encryption_handler(const struct aco_option *opt, struct ast_var return 0; } +static const char *media_encryption_map[] = { + [AST_SIP_MEDIA_TRANSPORT_INVALID] = "invalid", + [AST_SIP_MEDIA_ENCRYPT_NONE] = "none", + [AST_SIP_MEDIA_ENCRYPT_SDES] = "sdes", + [AST_SIP_MEDIA_ENCRYPT_DTLS] = "dtls", +}; + +static int media_encryption_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (ARRAY_IN_BOUNDS(endpoint->media.rtp.encryption, media_encryption_map)) { + *buf = ast_strdup(media_encryption_map[ + endpoint->media.rtp.encryption]); + } + return 0; +} + static int group_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -494,6 +681,30 @@ static int group_handler(const struct aco_option *opt, return 0; } +static int callgroup_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) { + return -1; + } + + ast_print_group(*buf, MAX_OBJECT_FIELD, endpoint->pickup.callgroup); + return 0; +} + +static int pickupgroup_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) { + return -1; + } + + ast_print_group(*buf, MAX_OBJECT_FIELD, endpoint->pickup.pickupgroup); + return 0; +} + static int named_groups_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -516,6 +727,26 @@ static int named_groups_handler(const struct aco_option *opt, return 0; } +static int named_callgroups_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + + ast_print_namedgroups(&str, endpoint->pickup.named_callgroups); + *buf = ast_strdup(ast_str_buffer(str)); + return 0; +} + +static int named_pickupgroups_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + + ast_print_namedgroups(&str, endpoint->pickup.named_pickupgroups); + *buf = ast_strdup(ast_str_buffer(str)); + return 0; +} + static int dtls_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -535,6 +766,72 @@ static int dtls_handler(const struct aco_option *opt, return ast_rtp_dtls_cfg_parse(&endpoint->media.rtp.dtls_cfg, name, var->value); } +static int dtlsverify_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(AST_YESNO(endpoint->media.rtp.dtls_cfg.verify)); + return 0; +} + +static int dtlsrekey_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + + return ast_asprintf( + buf, "%d", endpoint->media.rtp.dtls_cfg.rekey) >=0 ? 0 : -1; +} + +static int dtlscertfile_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->media.rtp.dtls_cfg.certfile); + return 0; +} + +static int dtlsprivatekey_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->media.rtp.dtls_cfg.pvtfile); + return 0; +} + +static int dtlscipher_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->media.rtp.dtls_cfg.cipher); + return 0; +} + +static int dtlscafile_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->media.rtp.dtls_cfg.cafile); + return 0; +} + +static int dtlscapath_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + *buf = ast_strdup(endpoint->media.rtp.dtls_cfg.capath); + return 0; +} + +static const char *ast_rtp_dtls_setup_map[] = { + [AST_RTP_DTLS_SETUP_ACTIVE] = "active", + [AST_RTP_DTLS_SETUP_PASSIVE] = "passive", + [AST_RTP_DTLS_SETUP_ACTPASS] = "actpass", + [AST_RTP_DTLS_SETUP_HOLDCONN] = "holdconn", +}; + +static int dtlssetup_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (ARRAY_IN_BOUNDS(endpoint->media.rtp.dtls_cfg.default_setup, ast_rtp_dtls_setup_map)) { + *buf = ast_strdup(ast_rtp_dtls_setup_map[endpoint->media.rtp.dtls_cfg.default_setup]); + } + return 0; +} + static int t38udptl_ec_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -553,6 +850,22 @@ static int t38udptl_ec_handler(const struct aco_option *opt, return 0; } +static const char *ast_t38_ec_modes_map[] = { + [UDPTL_ERROR_CORRECTION_NONE] = "none", + [UDPTL_ERROR_CORRECTION_FEC] = "fec", + [UDPTL_ERROR_CORRECTION_REDUNDANCY] = "redundancy" +}; + +static int t38udptl_ec_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_endpoint *endpoint = obj; + if (ARRAY_IN_BOUNDS(endpoint->media.t38.error_correction, ast_t38_ec_modes_map)) { + *buf = ast_strdup(ast_t38_ec_modes_map[ + endpoint->media.t38.error_correction]); + } + return 0; +} + static void *sip_nat_hook_alloc(const char *name) { return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL); @@ -631,12 +944,305 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o return 0; } -int ast_res_pjsip_initialize_configuration(void) +static const char *get_device_state(const struct ast_sip_endpoint *endpoint) +{ + char device[MAX_OBJECT_FIELD]; + + snprintf(device, MAX_OBJECT_FIELD, "PJSIP/%s", ast_sorcery_object_get_id(endpoint)); + return ast_devstate2str(ast_device_state(device)); +} + +static struct ast_endpoint_snapshot *sip_get_endpoint_snapshot( + const struct ast_sip_endpoint *endpoint) +{ + return ast_endpoint_latest_snapshot( + ast_endpoint_get_tech(endpoint->persistent), + ast_endpoint_get_resource(endpoint->persistent)); +} + +int ast_sip_for_each_channel_snapshot( + const struct ast_endpoint_snapshot *endpoint_snapshot, + on_channel_snapshot_t on_channel_snapshot, void *arg) +{ + int num, num_channels = endpoint_snapshot->num_channels; + RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); + + if (!on_channel_snapshot || !num_channels || + !(cache = ast_channel_cache())) { + return 0; + } + + ao2_ref(cache, +1); + + for (num = 0; num < num_channels; ++num) { + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + struct ast_channel_snapshot *snapshot; + + msg = stasis_cache_get(cache, ast_channel_snapshot_type(), + endpoint_snapshot->channel_ids[num]); + + if (!(snapshot = stasis_message_data(msg))) { + continue; + } + + if (on_channel_snapshot( + snapshot, num == (num_channels - 1), arg)) { + return -1; + } + } + return 0; +} + +static int active_channels_to_str_cb(const struct ast_channel_snapshot *snapshot, + int last, void *arg) +{ + struct ast_str **buf = arg; + if (last) { + ast_str_append(buf, 0, "%s", snapshot->name); + } else { + ast_str_append(buf, 0, "%s,", snapshot->name); + } + return 0; +} + +static void active_channels_to_str(const struct ast_sip_endpoint *endpoint, + struct ast_str **str) +{ + + RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, + sip_get_endpoint_snapshot(endpoint), ao2_cleanup); + + if (endpoint_snapshot) { + return; + } + + ast_sip_for_each_channel_snapshot(endpoint_snapshot, + active_channels_to_str_cb, str); +} + +#define AMI_DEFAULT_STR_SIZE 512 + +struct ast_str *ast_sip_create_ami_event(const char *event, struct ast_sip_ami *ami) +{ + struct ast_str *buf = ast_str_create(AMI_DEFAULT_STR_SIZE); + + if (!(buf)) { + astman_send_error_va(ami->s, ami->m, "Unable create event " + "for %s\n", event); + return NULL; + } + + ast_str_set(&buf, 0, "Event: %s\r\n", event); + return buf; +} + +static void sip_sorcery_object_ami_set_type_name(const void *obj, struct ast_str **buf) +{ + ast_str_append(buf, 0, "ObjectType: %s\r\n", + ast_sorcery_object_get_type(obj)); + ast_str_append(buf, 0, "ObjectName: %s\r\n", + ast_sorcery_object_get_id(obj)); +} + +int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf) +{ + RAII_VAR(struct ast_variable *, objset, ast_sorcery_objectset_create( + ast_sip_get_sorcery(), obj), ast_variables_destroy); + struct ast_variable *i; + + if (!objset) { + return -1; + } + + sip_sorcery_object_ami_set_type_name(obj, buf); + + for (i = objset; i; i = i->next) { + RAII_VAR(char *, camel, ast_to_camel_case(i->name), ast_free); + ast_str_append(buf, 0, "%s: %s\r\n", camel, i->value); + } + + return 0; +} + +static int sip_endpoints_aors_ami(void *obj, void *arg, int flags) +{ + const struct ast_sip_aor *aor = obj; + struct ast_str **buf = arg; + + ast_str_append(buf, 0, "Contacts: "); + ast_sip_for_each_contact(aor, ast_sip_contact_to_str, arg); + ast_str_append(buf, 0, "\r\n"); + + return 0; +} + +static int sip_endpoint_to_ami(const struct ast_sip_endpoint *endpoint, + struct ast_str **buf) +{ + if (ast_sip_sorcery_object_to_ami(endpoint, buf)) { + return -1; + } + + ast_str_append(buf, 0, "DeviceState: %s\r\n", + get_device_state(endpoint)); + + ast_str_append(buf, 0, "ActiveChannels: "); + active_channels_to_str(endpoint, buf); + ast_str_append(buf, 0, "\r\n"); + + return 0; +} + +static int format_ami_endpoint(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami) +{ + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("EndpointDetail", ami), ast_free); + + if (!buf) { + return -1; + } + + sip_endpoint_to_ami(endpoint, &buf); + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +#define AMI_SHOW_ENDPOINTS "PJSIPShowEndpoints" +#define AMI_SHOW_ENDPOINT "PJSIPShowEndpoint" + +static int ami_show_endpoint(struct mansession *s, const struct message *m) +{ + struct ast_sip_ami ami = { .s = s, .m = m }; + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + const char *endpoint_name = astman_get_header(m, "Endpoint"); + int count = 0; + + if (ast_strlen_zero(endpoint_name)) { + astman_send_error_va(s, m, "%s requires an endpoint name\n", + AMI_SHOW_ENDPOINT); + return 0; + } + + if (!strncasecmp(endpoint_name, "pjsip/", 6)) { + endpoint_name += 6; + } + + if (!(endpoint = ast_sorcery_retrieve_by_id( + ast_sip_get_sorcery(), "endpoint", endpoint_name))) { + astman_send_error_va(s, m, "Unable to retrieve endpoint %s\n", + endpoint_name); + return -1; + } + + astman_send_listack(s, m, "Following are Events for each object " + "associated with the the Endpoint", "start"); + + /* the endpoint detail needs to always come first so apply as such */ + if (format_ami_endpoint(endpoint, &ami) || + ast_sip_format_endpoint_ami(endpoint, &ami, &count)) { + astman_send_error_va(s, m, "Unable to format endpoint %s\n", + endpoint_name); + } + + astman_append(s, + "Event: EndpointDetailComplete\r\n" + "EventList: Complete\r\n" + "ListItems: %d\r\n\r\n", count + 1); + return 0; +} + +static int format_str_append_auth(const struct ast_sip_auth_array *auths, + struct ast_str **buf) +{ + char *str = NULL; + if (ast_sip_auths_to_str(auths, &str)) { + return -1; + } + ast_str_append(buf, 0, "%s", str ? str : ""); + ast_free(str); + return 0; +} + +static int format_ami_endpoints(void *obj, void *arg, int flags) +{ + + struct ast_sip_endpoint *endpoint = obj; + struct ast_sip_ami *ami = arg; + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("EndpointList", ami), ast_free); + + if (!buf) { + return -1; + } + + sip_sorcery_object_ami_set_type_name(endpoint, &buf); + ast_str_append(&buf, 0, "Transport: %s\r\n", + endpoint->transport); + ast_str_append(&buf, 0, "Aor: %s\r\n", + endpoint->aors); + + ast_str_append(&buf, 0, "Auths: "); + format_str_append_auth(&endpoint->inbound_auths, &buf); + ast_str_append(&buf, 0, "\r\n"); + + ast_str_append(&buf, 0, "OutboundAuths: "); + format_str_append_auth(&endpoint->outbound_auths, &buf); + ast_str_append(&buf, 0, "\r\n"); + + ast_sip_for_each_aor(endpoint->aors, + sip_endpoints_aors_ami, &buf); + + ast_str_append(&buf, 0, "DeviceState: %s\r\n", + get_device_state(endpoint)); + + ast_str_append(&buf, 0, "ActiveChannels: "); + active_channels_to_str(endpoint, &buf); + ast_str_append(&buf, 0, "\r\n"); + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +static int ami_show_endpoints(struct mansession *s, const struct message *m) +{ + struct ast_sip_ami ami = { .s = s, .m = m }; + RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup); + int num; + + endpoints = ast_sip_get_endpoints(); + if (!endpoints) { + return -1; + } + + if (!(num = ao2_container_count(endpoints))) { + astman_send_error(s, m, "No endpoints found\n"); + return 0; + } + + astman_send_listack(s, m, "A listing of Endpoints follows, " + "presented as EndpointList events", "start"); + + ao2_callback(endpoints, OBJ_NODATA, format_ami_endpoints, &ami); + + astman_append(s, + "Event: EndpointListComplete\r\n" + "EventList: Complete\r\n" + "ListItems: %d\r\n\r\n", num); + return 0; +} + +int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info) { if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) { return -1; } + if (ast_manager_register_xml(AMI_SHOW_ENDPOINTS, EVENT_FLAG_SYSTEM, ami_show_endpoints) || + ast_manager_register_xml(AMI_SHOW_ENDPOINT, EVENT_FLAG_SYSTEM, ami_show_endpoint)) { + return -1; + } + if (!(persistent_endpoints = ao2_container_alloc(PERSISTENT_BUCKETS, persistent_endpoint_hash, persistent_endpoint_cmp))) { return -1; } @@ -672,7 +1278,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, media.prefs, media.codecs)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmf_mode", "rfc4733", dtmf_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmf_mode", "rfc4733", dtmf_handler, dtmf_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ipv6)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.symmetric)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ice_support", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ice_support)); @@ -682,23 +1288,23 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.min_se)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.sess_expires)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, inbound_auths_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.address)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, ident_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.enabled)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, direct_media_method_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, connected_line_method_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, direct_media_glare_mitigation_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disable_direct_media_on_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.disable_on_nat)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, caller_id_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, caller_id_privacy_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, caller_id_tag_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_inbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai)); @@ -706,17 +1312,17 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, media_encryption_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "one_touch_recording", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, info.recording.enabled)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "inband_progress", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, inband_progress)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "pickup_group", "", group_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_call_group", "", named_groups_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_pickup_group", "", named_groups_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, callgroup_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "pickup_group", "", group_handler, pickupgroup_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_call_group", "", named_groups_handler, named_callgroups_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_pickup_group", "", named_groups_handler, named_pickupgroups_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "device_state_busy_at", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, devicestate_busy_at)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.enabled)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat)); @@ -738,14 +1344,14 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_file", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_path", "", dtls_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_verify", "", dtls_handler, dtlsverify_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_rekey", "", dtls_handler, dtlsrekey_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cert_file", "", dtls_handler, dtlscertfile_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_private_key", "", dtls_handler, dtlsprivatekey_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_cipher", "", dtls_handler, dtlscipher_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_file", "", dtls_handler, dtlscafile_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_ca_path", "", dtls_handler, dtlscapath_to_str, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, dtlssetup_to_str, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32)); if (ast_sip_initialize_sorcery_transport(sip_sorcery)) { @@ -793,6 +1399,8 @@ int ast_res_pjsip_initialize_configuration(void) void ast_res_pjsip_destroy_configuration(void) { ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); + ast_manager_unregister(AMI_SHOW_ENDPOINT); + ast_manager_unregister(AMI_SHOW_ENDPOINTS); ast_sorcery_unref(sip_sorcery); } diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index a8240e5dda..fe2948a163 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -29,6 +29,8 @@ #include "asterisk/res_pjsip.h" #include "asterisk/module.h" #include "asterisk/acl.h" +#include "asterisk/manager.h" +#include "res_pjsip/include/res_pjsip_private.h" /*** DOCUMENTATION @@ -164,6 +166,68 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va return error; } +static int ip_identify_match_to_str(const void *obj, const intptr_t *args, char **buf) +{ + RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free); + const struct ip_identify_match *identify = obj; + + ast_ha_join(identify->matches, &str); + *buf = ast_strdup(ast_str_buffer(str)); + return 0; +} + +static int sip_identify_to_ami(const struct ip_identify_match *identify, + struct ast_str **buf) +{ + return ast_sip_sorcery_object_to_ami(identify, buf); +} + +static int find_identify_by_endpoint(void *obj, void *arg, int flags) +{ + struct ip_identify_match *identify = obj; + const char *endpoint_name = arg; + + return strcmp(identify->endpoint_name, endpoint_name) ? 0 : CMP_MATCH | CMP_STOP; +} + +static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint, + struct ast_sip_ami *ami) +{ + RAII_VAR(struct ao2_container *, identifies, NULL, ao2_cleanup); + RAII_VAR(struct ip_identify_match *, identify, NULL, ao2_cleanup); + RAII_VAR(struct ast_str *, buf, NULL, ast_free); + + if (!(identifies = ast_sorcery_retrieve_by_fields( + ast_sip_get_sorcery(), "identify", AST_RETRIEVE_FLAG_MULTIPLE | + AST_RETRIEVE_FLAG_ALL, NULL))) { + return -1; + } + + if (!(identify = ao2_callback(identifies, OBJ_NOLOCK, + find_identify_by_endpoint, + (void*)ast_sorcery_object_get_id(endpoint)))) { + return 1; + } + + if (!(buf = ast_sip_create_ami_event("IdentifyDetail", ami))) { + return -1; + } + + if (sip_identify_to_ami(identify, &buf)) { + return -1; + } + + ast_str_append(&buf, 0, "EndpointName: %s\r\n", + ast_sorcery_object_get_id(endpoint)); + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +struct ast_sip_endpoint_formatter endpoint_identify_formatter = { + .format_ami = format_ami_endpoint_identify +}; + static int load_module(void) { ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "pjsip.conf,criteria=type=identify"); @@ -174,10 +238,11 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name)); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, ip_identify_match_to_str, 0, 0); ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify"); ast_sip_register_endpoint_identifier(&ip_identifier); + ast_sip_register_endpoint_formatter(&endpoint_identify_formatter); return AST_MODULE_LOAD_SUCCESS; } diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c index 0144e1cbbb..f312a522c8 100644 --- a/res/res_pjsip_exten_state.c +++ b/res/res_pjsip_exten_state.c @@ -515,6 +515,18 @@ static void subscription_terminated(struct ast_sip_subscription *sub, send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_TERMINATED); } +static void to_ami(struct ast_sip_subscription *sub, + struct ast_str **buf) +{ + struct exten_state_subscription *exten_state_sub = + get_exten_state_sub(sub); + + ast_str_append(buf, 0, "SubscriptionType: extension_state\r\n" + "Extension: %s\r\nExtensionStates: %s\r\n", + exten_state_sub->exten, ast_extension_state2str( + exten_state_sub->last_exten_state)); +} + #define DEFAULT_PRESENCE_BODY "application/pidf+xml" /*! @@ -545,6 +557,7 @@ static struct ast_sip_subscription_handler *create_and_register_handler( handler->resubscribe = resubscribe; handler->subscription_timeout = subscription_timeout; handler->subscription_terminated = subscription_terminated; + handler->to_ami = to_ami; if (ast_sip_register_subscription_handler(handler)) { ast_log(LOG_WARNING, "Unable to register subscription handler %s\n", diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c index bd865eec4c..4f32f382aa 100644 --- a/res/res_pjsip_mwi.c +++ b/res/res_pjsip_mwi.c @@ -54,6 +54,7 @@ static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data); static int mwi_refresh_subscription(struct ast_sip_subscription *sub); +static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf); static struct ast_sip_subscription_handler mwi_handler = { .event_name = "message-summary", @@ -67,6 +68,7 @@ static struct ast_sip_subscription_handler mwi_handler = { .notify_response = mwi_notify_response, .notify_request = mwi_notify_request, .refresh_subscription = mwi_refresh_subscription, + .to_ami = mwi_to_ami, }; /*! @@ -118,11 +120,11 @@ static struct mwi_stasis_subscription *mwi_stasis_subscription_alloc(const char { struct mwi_stasis_subscription *mwi_stasis_sub; struct stasis_topic *topic; - + if (!mwi_sub) { return NULL; } - + mwi_stasis_sub = ao2_alloc(sizeof(*mwi_stasis_sub) + strlen(mailbox), NULL); if (!mwi_stasis_sub) { return NULL; @@ -209,7 +211,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint * static int mwi_sub_hash(const void *obj, int flags) { const struct mwi_subscription *mwi_sub = obj; - + return ast_str_hash(mwi_sub->id); } @@ -572,6 +574,44 @@ static int mwi_refresh_subscription(struct ast_sip_subscription *sub) return 0; } +static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs, + struct ast_str **str) +{ + int num = ao2_container_count(stasis_subs); + + struct mwi_stasis_subscription *node; + struct ao2_iterator i = ao2_iterator_init(stasis_subs, 0); + + while ((node = ao2_iterator_next(&i))) { + if (--num) { + ast_str_append(str, 0, "%s,", node->mailbox); + } else { + ast_str_append(str, 0, "%s", node->mailbox); + } + ao2_ref(node, -1); + } + ao2_iterator_destroy(&i); +} + +static void mwi_to_ami(struct ast_sip_subscription *sub, + struct ast_str **buf) +{ + struct mwi_subscription *mwi_sub; + RAII_VAR(struct ast_datastore *, mwi_datastore, + ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + + if (!mwi_datastore) { + return; + } + + mwi_sub = mwi_datastore->data; + + ast_str_append(buf, 0, "SubscriptionType: mwi\r\n"); + ast_str_append(buf, 0, "Mailboxes: "); + mwi_subscription_mailboxes_str(mwi_sub->stasis_subs, buf); + ast_str_append(buf, 0, "\r\n"); +} + static int serialized_notify(void *userdata) { struct mwi_subscription *mwi_sub = userdata; diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 823304edf4..b2ccd3b954 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -32,6 +32,7 @@ #include "asterisk/taskprocessor.h" #include "asterisk/cli.h" #include "asterisk/stasis_system.h" +#include "res_pjsip/include/res_pjsip_private.h" /*** DOCUMENTATION @@ -129,6 +130,20 @@ + + + Lists PJSIP outbound registrations. + + + + + In response OutboundRegistrationDetail events showing configuration and status + information are raised for each outbound registration object. AuthDetail + events are raised for each associated auth object as well. Once all events are completed an + OutboundRegistrationDetailComplete is issued. + + + ***/ /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */ @@ -796,6 +811,12 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab return ast_sip_auth_array_init(®istration->outbound_auths, var->value); } +static int outbound_auths_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct sip_outbound_registration *registration = obj; + return ast_sip_auths_to_str(®istration->outbound_auths, buf); +} + static struct sip_outbound_registration *retrieve_registration(const char *registration_name) { return ast_sorcery_retrieve_by_id( @@ -937,6 +958,85 @@ static struct ast_cli_entry cli_outbound_registration[] = { AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0") }; +struct sip_ami_outbound { + struct ast_sip_ami *ami; + int registered; + int not_registered; + struct sip_outbound_registration *registration; +}; + +static int ami_outbound_registration_task(void *obj) +{ + struct sip_ami_outbound *ami = obj; + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("OutboundRegistrationDetail", ami->ami), ast_free); + + if (!buf) { + return -1; + } + + ast_sip_sorcery_object_to_ami(ami->registration, &buf); + + if (ami->registration->state) { + pjsip_regc_info info; + if (ami->registration->state->client_state->status == + SIP_REGISTRATION_REGISTERED) { + ++ami->registered; + } else { + ++ami->not_registered; + } + + ast_str_append(&buf, 0, "Status: %s%s", + sip_outbound_registration_status_str[ + ami->registration->state->client_state->status], "\r\n"); + + pjsip_regc_get_info(ami->registration->state->client_state->client, &info); + ast_str_append(&buf, 0, "NextReg: %d%s", info.next_reg, "\r\n"); + } + + astman_append(ami->ami->s, "%s\r\n", ast_str_buffer(buf)); + return ast_sip_format_auths_ami(&ami->registration->outbound_auths, ami->ami); +} + +static int ami_outbound_registration_detail(void *obj, void *arg, int flags) +{ + struct sip_ami_outbound *ami = arg; + + ami->registration = obj; + return ast_sip_push_task_synchronous( + NULL, ami_outbound_registration_task, ami); +} + +static int ami_show_outbound_registrations(struct mansession *s, + const struct message *m) +{ + struct ast_sip_ami ami = { s = s, m = m }; + struct sip_ami_outbound ami_outbound = { .ami = &ami }; + RAII_VAR(struct ao2_container *, regs, ast_sorcery_retrieve_by_fields( + ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | + AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); + + if (!regs) { + astman_send_error(s, m, "Unable to retreive " + "outbound registrations\n"); + return -1; + } + + astman_send_listack(s, m, "Following are Events for each Outbound " + "registration", "start"); + + ao2_callback(regs, OBJ_NODATA, ami_outbound_registration_detail, &ami_outbound); + + astman_append(s, + "Event: OutboundRegistrationDetailComplete\r\n" + "EventList: Complete\r\n" + "Registered: %d\r\n" + "NotRegistered: %d\r\n\r\n", + ami_outbound.registered, + ami_outbound.not_registered); + return 0; +} + static int load_module(void) { ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration"); @@ -956,12 +1056,13 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent)); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, 0, 0); ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); sip_outbound_registration_perform_all(); ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration)); ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister); + ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING,ami_show_outbound_registrations); return AST_MODULE_LOAD_SUCCESS; } @@ -975,6 +1076,7 @@ static int reload_module(void) static int unload_module(void) { ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration)); + ast_manager_unregister("PJSIPShowRegistrationsOutbound"); ast_manager_unregister("PJSIPUnregister"); return 0; } diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index 4daacd42c3..5bcfd07cec 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -40,6 +40,38 @@ #include "asterisk/taskprocessor.h" #include "asterisk/sched.h" #include "asterisk/res_pjsip.h" +#include "asterisk/callerid.h" +#include "asterisk/manager.h" +#include "res_pjsip/include/res_pjsip_private.h" + +/*** DOCUMENTATION + + + Lists subscriptions. + + + + + Provides a listing of all inbound subscriptions. An event InboundSubscriptionDetail + is issued for each subscription object. Once all detail events are completed an + InboundSubscriptionDetailComplete event is issued. + + + + + + Lists subscriptions. + + + + + Provides a listing of all outbound subscriptions. An event OutboundSubscriptionDetail + is issued for each subscription object. Once all detail events are completed an + OutboundSubscriptionDetailComplete event is issued. + + + + ***/ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata); @@ -163,8 +195,88 @@ struct ast_sip_subscription { pjsip_evsub *evsub; /*! The underlying PJSIP dialog */ pjsip_dialog *dlg; + /*! Next item in the list */ + AST_LIST_ENTRY(ast_sip_subscription) next; +}; + +static const char *sip_subscription_roles_map[] = { + [AST_SIP_SUBSCRIBER] = "Subscriber", + [AST_SIP_NOTIFIER] = "Notifier" }; +AST_RWLIST_HEAD_STATIC(subscriptions, ast_sip_subscription); + +static void add_subscription(struct ast_sip_subscription *obj) +{ + SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_INSERT_TAIL(&subscriptions, obj, next); + ast_module_ref(ast_module_info->self); +} + +static void remove_subscription(struct ast_sip_subscription *obj) +{ + struct ast_sip_subscription *i; + SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&subscriptions, i, next) { + if (i == obj) { + AST_RWLIST_REMOVE_CURRENT(next); + ast_module_unref(ast_module_info->self); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; +} + +typedef int (*on_subscription_t)(struct ast_sip_subscription *sub, void *arg); + +static int for_each_subscription(on_subscription_t on_subscription, void *arg) +{ + int num = 0; + struct ast_sip_subscription *i; + SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + + if (!on_subscription) { + return num; + } + + AST_RWLIST_TRAVERSE(&subscriptions, i, next) { + if (on_subscription(i, arg)) { + break; + } + ++num; + } + return num; +} + +static void sip_subscription_to_ami(struct ast_sip_subscription *sub, + struct ast_str **buf) +{ + char str[256]; + struct ast_sip_endpoint_id_configuration *id = &sub->endpoint->id; + + ast_str_append(buf, 0, "Role: %s\r\n", + sip_subscription_roles_map[sub->role]); + ast_str_append(buf, 0, "Endpoint: %s\r\n", + ast_sorcery_object_get_id(sub->endpoint)); + + ast_copy_pj_str(str, &sub->dlg->call_id->id, sizeof(str)); + ast_str_append(buf, 0, "Callid: %s\r\n", str); + + ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name( + ast_sip_subscription_get_evsub(sub))); + + ast_callerid_merge(str, sizeof(str), + S_COR(id->self.name.valid, id->self.name.str, NULL), + S_COR(id->self.number.valid, id->self.number.str, NULL), + "Unknown"); + + ast_str_append(buf, 0, "Callerid: %s\r\n", str); + + if (sub->handler->to_ami) { + sub->handler->to_ami(sub, buf); + } +} + #define DATASTORE_BUCKETS 53 #define DEFAULT_EXPIRES 3600 @@ -196,6 +308,7 @@ static void subscription_destructor(void *obj) struct ast_sip_subscription *sub = obj; ast_debug(3, "Destroying SIP subscription\n"); + remove_subscription(sub); ao2_cleanup(sub->datastores); ao2_cleanup(sub->endpoint); @@ -311,6 +424,8 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su ao2_ref(endpoint, +1); sub->endpoint = endpoint; sub->handler = handler; + + add_subscription(sub); return sub; } @@ -1114,6 +1229,71 @@ static void pubsub_on_server_timeout(pjsip_evsub *evsub) ast_sip_push_task(sub->serializer, serialized_pubsub_on_server_timeout, sub); } +static int ami_subscription_detail(struct ast_sip_subscription *sub, + struct ast_sip_ami *ami, + const char *event) +{ + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event(event, ami), ast_free); + + if (!buf) { + return -1; + } + + sip_subscription_to_ami(sub, &buf); + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + return 0; +} + +static int ami_subscription_detail_inbound(struct ast_sip_subscription *sub, void *arg) +{ + return sub->role == AST_SIP_NOTIFIER ? ami_subscription_detail( + sub, arg, "InboundSubscriptionDetail") : 0; +} + +static int ami_subscription_detail_outbound(struct ast_sip_subscription *sub, void *arg) +{ + return sub->role == AST_SIP_SUBSCRIBER ? ami_subscription_detail( + sub, arg, "OutboundSubscriptionDetail") : 0; +} + +static int ami_show_subscriptions_inbound(struct mansession *s, const struct message *m) +{ + struct ast_sip_ami ami = { .s = s, .m = m }; + int num; + + astman_send_listack(s, m, "Following are Events for " + "each inbound Subscription", "start"); + + num = for_each_subscription(ami_subscription_detail_inbound, &ami); + + astman_append(s, + "Event: InboundSubscriptionDetailComplete\r\n" + "EventList: Complete\r\n" + "ListItems: %d\r\n\r\n", num); + return 0; +} + +static int ami_show_subscriptions_outbound(struct mansession *s, const struct message *m) +{ + struct ast_sip_ami ami = { .s = s, .m = m }; + int num; + + astman_send_listack(s, m, "Following are Events for " + "each outbound Subscription", "start"); + + num = for_each_subscription(ami_subscription_detail_outbound, &ami); + + astman_append(s, + "Event: OutboundSubscriptionDetailComplete\r\n" + "EventList: Complete\r\n" + "ListItems: %d\r\n\r\n", num); + return 0; +} + +#define AMI_SHOW_SUBSCRIPTIONS_INBOUND "PJSIPShowSubscriptionsInbound" +#define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND "PJSIPShowSubscriptionsOutbound" + static int load_module(void) { static const pj_str_t str_PUBLISH = { "PUBLISH", 7 }; @@ -1139,11 +1319,19 @@ static int load_module(void) return AST_MODULE_LOAD_FAILURE; } + ast_manager_register_xml(AMI_SHOW_SUBSCRIPTIONS_INBOUND, EVENT_FLAG_SYSTEM, + ami_show_subscriptions_inbound); + ast_manager_register_xml(AMI_SHOW_SUBSCRIPTIONS_OUTBOUND, EVENT_FLAG_SYSTEM, + ami_show_subscriptions_outbound); + return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { + ast_manager_unregister(AMI_SHOW_SUBSCRIPTIONS_OUTBOUND); + ast_manager_unregister(AMI_SHOW_SUBSCRIPTIONS_INBOUND); + if (sched) { ast_sched_context_destroy(sched); } diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index 143f96d797..82d97a0d52 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -31,6 +31,25 @@ #include "asterisk/module.h" #include "asterisk/test.h" #include "asterisk/taskprocessor.h" +#include "asterisk/manager.h" +#include "res_pjsip/include/res_pjsip_private.h" + +/*** DOCUMENTATION + + + Lists PJSIP inbound registrations. + + + + + In response InboundRegistrationDetail events showing configuration and status + information are raised for each inbound registration object. As well as AuthDetail + events for each associated auth object. Once all events are completed an + InboundRegistrationDetailComplete is issued. + + + + ***/ /*! \brief Internal function which returns the expiration time for a contact */ static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata) @@ -569,6 +588,66 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) return PJ_TRUE; } +static int ami_registrations_aor(void *obj, void *arg, int flags) +{ + struct ast_sip_aor *aor = obj; + struct ast_sip_ami *ami = arg; + int *count = ami->arg; + RAII_VAR(struct ast_str *, buf, + ast_sip_create_ami_event("InboundRegistrationDetail", ami), ast_free); + + if (!buf) { + return -1; + } + + ast_sip_sorcery_object_to_ami(aor, &buf); + ast_str_append(&buf, 0, "Contacts: "); + ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &buf); + ast_str_append(&buf, 0, "\r\n"); + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + (*count)++; + return 0; +} + +static int ami_registrations_endpoint(void *obj, void *arg, int flags) +{ + struct ast_sip_endpoint *endpoint = obj; + return ast_sip_for_each_aor( + endpoint->aors, ami_registrations_aor, arg); +} + +static int ami_registrations_endpoints(void *arg) +{ + RAII_VAR(struct ao2_container *, endpoints, + ast_sip_get_endpoints(), ao2_cleanup); + + if (!endpoints) { + return 0; + } + + ao2_callback(endpoints, OBJ_NODATA, ami_registrations_endpoint, arg); + return 0; +} + +static int ami_show_registrations(struct mansession *s, const struct message *m) +{ + int count = 0; + struct ast_sip_ami ami = { .s = s, .m = m, .arg = &count }; + astman_send_listack(s, m, "Following are Events for each Inbound " + "registration", "start"); + + ami_registrations_endpoints(&ami); + + astman_append(s, + "Event: InboundRegistrationDetailComplete\r\n" + "EventList: Complete\r\n" + "ListItems: %d\r\n\r\n", count); + return 0; +} + +#define AMI_SHOW_REGISTRATIONS "PJSIPShowRegistrationsInbound" + static pjsip_module registrar_module = { .name = { "Registrar", 9 }, .id = -1, @@ -594,11 +673,15 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } + ast_manager_register_xml(AMI_SHOW_REGISTRATIONS, EVENT_FLAG_SYSTEM, + ami_show_registrations); + return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { + ast_manager_unregister(AMI_SHOW_REGISTRATIONS); ast_sip_unregister_service(®istrar_module); ao2_cleanup(serializers);