Index: channels/chan_gtalk.c =================================================================== --- channels/chan_gtalk.c (revision 265315) +++ channels/chan_gtalk.c (working copy) @@ -459,8 +459,7 @@ iks_insert_attrib(iq, "type", "set"); iks_insert_attrib(iq, "to", to); iks_insert_attrib(iq, "from", from); - iks_insert_attrib(iq, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(client->connection->mid)); iks_insert_attrib(gtalk, "xmlns", "http://www.google.com/session"); iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept"); @@ -508,8 +507,7 @@ iks_insert_attrib(iq, "from", from); iks_insert_attrib(iq, "to", to); iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "id",p->parent->connection->mid); - ast_aji_increment_mid(p->parent->connection->mid); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(p->parent->connection->mid)); iks_insert_attrib(session, "type", "transport-accept"); iks_insert_attrib(session, "id", sid); /* put the initiator attribute to lower case if we receive the call @@ -888,8 +886,7 @@ iks_insert_attrib(iq, "from", to); iks_insert_attrib(iq, "to", from); iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "id", c->mid); - ast_aji_increment_mid(c->mid); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(c->mid)); iks_insert_attrib(gtalk, "type", "transport-info"); iks_insert_attrib(gtalk, "id", sid); /* put the initiator attribute to lower case if we receive the call @@ -1107,8 +1104,7 @@ iks_insert_attrib(request, "type", "set"); iks_insert_attrib(request, "from", p->us); iks_insert_attrib(request, "to", p->them); - iks_insert_attrib(request, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); + iks_insert_attrib(request, "id", ast_aji_increment_mid2(client->connection->mid)); session = iks_new("session"); if (session) { iks_insert_attrib(session, "type", action); @@ -1552,8 +1548,7 @@ iks_insert_attrib(iq, "type", "set"); iks_insert_attrib(iq, "to", p->them); iks_insert_attrib(iq, "from", p->us); - iks_insert_attrib(iq, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(client->connection->mid)); iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk"); iks_insert_attrib(gtalk, "action", "session-info"); /* put the initiator attribute to lower case if we receive the call Index: include/asterisk/jabber.h =================================================================== --- include/asterisk/jabber.h (revision 265315) +++ include/asterisk/jabber.h (working copy) @@ -133,14 +133,75 @@ ASTOBJ_CONTAINER_COMPONENTS(struct aji_client); }; +/*!\brief Send a message + * \param client Source client from whom the message will be sent. Must NOT be locked. + * \param address The destination Jabber ID to which the message will be sent (must contain an at-sign) + * \param message A text string containing the message. + * \return Always returns 1. + */ int ast_aji_send(struct aji_client *client, const char *address, const char *message); + +/*!\brief Disconnect client from Jabber server + * \param client + * \return Always returns 1. + */ int ast_aji_disconnect(struct aji_client *client); + +/*!\brief Hmmm, interesting. There's a prototype, but no implementation. Probably best not to try to call it. + */ int ast_aji_check_roster(void); -void ast_aji_increment_mid(char *mid); -int ast_aji_create_chat(struct aji_client *client,char *room, char *server, char *topic); + +/*!\brief Increment the Message-ID string + * \deprecated + * \note This function is deprecated because there is a race condition between + * the time the MID is used and when it is incremented. By convention, the mid + * is incremented after it is used. Use ast_aji_increment_mid2, instead and use + * the return value for your MID; that function ensures thread-safety. + * \param mid A message ID string, which will be altered. + */ +void ast_aji_increment_mid(char *mid) __attribute_deprecated__; + +/*!\brief Increment the Message-ID string, returning the next one to use. + * \param mid Stored message-ID string, which will be altered. + * \return A unique MID to use in your next message. + */ +const char *ast_aji_increment_mid2(char *mid); + +/*!\brief Create a chatroom + * \param client + * \param room Name of the room to create + * \param server Server hostname on which to create the chatroom + * \param topic Initial topic for the chatroom + * \return Always returns 0 + */ +int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic); + +/*!\brief Invite a user to a chatroom + * \param client + * \param user Jabber-ID to invite + * \param room Name of the destination room + * \param message Text message accompanying the invite + * \return Always returns 0. + */ int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message); -int ast_aji_join_chat(struct aji_client *client,char *room); + +/*!\brief Join a chatroom + * \param client + * \param room Name of the destination room + * \return Always returns 0. + */ +int ast_aji_join_chat(struct aji_client *client, char *room); + +/*!\brief Lookup client by label or Jabber-ID + * \param name Client label or Jabber-ID + * \retval non-NULL aji_client + * \retval NULL No client could be found + */ struct aji_client *ast_aji_get_client(const char *name); + +/*!\brief Retrieve ASTOBJ_CONTAINER of all clients + * \return aji_client_container + */ struct aji_client_container *ast_aji_get_clients(void); #endif Index: res/res_jabber.c =================================================================== --- res/res_jabber.c (revision 265315) +++ res/res_jabber.c (working copy) @@ -57,6 +57,7 @@ #include "asterisk/astobj.h" #include "asterisk/astdb.h" #include "asterisk/manager.h" +#include "asterisk/threadstorage.h" #define JABBER_CONFIG "jabber.conf" @@ -189,9 +190,14 @@ struct aji_message *tmp; ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy); ASTOBJ_CONTAINER_DESTROY(&obj->buddies); + ASTOBJ_WRLOCK(obj); iks_filter_delete(obj->f); iks_parser_delete(obj->p); iks_stack_delete(obj->stack); + obj->f = NULL; + obj->p = NULL; + obj->stack = NULL; + ASTOBJ_UNLOCK(obj); AST_LIST_LOCK(&obj->messages); while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) { if (tmp->from) @@ -398,11 +404,13 @@ } if(!&client->buddies) { ast_log(LOG_WARNING, "No buddies for connection : %s\n", sender); + ASTOBJ_UNLOCK(client); return -1; } buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, resource ? screenname: jid); if (!buddy) { ast_log(LOG_WARNING, "Could not find buddy in list : %s\n", resource ? screenname : jid); + ASTOBJ_UNLOCK(client); return -1; } r = aji_find_resource(buddy, resource); @@ -414,6 +422,7 @@ stat = r->status; sprintf(status, "%d", stat); pbx_builtin_setvar_helper(chan, variable, status); + ASTOBJ_UNLOCK(client); return 0; } @@ -449,8 +458,12 @@ ast_log(LOG_WARNING, "Could not find sender connection: %s\n", sender); return -1; } - if (strchr(recipient, '@') && message) + ASTOBJ_UNLOCK(client); + + if (strchr(recipient, '@') && message) { ast_aji_send(client, recipient, message); + } + return 0; } @@ -480,6 +493,7 @@ /*! * \brief The action hook parses the inbound packets, constantly running. + * Assumes client lock is already held. * \param data aji client structure * \param type type of packet * \param node the actual packet. @@ -519,9 +533,8 @@ iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE); auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id")); if (auth) { - iks_insert_attrib(auth, "id", client->mid); + iks_insert_attrib(auth, "id", ast_aji_increment_mid2(client->mid)); iks_insert_attrib(auth, "to", client->jid->server); - ast_aji_increment_mid(client->mid); iks_send(client->p, auth); iks_delete(auth); } else @@ -541,8 +554,7 @@ iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE); auth = iks_make_resource_bind(client->jid); if (auth) { - iks_insert_attrib(auth, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(auth, "id", ast_aji_increment_mid2(client->mid)); iks_send(client->p, auth); iks_delete(auth); } else { @@ -555,7 +567,7 @@ auth = iks_make_session(); if (auth) { iks_insert_attrib(auth, "id", "auth"); - ast_aji_increment_mid(client->mid); + ast_aji_increment_mid2(client->mid); iks_send(client->p, auth); iks_delete(auth); } else { @@ -573,16 +585,16 @@ else { if (features == IKS_STREAM_SASL_PLAIN) { iks *x = NULL; + int len = strlen(client->jid->user) + strlen(client->password) + 3; + char *s = ast_malloc(80 + len); + char *base64 = ast_malloc(80 + len * 2); + x = iks_new("auth"); - if (x) { - int len = strlen(client->jid->user) + strlen(client->password) + 3; - /* XXX Check return values XXX */ - char *s = ast_malloc(80 + len); - char *base64 = ast_malloc(80 + len * 2); + if (x && s && base64) { iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL); iks_insert_attrib(x, "mechanism", "PLAIN"); - sprintf(s, "%c%s%c%s", 0, client->jid->user, 0, client->password); - + snprintf(s, 80 + len, "%c%s%c%s", 0, client->jid->user, 0, client->password); + /* exclude the NULL training byte from the base64 encoding operation as some XMPP servers will refuse it. The format for authentication is [authzid]\0authcid\0password @@ -590,14 +602,14 @@ ast_base64encode(base64, (const unsigned char *) s, len - 1, len * 2); iks_insert_cdata(x, base64, 0); iks_send(client->p, x); - iks_delete(x); - if (base64) - free(base64); - if (s) - free(s); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } + if (x) { + iks_delete(x); + } + ast_free(base64); + ast_free(s); } } } @@ -609,12 +621,12 @@ iks_send_header(client->p, client->jid->server); } break; - case IKS_NODE_ERROR: + case IKS_NODE_ERROR: ast_log(LOG_ERROR, "JABBER: Node Error\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; break; - case IKS_NODE_STOP: + case IKS_NODE_STOP: ast_log(LOG_WARNING, "JABBER: Disconnected\n"); ASTOBJ_UNREF(client, aji_client_destroy); return IKS_HOOK; @@ -698,6 +710,7 @@ return IKS_OK; } +/*!\note Assumes client lock is already held. */ static int aji_register_approve_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); @@ -716,12 +729,11 @@ iks_insert_attrib(presence, "from", client->jid->full); iks_insert_attrib(presence, "to", pak->from->partial); - iks_insert_attrib(presence, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(presence, "id", ast_aji_increment_mid2(client->mid)); iks_insert_attrib(presence, "type", "subscribe"); iks_insert_attrib(x, "xmlns", "vcard-temp:x:update"); iks_insert_node(presence, x); - iks_send(client->p, presence); + iks_send(client->p, presence); } } else { ast_log(LOG_ERROR, "Out of memory.\n"); @@ -737,6 +749,7 @@ return IKS_FILTER_EAT; } +/*!\note Assumes client lock is already held */ static int aji_register_query_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); @@ -777,7 +790,7 @@ iks_delete(error); if (notacceptable) iks_delete(notacceptable); - } else if (!(node = iks_find_attrib(pak->query, "node"))) { + } else if (!(node = iks_find_attrib(pak->query, "node"))) { iks *iq = NULL, *query = NULL, *instructions = NULL; char *explain = "Welcome to Asterisk - the Open Source PBX.\n"; iq = iks_new("iq"); @@ -807,6 +820,7 @@ return IKS_FILTER_EAT; } +/*!\note Assumes client lock is already held */ static int aji_ditems_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); @@ -903,6 +917,7 @@ } +/*!\note Assumes client lock is already held */ static int aji_client_info_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); @@ -962,6 +977,7 @@ return IKS_FILTER_EAT; } +/*!\note Assumes client lock is already held */ static int aji_dinfo_handler(void *data, ikspak *pak) { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); @@ -1327,18 +1343,19 @@ iks_insert_attrib(iq, "type", "get"); iks_insert_attrib(iq, "to", pak->from->full); iks_insert_attrib(iq,"from", client->jid->full); - iks_insert_attrib(iq, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(client->mid)); iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info"); iks_insert_node(iq, query); iks_send(client->p, iq); - - } else + } else { ast_log(LOG_ERROR, "Out of memory.\n"); - if(query) + } + if (query) { iks_delete(query); - if(iq) + } + if (iq) { iks_delete(iq); + } } } if (option_verbose > 4) { @@ -1400,14 +1417,18 @@ iks_insert_cdata(status, "Asterisk has approved subscription", 0); iks_insert_node(presence, status); iks_send(client->p, presence); - } else + } else { ast_log(LOG_ERROR, "Unable to allocate nodes\n"); - if(presence) + } + if (presence) { iks_delete(presence); - if(status) + } + if (status) { iks_delete(status); - if(client->component) + } + if (client->component) { aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), 1, client->statusmessage); + } case IKS_TYPE_SUBSCRIBED: buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial); if (!buddy && pak->from->partial) { @@ -1422,6 +1443,7 @@ /*! * \brief sends messages. + * Assumes client is NOT locked. * \param aji_client struct , reciever, message. * \return 1. */ @@ -1432,8 +1454,10 @@ if (client->state == AJI_CONNECTED) { message_packet = iks_make_msg(IKS_TYPE_CHAT, address, message); if (message_packet) { + ASTOBJ_RDLOCK(client); iks_insert_attrib(message_packet, "from", client->jid->full); res = iks_send(client->p, message_packet); + ASTOBJ_UNLOCK(client); } else { ast_log(LOG_ERROR, "Out of memory.\n"); } @@ -1458,11 +1482,13 @@ if (iq && client) { iks_insert_attrib(iq, "type", "get"); iks_insert_attrib(iq, "to", server); - iks_insert_attrib(iq, "id", client->mid); - ast_aji_increment_mid(client->mid); + ASTOBJ_RDLOCK(client); + iks_insert_attrib(iq, "id", ast_aji_increment_mid2(client->mid)); iks_send(client->p, iq); - } else + ASTOBJ_UNLOCK(client); + } else { ast_log(LOG_ERROR, "Out of memory.\n"); + } iks_delete(iq); @@ -1484,16 +1510,21 @@ iks_insert_cdata(priority, "0", 1); iks_insert_attrib(presence, "to", room); iks_insert_node(presence, priority); + ASTOBJ_RDLOCK(client); res = iks_send(client->p, presence); iks_insert_cdata(priority, "5", 1); iks_insert_attrib(presence, "to", room); res = iks_send(client->p, presence); - } else + ASTOBJ_UNLOCK(client); + } else { ast_log(LOG_ERROR, "Out of memory.\n"); - if (presence) + } + if (presence) { iks_delete(presence); - if (priority) + } + if (priority) { iks_delete(priority); + } return res; } @@ -1511,23 +1542,28 @@ body = iks_new("body"); namespace = iks_new("x"); if (client && invite && body && namespace) { + ASTOBJ_RDLOCK(client); iks_insert_attrib(invite, "to", user); - iks_insert_attrib(invite, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(invite, "id", ast_aji_increment_mid2(client->mid)); iks_insert_cdata(body, message, 0); iks_insert_attrib(namespace, "xmlns", "jabber:x:conference"); iks_insert_attrib(namespace, "jid", room); iks_insert_node(invite, body); iks_insert_node(invite, namespace); res = iks_send(client->p, invite); - } else + ASTOBJ_UNLOCK(client); + } else { ast_log(LOG_ERROR, "Out of memory.\n"); - if (body) + } + if (body) { iks_delete(body); - if (namespace) + } + if (namespace) { iks_delete(namespace); - if (invite) + } + if (invite) { iks_delete(invite); + } return res; } @@ -1541,41 +1577,52 @@ { struct aji_client *client = ASTOBJ_REF((struct aji_client *) data); int res = IKS_HOOK; - do { + for (;;) { if (res != IKS_OK) { - while(res != IKS_OK) { - if(option_verbose > 3) + while (res != IKS_OK) { + if (option_verbose > 3) { ast_verbose("JABBER: reconnecting.\n"); + } res = aji_reconnect(client); sleep(4); } } + ASTOBJ_RDLOCK(client); res = iks_recv(client->p, 1); + ASTOBJ_UNLOCK(client); if (client->state == AJI_DISCONNECTING) { - if (option_debug > 1) + if (option_debug > 1) { ast_log(LOG_DEBUG, "Ending our Jabber client's thread due to a disconnect\n"); + } + ASTOBJ_UNREF(client, aji_client_destroy); pthread_exit(NULL); } client->timeout--; - if (res == IKS_HOOK) + if (res == IKS_HOOK) { ast_log(LOG_WARNING, "JABBER: Got hook event.\n"); - else if (res == IKS_NET_TLSFAIL) + } else if (res == IKS_NET_TLSFAIL) { ast_log(LOG_WARNING, "JABBER: Failure in TLS.\n"); - else if (client->timeout == 0 && client->state == AJI_CONNECTED) { + } else if (client->timeout == 0 && client->state == AJI_CONNECTED) { + ASTOBJ_RDLOCK(client); res = client->keepalive ? iks_send_raw(client->p, " ") : IKS_OK; - if(res == IKS_OK) + ASTOBJ_UNLOCK(client); + if (res == IKS_OK) { client->timeout = 50; - else + } else { ast_log(LOG_WARNING, "JABBER: Network Timeout\n"); - } else if (res == IKS_NET_RWERR) + } + } else if (res == IKS_NET_RWERR) { ast_log(LOG_WARNING, "JABBER: socket read error\n"); - } while (client); - ASTOBJ_UNREF(client, aji_client_destroy); - return 0; + } + ASTOBJ_UNLOCK(client); + } } +AST_THREADSTORAGE(local_mid, local_mid_init); +AST_MUTEX_DEFINE_STATIC(midlock); + /*! * \brief increments the mid field for messages and other events. * \param message id. @@ -1583,15 +1630,30 @@ */ void ast_aji_increment_mid(char *mid) { - int i = 0; + ast_aji_increment_mid2(mid); +} +const char *ast_aji_increment_mid2(char *mid) +{ + int i; + char *buf; + if (!(buf = ast_threadstorage_get(&local_mid, 12))) { + return ""; + } + + ast_mutex_lock(&midlock); + ast_copy_string(buf, mid, 129); + for (i = strlen(mid) - 1; i >= 0; i--) { if (mid[i] != 'z') { mid[i] = mid[i] + 1; - i = 0; - } else + break; + } else { mid[i] = 'a'; + } } + ast_mutex_unlock(&midlock); + return buf; } @@ -1619,8 +1681,7 @@ iks_filter_remove_hook(client->f, aji_register_transport); iks_filter_add_rule(client->f, aji_register_transport2, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, IKS_NS_REGISTER, IKS_RULE_DONE); iks_insert_attrib(send, "to", buddy->host); - iks_insert_attrib(send, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(send, "id", ast_aji_increment_mid2(client->mid)); iks_insert_attrib(send, "from", client->user); res = iks_send(client->p, send); } else @@ -1659,8 +1720,7 @@ iks_filter_remove_hook(client->f, aji_register_transport2); iks_insert_attrib(regiq, "to", buddy->host); iks_insert_attrib(regiq, "type", "set"); - iks_insert_attrib(regiq, "id", client->mid); - ast_aji_increment_mid(client->mid); + iks_insert_attrib(regiq, "id", ast_aji_increment_mid2(client->mid)); iks_insert_attrib(regiq, "from", client->user); iks_insert_attrib(regquery, "xmlns", "jabber:iq:register"); iks_insert_cdata(reguser, buddy->user, 0); @@ -1685,6 +1745,7 @@ */ /*! * \brief goes through roster and prunes users not needed in list, or adds them accordingly. + * Assumes the client object has been locked beforehand. * \param aji_client struct. * \return void. */ @@ -1723,21 +1784,27 @@ } ASTOBJ_UNLOCK(iterator); }); - } else + } else { ast_log(LOG_ERROR, "Out of memory.\n"); - if (removeiq) + } + if (removeiq) { iks_delete(removeiq); - if (removequery) + } + if (removequery) { iks_delete(removequery); - if (removeitem) + } + if (removeitem) { iks_delete(removeitem); - if (send) + } + if (send) { iks_delete(send); + } ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy); } /*! * \brief filters the roster packet we get back from server. + * Assumes client lock already held * \param aji_client struct, and xml packet. * \return IKS_FILTER_EAT. */ @@ -1747,8 +1814,9 @@ int flag = 0; iks *x = NULL; struct aji_buddy *buddy; - + client->state = AJI_CONNECTED; + ASTOBJ_CONTAINER_RDLOCK(&client->buddies); ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); x = iks_child(pak->query); @@ -1768,6 +1836,7 @@ iks_delete(x); ASTOBJ_UNLOCK(iterator); }); + ASTOBJ_CONTAINER_UNLOCK(&client->buddies); x = iks_child(pak->query); while (x) { @@ -1813,42 +1882,52 @@ return IKS_FILTER_EAT; } +/*!\note Assumes client lock is NOT held. */ static int aji_reconnect(struct aji_client *client) { int res = 0; - if (client->state) + ASTOBJ_WRLOCK(client); + if (client->state) { client->state = AJI_DISCONNECTED; - client->timeout=50; - if (client->p) + } + client->timeout = 50; + if (client->p) { iks_parser_reset(client->p); - if (client->authorized) + } + if (client->authorized) { client->authorized = 0; + } - if(client->component) + if (client->component) { res = aji_component_initialize(client); - else + } else { res = aji_client_initialize(client); + } + ASTOBJ_UNLOCK(client); return res; } +/*!\note Assumes client lock is already held. */ static int aji_get_roster(struct aji_client *client) { iks *roster = NULL; roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER); - if(roster) { + if (roster) { iks_insert_attrib(roster, "id", "roster"); aji_set_presence(client, NULL, client->jid->full, 1, client->statusmessage); iks_send(client->p, roster); } - if (roster) + if (roster) { iks_delete(roster); + } return 1; } /*! * \brief connects as a client to jabber server. + * Assumes client lock is already held. * \param aji_client struct, and xml packet. * \return res. */ @@ -1863,11 +1942,13 @@ client->state = AJI_CONNECTING; client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid; iks_filter_remove_hook(client->f, aji_client_connect); - if(!client->component) /*client*/ + if (!client->component) { /*client*/ aji_get_roster(client); + } } - } else + } else { ast_log(LOG_ERROR, "Out of memory.\n"); + } ASTOBJ_UNREF(client, aji_client_destroy); return res; @@ -1875,6 +1956,7 @@ /*! * \brief prepares client for connect. + * Assumes client lock is already held. * \param aji_client struct. * \return 1. */ @@ -1887,16 +1969,18 @@ if (connected == IKS_NET_NOCONN) { ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n"); return IKS_HOOK; - } else if (connected == IKS_NET_NODNS) { + } else if (connected == IKS_NET_NODNS) { ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name, S_OR(client->serverhost, client->jid->server)); return IKS_HOOK; - } else + } else { iks_recv(client->p, 30); + } return IKS_OK; } /*! * \brief prepares component for connect. + * Assumes client lock is already held. * \param aji_client struct. * \return 1. */ @@ -1905,14 +1989,16 @@ int connected = 1; connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->user); + if (connected == IKS_NET_NOCONN) { ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n"); return IKS_HOOK; } else if (connected == IKS_NET_NODNS) { ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name, S_OR(client->serverhost, client->jid->server)); return IKS_HOOK; - } else if (!connected) + } else if (!connected) { iks_recv(client->p, 30); + } return IKS_OK; } @@ -1924,11 +2010,14 @@ int ast_aji_disconnect(struct aji_client *client) { if (client) { - if (option_verbose > 3) + if (option_verbose > 2) { ast_verbose(VERBOSE_PREFIX_3 "JABBER: Disconnecting\n"); + } + ASTOBJ_WRLOCK(client); iks_disconnect(client->p); iks_parser_delete(client->p); - ASTOBJ_UNREF(client, aji_client_destroy); + client->p = NULL; + ASTOBJ_UNLOCK(client); } return 1; @@ -1936,6 +2025,7 @@ /*! * \brief set presence of client. + * Assumes client lock is already held. * \param aji_client struct, user to send it to, and from, level, description. * \return void. */ @@ -1958,12 +2048,15 @@ iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps"); iks_insert_node(presence, cnode); res = iks_send(client->p, presence); - } else + } else { ast_log(LOG_ERROR, "Out of memory.\n"); - if (cnode) + } + if (cnode) { iks_delete(cnode); - if (presence) + } + if (presence) { iks_delete(presence); + } } /*! @@ -1973,11 +2066,13 @@ */ static int aji_do_debug(int fd, int argc, char *argv[]) { + ASTOBJ_CONTAINER_RDLOCK(&clients); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { - ASTOBJ_RDLOCK(iterator); + ASTOBJ_RDLOCK(iterator); iterator->debug = 1; ASTOBJ_UNLOCK(iterator); }); + ASTOBJ_CONTAINER_UNLOCK(&clients); ast_cli(fd, "Jabber Debugging Enabled.\n"); return RESULT_SUCCESS; } @@ -2001,11 +2096,13 @@ */ static int aji_no_debug(int fd, int argc, char *argv[]) { + ASTOBJ_CONTAINER_RDLOCK(&clients); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); iterator->debug = 0; ASTOBJ_UNLOCK(iterator); }); + ASTOBJ_CONTAINER_UNLOCK(&clients); ast_cli(fd, "Jabber Debugging Disabled.\n"); return RESULT_SUCCESS; } @@ -2020,6 +2117,7 @@ char *status; int count = 0; ast_cli(fd, "Jabber Users and their status:\n"); + ASTOBJ_CONTAINER_RDLOCK(&clients); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); count++; @@ -2039,6 +2137,7 @@ ast_cli(fd, " User: %s - %s\n", iterator->user, status); ASTOBJ_UNLOCK(iterator); }); + ASTOBJ_CONTAINER_UNLOCK(&clients); ast_cli(fd, "----\n"); ast_cli(fd, " Number of users: %d\n", count); return RESULT_SUCCESS; @@ -2068,6 +2167,7 @@ /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */ ast_aji_send(client, "mogorman@astjab.org", "blahblah"); + ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, { ASTOBJ_RDLOCK(iterator); ast_verbose("User: %s\n", iterator->name); @@ -2118,6 +2218,7 @@ ASTOBJ_INIT(client); ASTOBJ_WRLOCK(client); ASTOBJ_CONTAINER_INIT(&client->buddies); + AST_LIST_HEAD_INIT(&client->messages); } else { ASTOBJ_WRLOCK(client); ASTOBJ_UNMARK(client); @@ -2136,7 +2237,6 @@ client->keepalive = 1; client->timeout = 50; client->message_timeout = 100; - AST_LIST_HEAD_INIT(&client->messages); client->component = 0; ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage)); @@ -2190,16 +2290,22 @@ client->p = iks_stream_new(((client->component) ? "jabber:component:accept" : "jabber:client"), client, aji_act_hook); if (!client->p) { ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name); + ASTOBJ_UNLOCK(client); + ASTOBJ_UNREF(client, aji_client_destroy); return 0; } client->stack = iks_stack_new(8192, 8192); if (!client->stack) { ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name); + ASTOBJ_UNLOCK(client); + ASTOBJ_UNREF(client, aji_client_destroy); return 0; } client->f = iks_filter_new(); if (!client->f) { ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name); + ASTOBJ_UNLOCK(client); + ASTOBJ_UNREF(client, aji_client_destroy); return 0; } if (!strchr(client->user, '/') && !client->component) { /*client */ @@ -2367,19 +2473,24 @@ char *aux = NULL; client = ASTOBJ_CONTAINER_FIND(&clients, name); - if (!client && strchr(name, '@')) { - ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { - aux = ast_strdupa(iterator->user); - if (strchr(aux, '/')) { - /* strip resource for comparison */ - aux = strsep(&aux, "/"); - } - if (!strncasecmp(aux, name, strlen(aux))) { - client = iterator; - } - }); - } - + if (!client && strchr(name, '@')) { + ASTOBJ_CONTAINER_RDLOCK(&clients); + ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { + /* DANGER Will Robinson DANGER */ + aux = ast_strdupa(iterator->user); + if (strchr(aux, '/')) { + /* strip resource for comparison */ + aux = strsep(&aux, "/"); + } + if (!strncasecmp(aux, name, strlen(aux))) { + client = iterator; + break; + } + }); + ASTOBJ_RDLOCK(client); + ASTOBJ_CONTAINER_UNLOCK(&clients); + } + return client; } @@ -2422,7 +2533,9 @@ if (!client) { astman_send_error(s, m, "Could not find Sender"); return 0; - } + } + ASTOBJ_UNLOCK(client); + if (strchr(screenname, '@') && message) { ast_aji_send(client, screenname, message); astman_append(s, "Response: Success\r\n"); @@ -2439,22 +2552,27 @@ static int aji_reload() { + ASTOBJ_CONTAINER_WRLOCK(&clients); ASTOBJ_CONTAINER_MARKALL(&clients); if (!aji_load_config()) { ast_log(LOG_ERROR, "JABBER: Failed to load config.\n"); + ASTOBJ_CONTAINER_UNLOCK(&clients); return 0; } ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, aji_client_destroy); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { - ASTOBJ_RDLOCK(iterator); - if(iterator->state == AJI_DISCONNECTED) { - if (!iterator->thread) + ASTOBJ_WRLOCK(iterator); + if (iterator->state == AJI_DISCONNECTED) { + if (!iterator->thread) { ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator); - } else if (iterator->state == AJI_CONNECTING) + } + } else if (iterator->state == AJI_CONNECTING) { aji_get_roster(iterator); + } ASTOBJ_UNLOCK(iterator); }); - + ASTOBJ_CONTAINER_UNLOCK(&clients); + return 1; } @@ -2475,18 +2593,20 @@ ast_unregister_application(app_ajisend); ast_unregister_application(app_ajistatus); ast_manager_unregister("JabberSend"); - + + ASTOBJ_CONTAINER_WRLOCK(&clients); ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, { ASTOBJ_RDLOCK(iterator); if (option_debug > 2) - ast_log(LOG_DEBUG, "JABBER: Releasing and disconneing client: %s\n", iterator->name); + ast_log(LOG_DEBUG, "JABBER: Releasing and disconnecting client: %s\n", iterator->name); iterator->state = AJI_DISCONNECTING; + ASTOBJ_UNLOCK(iterator); ast_aji_disconnect(iterator); pthread_join(iterator->thread, NULL); - ASTOBJ_UNLOCK(iterator); }); ASTOBJ_CONTAINER_DESTROYALL(&clients, aji_client_destroy); + ASTOBJ_CONTAINER_UNLOCK(&clients); ASTOBJ_CONTAINER_DESTROY(&clients); return 0; }