405 XMPP_AUTOPRUNE = (1 << 0),
406 XMPP_AUTOREGISTER = (1 << 1),
407 XMPP_AUTOACCEPT = (1 << 2),
408 XMPP_DEBUG = (1 << 3),
409 XMPP_USETLS = (1 << 4),
410 XMPP_USESASL = (1 << 5),
411 XMPP_FORCESSL = (1 << 6),
412 XMPP_KEEPALIVE = (1 << 7),
413 XMPP_COMPONENT = (1 << 8),
414 XMPP_SEND_TO_DIALPLAN = (1 << 9),
415 XMPP_DISTRIBUTE_EVENTS = (1 << 10),
420 XMPP_XEP0248 = (1 << 0),
421 XMPP_PUBSUB = (1 << 1),
422 XMPP_PUBSUB_AUTOCREATE = (1 << 2),
426 #define CLIENT_BUCKETS 53
429 #define BUDDY_BUCKETS 53
432 #define RESOURCE_BUCKETS 53
435 #define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
438 #define STATUS_DISAPPEAR 6
493 } xmpp_state_handlers[] = {
510 } xmpp_pak_handlers[] = {
516 static const char *app_ajisend =
"JabberSend";
517 static const char *app_ajisendgroup =
"JabberSendGroup";
518 static const char *app_ajistatus =
"JabberStatus";
519 static const char *app_ajijoin =
"JabberJoin";
520 static const char *app_ajileave =
"JabberLeave";
522 static ast_cond_t message_received_condition;
525 static int xmpp_client_config_post_apply(
void *obj,
void *arg,
int flags);
541 ast_free(message->
from);
561 if (client->filter) {
562 iks_filter_delete(client->filter);
566 iks_stack_delete(client->stack);
569 ao2_cleanup(client->buddies);
581 const char *
id = obj;
590 const char *
id = arg;
598 if (state == client->state) {
601 client->state = state;
619 client->thread = AST_PTHREADT_NULL;
629 if (!client->buddies) {
630 ast_log(LOG_ERROR,
"Could not initialize buddy container for '%s'\n", name);
636 ast_log(LOG_ERROR,
"Could not initialize stringfields for '%s'\n", name);
641 if (!(client->stack = iks_stack_new(8192, 8192))) {
642 ast_log(LOG_ERROR,
"Could not create an Iksemel stack for '%s'\n", name);
649 client->timeout = 50;
659 return ao2_find(tmp_container, category,
OBJ_KEY);
668 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, category))) {
672 ao2_ref(clientcfg->client, +1);
673 return clientcfg->client;
719 const char *match = (flags &
OBJ_KEY) ? arg : two->
name;
732 if (!(cfg->
global = ao2_alloc(
sizeof(*cfg->
global), NULL))) {
748 static int xmpp_config_prelink(
void *newitem)
754 if (ast_strlen_zero(clientcfg->
user)) {
755 ast_log(LOG_ERROR,
"No user specified on client '%s'\n", clientcfg->
name);
758 ast_log(LOG_ERROR,
"No password or refresh_token specified on client '%s'\n", clientcfg->
name);
760 }
else if (ast_strlen_zero(clientcfg->
server)) {
761 ast_log(LOG_ERROR,
"No server specified on client '%s'\n", clientcfg->
name);
765 ast_log(LOG_ERROR,
"No oauth_clientid or oauth_secret specified, so client '%s' can't be used\n", clientcfg->
name);
770 if (!cfg || !cfg->clients || !(oldclientcfg =
xmpp_config_find(cfg->clients, clientcfg->
name))) {
776 if (strcmp(clientcfg->
user, oldclientcfg->user) ||
777 strcmp(clientcfg->
password, oldclientcfg->password) ||
778 strcmp(clientcfg->
refresh_token, oldclientcfg->refresh_token) ||
779 strcmp(clientcfg->
oauth_clientid, oldclientcfg->oauth_clientid) ||
780 strcmp(clientcfg->
oauth_secret, oldclientcfg->oauth_secret) ||
781 strcmp(clientcfg->
server, oldclientcfg->server) ||
782 (clientcfg->
port != oldclientcfg->port) ||
783 (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
784 (clientcfg->
priority != oldclientcfg->priority)) {
793 static void xmpp_config_post_apply(
void)
803 .item_offset = offsetof(
struct xmpp_config, global),
805 .category =
"general",
810 static struct aco_type client_option = {
814 .category =
"general",
817 .item_prelink = xmpp_config_prelink,
818 .item_offset = offsetof(
struct xmpp_config, clients),
825 .alias =
"jabber.conf",
826 .types =
ACO_TYPES(&global_option, &client_option),
830 .files = ACO_FILES(&res_xmpp_conf),
831 .post_apply_config = xmpp_config_post_apply,
875 return client->stream_flags & SECURE;
886 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
915 char from[XMPP_MAX_JIDLEN];
919 !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
923 if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT)) {
924 snprintf(from,
sizeof(from),
"%s@%s/%s", nick, client->jid->full, nick);
926 snprintf(from,
sizeof(from),
"%s", client->jid->full);
929 iks_insert_attrib(message_packet,
"from", from);
933 iks_delete(message_packet);
946 iks *invite, *body = NULL, *
namespace = NULL;
948 if (!(invite = iks_new(
"message")) || !(body = iks_new(
"body")) || !(
namespace = iks_new(
"x"))) {
953 iks_insert_attrib(invite,
"to", user);
955 iks_insert_attrib(invite,
"id", client->
mid);
958 iks_insert_cdata(body, message, 0);
959 iks_insert_node(invite, body);
960 iks_insert_attrib(
namespace,
"xmlns",
"jabber:x:conference");
961 iks_insert_attrib(
namespace,
"jid", room);
962 iks_insert_node(invite,
namespace);
967 iks_delete(
namespace);
974 static int xmpp_client_set_group_presence(
struct ast_xmpp_client *client,
const char *room,
int level,
const char *nick)
979 iks *presence = NULL, *x = NULL;
980 char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
983 !(presence = iks_make_pres(level, NULL)) || !(x = iks_new(
"x"))) {
988 if (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT)) {
989 snprintf(from,
sizeof(from),
"%s@%s/%s", nick, client->jid->full, nick);
990 snprintf(roomid,
sizeof(roomid),
"%s/%s", room, nick);
992 snprintf(from,
sizeof(from),
"%s", client->jid->full);
993 snprintf(roomid,
sizeof(roomid),
"%s/%s", room,
S_OR(nick, client->jid->user));
996 iks_insert_attrib(presence,
"to", roomid);
997 iks_insert_attrib(presence,
"from", from);
998 iks_insert_attrib(x,
"xmlns",
"http://jabber.org/protocol/muc");
999 iks_insert_node(presence, x);
1005 iks_delete(presence);
1012 return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
1022 return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
1029 for (i = strlen(mid) - 1; i >= 0; i--) {
1030 if (mid[i] !=
'z') {
1031 mid[i] = mid[i] + 1;
1052 !(request = iks_new(
"iq"))) {
1056 if (!ast_strlen_zero(clientcfg->
pubsubnode)) {
1057 iks_insert_attrib(request,
"to", clientcfg->
pubsubnode);
1060 iks_insert_attrib(request,
"from", client->jid->full);
1061 iks_insert_attrib(request,
"type", type);
1064 iks_insert_attrib(request,
"id", client->
mid);
1078 const char *event_type,
unsigned int cachable)
1081 iks *request, *pubsub, *publish, *item;
1087 pubsub = iks_insert(request,
"pubsub");
1088 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub");
1089 publish = iks_insert(pubsub,
"publish");
1090 iks_insert_attrib(publish,
"node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
1091 item = iks_insert(publish,
"item");
1092 iks_insert_attrib(item,
"id", node);
1095 iks *options, *x, *field_form_type, *field_persist;
1097 options = iks_insert(pubsub,
"publish-options");
1098 x = iks_insert(options,
"x");
1099 iks_insert_attrib(x,
"xmlns",
"jabber:x:data");
1100 iks_insert_attrib(x,
"type",
"submit");
1101 field_form_type = iks_insert(x,
"field");
1102 iks_insert_attrib(field_form_type,
"var",
"FORM_TYPE");
1103 iks_insert_attrib(field_form_type,
"type",
"hidden");
1104 iks_insert_cdata(iks_insert(field_form_type,
"value"),
"http://jabber.org/protocol/pubsub#publish-options", 0);
1105 field_persist = iks_insert(x,
"field");
1106 iks_insert_attrib(field_persist,
"var",
"pubsub#persist_items");
1107 iks_insert_cdata(iks_insert(field_persist,
"value"),
"0", 1);
1114 static iks* xmpp_pubsub_build_node_config(iks *pubsub,
const char *node_type,
const char *collection_name)
1116 iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
1117 *field_deliver_payload, *field_persist_items, *field_access_model,
1118 *field_pubsub_collection;
1119 configure = iks_insert(pubsub,
"configure");
1120 x = iks_insert(configure,
"x");
1121 iks_insert_attrib(x,
"xmlns",
"jabber:x:data");
1122 iks_insert_attrib(x,
"type",
"submit");
1123 field_owner = iks_insert(x,
"field");
1124 iks_insert_attrib(field_owner,
"var",
"FORM_TYPE");
1125 iks_insert_attrib(field_owner,
"type",
"hidden");
1126 iks_insert_cdata(iks_insert(field_owner,
"value"),
1127 "http://jabber.org/protocol/pubsub#owner", 39);
1129 field_node_type = iks_insert(x,
"field");
1130 iks_insert_attrib(field_node_type,
"var",
"pubsub#node_type");
1131 iks_insert_cdata(iks_insert(field_node_type,
"value"), node_type, strlen(node_type));
1133 field_node_config = iks_insert(x,
"field");
1134 iks_insert_attrib(field_node_config,
"var",
"FORM_TYPE");
1135 iks_insert_attrib(field_node_config,
"type",
"hidden");
1136 iks_insert_cdata(iks_insert(field_node_config,
"value"),
1137 "http://jabber.org/protocol/pubsub#node_config", 45);
1138 field_deliver_payload = iks_insert(x,
"field");
1139 iks_insert_attrib(field_deliver_payload,
"var",
"pubsub#deliver_payloads");
1140 iks_insert_cdata(iks_insert(field_deliver_payload,
"value"),
"1", 1);
1141 field_persist_items = iks_insert(x,
"field");
1142 iks_insert_attrib(field_persist_items,
"var",
"pubsub#persist_items");
1143 iks_insert_cdata(iks_insert(field_persist_items,
"value"),
"1", 1);
1144 field_access_model = iks_insert(x,
"field");
1145 iks_insert_attrib(field_access_model,
"var",
"pubsub#access_model");
1146 iks_insert_cdata(iks_insert(field_access_model,
"value"),
"whitelist", 9);
1147 if (node_type && !strcasecmp(node_type,
"leaf")) {
1148 field_pubsub_collection = iks_insert(x,
"field");
1149 iks_insert_attrib(field_pubsub_collection,
"var",
"pubsub#collection");
1150 iks_insert_cdata(iks_insert(field_pubsub_collection,
"value"), collection_name,
1151 strlen(collection_name));
1164 iks *pubsub, *affiliations, *affiliate;
1168 if (!modify_affiliates) {
1169 ast_log(LOG_ERROR,
"Could not create IQ for creating affiliations on client '%s'\n", client->
name);
1173 pubsub = iks_insert(modify_affiliates,
"pubsub");
1174 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub#owner");
1175 affiliations = iks_insert(pubsub,
"affiliations");
1176 iks_insert_attrib(affiliations,
"node", node);
1179 while ((buddy = ao2_iterator_next(&i))) {
1180 affiliate = iks_insert(affiliations,
"affiliation");
1181 iks_insert_attrib(affiliate,
"jid", buddy->
id);
1182 iks_insert_attrib(affiliate,
"affiliation",
"owner");
1188 iks_delete(modify_affiliates);
1199 char *name,
const char *collection_name)
1201 iks *
node, *pubsub, *create;
1207 pubsub = iks_insert(node,
"pubsub");
1208 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub");
1209 create = iks_insert(pubsub,
"create");
1210 iks_insert_attrib(create,
"node", name);
1211 xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
1224 iks *request, *pubsub, *
delete;
1230 pubsub = iks_insert(request,
"pubsub");
1231 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub#owner");
1232 delete = iks_insert(pubsub,
"delete");
1233 iks_insert_attrib(
delete,
"node", node_name);
1236 iks_delete(request);
1257 const char *leaf_name)
1270 const char *oldmsgs,
const char *newmsgs)
1273 iks *mailbox_node, *request;
1282 mailbox_node = iks_insert(request,
"mailbox");
1283 iks_insert_attrib(mailbox_node,
"xmlns",
"http://asterisk.org");
1284 iks_insert_attrib(mailbox_node,
"eid", eid_str);
1285 iks_insert_cdata(iks_insert(mailbox_node,
"NEWMSGS"), newmsgs, strlen(newmsgs));
1286 iks_insert_cdata(iks_insert(mailbox_node,
"OLDMSGS"), oldmsgs, strlen(oldmsgs));
1290 iks_delete(request);
1301 const char *device_state,
unsigned int cachable)
1304 iks *request, *
state;
1305 char eid_str[20], cachable_str[2];
1311 if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
1312 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1320 state = iks_insert(request,
"state");
1321 iks_insert_attrib(state,
"xmlns",
"http://asterisk.org");
1322 iks_insert_attrib(state,
"eid", eid_str);
1323 snprintf(cachable_str,
sizeof(cachable_str),
"%u", cachable);
1324 iks_insert_attrib(state,
"cachable", cachable_str);
1325 iks_insert_cdata(state, device_state, strlen(device_state));
1327 iks_delete(request);
1338 char oldmsgs[10], newmsgs[10];
1352 snprintf(oldmsgs,
sizeof(oldmsgs),
"%d", mwi_state->
old_msgs);
1353 snprintf(newmsgs,
sizeof(newmsgs),
"%d", mwi_state->
new_msgs);
1388 iks *pubsub, *unsubscribe;
1391 ast_log(LOG_ERROR,
"Could not create IQ when creating pubsub unsubscription on client '%s'\n", client->
name);
1395 pubsub = iks_insert(request,
"pubsub");
1396 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub");
1397 unsubscribe = iks_insert(pubsub,
"unsubscribe");
1398 iks_insert_attrib(unsubscribe,
"jid", client->jid->partial);
1399 iks_insert_attrib(unsubscribe,
"node", node);
1402 iks_delete(request);
1414 iks *pubsub, *subscribe;
1416 if (!cfg || !cfg->global || !request) {
1417 ast_log(LOG_ERROR,
"Could not create IQ when creating pubsub subscription on client '%s'\n", client->
name);
1421 pubsub = iks_insert(request,
"pubsub");
1422 iks_insert_attrib(pubsub,
"xmlns",
"http://jabber.org/protocol/pubsub");
1423 subscribe = iks_insert(pubsub,
"subscribe");
1424 iks_insert_attrib(subscribe,
"jid", client->jid->partial);
1425 iks_insert_attrib(subscribe,
"node", node);
1426 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1427 iks *options, *x, *sub_options, *sub_type, *sub_depth, *sub_expire;
1428 options = iks_insert(pubsub,
"options");
1429 x = iks_insert(options,
"x");
1430 iks_insert_attrib(x,
"xmlns",
"jabber:x:data");
1431 iks_insert_attrib(x,
"type",
"submit");
1432 sub_options = iks_insert(x,
"field");
1433 iks_insert_attrib(sub_options,
"var",
"FORM_TYPE");
1434 iks_insert_attrib(sub_options,
"type",
"hidden");
1435 iks_insert_cdata(iks_insert(sub_options,
"value"),
1436 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
1437 sub_type = iks_insert(x,
"field");
1438 iks_insert_attrib(sub_type,
"var",
"pubsub#subscription_type");
1439 iks_insert_cdata(iks_insert(sub_type,
"value"),
"items", 5);
1440 sub_depth = iks_insert(x,
"field");
1441 iks_insert_attrib(sub_depth,
"var",
"pubsub#subscription_depth");
1442 iks_insert_cdata(iks_insert(sub_depth,
"value"),
"all", 3);
1443 sub_expire = iks_insert(x,
"field");
1444 iks_insert_attrib(sub_expire,
"var",
"pubsub#expire");
1445 iks_insert_cdata(iks_insert(sub_expire,
"value"),
"presence", 8);
1448 iks_delete(request);
1459 char *item_id, *device_state, *mailbox, *cachable_str;
1460 int oldmsgs, newmsgs;
1461 iks *item, *item_content;
1464 item = iks_find(iks_find(iks_find(pak->x,
"event"),
"items"),
"item");
1466 ast_log(LOG_ERROR,
"Could not parse incoming PubSub event\n");
1467 return IKS_FILTER_EAT;
1469 item_id = iks_find_attrib(item,
"id");
1470 item_content = iks_child(item);
1471 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content,
"eid"));
1473 ast_debug(1,
"Returning here, eid of incoming event matches ours!\n");
1474 return IKS_FILTER_EAT;
1476 if (!strcasecmp(iks_name(item_content),
"state")) {
1477 if ((cachable_str = iks_find_attrib(item_content,
"cachable"))) {
1478 sscanf(cachable_str,
"%30u", &cachable);
1480 device_state = iks_find_cdata(item,
"state");
1485 return IKS_FILTER_EAT;
1486 }
else if (!strcasecmp(iks_name(item_content),
"mailbox")) {
1487 mailbox = strsep(&item_id,
"@");
1488 sscanf(iks_find_cdata(item_content,
"OLDMSGS"),
"%10d", &oldmsgs);
1489 sscanf(iks_find_cdata(item_content,
"NEWMSGS"),
"%10d", &newmsgs);
1493 return IKS_FILTER_EAT;
1495 ast_debug(1,
"Don't know how to handle PubSub event of type %s\n",
1496 iks_name(item_content));
1497 return IKS_FILTER_EAT;
1500 return IKS_FILTER_EAT;
1503 static int xmpp_pubsub_handle_error(
void *data, ikspak *pak)
1506 char *node_name, *error;
1508 iks *orig_request, *orig_pubsub = iks_find(pak->x,
"pubsub");
1511 if (!cfg || !cfg->global) {
1512 ast_log(LOG_ERROR,
"No global configuration available\n");
1513 return IKS_FILTER_EAT;
1517 ast_debug(1,
"Error isn't a PubSub error, why are we here?\n");
1518 return IKS_FILTER_EAT;
1521 orig_request = iks_child(orig_pubsub);
1522 error = iks_find_attrib(iks_find(pak->x,
"error"),
"code");
1523 node_name = iks_find_attrib(orig_request,
"node");
1525 if (!sscanf(error,
"%30d", &error_num)) {
1526 return IKS_FILTER_EAT;
1529 if (error_num > 399 && error_num < 500 && error_num != 404) {
1531 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
1532 return IKS_FILTER_EAT;
1533 }
else if (error_num > 499 && error_num < 600) {
1534 ast_log(LOG_ERROR,
"PubSub Server error, %s\n", error);
1535 return IKS_FILTER_EAT;
1538 if (!strcasecmp(iks_name(orig_request),
"publish")) {
1541 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1542 if (iks_find(iks_find(orig_request,
"item"),
"state")) {
1544 }
else if (iks_find(iks_find(orig_request,
"item"),
"mailbox")) {
1552 iks_insert_node(request, orig_pubsub);
1554 iks_delete(request);
1556 ast_log(LOG_ERROR,
"PubSub publish could not create IQ\n");
1559 return IKS_FILTER_EAT;
1560 }
else if (!strcasecmp(iks_name(orig_request),
"subscribe")) {
1561 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1568 return IKS_FILTER_EAT;
1571 static int cached_devstate_cb(
void *obj,
void *arg,
int flags)
1615 IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->
pubsubnode, IKS_RULE_DONE);
1616 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
1617 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1627 #define BUDDY_OFFLINE 6
1628 #define BUDDY_NOT_IN_ROSTER 7
1632 int status = BUDDY_OFFLINE;
1637 return BUDDY_NOT_IN_ROSTER;
1666 static int acf_jabberstatus_read(
struct ast_channel *chan,
const char *name,
char *data,
char *buf,
size_t buflen)
1679 if (ast_strlen_zero(data)) {
1680 ast_log(LOG_ERROR,
"Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
1685 if (args.argc != 2) {
1686 ast_log(LOG_ERROR,
"JABBER_STATUS requires 2 arguments: sender and jid.\n");
1691 if (jid.argc < 1 || jid.argc > 2) {
1692 ast_log(LOG_WARNING,
"Wrong JID %s, exiting\n", args.jid);
1696 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.sender))) {
1697 ast_log(LOG_WARNING,
"Could not find sender connection: '%s'\n", args.sender);
1701 snprintf(buf, buflen,
"%d", get_buddy_status(clientcfg, jid.screenname, jid.resource));
1707 .
name =
"JABBER_STATUS",
1708 .read = acf_jabberstatus_read,
1729 if (ast_strlen_zero(data)) {
1730 ast_log(LOG_ERROR,
"%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1736 if (args.argc < 2 || args.argc > 3) {
1737 ast_log(LOG_ERROR,
"%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1741 if (strchr(args.jid,
'/')) {
1742 ast_log(LOG_ERROR,
"Invalid room name : resource must not be appended\n");
1746 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.sender))) {
1747 ast_log(LOG_ERROR,
"Could not find sender connection: '%s'\n", args.sender);
1751 if (ast_strlen_zero(args.nick)) {
1752 if (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT)) {
1753 snprintf(nick,
sizeof(nick),
"asterisk");
1755 snprintf(nick,
sizeof(nick),
"%s", clientcfg->
client->jid->user);
1758 snprintf(nick,
sizeof(nick),
"%s", args.nick);
1761 if (!ast_strlen_zero(args.jid) && strchr(args.jid,
'@')) {
1764 ast_log(LOG_ERROR,
"Problem with specified jid of '%s'\n", args.jid);
1788 if (ast_strlen_zero(data)) {
1789 ast_log(LOG_ERROR,
"%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1795 if (args.argc < 2 || args.argc > 3) {
1796 ast_log(LOG_ERROR,
"%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1800 if (strchr(args.jid,
'/')) {
1801 ast_log(LOG_ERROR,
"Invalid room name, resource must not be appended\n");
1805 if (ast_strlen_zero(args.jid) || !strchr(args.jid,
'@')) {
1806 ast_log(LOG_ERROR,
"No jabber ID specified\n");
1810 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.sender))) {
1811 ast_log(LOG_ERROR,
"Could not find sender connection: '%s'\n", args.sender);
1815 if (ast_strlen_zero(args.nick)) {
1816 if (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT)) {
1817 snprintf(nick,
sizeof(nick),
"asterisk");
1819 snprintf(nick,
sizeof(nick),
"%s", clientcfg->
client->jid->user);
1822 snprintf(nick,
sizeof(nick),
"%s", args.nick);
1838 static int xmpp_send_exec(
struct ast_channel *chan,
const char *data)
1849 if (ast_strlen_zero(data)) {
1850 ast_log(LOG_WARNING,
"%s requires arguments (account,jid,message)\n", app_ajisend);
1857 if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient,
'@')) {
1858 ast_log(LOG_WARNING,
"%s requires arguments (account,jid,message)\n", app_ajisend);
1862 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.sender))) {
1863 ast_log(LOG_WARNING,
"Could not find sender connection: '%s'\n", args.sender);
1891 if (ast_strlen_zero(data)) {
1892 ast_log(LOG_ERROR,
"%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1898 if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat,
'@')) {
1899 ast_log(LOG_ERROR,
"%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1903 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.sender))) {
1904 ast_log(LOG_ERROR,
"Could not find sender connection: '%s'\n", args.sender);
1908 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1909 if (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT)) {
1910 snprintf(nick,
sizeof(nick),
"asterisk");
1912 snprintf(nick,
sizeof(nick),
"%s", clientcfg->
client->jid->user);
1915 snprintf(nick,
sizeof(nick),
"%s", args.nick);
1933 static int acf_jabberreceive_read(
struct ast_channel *chan,
const char *name,
char *data,
char *buf,
size_t buflen)
1938 int timeout, jidlen, resourcelen, found = 0;
1939 struct timeval start;
1952 if (ast_strlen_zero(data)) {
1953 ast_log(LOG_WARNING,
"%s requires arguments (account,jid[,timeout])\n", name);
1960 if (args.argc < 2 || args.argc > 3) {
1961 ast_log(LOG_WARNING,
"%s requires arguments (account,jid[,timeout])\n", name);
1967 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
1968 ast_log(LOG_WARNING,
"Invalid JID : %s\n", parse);
1972 if (ast_strlen_zero(args.timeout)) {
1975 sscanf(args.timeout,
"%d", &timeout);
1977 ast_log(LOG_WARNING,
"Invalid timeout specified: '%s'\n", args.timeout);
1982 jidlen = strlen(jid.screenname);
1983 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
1985 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, args.account))) {
1986 ast_log(LOG_WARNING,
"Could not find client %s, exiting\n", args.account);
1990 ast_debug(3,
"Waiting for an XMPP message from %s\n", args.jid);
1995 ast_log(LOG_WARNING,
"Cannot start autoservice for channel %s\n", ast_channel_name(chan));
2001 while (diff < timeout) {
2002 struct timespec ts = { 0, };
2003 struct timeval wait;
2007 ts.tv_sec = wait.tv_sec;
2008 ts.tv_nsec = wait.tv_usec * 1000;
2011 ast_mutex_lock(&messagelock);
2013 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
2015 ast_mutex_unlock(&messagelock);
2016 if (res == ETIMEDOUT) {
2017 ast_debug(3,
"No message received from %s in %d seconds\n", args.jid, timeout);
2023 if (jid.argc == 1) {
2025 if (strncasecmp(jid.screenname, message->
from, jidlen)) {
2030 char *resource = strchr(message->
from,
'/');
2031 if (!resource || strlen(resource) == 0) {
2032 ast_log(LOG_WARNING,
"Remote JID has no resource : %s\n", message->
from);
2033 if (strncasecmp(jid.screenname, message->
from, jidlen)) {
2038 if (strncasecmp(jid.screenname, message->
from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
2045 ast_debug(3,
"Found old message from %s, deleting it\n", message->
from);
2067 ast_log(LOG_WARNING,
"Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
2072 ast_log(LOG_NOTICE,
"Timed out : no message received from %s\n", args.jid);
2080 .
name =
"JABBER_RECEIVE",
2081 .read = acf_jabberreceive_read,
2092 static int delete_old_messages(
struct ast_xmpp_client *client,
char *from)
2096 int deleted = 0, isold = 0;
2106 if (!from || !strncasecmp(from, message->
from, strlen(from))) {
2113 if (!from || !strncasecmp(from, message->
from, strlen(from))) {
2126 static int xmpp_send_cb(
const struct ast_msg *msg,
const char *to,
const char *from)
2130 char *sender, *dest;
2134 strsep(&sender,
":");
2138 if (ast_strlen_zero(sender)) {
2139 ast_log(LOG_ERROR,
"MESSAGE(from) of '%s' invalid for XMPP\n", from);
2143 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, sender))) {
2144 ast_log(LOG_WARNING,
"Could not finder account to send from as '%s'\n", sender);
2148 ast_debug(1,
"Sending message to '%s' from '%s'\n", dest, clientcfg->
name);
2151 ast_log(LOG_WARNING,
"Failed to send XMPP message (%d).\n", res);
2154 return res == IKS_OK ? 0 : -1;
2159 .msg_send = xmpp_send_cb,
2191 iks *iq, *query = NULL, *item = NULL;
2194 "Goodbye. Your status is no longer required.\n"))) {
2198 if (!(iq = iks_new(
"iq")) || !(query = iks_new(
"query")) || !(item = iks_new(
"item"))) {
2199 ast_log(LOG_WARNING,
"Could not allocate memory for roster removal of '%s' from client '%s'\n",
2200 user, client->
name);
2204 iks_insert_attrib(iq,
"from", client->jid->full);
2205 iks_insert_attrib(iq,
"type",
"set");
2206 iks_insert_attrib(query,
"xmlns",
"jabber:iq:roster");
2207 iks_insert_node(iq, query);
2208 iks_insert_attrib(item,
"jid", user);
2209 iks_insert_attrib(item,
"subscription",
"remove");
2210 iks_insert_node(query, item);
2213 ast_log(LOG_WARNING,
"Could not send roster removal request of '%s' from client '%s'\n",
2214 user, client->
name);
2236 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
2237 ast_log(LOG_WARNING,
"Could not send subscription for '%s' on client '%s'\n",
2238 buddy->
id, client->
name);
2255 return IKS_FILTER_EAT;
2258 for (item = iks_child(pak->query); item; item = iks_next(item)) {
2261 if (iks_strcmp(iks_name(item),
"item")) {
2265 if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item,
"jid"),
OBJ_KEY))) {
2266 if (ast_test_flag(&clientcfg->
flags, XMPP_AUTOPRUNE)) {
2270 ast_log(LOG_ERROR,
"Could not unsubscribe user '%s' on client '%s'\n",
2271 iks_find_attrib(item,
"jid"), client->
name);
2277 ast_log(LOG_ERROR,
"Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item,
"jid"),
2284 if (!iks_strcmp(iks_find_attrib(item,
"subscription"),
"none") ||
2285 !iks_strcmp(iks_find_attrib(item,
"subscription"),
"from")) {
2295 if (ast_test_flag(&clientcfg->
flags, XMPP_AUTOREGISTER)) {
2301 return IKS_FILTER_EAT;
2309 iks *presence = NULL, *cnode = NULL, *priority = NULL;
2313 !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new(
"c")) || !(priority = iks_new(
"priority"))) {
2314 ast_log(LOG_ERROR,
"Unable to allocate stanzas for setting presence status for client '%s'\n", client->
name);
2318 if (!ast_strlen_zero(to)) {
2319 iks_insert_attrib(presence,
"to", to);
2322 if (!ast_strlen_zero(from)) {
2323 iks_insert_attrib(presence,
"from", from);
2326 snprintf(priorityS,
sizeof(priorityS),
"%d", clientcfg->
priority);
2327 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2328 iks_insert_node(presence, priority);
2329 iks_insert_attrib(cnode,
"node",
"http://www.asterisk.org/xmpp/client/caps");
2330 iks_insert_attrib(cnode,
"ver",
"asterisk-xmpp");
2331 iks_insert_attrib(cnode,
"ext",
"voice-v1 video-v1 camera-v1");
2332 iks_insert_attrib(cnode,
"xmlns",
"http://jabber.org/protocol/caps");
2333 iks_insert_node(presence, cnode);
2338 iks_delete(presence);
2339 iks_delete(priority);
2346 iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
2348 if (!(iq = iks_new(
"iq")) || !(query = iks_new(
"query")) || !(ident = iks_new(
"identity")) || !(disco = iks_new(
"feature")) ||
2349 !(google = iks_new(
"feature")) || !(jingle = iks_new(
"feature")) || !(ice = iks_new(
"feature")) || !(rtp = iks_new(
"feature")) ||
2350 !(audio = iks_new(
"feature")) || !(video = iks_new(
"feature"))) {
2351 ast_log(LOG_ERROR,
"Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
2352 pak->from->full, client->
name);
2356 iks_insert_attrib(iq,
"from", client->jid->full);
2359 iks_insert_attrib(iq,
"to", pak->from->full);
2362 iks_insert_attrib(iq,
"type",
"result");
2363 iks_insert_attrib(iq,
"id", pak->id);
2364 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#info");
2365 iks_insert_attrib(ident,
"category",
"client");
2366 iks_insert_attrib(ident,
"type",
"pc");
2367 iks_insert_attrib(ident,
"name",
"asterisk");
2368 iks_insert_attrib(disco,
"var",
"http://jabber.org/protocol/disco#info");
2370 iks_insert_attrib(google,
"var",
"http://www.google.com/xmpp/protocol/voice/v1");
2371 iks_insert_attrib(jingle,
"var",
"urn:xmpp:jingle:1");
2372 iks_insert_attrib(ice,
"var",
"urn:xmpp:jingle:transports:ice-udp:1");
2373 iks_insert_attrib(rtp,
"var",
"urn:xmpp:jingle:apps:rtp:1");
2374 iks_insert_attrib(audio,
"var",
"urn:xmpp:jingle:apps:rtp:audio");
2375 iks_insert_attrib(video,
"var",
"urn:xmpp:jingle:apps:rtp:video");
2376 iks_insert_node(iq, query);
2377 iks_insert_node(query, ident);
2378 iks_insert_node(query, google);
2379 iks_insert_node(query, disco);
2380 iks_insert_node(query, jingle);
2381 iks_insert_node(query, ice);
2382 iks_insert_node(query, rtp);
2383 iks_insert_node(query, audio);
2384 iks_insert_node(query, video);
2399 return IKS_FILTER_EAT;
2409 if (!(buddy = ao2_find(client->buddies, pak->from->partial,
OBJ_KEY))) {
2410 return IKS_FILTER_EAT;
2415 return IKS_FILTER_EAT;
2420 if (iks_find_with_attrib(pak->query,
"feature",
"var",
"urn:xmpp:jingle:1")) {
2424 ao2_unlock(resource);
2429 return IKS_FILTER_EAT;
2444 client->jid = (iks_find_cdata(pak->query,
"jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query,
"jid")) : client->jid;
2446 if (ast_test_flag(&clientcfg->
flags, XMPP_DISTRIBUTE_EVENTS)) {
2450 if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
2451 ast_log(LOG_ERROR,
"Unable to allocate memory for roster request for client '%s'\n", client->
name);
2455 iks_filter_add_rule(client->filter,
xmpp_client_service_discovery_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS,
"http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2458 iks_insert_attrib(roster,
"id",
"roster");
2462 iks_filter_add_rule(client->filter,
xmpp_roster_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID,
"roster", IKS_RULE_DONE);
2467 return IKS_FILTER_EAT;
2471 static void xmpp_log_hook(
void *data,
const char *xmpp,
size_t size,
int incoming)
2477 if (!debug && (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, client->
name)) || !ast_test_flag(&clientcfg->
flags, XMPP_DEBUG))) {
2482 ast_verbose(
"\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->
name, xmpp);
2484 ast_verbose(
"\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->
name, xmpp);
2495 return IKS_NET_NOCONN;
2500 int len = strlen(message);
2502 ret = SSL_write(client->ssl_session, message, len);
2513 ret = iks_send_raw(client->parser, message);
2514 if (ret != IKS_OK) {
2524 char *
namespace = ast_test_flag(&cfg->
flags, XMPP_COMPONENT) ?
"jabber:component:accept" :
"jabber:client";
2525 char msg[91 + strlen(
namespace) + 6 + strlen(to) + 16 + 1];
2527 snprintf(msg,
sizeof(msg),
"<?xml version='1.0'?>"
2528 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
2529 "%s' to='%s' version='1.0'>",
namespace, to);
2548 #ifndef HAVE_OPENSSL
2549 ast_log(LOG_ERROR,
"TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->
name);
2552 if (iks_send_raw(client->parser,
"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
2553 ast_log(LOG_ERROR,
"TLS connection for client '%s' cannot be started.\n", client->
name);
2557 client->stream_flags |= TRY_SECURE;
2566 static char *openssl_error_string(
void)
2568 char *buf = NULL, *ret;
2570 BIO *bio = BIO_new(BIO_s_mem());
2572 ERR_print_errors(bio);
2573 len = BIO_get_mem_data(bio, &buf);
2576 memcpy(ret, buf, len);
2592 if (!strcmp(iks_name(node),
"success")) {
2596 }
else if (!strcmp(iks_name(node),
"failure")) {
2599 }
else if (strcmp(iks_name(node),
"proceed")) {
2604 #ifndef HAVE_OPENSSL
2605 ast_log(LOG_ERROR,
"Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->
name);
2608 client->ssl_method = SSLv23_method();
2609 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
2613 ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
2614 SSL_CTX_set_options(client->ssl_context, ssl_opts);
2616 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
2620 sock = iks_fd(client->parser);
2621 if (!SSL_set_fd(client->ssl_session, sock)) {
2625 if (SSL_connect(client->ssl_session) <= 0) {
2629 client->stream_flags &= (~TRY_SECURE);
2630 client->stream_flags |= SECURE;
2633 ast_log(LOG_ERROR,
"TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
2638 ast_debug(1,
"TLS connection for client '%s' started with server\n", client->
name);
2645 err = openssl_error_string();
2646 ast_log(LOG_ERROR,
"TLS connection for client '%s' cannot be established. "
2647 "OpenSSL initialization failed: %s\n", client->
name, err);
2656 iks *iq = NULL, *query = NULL;
2657 char buf[41], sidpass[100];
2659 if (!(iq = iks_new(
"iq")) || !(query = iks_insert(iq,
"query"))) {
2660 ast_log(LOG_ERROR,
"Stanzas could not be allocated for authentication on client '%s'\n", client->
name);
2665 iks_insert_attrib(iq,
"type",
"set");
2666 iks_insert_cdata(iks_insert(query,
"username"), client->jid->user, 0);
2667 iks_insert_cdata(iks_insert(query,
"resource"), client->jid->resource, 0);
2669 iks_insert_attrib(query,
"xmlns",
"jabber:iq:auth");
2670 snprintf(sidpass,
sizeof(sidpass),
"%s%s", iks_find_attrib(node,
"id"), cfg->
password);
2672 iks_insert_cdata(iks_insert(query,
"digest"), buf, 0);
2675 iks_filter_add_rule(client->filter,
xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->
mid, IKS_RULE_DONE);
2676 iks_insert_attrib(iq,
"id", client->
mid);
2680 iks_insert_attrib(iq,
"to", client->jid->server);
2694 int features, len = strlen(client->jid->user) + strlen(cfg->
password) + 3;
2697 char base64[(len + 2) * 4 / 3];
2699 if (strcmp(iks_name(node),
"stream:features")) {
2704 features = iks_stream_features(node);
2706 if ((features & IKS_STREAM_SASL_MD5) && !
xmpp_is_secure(client)) {
2707 if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (
char*)client->jid->user, (
char*)cfg->
password) != IKS_OK) {
2708 ast_log(LOG_ERROR,
"Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->
name);
2717 if (!(features & IKS_STREAM_SASL_PLAIN)) {
2718 ast_log(LOG_ERROR,
"Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->
name);
2722 if (!(auth = iks_new(
"auth"))) {
2723 ast_log(LOG_ERROR,
"Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->
name);
2727 iks_insert_attrib(auth,
"xmlns", IKS_NS_XMPP_SASL);
2729 iks_insert_attrib(auth,
"mechanism",
"X-OAUTH2");
2730 iks_insert_attrib(auth,
"auth:service",
"oauth2");
2731 iks_insert_attrib(auth,
"xmlns:auth",
"http://www.google.com/talk/protocol/auth");
2733 iks_insert_attrib(auth,
"mechanism",
"PLAIN");
2736 if (strchr(client->jid->user,
'/')) {
2739 snprintf(combined,
sizeof(combined),
"%c%s%c%s", 0, strsep(&user,
"/"), 0, cfg->
password);
2741 snprintf(combined,
sizeof(combined),
"%c%s%c%s", 0, client->jid->user, 0, cfg->
password);
2744 ast_base64encode(base64, (
const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
2745 iks_insert_cdata(auth, base64, 0);
2767 if (!strcmp(iks_name(node),
"success")) {
2772 }
else if (!strcmp(iks_name(node),
"failure")) {
2775 }
else if (strcmp(iks_name(node),
"stream:features")) {
2780 features = iks_stream_features(node);
2782 if (features & IKS_STREAM_BIND) {
2785 if (!(auth = iks_make_resource_bind(client->jid))) {
2786 ast_log(LOG_ERROR,
"Failed to allocate memory for stream bind on client '%s'\n", client->
name);
2791 iks_insert_attrib(auth,
"id", client->
mid);
2798 iks_filter_add_rule(client->filter,
xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
2801 if (features & IKS_STREAM_SESSION) {
2804 if (!(auth = iks_make_session())) {
2805 ast_log(LOG_ERROR,
"Failed to allocate memory for stream session on client '%s'\n", client->
name);
2809 iks_insert_attrib(auth,
"id",
"auth");
2817 iks_filter_add_rule(client->filter,
xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID,
"auth", IKS_RULE_DONE);
2826 char secret[160], shasum[320], message[344];
2827 ikspak *pak = iks_packet(node);
2829 snprintf(secret,
sizeof(secret),
"%s%s", pak->id, cfg->
password);
2831 snprintf(message,
sizeof(message),
"<handshake>%s</handshake>", shasum);
2834 ast_log(LOG_ERROR,
"Unable to send handshake for component '%s'\n", client->
name);
2849 iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *
commands = NULL, *gateway = NULL;
2850 iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
2854 !(iq = iks_new(
"iq")) || !(query = iks_new(
"query")) || !(identity = iks_new(
"identity")) || !(disco = iks_new(
"feature")) ||
2855 !(reg = iks_new(
"feature")) || !(commands = iks_new(
"feature")) || !(gateway = iks_new(
"feature")) || !(version = iks_new(
"feature")) ||
2856 !(vcard = iks_new(
"feature")) || !(search = iks_new(
"search")) || !(item = iks_new(
"item"))) {
2857 ast_log(LOG_ERROR,
"Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
2858 pak->from->partial, client->
name);
2862 iks_insert_attrib(iq,
"from", clientcfg->
user);
2863 iks_insert_attrib(iq,
"to", pak->from->full);
2864 iks_insert_attrib(iq,
"id", pak->id);
2865 iks_insert_attrib(iq,
"type",
"result");
2867 if (!(node = iks_find_attrib(pak->query,
"node"))) {
2868 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#info");
2869 iks_insert_attrib(identity,
"category",
"gateway");
2870 iks_insert_attrib(identity,
"type",
"pstn");
2871 iks_insert_attrib(identity,
"name",
"Asterisk The Open Source PBX");
2872 iks_insert_attrib(disco,
"var",
"http://jabber.org/protocol/disco");
2873 iks_insert_attrib(reg,
"var",
"jabber:iq:register");
2874 iks_insert_attrib(commands,
"var",
"http://jabber.org/protocol/commands");
2875 iks_insert_attrib(gateway,
"var",
"jabber:iq:gateway");
2876 iks_insert_attrib(version,
"var",
"jabber:iq:version");
2877 iks_insert_attrib(vcard,
"var",
"vcard-temp");
2878 iks_insert_attrib(search,
"var",
"jabber:iq:search");
2880 iks_insert_node(iq, query);
2881 iks_insert_node(query, identity);
2882 iks_insert_node(query, disco);
2883 iks_insert_node(query, reg);
2884 iks_insert_node(query, commands);
2885 iks_insert_node(query, gateway);
2886 iks_insert_node(query, version);
2887 iks_insert_node(query, vcard);
2888 iks_insert_node(query, search);
2889 }
else if (!strcasecmp(node,
"http://jabber.org/protocol/commands")) {
2890 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#items");
2891 iks_insert_attrib(query,
"node",
"http://jabber.org/protocol/commands");
2892 iks_insert_attrib(item,
"node",
"confirmaccount");
2893 iks_insert_attrib(item,
"name",
"Confirm account");
2894 iks_insert_attrib(item,
"jid", clientcfg->
user);
2896 iks_insert_node(iq, query);
2897 iks_insert_node(query, item);
2898 }
else if (!strcasecmp(node,
"confirmaccount")) {
2899 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#info");
2900 iks_insert_attrib(commands,
"var",
"http://jabber.org/protocol/commands");
2902 iks_insert_node(iq, query);
2903 iks_insert_node(query, commands);
2905 ast_debug(3,
"Unsupported service discovery info request received with node '%s' on component '%s'\n",
2906 node, client->
name);
2911 ast_log(LOG_WARNING,
"Could not send response to service discovery request on component '%s'\n",
2918 iks_delete(version);
2919 iks_delete(gateway);
2920 iks_delete(commands);
2923 iks_delete(identity);
2927 return IKS_FILTER_EAT;
2936 iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
2941 !(iq = iks_new(
"iq")) || !(query = iks_new(
"query")) || !(error = iks_new(
"error")) || !(notacceptable = iks_new(
"not-acceptable")) ||
2942 !(instructions = iks_new(
"instructions"))) {
2943 ast_log(LOG_ERROR,
"Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
2944 pak->from->partial, client->
name);
2948 iks_insert_attrib(iq,
"from", clientcfg->
user);
2949 iks_insert_attrib(iq,
"to", pak->from->full);
2950 iks_insert_attrib(iq,
"id", pak->id);
2951 iks_insert_attrib(iq,
"type",
"result");
2952 iks_insert_attrib(query,
"xmlns",
"jabber:iq:register");
2953 iks_insert_node(iq, query);
2955 if (!(buddy = ao2_find(client->buddies, pak->from->partial,
OBJ_KEY))) {
2956 iks_insert_attrib(error,
"code",
"406");
2957 iks_insert_attrib(error,
"type",
"modify");
2958 iks_insert_attrib(notacceptable,
"xmlns",
"urn:ietf:params:xml:ns:xmpp-stanzas");
2960 iks_insert_node(iq, error);
2961 iks_insert_node(error, notacceptable);
2963 ast_log(LOG_ERROR,
"Received register attempt from '%s' but buddy is not configured on component '%s'\n",
2964 pak->from->partial, client->
name);
2965 }
else if (!(node = iks_find_attrib(pak->query,
"node"))) {
2966 iks_insert_cdata(instructions,
"Welcome to Asterisk - the Open Source PBX.\n", 0);
2967 iks_insert_node(query, instructions);
2970 ast_log(LOG_WARNING,
"Received register get to component '%s' using unsupported node '%s' from '%s'\n",
2971 client->
name, node, pak->from->partial);
2977 ast_log(LOG_WARNING,
"Could not send response to '%s' for received register get on component '%s'\n",
2978 pak->from->partial, client->
name);
2982 iks_delete(instructions);
2983 iks_delete(notacceptable);
2988 return IKS_FILTER_EAT;
2995 iks *iq, *presence = NULL, *x = NULL;
2997 if (!(iq = iks_new(
"iq")) || !(presence = iks_new(
"presence")) || !(x = iks_new(
"x"))) {
2998 ast_log(LOG_ERROR,
"Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
2999 pak->from->partial, client->
name);
3003 iks_insert_attrib(iq,
"from", client->jid->full);
3004 iks_insert_attrib(iq,
"to", pak->from->full);
3005 iks_insert_attrib(iq,
"id", pak->id);
3006 iks_insert_attrib(iq,
"type",
"result");
3009 ast_log(LOG_WARNING,
"Could not send response to '%s' for received register set on component '%s'\n",
3010 pak->from->partial, client->
name);
3014 iks_insert_attrib(presence,
"from", client->jid->full);
3015 iks_insert_attrib(presence,
"to", pak->from->partial);
3017 iks_insert_attrib(presence,
"id", client->
mid);
3020 iks_insert_attrib(presence,
"type",
"subscribe");
3021 iks_insert_attrib(x,
"xmlns",
"vcard-temp:x:update");
3023 iks_insert_node(presence, x);
3026 ast_log(LOG_WARNING,
"Could not send subscription to '%s' on component '%s'\n",
3027 pak->from->partial, client->
name);
3032 iks_delete(presence);
3035 return IKS_FILTER_EAT;
3044 iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
3048 !(iq = iks_new(
"iq")) || !(query = iks_new(
"query")) || !(item = iks_new(
"item")) || !(feature = iks_new(
"feature"))) {
3049 ast_log(LOG_ERROR,
"Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
3050 pak->from->partial, client->
name);
3054 iks_insert_attrib(iq,
"from", clientcfg->
user);
3055 iks_insert_attrib(iq,
"to", pak->from->full);
3056 iks_insert_attrib(iq,
"id", pak->id);
3057 iks_insert_attrib(iq,
"type",
"result");
3058 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#items");
3059 iks_insert_node(iq, query);
3061 if (!(node = iks_find_attrib(pak->query,
"node"))) {
3062 iks_insert_attrib(item,
"node",
"http://jabber.org/protocol/commands");
3063 iks_insert_attrib(item,
"name",
"Asterisk Commands");
3064 iks_insert_attrib(item,
"jid", clientcfg->
user);
3066 iks_insert_node(query, item);
3067 }
else if (!strcasecmp(node,
"http://jabber.org/protocol/commands")) {
3068 iks_insert_attrib(query,
"node",
"http://jabber.org/protocol/commands");
3070 ast_log(LOG_WARNING,
"Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
3071 client->
name, node, pak->from->partial);
3076 ast_log(LOG_WARNING,
"Could not send response to service discovery items request from '%s' on component '%s'\n",
3077 pak->from->partial, client->
name);
3081 iks_delete(feature);
3086 return IKS_FILTER_EAT;
3092 if (!strcmp(iks_name(node),
"stream:features")) {
3096 if (strcmp(iks_name(node),
"handshake")) {
3097 ast_log(LOG_ERROR,
"Failed to authenticate component '%s'\n", client->
name);
3108 iks_filter_add_rule(client->filter,
xmpp_component_register_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS,
"jabber:iq:register", IKS_RULE_DONE);
3109 iks_filter_add_rule(client->filter,
xmpp_component_register_set_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS,
"jabber:iq:register", IKS_RULE_DONE);
3123 ast_debug(3,
"XMPP client '%s' received a message\n", client->
name);
3125 if (!(body = iks_find_cdata(pak->x,
"body"))) {
3130 if (!(message =
ast_calloc(1,
sizeof(*message)))) {
3139 message->
from = !ast_strlen_zero(pak->from->full) ?
ast_strdup(pak->from->full) : NULL;
3141 if (ast_test_flag(&cfg->
flags, XMPP_SEND_TO_DIALPLAN)) {
3177 deleted = delete_old_messages(client, pak->from->partial);
3178 ast_debug(3,
"Deleted %d messages for client %s from JID %s\n", deleted, client->
name, pak->from->partial);
3184 ast_mutex_lock(&messagelock);
3185 ast_cond_broadcast(&message_received_condition);
3186 ast_mutex_unlock(&messagelock);
3197 if (!(iq = iks_new(
"iq")) || !(query = iks_new(
"query"))) {
3202 iks_insert_attrib(iq,
"type",
"get");
3203 iks_insert_attrib(iq,
"to", to);
3204 iks_insert_attrib(iq,
"from", from);
3206 iks_insert_attrib(iq,
"id", client->
mid);
3209 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#info");
3210 iks_insert_node(iq, query);
3234 ast_debug(2,
"JABBER: Sending Keep-Alive Ping for client '%s'\n", client->
name);
3236 if (!(iq = iks_new(
"iq")) || !(ping = iks_new(
"ping"))) {
3241 iks_insert_attrib(iq,
"type",
"get");
3242 iks_insert_attrib(iq,
"to", to);
3243 iks_insert_attrib(iq,
"from", from);
3246 iks_insert_attrib(iq,
"id", client->
mid);
3250 iks_insert_attrib(ping,
"xmlns",
"urn:xmpp:ping");
3251 iks_insert_node(iq, ping);
3267 char *type = iks_find_attrib(pak->x,
"type");
3272 if (ast_test_flag(&cfg->
flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type,
"probe")) {
3277 if (!pak->from->resource) {
3281 if (!(buddy = ao2_find(client->buddies, pak->from->partial,
OBJ_KEY))) {
3283 if (strcmp(client->jid->partial, pak->from->partial)) {
3284 ast_log(LOG_WARNING,
"Received presence information about '%s' despite not having them in roster on client '%s'\n",
3285 pak->from->partial, client->
name);
3296 ast_log(LOG_ERROR,
"Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
3297 pak->from->resource, buddy->
id, client->
name);
3315 if (!(node = iks_find_attrib(iks_find(pak->x,
"c"),
"node"))) {
3316 node = iks_find_attrib(iks_find(pak->x,
"caps:c"),
"node");
3319 if (!(ver = iks_find_attrib(iks_find(pak->x,
"c"),
"ver"))) {
3320 ver = iks_find_attrib(iks_find(pak->x,
"caps:c"),
"ver");
3327 if ((node && strcmp(resource->
caps.
node, node)) || (ver && strcmp(resource->
caps.
version, ver))) {
3337 if (iks_find_with_attrib(pak->x,
"c",
"node",
"http://www.google.com/xmpp/client/caps") ||
3338 iks_find_with_attrib(pak->x,
"caps:c",
"node",
"http://www.google.com/xmpp/client/caps") ||
3339 iks_find_with_attrib(pak->x,
"c",
"node",
"http://www.android.com/gtalk/client/caps") ||
3340 iks_find_with_attrib(pak->x,
"caps:c",
"node",
"http://www.android.com/gtalk/client/caps") ||
3341 iks_find_with_attrib(pak->x,
"c",
"node",
"http://mail.google.com/xmpp/client/caps") ||
3342 iks_find_with_attrib(pak->x,
"caps:c",
"node",
"http://mail.google.com/xmpp/client/caps")) {
3348 ast_log(LOG_WARNING,
"Could not send discovery information request to resource '%s' of buddy '%s' on client '%s', capabilities may be incomplete\n", resource->
resource, buddy->
id, client->
name);
3354 resource->
priority = atoi((iks_find_cdata(pak->x,
"priority")) ? iks_find_cdata(pak->x,
"priority") :
"0");
3359 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
3360 "\r\nDescription: %s\r\n",
3372 "Account: %s\r\nJID: %s\r\nStatus: %u\r\n",
3373 client->
name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
3396 switch (pak->subtype) {
3397 case IKS_TYPE_SUBSCRIBE:
3398 if (ast_test_flag(&cfg->
flags, XMPP_AUTOREGISTER)) {
3399 iks *presence, *status = NULL;
3401 if ((presence = iks_new(
"presence")) && (status = iks_new(
"status"))) {
3402 iks_insert_attrib(presence,
"type",
"subscribed");
3403 iks_insert_attrib(presence,
"to", pak->from->full);
3404 iks_insert_attrib(presence,
"from", client->jid->full);
3407 iks_insert_attrib(presence,
"id", pak->id);
3410 iks_insert_cdata(status,
"Asterisk has approved your subscription", 0);
3411 iks_insert_node(presence, status);
3414 ast_log(LOG_ERROR,
"Could not send subscription acceptance to '%s' from client '%s'\n",
3415 pak->from->partial, client->
name);
3418 ast_log(LOG_ERROR,
"Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
3419 pak->from->partial, client->
name);
3423 iks_delete(presence);
3426 if (ast_test_flag(&cfg->
flags, XMPP_COMPONENT)) {
3430 case IKS_TYPE_SUBSCRIBED:
3431 ao2_lock(client->buddies);
3433 if (!(buddy = ao2_find(client->buddies, pak->from->partial,
OBJ_KEY |
OBJ_NOLOCK))) {
3438 ast_log(LOG_WARNING,
"Could not find or create buddy '%s' on client '%s'\n",
3439 pak->from->partial, client->
name);
3444 ao2_unlock(client->buddies);
3464 ast_log(LOG_ERROR,
"xmpp_action_hook was called without a packet\n");
3477 pak = iks_packet(node);
3482 if (iks_has_children(node) && strchr(iks_name(iks_child(node)),
':')) {
3483 char *node_ns = NULL;
3485 char *node_name = iks_name(iks_child(node));
3486 char *aux = strchr(node_name,
':') + 1;
3487 snprintf(attr, strlen(
"xmlns:") + (strlen(node_name) - strlen(aux)),
"xmlns:%s", node_name);
3488 node_ns = iks_find_attrib(iks_child(node), attr);
3491 pak->query = iks_child(node);
3496 for (i = 0; i < ARRAY_LEN(xmpp_state_handlers); i++) {
3497 if ((xmpp_state_handlers[i].
state == client->state) && (xmpp_state_handlers[i].component == (ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT) ? 1 : 0))) {
3498 if (xmpp_state_handlers[i].handler(client, clientcfg, type, node)) {
3507 for (i = 0; i < ARRAY_LEN(xmpp_pak_handlers); i++) {
3508 if (xmpp_pak_handlers[i].type == pak->type) {
3509 if (xmpp_pak_handlers[i].handler(client, clientcfg, node, pak)) {
3518 iks_filter_packet(client->filter, pak);
3527 if ((client->thread != AST_PTHREADT_NULL) && !pthread_equal(pthread_self(), client->thread)) {
3529 pthread_cancel(client->thread);
3530 pthread_join(client->thread, NULL);
3531 client->thread = AST_PTHREADT_NULL;
3545 if (client->stream_flags & SECURE) {
3546 SSL_shutdown(client->ssl_session);
3547 SSL_CTX_free(client->ssl_context);
3548 SSL_free(client->ssl_session);
3551 client->stream_flags = 0;
3554 if (client->parser) {
3555 iks_disconnect(client->parser);
3566 struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
3569 int res = IKS_NET_NOCONN;
3577 client->timeout = 50;
3578 iks_parser_reset(client->parser);
3580 if (!client->filter && !(client->filter = iks_filter_new())) {
3581 ast_log(LOG_ERROR,
"Could not create IKS filter for client connection '%s'\n", client->
name);
3586 ast_debug(2,
"Obtaining OAuth access token for client '%s'\n", client->
name);
3587 if (fetch_access_token(clientcfg)) {
3593 res = iks_connect_via(client->parser,
S_OR(clientcfg->
server, client->jid->server), clientcfg->
port,
3594 ast_test_flag(&clientcfg->
flags, XMPP_COMPONENT) ? clientcfg->
user : client->jid->server);
3597 setsockopt(iks_fd(client->parser), SOL_SOCKET, SO_RCVTIMEO, (
char *)&tv,
sizeof(
struct timeval));
3599 if (res == IKS_NET_NOCONN) {
3600 ast_log(LOG_ERROR,
"No XMPP connection available when trying to connect client '%s'\n", client->
name);
3602 }
else if (res == IKS_NET_NODNS) {
3603 ast_log(LOG_ERROR,
"No DNS available for XMPP connection when trying to connect client '%s'\n", client->
name);
3616 struct pollfd pfd = { .events = POLLIN };
3621 pfd.fd = SSL_get_fd(client->ssl_session);
3627 pfd.fd = iks_fd(client->parser);
3629 res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
3633 len = SSL_read(client->ssl_session, buffer, buf_len);
3636 len = recv(pfd.fd, buffer, buf_len, 0);
3640 }
else if (len <= 0) {
3650 int len, ret, pos = 0, newbufpos = 0;
3651 char buf[NET_IO_BUF_SIZE - 1] =
"";
3652 char newbuf[NET_IO_BUF_SIZE - 1] =
"";
3656 len =
xmpp_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
3657 if (len < 0)
return IKS_NET_RWERR;
3658 if (len == 0)
return IKS_NET_EXPIRED;
3669 while (isspace(buf[pos+1])) {
3673 newbuf[newbufpos] = c;
3685 ast_debug(1,
"JABBER: Detected Google Keep Alive. "
3686 "Sending out Ping request for client '%s'\n", client->
name);
3694 ret = iks_parse(client->parser, newbuf, 0, 0);
3695 memset(newbuf, 0,
sizeof(newbuf));
3699 ast_log(LOG_WARNING,
"Parsing failure: Out of memory.\n");
3702 ast_log(LOG_WARNING,
"Parsing failure: Invalid XML.\n");
3705 ast_log(LOG_WARNING,
"Parsing failure: Hook returned an error.\n");
3708 if (ret != IKS_OK) {
3711 ast_debug(3,
"XML parsing successful\n");
3716 static void sleep_with_backoff(
unsigned int *sleep_time)
3719 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
3722 *sleep_time = MIN(60, *sleep_time * 2);
3724 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
3731 int res = IKS_NET_RWERR;
3732 unsigned int sleep_time = 1;
3735 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
3743 if (res == IKS_NET_RWERR || client->timeout == 0) {
3746 sleep_with_backoff(&sleep_time);
3747 res = IKS_NET_RWERR;
3756 if (res == IKS_NET_EXPIRED) {
3760 if (res == IKS_HOOK) {
3762 }
else if (res == IKS_NET_TLSFAIL) {
3763 ast_log(LOG_ERROR,
"[%s] TLS failure\n", client->
name);
3768 if (cfg && cfg->clients) {
3772 if (clientcfg && ast_test_flag(&clientcfg->
flags, XMPP_KEEPALIVE)) {
3778 if (res == IKS_OK) {
3779 client->timeout = 50;
3781 ast_log(LOG_WARNING,
"[%s] Network timeout\n", client->
name);
3783 }
else if (res == IKS_NET_RWERR) {
3784 ast_log(LOG_WARNING,
"[%s] Socket read error\n", client->
name);
3786 sleep_with_backoff(&sleep_time);
3787 }
else if (res == IKS_NET_NOSOCK) {
3788 ast_log(LOG_WARNING,
"[%s] No socket\n", client->
name);
3789 }
else if (res == IKS_NET_NOCONN) {
3790 ast_log(LOG_WARNING,
"[%s] No connection\n", client->
name);
3791 }
else if (res == IKS_NET_NODNS) {
3792 ast_log(LOG_WARNING,
"[%s] No DNS\n", client->
name);
3793 }
else if (res == IKS_NET_NOTSUPP) {
3794 ast_log(LOG_WARNING,
"[%s] Not supported\n", client->
name);
3795 }
else if (res == IKS_NET_DROPPED) {
3796 ast_log(LOG_WARNING,
"[%s] Dropped?\n", client->
name);
3797 }
else if (res == IKS_NET_UNKNOWN) {
3799 }
else if (res == IKS_OK) {
3808 static int xmpp_client_config_merge_buddies(
void *obj,
void *arg,
int flags)
3814 if (!(buddy2 = ao2_find(buddies, buddy1->
id,
OBJ_KEY))) {
3826 RAII_VAR(
char *, cmd, NULL, ast_free);
3827 char cBuf[1024] =
"";
3828 const char *url =
"https://www.googleapis.com/oauth2/v3/token";
3833 "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",
3838 ast_debug(2,
"Performing OAuth 2.0 authentication for client '%s' using command: %s\n",
3842 ast_log(LOG_ERROR,
"CURL is unavailable. This is required for OAuth 2.0 authentication of XMPP client '%s'. Please ensure it is loaded.\n",
3847 ast_debug(2,
"OAuth 2.0 authentication for client '%s' returned: %s\n", cfg->
name, cBuf);
3858 ast_log(LOG_ERROR,
"An error occurred while performing OAuth 2.0 authentication for client '%s': %s\n", cfg->
name, cBuf);
3863 static int xmpp_client_config_post_apply(
void *obj,
void *arg,
int flags)
3869 ast_copy_flags(&cfg->
flags, &gcfg->global->general, ~(cfg->
mod_flags.flags) & (XMPP_AUTOPRUNE | XMPP_AUTOREGISTER | XMPP_AUTOACCEPT));
3878 if (!(cfg->
client->parser = iks_stream_new(ast_test_flag(&cfg->
flags, XMPP_COMPONENT) ?
"jabber:component:accept" :
"jabber:client", cfg->
client,
3880 ast_log(LOG_ERROR,
"Iksemel stream could not be created for client '%s' - client not active\n", cfg->
name);
3887 if (!strchr(cfg->
user,
'/') && !ast_test_flag(&cfg->
flags, XMPP_COMPONENT)) {
3888 char resource[strlen(cfg->
user) + strlen(
"/asterisk-xmpp") + 1];
3890 snprintf(resource,
sizeof(resource),
"%s/asterisk-xmpp", cfg->
user);
3891 cfg->
client->jid = iks_id_new(cfg->
client->stack, resource);
3896 if (!cfg->
client->jid || (ast_strlen_zero(cfg->
client->jid->user) && !ast_test_flag(&cfg->
flags, XMPP_COMPONENT))) {
3897 ast_log(LOG_ERROR,
"Jabber identity '%s' could not be created for client '%s' - client not active\n", cfg->
user, cfg->
name);
3909 if (ast_test_flag(&cfg->
flags, XMPP_AUTOREGISTER)) {
3924 static int manager_jabber_send(
struct mansession *s,
const struct message *m)
3933 if (ast_strlen_zero(jabber)) {
3937 if (ast_strlen_zero(screenname)) {
3941 if (ast_strlen_zero(message)) {
3948 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, jabber))) {
3959 if (!ast_strlen_zero(
id)) {
3982 query = iks_insert(request,
"query");
3983 iks_insert_attrib(query,
"xmlns",
"http://jabber.org/protocol/disco#items");
3986 iks_insert_attrib(query,
"node", collection);
4003 if (iks_has_children(pak->query)) {
4004 item = iks_first_tag(pak->query);
4005 ast_verbose(
"Connection %s: %s\nNode name: %s\n", client->
name, client->jid->partial,
4006 iks_find_attrib(item,
"node"));
4007 while ((item = iks_next_tag(item))) {
4008 ast_verbose(
"Node name: %s\n", iks_find_attrib(item,
"node"));
4017 return IKS_FILTER_EAT;
4030 ast_log(LOG_ERROR,
"Could not request pubsub nodes on client '%s' - IQ could not be created\n", client->
name);
4035 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->
mid,
4038 iks_delete(request);
4054 const char *name = NULL, *collection = NULL;
4058 e->
command =
"xmpp list nodes";
4060 "Usage: xmpp list nodes <connection> [collection]\n"
4061 " Lists the user's nodes on the respective connection\n"
4062 " ([connection] as configured in xmpp.conf.)\n";
4068 if (a->argc > 5 || a->argc < 4) {
4069 return CLI_SHOWUSAGE;
4070 }
else if (a->argc == 4 || a->argc == 5) {
4075 collection = a->argv[4];
4078 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
4079 ast_cli(a->fd,
"Unable to find client '%s'!\n", name);
4083 ast_cli(a->fd,
"Listing pubsub nodes.\n");
4101 if (iks_has_children(pak->query)) {
4102 item = iks_first_tag(pak->query);
4103 ast_log(LOG_WARNING,
"Connection: %s Node name: %s\n", client->jid->partial,
4104 iks_find_attrib(item,
"node"));
4105 while ((item = iks_next_tag(item))) {
4114 return IKS_FILTER_EAT;
4117 static void xmpp_pubsub_purge_nodes(
struct ast_xmpp_client *client,
const char* collection_name)
4122 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->
mid,
4125 iks_delete(request);
4144 e->
command =
"xmpp purge nodes";
4146 "Usage: xmpp purge nodes <connection> <node>\n"
4147 " Purges nodes on PubSub server\n"
4148 " as configured in xmpp.conf.\n";
4155 return CLI_SHOWUSAGE;
4159 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
4160 ast_cli(a->fd,
"Unable to find client '%s'!\n", name);
4164 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
4165 xmpp_pubsub_purge_nodes(clientcfg->
client, a->argv[4]);
4189 e->
command =
"xmpp delete node";
4191 "Usage: xmpp delete node <connection> <node>\n"
4192 " Deletes a node on PubSub server\n"
4193 " as configured in xmpp.conf.\n";
4200 return CLI_SHOWUSAGE;
4204 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
4205 ast_cli(a->fd,
"Unable to find client '%s'!\n", name);
4222 const char *
name, *collection_name;
4226 e->
command =
"xmpp create collection";
4228 "Usage: xmpp create collection <connection> <collection>\n"
4229 " Creates a PubSub collection node using the account\n"
4230 " as configured in xmpp.conf.\n";
4237 return CLI_SHOWUSAGE;
4240 collection_name = a->argv[4];
4242 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
4243 ast_cli(a->fd,
"Unable to find client '%s'!\n", name);
4247 ast_cli(a->fd,
"Creating test PubSub node collection.\n");
4262 const char *
name, *collection_name, *leaf_name;
4266 e->
command =
"xmpp create leaf";
4268 "Usage: xmpp create leaf <connection> <collection> <leaf>\n"
4269 " Creates a PubSub leaf node using the account\n"
4270 " as configured in xmpp.conf.\n";
4277 return CLI_SHOWUSAGE;
4280 collection_name = a->argv[4];
4281 leaf_name = a->argv[5];
4283 if (!cfg || !cfg->clients || !(clientcfg =
xmpp_config_find(cfg->clients, name))) {
4284 ast_cli(a->fd,
"Unable to find client '%s'!\n", name);
4288 ast_cli(a->fd,
"Creating test PubSub node collection.\n");
4304 e->
command =
"xmpp set debug {on|off}";
4306 "Usage: xmpp set debug {on|off}\n"
4307 " Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
4313 if (a->argc != e->
args) {
4314 return CLI_SHOWUSAGE;
4317 if (!strncasecmp(a->argv[e->
args - 1],
"on", 2)) {
4319 ast_cli(a->fd,
"XMPP Debugging Enabled.\n");
4321 }
else if (!strncasecmp(a->argv[e->
args - 1],
"off", 3)) {
4323 ast_cli(a->fd,
"XMPP Debugging Disabled.\n");
4326 return CLI_SHOWUSAGE;
4342 e->
command =
"xmpp show connections";
4344 "Usage: xmpp show connections\n"
4345 " Shows state of client and component connections\n";
4351 if (!cfg || !cfg->clients) {
4355 ast_cli(a->fd,
"Jabber Users and their status:\n");
4358 while ((clientcfg = ao2_iterator_next(&i))) {
4361 switch (clientcfg->
client->state) {
4363 state =
"Disconnecting";
4366 state =
"Disconnected";
4369 state =
"Connecting";
4372 state =
"Waiting to request TLS";
4375 state =
"Requested TLS";
4378 state =
"Waiting to authenticate";
4381 state =
"Authenticating";
4384 state =
"Retrieving roster";
4387 state =
"Connected";
4393 ast_cli(a->fd,
" [%s] %s - %s\n", clientcfg->
name, clientcfg->
user, state);
4399 ast_cli(a->fd,
"----\n");
4418 e->
command =
"xmpp show buddies";
4420 "Usage: xmpp show buddies\n"
4421 " Shows buddy lists of our clients\n";
4427 if (!cfg || !cfg->clients) {
4431 ast_cli(a->fd,
"XMPP buddy lists\n");
4434 while ((clientcfg = ao2_iterator_next(&i))) {
4438 ast_cli(a->fd,
"Client: %s\n", clientcfg->
name);
4441 while ((buddy = ao2_iterator_next(&bud))) {
4445 ast_cli(a->fd,
"\tBuddy:\t%s\n", buddy->
id);
4448 while ((resource = ao2_iterator_next(&res))) {
4449 ast_cli(a->fd,
"\t\tResource: %s\n", resource->
resource);
4450 ast_cli(a->fd,
"\t\t\tnode: %s\n", resource->
caps.
node);
4451 ast_cli(a->fd,
"\t\t\tversion: %s\n", resource->
caps.
version);
4452 ast_cli(a->fd,
"\t\t\tGoogle Talk capable: %s\n", resource->
caps.
google ?
"yes" :
"no");
4453 ast_cli(a->fd,
"\t\t\tJingle capable: %s\n", resource->
caps.
jingle ?
"yes" :
"no");
4471 AST_CLI_DEFINE(xmpp_do_set_debug,
"Enable/Disable Jabber debug"),
4472 AST_CLI_DEFINE(xmpp_show_clients,
"Show state of clients and components"),
4473 AST_CLI_DEFINE(xmpp_show_buddies,
"Show buddy lists of our clients"),
4481 static int unload_module(
void)
4496 ast_cond_destroy(&message_received_condition);
4497 ast_mutex_destroy(&messagelock);
4506 if (!strcasecmp(var->
name,
"debug")) {
4508 }
else if (!strcasecmp(var->
name,
"autoprune")) {
4510 }
else if (!strcasecmp(var->
name,
"autoregister")) {
4512 }
else if (!strcasecmp(var->
name,
"auth_policy")) {
4513 ast_set2_flag(&global->
general, !strcasecmp(var->
value,
"accept") ? 1 : 0, XMPP_AUTOACCEPT);
4514 }
else if (!strcasecmp(var->
name,
"collection_nodes")) {
4516 }
else if (!strcasecmp(var->
name,
"pubsub_autocreate")) {
4529 if (!strcasecmp(var->
name,
"debug")) {
4531 }
else if (!strcasecmp(var->
name,
"type")) {
4532 ast_set2_flag(&cfg->
flags, !strcasecmp(var->
value,
"component") ? 1 : 0, XMPP_COMPONENT);
4533 }
else if (!strcasecmp(var->
name,
"distribute_events")) {
4535 }
else if (!strcasecmp(var->
name,
"usetls")) {
4537 }
else if (!strcasecmp(var->
name,
"usesasl")) {
4539 }
else if (!strcasecmp(var->
name,
"forceoldssl")) {
4541 }
else if (!strcasecmp(var->
name,
"keepalive")) {
4543 }
else if (!strcasecmp(var->
name,
"autoprune")) {
4545 ast_set2_flag(&cfg->
mod_flags, 1, XMPP_AUTOPRUNE);
4546 }
else if (!strcasecmp(var->
name,
"autoregister")) {
4548 ast_set2_flag(&cfg->
mod_flags, 1, XMPP_AUTOREGISTER);
4549 }
else if (!strcasecmp(var->
name,
"auth_policy")) {
4550 ast_set2_flag(&cfg->
flags, !strcasecmp(var->
value,
"accept") ? 1 : 0, XMPP_AUTOACCEPT);
4551 ast_set2_flag(&cfg->
mod_flags, 1, XMPP_AUTOACCEPT);
4552 }
else if (!strcasecmp(var->
name,
"sendtodialplan")) {
4565 if (!strcasecmp(var->
value,
"unavailable")) {
4566 cfg->
status = IKS_SHOW_UNAVAILABLE;
4567 }
else if (!strcasecmp(var->
value,
"available") || !strcasecmp(var->
value,
"online")) {
4568 cfg->
status = IKS_SHOW_AVAILABLE;
4569 }
else if (!strcasecmp(var->
value,
"chat") || !strcasecmp(var->
value,
"chatty")) {
4570 cfg->
status = IKS_SHOW_CHAT;
4571 }
else if (!strcasecmp(var->
value,
"away")) {
4572 cfg->
status = IKS_SHOW_AWAY;
4573 }
else if (!strcasecmp(var->
value,
"xa") || !strcasecmp(var->
value,
"xaway")) {
4574 cfg->
status = IKS_SHOW_XA;
4575 }
else if (!strcasecmp(var->
value,
"dnd")) {
4576 cfg->
status = IKS_SHOW_DND;
4577 }
else if (!strcasecmp(var->
value,
"invisible")) {
4578 #ifdef IKS_SHOW_INVISIBLE
4579 cfg->
status = IKS_SHOW_INVISIBLE;
4581 cfg->
status = IKS_SHOW_DND;
4678 ast_mutex_init(&messagelock);
4679 ast_cond_init(&message_received_condition, NULL);
4682 ast_log(LOG_WARNING,
"Entity ID is not set. The distributing device state or MWI will not work.\n");
4688 static int reload(
void)
4697 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"Asterisk XMPP Interface",
4698 .support_level = AST_MODULE_SUPPORT_CORE,
4700 .unload = unload_module,
static struct ast_xmpp_buddy * xmpp_client_create_buddy(struct ao2_container *container, const char *id)
Internal function which creates a buddy on a client.
int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
Leave an XMPP multi-user chatroom.
int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
executes a read operation on a function
Main Channel structure associated with a channel.
static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
Hook function called when client receives a service discovery get message.
struct ast_xmpp_message::@293 list
int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
Send a message to an XMPP multi-user chatroom.
ast_device_state
Device States.
static int xmpp_resource_hash(const void *obj, const int flags)
Hashing function for XMPP resource.
int ast_msg_set_tech(struct ast_msg *msg, const char *fmt,...)
Set the technology associated with this message.
#define AST_LIST_LOCK(head)
Locks a list.
static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we need to authenticate using SASL.
void astman_append(struct mansession *s, const char *fmt,...)
Asterisk main include file. File version handling, generic pbx functions.
static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
Create a PubSub collection node.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
static char * xmpp_cli_create_collection(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub collection node creation via CLI.
static void * ast_xmpp_client_config_alloc(const char *cat)
Allocator function for configuration.
XMPP Client Configuration.
int ast_msg_set_context(struct ast_msg *msg, const char *fmt,...)
Set the dialplan context for this message.
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int timeout)
Internal function which receives data from the XMPP client connection.
static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
Helper function which unsubscribes a user and removes them from the roster.
int ast_msg_set_body(struct ast_msg *msg, const char *fmt,...)
Set the 'body' text of a message (in UTF-8)
struct ast_endpoint * ast_endpoint_create(const char *tech, const char *resource)
Create an endpoint struct.
static char * xmpp_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub node deletion via CLI.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
Hook function called when client receives a service discovery result message.
static int xmpp_pubsub_delete_node_list(void *data, ikspak *pak)
Delete pubsub item lists.
static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
Application to leave a chat room.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
Hook function called when we receive a service discovery items request.
descriptor for a cli entry.
int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
Invite a user to an XMPP multi-user chatroom.
static void * xmpp_config_find(struct ao2_container *tmp_container, const char *category)
Find function for configuration.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
static void xmpp_pubsub_mwi_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for MWI events.
int64_t ast_tvdiff_sec(struct timeval end, struct timeval start)
Computes the difference (in seconds) between two struct timeval instances.
static int debug
Global debug status.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
struct ast_msg * ast_msg_alloc(void)
Allocate a message.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
#define RESOURCE_BUCKETS
Number of buckets for resources (per buddy)
enum ast_device_state state
static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we are authenticating.
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
void ast_xmpp_increment_mid(char *mid)
Helper function which increments the message identifier.
static void * xmpp_client_thread(void *data)
XMPP client connection thread.
static char * xmpp_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to purge PubSub nodes via CLI.
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
Join an XMPP multi-user chatroom.
Structure for variables, used for configurations and for channel variables.
const ast_string_field password
static void xmpp_pubsub_request_nodes(struct ast_xmpp_client *client, const char *collection)
Request item list from pubsub.
static struct ast_xmpp_client * xmpp_client_alloc(const char *name)
Allocator function for ast_xmpp_client.
const char *const name
Name of this message technology.
static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we need to request TLS support.
Assume that the ao2_container is already locked.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
void ast_xmpp_client_lock(struct ast_xmpp_client *client)
Lock an XMPP client connection.
#define XMPP_MAX_ATTRLEN
Maximum size of an attribute.
static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
Helper function which sends a discovery information request to a user.
const ast_string_field pubsubnode
int ast_msg_tech_register(const struct ast_msg_tech *tech)
Register a message technology.
static void xmpp_resource_destructor(void *obj)
Destructor callback function for XMPP resource.
char resource[XMPP_MAX_RESJIDLEN]
#define ast_cli_register_multiple(e, len)
Register multiple commands.
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
enum ast_devstate_cache cachable
static char * xmpp_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub leaf node creation via CLI.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
void ast_endpoint_set_state(struct ast_endpoint *endpoint, enum ast_endpoint_state state)
Updates the state of the given endpoint.
static int xmpp_roster_hook(void *data, ikspak *pak)
Hook function called when roster is received from server.
struct ast_json * ast_json_load_string(const char *input, struct ast_json_error *error)
Parse null terminated string into a JSON object or array.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
Comparator function for XMPP buddy.
#define ast_strdup(str)
A wrapper for strdup()
Defined handlers for XMPP client states.
struct ao2_container * stasis_cache_dump(struct stasis_cache *cache, struct stasis_message_type *type)
Dump cached items to a subscription for the ast_eid_default entity.
Out-of-call text message support.
struct stasis_subscription * device_state_sub
The representation of a single configuration file to be processed.
static int xmpp_component_register_get_hook(void *data, ikspak *pak)
Hook function called when the component is queried about registration.
static void xmpp_pubsub_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for device state events.
int ast_unregister_application(const char *app)
Unregister an application.
static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
Initialize collections for event distribution.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
const char * ast_msg_get_body(const struct ast_msg *msg)
Get the body of a message.
struct ast_xmpp_client * ast_xmpp_client_find(const char *name)
Find an XMPP client connection using a given name.
An Entity ID is essentially a MAC address, brief and unique.
JSON parsing error information.
static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
Subscribe to a PubSub node.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_msg_set_to(struct ast_msg *msg, const char *fmt,...)
Set the 'to' URI of a message.
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
int args
This gets set in ast_cli_register()
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
Internal function which changes the presence status of an XMPP client.
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
Callback for handling PubSub events.
#define BUDDY_BUCKETS
Number of buckets for buddies (per client)
struct stasis_subscription * mwi_sub
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
#define XMPP_MAX_RESJIDLEN
Maximum size of a resource JID.
Asterisk JSON abstraction layer.
static int xmpp_component_register_set_hook(void *data, ikspak *pak)
Hook function called when someone registers to the component.
static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we need to authenticate using non-SASL.
Type for default option handler for unsigned integers.
const struct ast_eid * eid
The EID of the server where this message originated.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define STATUS_DISAPPEAR
Status for a disappearing buddy.
Data structure associated with a custom dialplan function.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_STRING_FIELD(name)
Declare a string field.
static int xmpp_client_reconnect(struct ast_xmpp_client *client)
Internal function used to reconnect an XMPP client to its server.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
static struct agi_command commands[]
AGI commands list.
static void ast_xmpp_client_config_destructor(void *obj)
Destructor function for configuration.
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
static void * xmpp_config_alloc(void)
Allocator for XMPP configuration.
const ast_string_field user
const ast_string_field statusmsg
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
struct ao2_container * container
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
int ast_msg_set_var(struct ast_msg *msg, const char *name, const char *value)
Set a variable on the message going to the dialplan.
struct ao2_container * buddies
int ast_xmpp_client_disconnect(struct ast_xmpp_client *client)
Disconnect an XMPP client connection.
static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
Internal function called when a presence message is received.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
static int xmpp_is_secure(struct ast_xmpp_client *client)
Helper function which returns whether an XMPP client connection is secure or not. ...
int ast_publish_mwi_state_full(const char *mailbox, const char *context, int new_msgs, int old_msgs, const char *channel_id, struct ast_eid *eid)
Publish a MWI state update via stasis with all parameters.
Their was an error and no changes were applied.
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
static int xmpp_resource_immediate(void *obj, void *arg, int flags)
Internal astobj2 callback function which returns the first resource, which is the highest priority on...
static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
Internal function which sends a raw message.
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
int ast_msg_queue(struct ast_msg *msg)
Queue a message for routing through the dialplan.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static iks * xmpp_pubsub_build_node_request(struct ast_xmpp_client *client, const char *collection)
Build the a node request.
const ast_string_field name
Configuration option-handling.
int ast_publish_device_state_full(const char *device, enum ast_device_state state, enum ast_devstate_cache cachable, struct ast_eid *eid)
Publish a device state update with EID.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
Add Owner affiliations for pubsub node.
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
static int load_module(void)
Load the module.
Defined handlers for different PAK types.
static iks * xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node, const char *event_type, unsigned int cachable)
Build the skeleton of a publish.
static int xmpp_pubsub_receive_node_list(void *data, ikspak *pak)
Receive pubsub item lists.
int ast_msg_tech_unregister(const struct ast_msg_tech *tech)
Unregister a message technology.
static int xmpp_buddy_hash(const void *obj, const int flags)
Hashing function for XMPP buddy.
static void xmpp_message_destroy(struct ast_xmpp_message *message)
Destroy function for XMPP messages.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
static void xmpp_config_destructor(void *obj)
Destructor for XMPP configuration.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
static int xmpp_ping_request(struct ast_xmpp_client *client, const char *to, const char *from)
Helper function which sends a ping request to a server.
struct ast_xmpp_client * client
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
static int xmpp_config_cmp(void *obj, void *arg, int flags)
Comparator function for configuration.
static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
Helper function which sends an XMPP stream header to the server.
static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox, const char *oldmsgs, const char *newmsgs)
Publish MWI to a PubSub node.
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
const ast_string_field context
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const char *name, const char *collection_name)
Create a pubsub node.
static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
Logging hook function.
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
Application to send a message to a groupchat.
#define ast_calloc(num, len)
A wrapper for calloc()
struct ast_endpoint * endpoint
static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we receive a response to our TLS initiation request.
static int xmpp_io_recv(struct ast_xmpp_client *client, char *buffer, size_t buf_len, int timeout)
Internal function which polls on an XMPP client and receives data.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
static char * xmpp_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub node list via CLI.
Module has failed to load, may be in an inconsistent state.
static struct aco_type global_option
An aco_type structure to link the "general" category to the skel_global_config type.
struct ao2_container * resources
static int xmpp_action_hook(void *data, int type, iks *node)
Action hook for when things occur.
static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
Callback function which subscribes to a user if needed.
structure to hold users read from users.conf
static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device, const char *device_state, unsigned int cachable)
Publish device state to a PubSub node.
Structure used to handle boolean flags.
const ast_string_field oauth_secret
const ast_string_field name
const ast_string_field uniqueid
static int xmpp_resource_cmp(void *obj, void *arg, int flags)
Comparator function for XMPP resource.
int ast_msg_set_from(struct ast_msg *msg, const char *fmt,...)
Set the 'from' URI of a message.
static void xmpp_client_destructor(void *obj)
Destructor callback function for XMPP client.
struct ast_eid ast_eid_default
Global EID.
static void xmpp_pubsub_unsubscribe(struct ast_xmpp_client *client, const char *node)
Unsubscribe from a PubSub node.
static void xmpp_buddy_destructor(void *obj)
Destructor callback function for XMPP buddy.
static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
Internal function called when a subscription message is received.
int ast_msg_set_endpoint(struct ast_msg *msg, const char *fmt,...)
Set the technology's endpoint associated with this message.
const ast_string_field oauth_clientid
static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name, const char *leaf_name)
Create a PubSub leaf node.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Type information about a category-level configurable object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
static int xmpp_join_exec(struct ast_channel *chan, const char *data)
Application to join a chat room.
static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
Delete a PubSub node.
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
Abstract JSON element (object, array, string, int, ...).
Type for default option handler for stringfields.
static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
Internal function called when a message is received.
void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
Unlock an XMPP client connection.
The structure that contains device state.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
struct ast_xmpp_global_config * global
const ast_string_field refresh_token
static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
Internal function used to send a message to a user or chatroom.
static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we need to authenticate.
struct stasis_topic * ast_mwi_topic_all(void)
Get the Stasis Message Bus API topic for MWI messages.
struct ast_xmpp_capabilities caps
struct ast_msg * ast_msg_destroy(struct ast_msg *msg)
Destroy an ast_msg.
static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
Hook function called when component receives a service discovery get message.
const ast_string_field server
The structure that contains MWI state.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
void ast_endpoint_shutdown(struct ast_endpoint *endpoint)
Shutsdown an ast_endpoint.
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
#define ASTERISK_GPL_KEY
The text the key() function should return.
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
static void * xmpp_client_find_or_create(const char *category)
Look up existing client or create a new one.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
void ast_xmpp_client_unref(struct ast_xmpp_client *client)
Release XMPP client connection reference.
XMPP Global Configuration.
static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
Internal function which changes the XMPP client state.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
#define CONFIG_INFO_STANDARD(name, arr, alloc,...)
Declare an aco_info struct with default module and preload values.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we authenticated as a component.
static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
Internal function called when we should authenticate as a component.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_custom_function_register(acf)
Register a custom function.
struct ast_flags mod_flags
static iks * xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
Create an IQ packet.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
Send an XML stanza out using an established XMPP client connection.
struct ao2_container * clients
Structure for mutex and tracking information.
static int xmpp_resource_is_available(void *obj, void *arg, int flags)
Callback function which returns when the resource is available.
int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
Send a message to a given user using an established XMPP client connection.
#define AST_APP_ARG(name)
Define an application argument.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ao2_link(container, obj)
Add an object to a container.
static int xmpp_connect_hook(void *data, ikspak *pak)
Hook function called when client finishes authenticating with the server.