diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 0311dfd4b38261002ae483ddb019bcb16f0eadbd..1f658323d8c1759d579ac55190012f7fb37a4ebb 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -2362,6 +2362,77 @@ </syntax> </managerEventInstance> </managerEvent> + <managerEvent language="en_US" name="ContactList"> + <managerEventInstance class="EVENT_FLAG_COMMAND"> + <synopsis>Provide details about a contact section.</synopsis> + <syntax> + <parameter name="ObjectType"> + <para>The object's type. This will always be 'contact'.</para> + </parameter> + <parameter name="ObjectName"> + <para>The name of this object.</para> + </parameter> + <parameter name="ViaAddr"> + <para>IP address of the last Via header in REGISTER request. + Will only appear in the event if available.</para> + </parameter> + <parameter name="ViaPort"> + <para>Port number of the last Via header in REGISTER request. + Will only appear in the event if available.</para> + </parameter> + <parameter name="QualifyTimeout"> + <para>The elapsed time in decimal seconds after which an OPTIONS + message is sent before the contact is considered unavailable.</para> + </parameter> + <parameter name="CallId"> + <para>Content of the Call-ID header in REGISTER request. + Will only appear in the event if available.</para> + </parameter> + <parameter name="RegServer"> + <para>Asterisk Server name.</para> + </parameter> + <parameter name="PruneOnBoot"> + <para>If true delete the contact on Asterisk restart/boot.</para> + </parameter> + <parameter name="Path"> + <para>The Path header received on the REGISTER.</para> + </parameter> + <parameter name="Endpoint"> + <para>The name of the endpoint associated with this information.</para> + </parameter> + <parameter name="AuthenticateQualify"> + <para>A boolean indicating whether a qualify should be authenticated.</para> + </parameter> + <parameter name="Uri"> + <para>This contact's URI.</para> + </parameter> + <parameter name="QualifyFrequency"> + <para>The interval in seconds at which the contact will be qualified.</para> + </parameter> + <parameter name="UserAgent"> + <para>Content of the User-Agent header in REGISTER request</para> + </parameter> + <parameter name="ExpirationTime"> + <para>Absolute time that this contact is no longer valid after</para> + </parameter> + <parameter name="OutboundProxy"> + <para>The contact's outbound proxy.</para> + </parameter> + <parameter name="Status"> + <para>This contact's status.</para> + <enumlist> + <enum name="Reachable"/> + <enum name="Unreachable"/> + <enum name="NonQualified"/> + <enum name="Unknown"/> + </enumlist> + </parameter> + <parameter name="RoundtripUsec"> + <para>The round trip time in microseconds.</para> + </parameter> + </syntax> + </managerEventInstance> + </managerEvent> <managerEvent language="en_US" name="ContactStatusDetail"> <managerEventInstance class="EVENT_FLAG_COMMAND"> <synopsis>Provide details about a contact's status.</synopsis> @@ -2378,6 +2449,7 @@ <enum name="Reachable"/> <enum name="Unreachable"/> <enum name="NonQualified"/> + <enum name="Unknown"/> </enumlist> </parameter> <parameter name="RoundtripUsec"> @@ -2574,6 +2646,33 @@ </managerEvent> </responses> </manager> + <manager name="PJSIPShowContacts" language="en_US"> + <synopsis> + Lists PJSIP Contacts. + </synopsis> + <syntax /> + <description> + <para>Provides a listing of all Contacts. For each Contact a <literal>ContactList</literal> + event is raised that contains relevant attributes and status information. + Once all contacts have been listed a <literal>ContactListComplete</literal> event + is issued. + </para> + </description> + <responses> + <list-elements> + <xi:include xpointer="xpointer(/docs/managerEvent[@name='ContactList'])" /> + </list-elements> + <managerEvent language="en_US" name="ContactListComplete"> + <managerEventInstance class="EVENT_FLAG_COMMAND"> + <synopsis>Provide final information about a contact list.</synopsis> + <syntax> + <parameter name="EventList"/> + <parameter name="ListItems"/> + </syntax> + </managerEventInstance> + </managerEvent> + </responses> + </manager> ***/ diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index 9d7402b2a46f400d5a72bbea428595d74998da49..3465eae34acc0979598252d138a481ccea37ce73 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -929,6 +929,91 @@ static int ami_contact_cb(void *obj, void *arg, int flags) return 0; } +static struct ao2_container *get_all_contacts(void) +{ + struct ao2_container *contacts; + + contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", + AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + + return contacts; +} + +static int sip_contact_to_ami(const struct ast_sip_contact *contact, + struct ast_str **buf) +{ + return ast_sip_sorcery_object_to_ami(contact, buf); +} + +static int format_ami_contactlist_handler(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + struct ast_sip_ami *ami = arg; + struct ast_str *buf; + struct ast_sip_contact_status *status; + + buf = ast_sip_create_ami_event("ContactList", ami); + + if (!buf) { + return CMP_STOP; + } + + if (sip_contact_to_ami(contact, &buf)) { + ast_free(buf); + return CMP_STOP; + } + + /* Add extra info */ + status = ast_sorcery_retrieve_by_id( + ast_sip_get_sorcery(), CONTACT_STATUS, + ast_sorcery_object_get_id(contact)); + ast_str_append(&buf, 0, "Status: %s\r\n", + ast_sip_get_contact_status_label(status ? status->status : UNKNOWN)); + if (!status || status->status == UNKNOWN) { + ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n"); + } else { + ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt); + } + + astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); + + ami->count++; + + ast_free(buf); + + return 0; +} + +static int ami_show_contacts(struct mansession *s, const struct message *m) +{ + struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), }; + struct ao2_container *contacts; + + contacts = get_all_contacts(); + if (!contacts) { + astman_send_error(s, m, "Could not get Contacts\n"); + return 0; + } + + if (!ao2_container_count(contacts)) { + astman_send_error(s, m, "No Contacts found\n"); + ao2_ref(contacts, -1); + return 0; + } + + astman_send_listack(s, m, "A listing of Contacts follows, presented as ContactList events", + "start"); + + ao2_callback(contacts, OBJ_NODATA, format_ami_contactlist_handler, &ami); + + astman_send_list_complete_start(s, m, "ContactListComplete", ami.count); + astman_send_list_complete_end(s); + + ao2_ref(contacts, -1); + + return 0; +} + static int ami_sip_qualify(struct mansession *s, const struct message *m) { const char *endpoint_name = astman_get_header(m, "Endpoint"); @@ -1479,6 +1564,7 @@ int ast_res_pjsip_init_options_handling(int reload) internal_sip_register_endpoint_formatter(&contact_status_formatter); ast_manager_register_xml("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify); + ast_manager_register_xml("PJSIPShowContacts", EVENT_FLAG_SYSTEM, ami_show_contacts); ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options)); update_all_unqualified_endpoints(); @@ -1491,6 +1577,7 @@ void ast_res_pjsip_cleanup_options_handling(void) { ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options)); ast_manager_unregister("PJSIPQualify"); + ast_manager_unregister("PJSIPShowContacts"); internal_sip_unregister_endpoint_formatter(&contact_status_formatter); ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options);