2 Copyright (C) 2005 Georgia Public Library Service
3 Bill Erickson <billserickson@gmail.com>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
16 #include "osrf_chat.h"
20 int __osrfChatXMLErrorOcurred = 0;
21 int __osrfChatClientSentDisconnect = 0;
23 /* shorter version of strcmp */
24 static int eq(const char* a, const char* b) { return (a && b && !strcmp(a,b)); }
26 /* gnarly debug function */
27 static void chatdbg( osrfChatServer* server ) {
30 return; /* heavy logging, should only be used in heavy debug mode */
32 growing_buffer* buf = buffer_init(256);
34 buffer_add(buf, "---------------------------------------------------------------------\n");
39 "Named nodes in hash: %lu\n"
43 "-------------------------------------------------------\n",
44 osrfListGetCount(server->nodeList), osrfHashGetCount(server->nodeHash),
45 server->domain, server->port, server->s2sport );
47 osrfListIterator* itr = osrfNewListIterator(server->nodeList);
50 while( (node = osrfListIteratorNext(itr)) ) {
64 "-------------------------------------------------------\n",
65 node->sockid, node->remote, node->state, node->xmlstate, node->inparse,
66 node->to, node->resource, node->username, node->domain, node->authkey, node->type );
69 debug_handler("DEBUG:\n%s", buf->buf );
71 osrfListIteratorFree(itr);
74 osrfChatServer* osrfNewChatServer( char* domain, char* secret, int s2sport ) {
75 if(!(domain && secret)) return NULL;
77 osrfChatServer* server = safe_malloc(sizeof(osrfChatServer));
79 server->nodeHash = osrfNewHash();
80 server->nodeList = osrfNewList();
81 server->deadNodes = osrfNewList();
82 server->nodeList->freeItem = &osrfChatNodeFree;
83 server->domain = strdup(domain);
84 server->s2sport = s2sport;
86 server->mgr = safe_malloc(sizeof(socket_manager));
87 server->mgr->data_received = &osrfChatHandleData;
88 server->mgr->blob = server;
89 server->mgr->on_socket_closed = &osrfChatSocketClosed;
91 if(secret) server->secret = strdup(secret);
95 void osrfChatCleanupClients( osrfChatServer* server ) {
97 osrfListFree(server->deadNodes);
98 server->deadNodes = osrfNewList();
104 osrfChatNode* osrfNewChatNode( int sockid, char* domain ) {
105 if(sockid < 1 || !domain) return NULL;
106 osrfChatNode* node = safe_malloc(sizeof(osrfChatNode));
107 node->state = OSRF_CHAT_STATE_NONE;
108 node->msgs = NULL; /* only s2s nodes cache messages */
109 node->parserCtx = xmlCreatePushParserCtxt(osrfChatSaxHandler, node, "", 0, NULL);
110 node->msgDoc = xmlNewDoc(BAD_CAST "1.0");
111 node->domain = strdup(domain);
112 xmlKeepBlanksDefault(0);
113 node->authkey = NULL;
114 node->username = NULL;
115 node->resource = NULL;
122 osrfChatNode* osrfNewChatS2SNode( char* domain, char* remote ) {
123 if(!(domain && remote)) return NULL;
124 osrfChatNode* n = osrfNewChatNode( 1, domain );
125 n->state = OSRF_CHAT_STATE_S2S_CHALLENGE;
127 n->remote = strdup(remote);
128 n->msgs = osrfNewList();
129 n->msgs->freeItem = &osrfChatS2SMessageFree;
134 void osrfChatS2SMessageFree(void* n) { free(n); }
136 void osrfChatNodeFree( void* node ) {
138 osrfChatNode* n = (osrfChatNode*) node;
140 /* we can't free messages that are mid-parse because the
141 we can't free the parser context */
144 osrfListPush(n->parent->deadNodes, n);
155 osrfListFree(n->msgs);
158 xmlFreeDoc(n->parserCtx->myDoc);
159 xmlFreeParserCtxt(n->parserCtx);
162 xmlFreeDoc(n->msgDoc);
168 int osrfChatServerConnect( osrfChatServer* cs, int port, int s2sport, char* listenAddr ) {
169 if(!(cs && port && listenAddr)) return -1;
171 cs->s2sport = s2sport;
172 if( socket_open_tcp_server(cs->mgr, port, listenAddr ) < 0 )
174 if( socket_open_tcp_server(cs->mgr, s2sport, listenAddr ) < 0 )
180 int osrfChatServerWait( osrfChatServer* server ) {
181 if(!server) return -1;
183 if(socket_wait_all(server->mgr, -1) < 0)
184 warning_handler( "jserver_wait(): socket_wait_all() returned error");
190 void osrfChatServerFree(osrfChatServer* server ) {
192 osrfHashFree(server->nodeHash);
193 osrfListFree(server->nodeList);
195 free(server->secret);
199 void osrfChatHandleData( void* cs,
200 socket_manager* mgr, int sockid, char* data, int parent_id ) {
202 if(!(cs && mgr && sockid && data)) return;
204 osrfChatServer* server = (osrfChatServer*) cs;
206 osrfChatNode* node = osrfListGetIndex( server->nodeList, sockid );
209 debug_handler("Adding new connection for sockid %d", sockid );
210 node = osrfChatAddNode( server, sockid );
214 if( (osrfChatPushData( server, node, data ) == -1) ) {
215 warning_handler("Node at socket %d received bad XML, disconnecting...", sockid );
216 osrfChatSendRaw( node, OSRF_CHAT_PARSE_ERROR );
217 osrfChatRemoveNode( server, node );
221 osrfChatCleanupClients(server); /* clean up old dead clients */
225 void osrfChatSocketClosed( void* blob, int sockid ) {
227 osrfChatServer* server = (osrfChatServer*) blob;
228 osrfChatNode* node = osrfListGetIndex(server->nodeList, sockid);
229 osrfChatRemoveNode( server, node );
232 osrfChatNode* osrfChatAddNode( osrfChatServer* server, int sockid ) {
233 if(!(server && sockid)) return NULL;
234 osrfChatNode* node = osrfNewChatNode(sockid, server->domain);
235 node->parent = server;
236 node->sockid = sockid;
237 osrfListSet( server->nodeList, node, sockid );
241 void osrfChatRemoveNode( osrfChatServer* server, osrfChatNode* node ) {
242 if(!(server && node)) return;
243 socket_disconnect(server->mgr, node->sockid);
245 osrfHashRemove( server->nodeHash, node->remote );
246 osrfListRemove( server->nodeList, node->sockid ); /* this will free it */
249 int osrfChatSendRaw( osrfChatNode* node, char* msgXML ) {
250 if(!(node && msgXML)) return -1;
251 return socket_send( node->sockid, msgXML );
257 void osrfChatNodeFinish( osrfChatServer* server, osrfChatNode* node ) {
258 if(!(server && node)) return;
259 osrfChatSendRaw( node, "</stream:stream>");
260 osrfChatRemoveNode( server, node );
264 int osrfChatSend( osrfChatServer* cs, osrfChatNode* node, char* toAddr, char* fromAddr, char* msgXML ) {
265 if(!(cs && node && toAddr && msgXML)) return -1;
267 int l = strlen(toAddr);
270 jid_get_domain( toAddr, dombuf );
272 if( eq( dombuf, cs->domain ) ) { /* this is to a user we host */
274 info_handler("Sending message on local connection\nfrom: %s\nto: %s", fromAddr, toAddr );
275 osrfChatNode* tonode = osrfHashGet(cs->nodeHash, toAddr);
277 osrfChatSendRaw( tonode, msgXML );
281 /* send an error message saying we don't have this connection */
282 warning_handler("We have no connection for %s", toAddr);
283 char* xml = va_list_to_string( OSRF_CHAT_NO_RECIPIENT, toAddr, fromAddr );
284 osrfChatSendRaw( node, xml );
290 osrfChatNode* tonode = osrfHashGet(cs->nodeHash, dombuf);
292 if( tonode->state == OSRF_CHAT_STATE_CONNECTED ) {
293 debug_handler("Routing message to server %s", dombuf);
294 osrfChatSendRaw( tonode, msgXML );
297 info_handler("Received s2s message and we're still trying to connect...caching");
298 osrfListPush( tonode->msgs, strdup(msgXML) );
303 if( osrfChatInitS2S( cs, dombuf, toAddr, msgXML ) != 0 ) {
304 warning_handler("We are unable to connect to remote server %s for recipient %s", dombuf, toAddr);
305 char* xml = va_list_to_string( OSRF_CHAT_NO_RECIPIENT, toAddr, fromAddr );
306 osrfChatSendRaw( node, xml );
317 void osrfChatCacheS2SMessage( char* toAddr, char* msgXML, osrfChatNode* snode ) {
318 if(!(toAddr && msgXML)) return;
319 osrfChatS2SMessage* msg = safe_malloc(sizeof(osrfChatS2SMessage));
320 msg->toAddr = strdup(toAddr);
321 msg->msgXML = strdup(msgXML);
322 info_handler("Pushing client message onto s2s queue waiting for connect... ");
323 osrfListPush( snode->msgs, msgXML );
328 int osrfChatInitS2S( osrfChatServer* cs, char* remote, char* toAddr, char* msgXML ) {
329 if(!(cs && remote && toAddr && msgXML)) return -1;
331 info_handler("Initing server2server connection to domain %s", remote );
332 osrfChatNode* snode = osrfNewChatS2SNode( cs->domain, remote );
335 /* try to connect to the remote site */
336 snode->sockid = socket_open_tcp_client(cs->mgr, cs->s2sport, remote);
337 if(snode->sockid < 1) {
338 warning_handler("Unable to connect to remote server at %s", remote );
342 /* store the message we were supposed to deliver until we're fully connected */
343 //osrfChatCacheS2SMessage( toAddr, msgXML, snode );
344 osrfListPush( snode->msgs, strdup(msgXML) );
345 osrfHashSet(cs->nodeHash, snode, remote );
346 osrfListSet(cs->nodeList, snode, snode->sockid );
348 /* send the initial s2s request */
349 osrfChatSendRaw( snode, OSRF_CHAT_S2S_INIT );
351 debug_handler("Added new s2s node...");
358 /* commence SAX handling code */
360 int osrfChatPushData( osrfChatServer* server, osrfChatNode* node, char* data ) {
361 if(!(node && data)) return -1;
365 debug_handler("pushing data into xml parser for node %d:\n%s", node->sockid, data);
367 xmlParseChunk(node->parserCtx, data, strlen(data), 0);
370 if(__osrfChatXMLErrorOcurred) {
371 __osrfChatXMLErrorOcurred = 0;
375 /* we can't do cleanup of the XML handlers while in the middle of a
376 data push, so set flags in the data push and doe the cleanup here */
378 if(__osrfChatClientSentDisconnect) {
379 __osrfChatClientSentDisconnect = 0;
380 osrfChatNodeFinish( server, node );
388 void osrfChatStartStream( void* blob ) {
389 debug_handler("Starting new client stream...");
393 void osrfChatStartElement( void* blob, const xmlChar *name, const xmlChar **atts ) {
394 if(!(blob && name)) return;
395 osrfChatNode* node = (osrfChatNode*) blob;
398 char* nm = (char*) name;
400 debug_handler("Starting element %s with namespace %s", nm, xmlSaxAttr(atts, "xmlns") );
402 switch( node->state ) {
404 case OSRF_CHAT_STATE_NONE:
405 status = osrfChatHandleNewConnection( node, nm, atts );
408 case OSRF_CHAT_STATE_CONNECTING:
409 status = osrfChatHandleConnecting( node, nm, atts );
412 case OSRF_CHAT_STATE_CONNECTED:
413 status = osrfChatHandleConnected( node, nm, atts );
416 case OSRF_CHAT_STATE_S2S_CHALLENGE:
417 status = osrfChatHandleS2SChallenge( node, nm, atts );
420 case OSRF_CHAT_STATE_S2S_RESPONSE: /* server waiting for client response to challenge */
421 if(eq(nm, "db:result")) {
422 char* remote = xmlSaxAttr(atts, "from");
423 if(remote) node->remote = strdup(remote); /* copy off the client's id */
425 node->xmlstate |= OSRF_CHAT_STATE_INS2SRESULT;
429 case OSRF_CHAT_STATE_S2S_VERIFY: /* client : waiting for server verify message */
430 if(eq(nm, "db:verify")) {
431 char* id = xmlSaxAttr( atts, "id" );
433 char* xml = va_list_to_string( OSRF_CHAT_S2S_VERIFY_RESPONSE,
434 node->remote, node->domain, id );
435 osrfChatSendRaw( node, xml );
437 node->state = OSRF_CHAT_STATE_S2S_VERIFY_FINAL;
443 case OSRF_CHAT_STATE_S2S_VERIFY_RESPONSE: /* server waiting for client verify response */
444 case OSRF_CHAT_STATE_S2S_VERIFY_FINAL: /* client waitig for final verify */
445 status = osrfChatHandleS2SConnected( node, nm, atts );
451 osrfChatParseError( node, "We don't know how to handle the XML data received" );
454 #define CHAT_CHECK_VARS(x,y,z) if(!(x && y)) return -1; if(z) debug_handler(z);
458 int osrfChatHandleS2SConnected( osrfChatNode* node, const char* name, const xmlChar**atts ) {
459 CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SConnected" );
463 if(eq(name,"db:verify")) { /* server receives verify from client */
464 char* xml = va_list_to_string(OSRF_CHAT_S2S_VERIFY_FINAL, node->domain, node->remote );
465 osrfChatSendRaw(node, xml );
470 if(eq(name, "db:result")) {
471 /* send all the messages that we have queued for this server */
472 node->state = OSRF_CHAT_STATE_CONNECTED;
473 osrfListIterator* itr = osrfNewListIterator(node->msgs);
476 while( (xml = (char*) osrfListIteratorNext(itr)) ) {
477 xmlDocPtr doc = xmlParseMemory(xml, strlen(xml));
479 char* from = (char*) xmlGetProp(xmlDocGetRootElement(doc), BAD_CAST "from");
480 char* to = (char*) xmlGetProp(xmlDocGetRootElement(doc), BAD_CAST "to");
481 osrfChatSend( node->parent, node, to, from, xml );
482 debug_handler("Sending cached message from %s to %s", from, to);
483 xmlFree(to); xmlFree(from);
488 osrfListIteratorFree(itr);
489 osrfListFree(node->msgs);
495 info_handler("Successfully made S2S connection to %s", node->remote );
496 node->state = OSRF_CHAT_STATE_CONNECTED;
504 /** check the namespace of the stream message to see if it's a server or client connection */
505 int osrfChatHandleNewConnection( osrfChatNode* node, const char* name, const xmlChar** atts ) {
506 CHAT_CHECK_VARS(node, name, "osrfChatHandleNewConnection()");
508 if(!eq(name, "stream:stream")) return -1;
510 node->authkey = osrfChatMkAuthKey();
511 char* ns = xmlSaxAttr(atts, "xmlns");
514 if(eq(ns, "jabber:client")) { /* client connection */
516 char* domain = xmlSaxAttr( atts, "to" );
517 if(!domain) return -1;
519 if(!eq(domain, node->domain)) {
520 warning_handler("Client attempting to connect to invalid domain");
524 char* buf = va_list_to_string( OSRF_CHAT_START_STREAM, domain, node->authkey );
525 node->state = OSRF_CHAT_STATE_CONNECTING;
527 debug_handler("Server responding to connect message with\n%s\n", buf );
528 osrfChatSendRaw( node, buf );
533 /* server to server init */
534 if(eq(ns, "jabber:server")) { /* client connection */
535 info_handler("We received a new server 2 server connection, generating auth key...");
536 char* xml = va_list_to_string( OSRF_CHAT_S2S_CHALLENGE, node->authkey );
537 osrfChatSendRaw( node, xml );
539 node->state = OSRF_CHAT_STATE_S2S_RESPONSE; /* the next message should be the response */
549 char* osrfChatMkAuthKey() {
552 snprintf(keybuf, 111, "%d%d%s", (int) time(NULL), getpid(), getenv("HOSTNAME"));
553 return strdup(shahash(keybuf));
556 int osrfChatHandleConnecting( osrfChatNode* node, const char* name, const xmlChar** atts ) {
557 CHAT_CHECK_VARS(node, name, "osrfChatHandleConnecting()");
558 debug_handler("Handling connect node %s", name );
560 if(eq(name, "iq")) node->xmlstate |= OSRF_CHAT_STATE_INIQ;
561 else if(eq(name,"username")) node->xmlstate |= OSRF_CHAT_STATE_INUSERNAME;
562 else if(eq(name,"resource")) node->xmlstate |= OSRF_CHAT_STATE_INRESOURCE;
566 int osrfChatHandleConnected( osrfChatNode* node, const char* name, const xmlChar** atts ) {
567 CHAT_CHECK_VARS(node, name, "osrfChatHandleConnected()");
569 if(eq(name,"message")) {
571 /* drop the old message and start with a new one */
572 xmlNodePtr root = xmlNewNode(NULL, name);
573 xmlAddAttrs(root, atts);
574 xmlNodePtr oldRoot = xmlDocSetRootElement(node->msgDoc, root);
577 char* to = xmlSaxAttr(atts, "to");
580 node->to = strdup(to);
581 if(oldRoot) xmlFreeNode(oldRoot);
582 node->xmlstate = OSRF_CHAT_STATE_INMESSAGE;
586 /* all non "message" nodes are simply added to the message */
587 xmlNodePtr nodep = xmlNewNode(NULL, name);
588 xmlAddAttrs(nodep, atts);
589 xmlAddChild(xmlDocGetRootElement(node->msgDoc), nodep);
595 /* takes s2s secret, hashdomain, and the s2s auth token */
596 static char* osrfChatGenerateS2SKey( char* secret, char* hashdomain, char* authtoken ) {
597 if(!(secret && hashdomain && authtoken)) return NULL;
598 info_handler("Generating s2s key with auth token: %s", authtoken );
599 char* a = shahash(secret);
600 debug_handler("S2S secret hash: %s", a);
601 char* b = va_list_to_string("%s%s", a, hashdomain);
602 char* c = shahash(b);
603 debug_handler("S2S intermediate hash: %s", c);
604 char* d = va_list_to_string("%s%s", c, authtoken);
605 char* e = strdup(shahash(d));
610 int osrfChatHandleS2SChallenge( osrfChatNode* node, const char* name, const xmlChar** atts ) {
611 CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SChallenge()");
613 /* here we respond to the stream challenge */
614 if(eq(name, "stream:stream")) {
615 char* id = xmlSaxAttr(atts, "id");
617 /* we use our domain in the s2s challenge hash */
618 char* d = osrfChatGenerateS2SKey(node->parent->secret, node->domain, id );
619 char* e = va_list_to_string(OSRF_CHAT_S2S_RESPONSE, node->remote, node->domain, d );
620 info_handler("Answering s2s challenge with key: %s", e );
621 osrfChatSendRaw( node, e );
623 node->state = OSRF_CHAT_STATE_S2S_VERIFY;
632 int osrfChatHandleS2SResponse( osrfChatNode* node, const char* name, const xmlChar** atts ) {
633 CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SResponse()");
635 if(eq(name, "db:result")) {
636 node->xmlstate |= OSRF_CHAT_STATE_INS2SRESULT;
646 void osrfChatEndElement( void* blob, const xmlChar* name ) {
647 if(!(blob && name)) return;
648 osrfChatNode* node = (osrfChatNode*) blob;
650 char* nm = (char*) name;
652 if(eq(nm,"stream:stream")) {
653 osrfChatNodeFinish( node->parent, node );
657 if( node->state == OSRF_CHAT_STATE_CONNECTED ) {
658 if(eq(nm, "message")) {
660 xmlNodePtr msg = xmlDocGetRootElement(node->msgDoc);
661 if(msg && node->type == 0)
662 xmlSetProp(msg, BAD_CAST "from", BAD_CAST node->remote );
663 char* string = xmlDocToString(node->msgDoc, 0 );
665 char* from = (char*) xmlGetProp(msg, BAD_CAST "from");
666 debug_handler( "Routing message to %s\n%s\n", node->to, from, string );
667 osrfChatSend( node->parent, node, node->to, from, string );
673 if( node->state == OSRF_CHAT_STATE_CONNECTING ) {
674 if( node->xmlstate & OSRF_CHAT_STATE_INIQ ) {
677 node->xmlstate &= ~OSRF_CHAT_STATE_INIQ;
678 node->remote = va_list_to_string(
679 "%s@%s/%s", node->username, node->domain, node->resource );
681 info_handler("%s successfully logged in", node->remote );
683 debug_handler("Setting remote address to %s", node->remote );
684 osrfChatSendRaw( node, OSRF_CHAT_LOGIN_OK );
685 osrfHashSet( node->parent->nodeHash, node, node->remote );
686 node->state = OSRF_CHAT_STATE_CONNECTED;
693 void osrfChatHandleCharacter( void* blob, const xmlChar *ch, int len) {
694 if(!(blob && ch && len)) return;
695 osrfChatNode* node = (osrfChatNode*) blob;
698 debug_handler("Char Handler: state %d, xmlstate %d, chardata %s",
699 node->state, node->xmlstate, (char*) ch );
702 if( node->state == OSRF_CHAT_STATE_CONNECTING ) {
703 if( node->xmlstate & OSRF_CHAT_STATE_INIQ ) {
705 if( node->xmlstate & OSRF_CHAT_STATE_INUSERNAME ) {
706 free(node->username);
707 node->username = strndup((char*) ch, len);
708 node->xmlstate &= ~OSRF_CHAT_STATE_INUSERNAME;
711 if( node->xmlstate & OSRF_CHAT_STATE_INRESOURCE ) {
712 free(node->resource);
713 node->resource = strndup((char*) ch, len);
714 node->xmlstate &= ~OSRF_CHAT_STATE_INRESOURCE;
721 if( node->state == OSRF_CHAT_STATE_CONNECTED ) {
722 xmlNodePtr last = xmlGetLastChild(xmlDocGetRootElement(node->msgDoc));
723 xmlNodePtr txt = xmlNewTextLen(ch, len);
724 xmlAddChild(last, txt);
728 if( node->state == OSRF_CHAT_STATE_S2S_RESPONSE &&
729 (node->xmlstate & OSRF_CHAT_STATE_INS2SRESULT) ) {
731 char* key = strndup((char*) ch, len);
732 debug_handler("Got s2s key from %s : %s", node->remote, key );
733 char* e = osrfChatGenerateS2SKey(node->parent->secret, node->remote, node->authkey );
734 info_handler("\nReceived s2s key from server: %s\nKey should be: %s", key, e );
737 char* msg = va_list_to_string(OSRF_CHAT_S2S_VERIFY_REQUEST,
738 node->authkey, node->domain, node->remote, e );
739 osrfChatSendRaw(node, msg );
741 node->state = OSRF_CHAT_STATE_S2S_VERIFY_RESPONSE;
745 warning_handler("Server2Server keys do not match!");
748 /* do the hash dance again */
754 void osrfChatParseError( void* blob, const char* msg, ... ) {
756 __osrfChatXMLErrorOcurred = 1;