]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/router/osrf_router.c
LP#1666706: add --with-websockets-port configure option
[OpenSRF.git] / src / router / osrf_router.c
1 #include <sys/select.h>
2 #include <signal.h>
3 #include "opensrf/utils.h"
4 #include "opensrf/log.h"
5 #include "opensrf/osrf_list.h"
6 #include "opensrf/string_array.h"
7 #include "opensrf/osrf_hash.h"
8 #include "osrf_router.h"
9 #include "opensrf/transport_client.h"
10 #include "opensrf/transport_message.h"
11 #include "opensrf/osrf_message.h"
12
13 /**
14         @file osrf_router.c
15         @brief Implementation of osrfRouter.
16
17         The router opens multiple Jabber sessions for the same username and domain, one for
18         each server class.  The Jabber IDs for these sessions are distinguished by the use of
19         the class names as Jabber resource names.
20
21         For each server class there may be multiple server nodes.  Each node corresponds to a
22         listener process for a service.
23 */
24
25 /**
26         @brief Collection of server classes, with connection parameters for Jabber.
27  */
28 struct osrfRouterStruct {
29
30         /** 
31                 @brief Hash store of server classes.
32         
33                 For each entry, the key is the class name, and the corresponding datum is an
34                 osrfRouterClass.
35         */
36         osrfHash* classes;
37         osrfHashIterator* class_itr;  /**< For traversing the list of classes. */
38         char* domain;         /**< Domain name of Jabber server. */
39         char* name;           /**< Router's username for the Jabber logon. */
40         char* resource;       /**< Router's resource name for the Jabber logon. */
41         char* password;       /**< Router's password for the Jabber logon. */
42         int port;             /**< Jabber's port number. */
43         volatile sig_atomic_t stop; /**< To be set by signal handler to interrupt main loop. */
44
45         /** Array of client domains that we allow to send requests through us. */
46         osrfStringArray* trustedClients;
47         /** Array of server domains that we allow to register, etc. with us. */
48         osrfStringArray* trustedServers;
49         /** List of osrfMessages to be returned from osrfMessageDeserialize() */
50         osrfList* message_list;
51
52         transport_client* connection;
53 };
54
55 /**
56         @brief Maintains a set of server nodes belonging to the same class.
57 */
58 struct _osrfRouterClassStruct {
59         osrfRouter* router;         /**< The osrfRouter that owns this osrfRouterClass. */
60         osrfHashIterator* itr;      /**< Iterator for set of osrfRouterNodes. */
61         /**
62                 @brief Hash store of server nodes.
63
64                 The key of each entry is a node name, and the associated data is an osrfRouterNode.
65                 We install a callback for freeing the entries.
66         */
67         osrfHash* nodes;
68         /** The transport_client used for communicating with this server. */
69         transport_client* connection;
70 };
71 typedef struct _osrfRouterClassStruct osrfRouterClass;
72
73 /**
74         @brief Represents a link to a single server's inbound connection.
75 */
76 struct _osrfRouterNodeStruct {
77         char* remoteId;     /**< Send message to me via this login. */
78         int count;          /**< How many message have been sent to this node. */
79         transport_message* lastMessage;
80 };
81 typedef struct _osrfRouterNodeStruct osrfRouterNode;
82
83 static osrfRouterClass* osrfRouterAddClass( osrfRouter* router, const char* classname );
84 static void osrfRouterClassAddNode( osrfRouterClass* rclass, const char* remoteId );
85 static void osrfRouterHandleCommand( osrfRouter* router, const transport_message* msg );
86 static void osrfRouterClassHandleMessage( osrfRouter* router,
87                 osrfRouterClass* rclass, const transport_message* msg );
88 static void osrfRouterRemoveClass( osrfRouter* router, const char* classname );
89 static void osrfRouterClassRemoveNode( osrfRouter* router, const char* classname,
90                 const char* remoteId );
91 static void osrfRouterClassFree( char* classname, void* rclass );
92 static void osrfRouterNodeFree( char* remoteId, void* node );
93 static osrfRouterClass* osrfRouterFindClass( osrfRouter* router, const char* classname );
94 static osrfRouterNode* osrfRouterClassFindNode( osrfRouterClass* rclass,
95                 const char* remoteId );
96 static int _osrfRouterFillFDSet( osrfRouter* router, fd_set* set );
97 static void osrfRouterHandleIncoming( osrfRouter* router );
98 static void osrfRouterClassHandleIncoming( osrfRouter* router,
99                 const char* classname,  osrfRouterClass* class );
100 static transport_message* osrfRouterClassHandleBounce( osrfRouter* router,
101                 const char* classname, osrfRouterClass* rclass, const transport_message* msg );
102 static void osrfRouterHandleAppRequest( osrfRouter* router, const transport_message* msg );
103 static void osrfRouterRespondConnect( osrfRouter* router, const transport_message* msg,
104                 const osrfMessage* omsg );
105 static void osrfRouterProcessAppRequest( osrfRouter* router, const transport_message* msg,
106                 const osrfMessage* omsg );
107 static void osrfRouterSendAppResponse( osrfRouter* router, const transport_message* msg,
108                 const osrfMessage* omsg, const jsonObject* response );
109 static void osrfRouterHandleMethodNFound( osrfRouter* router,
110                 const transport_message* msg, const osrfMessage* omsg );
111
112 #define ROUTER_REGISTER "register"
113 #define ROUTER_UNREGISTER "unregister"
114
115 #define ROUTER_REQUEST_CLASS_LIST "opensrf.router.info.class.list"
116 #define ROUTER_REQUEST_STATS_NODE_FULL "opensrf.router.info.stats.class.node.all"
117 #define ROUTER_REQUEST_STATS_CLASS_FULL "opensrf.router.info.stats.class.all"
118 #define ROUTER_REQUEST_STATS_CLASS "opensrf.router.info.stats.class"
119 #define ROUTER_REQUEST_STATS_CLASS_SUMMARY "opensrf.router.info.stats.class.summary"
120
121 /**
122         @brief Stop the otherwise endless main loop of the router.
123         @param router Pointer to the osrfRouter to be stopped.
124
125         To be called by a signal handler.  We don't stop the loop immediately; we just set
126         a switch that the main loop checks on each iteration.
127 */
128 void router_stop( osrfRouter* router )
129 {
130         if( router )
131                 router->stop = 1;
132 }
133
134 /**
135         @brief Allocate and initialize a new osrfRouter.
136         @param domain Domain name of Jabber server.
137         @param name Router's username for the Jabber logon.
138         @param resource Router's resource name for the Jabber logon.
139         @param password Router's password for the Jabber logon.
140         @param port Jabber's port number.
141         @param trustedClients Array of client domains that we allow to send requests through us.
142         @param trustedServers Array of server domains that we allow to register, etc. with us.
143         @return Pointer to the newly allocated osrfRouter, or NULL upon error.
144
145         Don't connect to Jabber yet.  We'll do that later, upon a call to osrfRouterConnect().
146
147         The calling code is responsible for freeing the osrfRouter by calling osrfRouterFree().
148 */
149 osrfRouter* osrfNewRouter(
150                 const char* domain, const char* name,
151                 const char* resource, const char* password, int port,
152                 osrfStringArray* trustedClients, osrfStringArray* trustedServers ) {
153
154         if(!( domain && name && resource && password && port && trustedClients && trustedServers ))
155                 return NULL;
156
157         osrfRouter* router     = safe_malloc(sizeof(osrfRouter));
158         router->domain         = strdup(domain);
159         router->name           = strdup(name);
160         router->password       = strdup(password);
161         router->resource       = strdup(resource);
162         router->port           = port;
163         router->stop           = 0;
164
165         router->trustedClients = trustedClients;
166         router->trustedServers = trustedServers;
167
168
169         router->classes = osrfNewHash();
170         osrfHashSetCallback(router->classes, &osrfRouterClassFree);
171         router->class_itr = osrfNewHashIterator( router->classes );
172         router->message_list = NULL;   // We'll allocate one later
173
174         // Prepare to connect to Jabber, as a non-component, over TCP (not UNIX domain).
175         router->connection = client_init( domain, port, NULL, 0 );
176
177         return router;
178 }
179
180 /**
181         @brief Connect to Jabber.
182         @param router Pointer to the osrfRouter to connect to Jabber.
183         @return 0 if successful, or -1 on error.
184
185         Allow up to 10 seconds for the logon to succeed.
186
187         We connect over TCP (not over a UNIX domain), as a non-component.
188 */
189 int osrfRouterConnect( osrfRouter* router ) {
190         if(!router) return -1;
191         int ret = client_connect( router->connection, router->name,
192                         router->password, router->resource, 10, AUTH_DIGEST );
193         if( ret == 0 ) return -1;
194         return 0;
195 }
196
197 /**
198         @brief Enter endless loop to receive and respond to input.
199         @param router Pointer to the osrfRouter that's looping.
200
201         On each iteration: wait for incoming messages to arrive on any of our sockets -- i.e.
202         either the top level socket belonging to the router or any of the lower level sockets
203         belonging to the classes.  React to the incoming activity as needed.
204
205         We don't exit the loop until we receive a signal to stop, or until we encounter an error.
206 */
207 void osrfRouterRun( osrfRouter* router ) {
208         if(!(router && router->classes)) return;
209
210         int routerfd = client_sock_fd( router->connection );
211         int selectret = 0;
212
213         // Loop until a signal handler sets router->stop
214         while( ! router->stop ) {
215
216                 fd_set set;
217                 int maxfd = _osrfRouterFillFDSet( router, &set );
218
219                 // Wait indefinitely for an incoming message
220                 if( (selectret = select(maxfd + 1, &set, NULL, NULL, NULL)) < 0 ) {
221                         if( EINTR == errno ) {
222                                 if( router->stop ) {
223                                         osrfLogInfo(OSRF_LOG_MARK, "Router shutting down");
224                                         break;
225                                 }
226                                 else
227                                         continue;    // Irrelevant signal; ignore it
228                         } else {
229                                 osrfLogWarning( OSRF_LOG_MARK, "Top level select call failed with errno %d: %s",
230                                                 errno, strerror( errno ) );
231                                 break;
232                         }
233                 }
234
235                 /* see if there is a top level router message */
236                 if( FD_ISSET(routerfd, &set) ) {
237                         osrfLogDebug( OSRF_LOG_MARK, "Top router socket is active: %d", routerfd );
238                         osrfRouterHandleIncoming( router );
239                 }
240
241                 /* Check each of the connected classes and see if they have data to route */
242                 osrfRouterClass* class;
243                 osrfHashIterator* itr = router->class_itr;  // remove a layer of indirection
244                 osrfHashIteratorReset( itr );
245
246                 while( (class = osrfHashIteratorNext(itr)) ) {   // for each class
247
248                         const char* classname = osrfHashIteratorKey(itr);
249
250                         if( classname ) {
251
252                                 osrfLogDebug( OSRF_LOG_MARK, "Checking %s for activity...", classname );
253
254                                 int sockfd = client_sock_fd( class->connection );
255                                 if(FD_ISSET( sockfd, &set )) {
256                                         osrfLogDebug( OSRF_LOG_MARK, "Socket is active: %d", sockfd );
257                                         osrfRouterClassHandleIncoming( router, classname, class );
258                                 }
259                         }
260                 } // end while
261         } // end while
262 }
263
264
265 /**
266         @brief Handle incoming requests to the router.
267         @param router Pointer to the osrfRouter.
268
269         Read all available input messages from the top-level transport_client.  For each one:
270         if the domain of the sender's Jabber id is on the list of approved domains, pass the
271         message to osrfRouterHandleCommand() (if there's a message) or osrfRouterHandleAppRequest()
272         (if there isn't).  If the domain is @em not on the approved list, log a warning and
273         discard the message.
274 */
275 static void osrfRouterHandleIncoming( osrfRouter* router ) {
276         if(!router) return;
277
278         transport_message* msg = NULL;
279
280         while( (msg = client_recv( router->connection, 0 )) ) {  // for each message
281
282                 if( msg->sender ) {
283
284                         osrfLogDebug(OSRF_LOG_MARK,
285                                 "osrfRouterHandleIncoming(): investigating message from %s", msg->sender);
286
287                         /* if the server is not on a trusted domain, drop the message */
288                         int len = strlen(msg->sender) + 1;
289                         char domain[len];
290                         jid_get_domain( msg->sender, domain, len - 1 );
291
292                         if(osrfStringArrayContains( router->trustedServers, domain)) {
293
294                                 // If there's a command, obey it.  Otherwise, treat
295                                 // the message as an app session level request.
296                                 if( msg->router_command && *msg->router_command )
297                                         osrfRouterHandleCommand( router, msg );
298                                 else
299                                         osrfRouterHandleAppRequest( router, msg );
300                         }
301                         else
302                                 osrfLogWarning( OSRF_LOG_MARK, 
303                                                 "Received message from un-trusted server domain %s", msg->sender);
304                 }
305
306                 message_free(msg);
307         }
308 }
309
310 /**
311         @brief Handle all available incoming messages for a router class.
312         @param router Pointer to the osrfRouter.
313         @param classname Class name.
314         @param class Pointer to the osrfRouterClass.
315
316         For each message: if the sender is on a trusted domain, process the message.
317 */
318 static void osrfRouterClassHandleIncoming( osrfRouter* router, const char* classname,
319                 osrfRouterClass* class ) {
320         if(!(router && class)) return;
321
322         transport_message* msg;
323         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleIncoming()");
324
325         // For each incoming message for this class:
326         while( (msg = client_recv( class->connection, 0 )) ) {
327
328                 // Save the transaction id so that we can incorporate it
329                 // into any relevant messages
330                 osrfLogSetXid(msg->osrf_xid);
331
332                 if( msg->sender ) {
333
334                         osrfLogDebug(OSRF_LOG_MARK,
335                                 "osrfRouterClassHandleIncoming(): investigating message from %s", msg->sender);
336
337                         /* if the client is not from a trusted domain, drop the message */
338                         int len = strlen(msg->sender) + 1;
339                         char domain[len];
340                         jid_get_domain( msg->sender, domain, len - 1 );
341
342                         if(osrfStringArrayContains( router->trustedClients, domain )) {
343
344                                 if( msg->is_error )  {
345
346                                         // A previous message bounced.  Try to send a clone of it to a
347                                         // different node of the same class.
348                                         
349                                         // First make a local copy of the class name.  If the class gets
350                                         // deleted, the classname parameter becomes invalid.
351                                         char classname_copy[ strlen( classname ) + 1 ];
352                                         strcpy( classname_copy, classname );
353
354                                         transport_message* bouncedMessage = osrfRouterClassHandleBounce(
355                                                         router, classname, class, msg );
356                                         /* handle bounced message */
357                                         if( !bouncedMessage ) {
358                                                 /* we have no one to send the requested message to on our domain */
359
360                         /* XXX */
361                         /* XXX Here's where we plug in peer domains, inserting this brick into a wall */
362                         /* XXX */
363
364                                                 message_free( msg );
365                                                 osrfLogClearXid();
366                                                 
367                                                 // See if the class still exists
368                                                 if( osrfHashGet( router->classes, classname_copy ) )
369                                                         continue;   // It does; keep going
370                                                 else
371                                                         break;      // It doesn't; don't try to read from it any more
372                                         }
373                                         osrfRouterClassHandleMessage( router, class, bouncedMessage );
374                                         message_free( bouncedMessage );
375                                 } else
376                                         osrfRouterClassHandleMessage( router, class, msg );
377
378                         } else {
379                                 osrfLogWarning( OSRF_LOG_MARK, 
380                                                 "Received client message from untrusted client domain %s", domain );
381                         }
382                 }
383
384                 message_free( msg );
385                 osrfLogClearXid();  // We're done with this transaction id
386         }
387 }
388
389 /**
390         @brief Handle a top level router command.
391         @param router Pointer to the osrfRouter.
392         @param msg Pointer to the transport_message to be handled.
393
394         Currently supported commands:
395         - "register" -- Add a server class and/or a server node to our lists.
396         - "unregister" -- Remove a node from a class, and the class as well if no nodes are
397         left for it.
398 */
399 static void osrfRouterHandleCommand( osrfRouter* router, const transport_message* msg ) {
400         if(!(router && msg && msg->router_class)) return;
401
402         if( !strcmp( msg->router_command, ROUTER_REGISTER ) ) {
403
404                 osrfLogInfo( OSRF_LOG_MARK, "Registering class %s", msg->router_class );
405
406                 // Add the server class to the list, if it isn't already there
407                 osrfRouterClass* class = osrfRouterFindClass( router, msg->router_class );
408                 if(!class)
409                         class = osrfRouterAddClass( router, msg->router_class );
410
411                 // Add the node to the osrfRouterClass's list, if it isn't already there
412                 if(class && ! osrfRouterClassFindNode( class, msg->sender ) )
413                         osrfRouterClassAddNode( class, msg->sender );
414
415         } else if( !strcmp( msg->router_command, ROUTER_UNREGISTER ) ) {
416
417                 if( msg->router_class && *msg->router_class ) {
418                         osrfLogInfo( OSRF_LOG_MARK, "Unregistering router class %s", msg->router_class );
419                         osrfRouterClassRemoveNode( router, msg->router_class, msg->sender );
420                 }
421         }
422 }
423
424
425 /**
426         @brief Add an osrfRouterClass to a router, and open a connection for it.
427         @param router Pointer to the osrfRouter.
428         @param classname The name of the class this node handles.
429         @return A pointer to the new osrfRouterClass, or NULL upon error.
430
431         Open a Jabber session to be used for this server class.  The Jabber ID incorporates the
432         class name as the resource name.
433 */
434 static osrfRouterClass* osrfRouterAddClass( osrfRouter* router, const char* classname ) {
435         if(!(router && router->classes && classname)) return NULL;
436
437         osrfRouterClass* class = safe_malloc(sizeof(osrfRouterClass));
438         class->nodes = osrfNewHash();
439         class->itr = osrfNewHashIterator(class->nodes);
440         osrfHashSetCallback(class->nodes, &osrfRouterNodeFree);
441         class->router = router;
442
443         class->connection = client_init( router->domain, router->port, NULL, 0 );
444
445         if(!client_connect( class->connection, router->name,
446                         router->password, classname, 10, AUTH_DIGEST ) ) {
447                 // Cast away the constness of classname.  Though ugly, this
448                 // cast is benign because osrfRouterClassFree doesn't actually
449                 // write through the pointer.  We can't readily change its
450                 // signature because it is used for a function pointer, and
451                 // we would have to change other signatures the same way.
452                 osrfRouterClassFree( (char *) classname, class );
453                 return NULL;
454         }
455
456         osrfHashSet( router->classes, class, classname );
457         return class;
458 }
459
460
461 /**
462         @brief Add a new server node to an osrfRouterClass.
463         @param rclass Pointer to the osrfRouterClass to which we are to add the node.
464         @param remoteId The remote login of the osrfRouterNode.
465 */
466 static void osrfRouterClassAddNode( osrfRouterClass* rclass, const char* remoteId ) {
467         if(!(rclass && rclass->nodes && remoteId)) return;
468
469         osrfLogInfo( OSRF_LOG_MARK, "Adding router node for remote id %s", remoteId );
470
471         osrfRouterNode* node = safe_malloc(sizeof(osrfRouterNode));
472         node->count = 0;
473         node->lastMessage = NULL;
474         node->remoteId = strdup(remoteId);
475
476         osrfHashSet( rclass->nodes, node, remoteId );
477 }
478
479 /**
480         @brief Handle an input message representing a Jabber error stanza.
481         @param router Pointer to the current osrfRouter.
482         @param classname Name of the class to which the error stanza was sent.
483         @param rclass Pointer to the osrfRouterClass to which the error stanza was sent.
484         @param msg Pointer to the transport_message representing the error stanza.
485         @return Pointer to a newly allocated transport_message; or NULL (see remarks).
486
487         The presumption is that the relevant node is dead.  If another node is available for
488         the same class, then remove the dead one, create a clone of the message to be sent
489         elsewhere, and return a pointer to it.  If there is no other node for the same class,
490         send a cancel message back to the sender, remove both the node and the class it belongs
491         to, and return NULL.  If we can't even do that because the entire class is dead, log
492         a message to that effect and return NULL.
493 */
494 static transport_message* osrfRouterClassHandleBounce( osrfRouter* router,
495                 const char* classname, osrfRouterClass* rclass, const transport_message* msg ) {
496
497         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleBounce()");
498
499         osrfLogInfo( OSRF_LOG_MARK, "Received network layer error message from %s", msg->sender );
500         osrfRouterNode* node = osrfRouterClassFindNode( rclass, msg->sender );
501         if( ! node ) {
502                 osrfLogInfo( OSRF_LOG_MARK,
503                         "network error occurred after we removed the class.. ignoring");
504                 return NULL;
505         }
506
507         if( osrfHashGetCount(rclass->nodes) == 1 ) { /* the last node is dead */
508
509                 if( node->lastMessage ) {
510                         osrfLogWarning( OSRF_LOG_MARK,
511                                         "We lost the last node in the class, responding with error and removing...");
512
513                         transport_message* error = message_init(
514                                 node->lastMessage->body, node->lastMessage->subject,
515                                 node->lastMessage->thread, node->lastMessage->router_from,
516                                 node->lastMessage->recipient );
517                         message_set_osrf_xid(error, node->lastMessage->osrf_xid);
518                         set_msg_error( error, "cancel", 501 );
519
520                         /* send the error message back to the original sender */
521                         client_send_message( rclass->connection, error );
522                         message_free( error );
523                 }
524
525                 /* remove the dead node */
526                 osrfRouterClassRemoveNode( router, classname, msg->sender);
527                 return NULL;
528
529         } else {
530
531                 transport_message* lastSent = NULL;
532
533                 if( node->lastMessage ) {
534                         osrfLogDebug( OSRF_LOG_MARK, "Cloning lastMessage so next node can send it");
535                         lastSent = message_init( node->lastMessage->body,
536                                 node->lastMessage->subject, node->lastMessage->thread, "",
537                                 node->lastMessage->router_from );
538                         message_set_router_info( lastSent, node->lastMessage->router_from,
539                                 NULL, NULL, NULL, 0 );
540                         message_set_osrf_xid( lastSent, node->lastMessage->osrf_xid );
541                 }
542
543                 /* remove the dead node */
544                 osrfRouterClassRemoveNode( router, classname, msg->sender);
545                 return lastSent;
546         }
547 }
548
549
550 /**
551         @brief Forward a class-level message to a listener for the corresponding service.
552         @param router Pointer to the current osrfRouter.
553         @param rclass Pointer to the class to which the message is directed.
554         @param msg Pointer to the message to be forwarded.
555
556         Pick a node for the specified class, and forward the message to it.
557
558         We use an iterator, stored with the class, to maintain a position in the class's list
559         of nodes.  Advance the iterator to pick the next node, and if we reach the end, go
560         back to the beginning of the list.
561 */
562 static void osrfRouterClassHandleMessage(
563                 osrfRouter* router, osrfRouterClass* rclass, const transport_message* msg ) {
564         if(!(router && rclass && msg)) return;
565
566         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleMessage()");
567
568         // Pick a node, in a round-robin.
569         osrfRouterNode* node = osrfHashIteratorNext( rclass->itr );
570         if(!node) {   // wrap around to the beginning of the list
571                 osrfHashIteratorReset(rclass->itr);
572                 node = osrfHashIteratorNext( rclass->itr );
573         }
574
575         if(node) {  // should always be true -- no class without a node
576
577                 // Build a transport message
578                 transport_message* new_msg = message_init( msg->body,
579                                 msg->subject, msg->thread, node->remoteId, msg->sender );
580                 message_set_router_info( new_msg, msg->sender, NULL, NULL, NULL, 0 );
581                 message_set_osrf_xid( new_msg, msg->osrf_xid );
582
583                 osrfLogInfo( OSRF_LOG_MARK,  "Routing message:\nfrom: [%s]\nto: [%s]",
584                                 new_msg->router_from, new_msg->recipient );
585
586                 // Save it for possible future reference
587                 message_free( node->lastMessage );
588                 node->lastMessage = new_msg;
589
590                 // Send it
591                 if ( client_send_message( rclass->connection, new_msg ) == 0 )
592                         node->count++;
593
594                 else {
595                         message_prepare_xml(new_msg);
596                         osrfLogWarning( OSRF_LOG_MARK, "Error sending message from %s to %s\n%s",
597                                         new_msg->sender, new_msg->recipient, new_msg->msg_xml );
598                 }
599                 // We don't free new_msg here because we saved it as node->lastMessage.
600         }
601 }
602
603
604 /**
605         @brief Remove a given osrfRouterClass from an osrfRouter
606         @param router Pointer to the osrfRouter.
607         @param classname The name of the class to be removed.
608
609         Delete an osrfRouterClass from the router's list of classes.  Indirectly (via a callback
610         function installed in the osrfHash), free the osrfRouterClass and any associated nodes.
611 */
612 static void osrfRouterRemoveClass( osrfRouter* router, const char* classname ) {
613         if( router && router->classes && classname ) {
614                 osrfLogInfo( OSRF_LOG_MARK, "Removing router class %s", classname );
615                 osrfHashRemove( router->classes, classname );
616         }
617 }
618
619
620 /**
621         @brief Remove a node from a class.  If the class thereby becomes empty, remove it as well.
622         @param router Pointer to the current osrfRouter.
623         @param classname Class name.
624         @param remoteId Identifier for the node to be removed.
625 */
626 static void osrfRouterClassRemoveNode(
627                 osrfRouter* router, const char* classname, const char* remoteId ) {
628
629         if(!(router && router->classes && classname && remoteId))  // sanity check
630                 return;
631
632         osrfLogInfo( OSRF_LOG_MARK, "Removing router node %s", remoteId );
633
634         osrfRouterClass* class = osrfRouterFindClass( router, classname );
635         if( class ) {
636                 osrfHashRemove( class->nodes, remoteId );
637                 if( osrfHashGetCount(class->nodes) == 0 ) {
638                         osrfRouterRemoveClass( router, classname );
639                 }
640         }
641 }
642
643
644 /**
645         @brief Free a router class object.
646         @param classname Class name (not used).
647         @param c Pointer to the osrfRouterClass, cast to a void pointer.
648
649         This function is invoked as a callback when we remove an osrfRouterClass from the
650         router's list of classes.
651 */
652 static void osrfRouterClassFree( char* classname, void* c ) {
653         if( !c )
654                 return;
655         osrfRouterClass* rclass = (osrfRouterClass*) c;
656         client_disconnect( rclass->connection );
657         client_free( rclass->connection );
658
659         osrfHashIteratorReset( rclass->itr );
660         osrfRouterNode* node;
661
662         while( (node = osrfHashIteratorNext(rclass->itr)) )
663                 osrfHashRemove( rclass->nodes, node->remoteId );
664
665         osrfHashIteratorFree(rclass->itr);
666         osrfHashFree(rclass->nodes);
667
668         free(rclass);
669 }
670
671 /**
672         @brief Free an osrfRouterNode.
673         @param remoteId Jabber ID of node (not used).
674         @param n Pointer to the osrfRouterNode to be freed, cast to a void pointer.
675
676         This is a callback installed in an osrfHash (the nodes member of an osrfRouterClass).
677 */
678 static void osrfRouterNodeFree( char* remoteId, void* n ) {
679         if(!n) return;
680         osrfRouterNode* node = (osrfRouterNode*) n;
681         free(node->remoteId);
682         message_free(node->lastMessage);
683         free(node);
684 }
685
686
687 /**
688         @brief Free an osrfRouter and everything it owns.
689         @param router Pointer to the osrfRouter to be freed.
690
691         The osrfRouterClasses and osrfRouterNodes are freed by callback functions installed in
692         the osrfHashes.
693 */
694 void osrfRouterFree( osrfRouter* router ) {
695         if(!router) return;
696
697         osrfHashIteratorFree( router->class_itr);
698         osrfHashFree(router->classes);
699         free(router->domain);
700         free(router->name);
701         free(router->resource);
702         free(router->password);
703
704         osrfStringArrayFree( router->trustedClients );
705         osrfStringArrayFree( router->trustedServers );
706         osrfListFree( router->message_list );
707
708         client_free( router->connection );
709         free(router);
710 }
711
712
713 /**
714         @brief Given a class name, find the corresponding osrfRouterClass.
715         @param router Pointer to the osrfRouter that owns the osrfRouterClass.
716         @param classname Name of the class.
717         @return Pointer to a matching osrfRouterClass if found, or NULL if not.
718 */
719 static osrfRouterClass* osrfRouterFindClass( osrfRouter* router, const char* classname ) {
720         if(!( router && router->classes && classname )) return NULL;
721         return (osrfRouterClass*) osrfHashGet( router->classes, classname );
722 }
723
724
725 /**
726         @brief Find a given node for a given class.
727         @param rclass Pointer to the osrfRouterClass in which to search.
728         @param remoteId Jabber ID of the node for which to search.
729         @return Pointer to the matching osrfRouterNode, if found; otherwise NULL.
730 */
731 static osrfRouterNode* osrfRouterClassFindNode( osrfRouterClass* rclass,
732                 const char* remoteId ) {
733         if(!(rclass && remoteId))  return NULL;
734         return (osrfRouterNode*) osrfHashGet( rclass->nodes, remoteId );
735 }
736
737
738 /**
739         @brief Fill an fd_set with all the sockets owned by the osrfRouter.
740         @param router Pointer to the osrfRouter whose sockets are to be used.
741         @param set Pointer to the fd_set that is to be filled.
742         @return The largest file descriptor loaded into the fd_set; or -1 upon error.
743
744         There's one socket for the osrfRouter as a whole, and one for each osrfRouterClass
745         that belongs to it.  We load them all.
746 */
747 static int _osrfRouterFillFDSet( osrfRouter* router, fd_set* set ) {
748         if(!(router && router->classes && set)) return -1;
749
750         FD_ZERO(set);
751         int maxfd = client_sock_fd( router->connection );
752         FD_SET(maxfd, set);
753
754         int sockid;
755
756         osrfRouterClass* class = NULL;
757         osrfHashIterator* itr = router->class_itr;
758         osrfHashIteratorReset( itr );
759
760         while( (class = osrfHashIteratorNext(itr)) ) {
761                 const char* classname = osrfHashIteratorKey(itr);
762
763                 if( classname && (class = osrfRouterFindClass( router, classname )) ) {
764                         sockid = client_sock_fd( class->connection );
765
766                         if( osrfUtilsCheckFileDescriptor( sockid ) ) {
767
768                                 osrfLogWarning(OSRF_LOG_MARK,
769                                         "Removing router class '%s' because of a bad top-level file descriptor [%d]",
770                                         classname, sockid );
771                                 osrfRouterRemoveClass( router, classname );
772
773                         } else {
774                                 if( sockid > maxfd ) maxfd = sockid;
775                                 FD_SET(sockid, set);
776                         }
777                 }
778         }
779
780         return maxfd;
781 }
782
783 /**
784         @brief Handler a router-level message that isn't a command; presumed to be an app request.
785         @param router Pointer to the current osrfRouter.
786         @param msg Pointer to the incoming message.
787
788         The body of the transport_message is a JSON string, specifically an JSON array.
789         Translate the JSON into a series of osrfMessages, one for each element of the JSON
790         array.  Process each osrfMessage in turn.  Each message is either a CONNECT or a
791         REQUEST.
792 */
793 static void osrfRouterHandleAppRequest( osrfRouter* router, const transport_message* msg ) {
794
795         // Translate the JSON into a list of osrfMessages
796         router->message_list = osrfMessageDeserialize( msg->body, router->message_list );
797         const osrfMessage* omsg = NULL;
798
799         // Process each osrfMessage
800         unsigned int i;
801         for( i = 0; i < router->message_list->size; ++i ) {
802
803                 omsg = osrfListGetIndex( router->message_list, i );
804                 if( omsg ) {
805
806                         switch( omsg->m_type ) {
807
808                                 case CONNECT:
809                                         osrfRouterRespondConnect( router, msg, omsg );
810                                         break;
811
812                                 case REQUEST:
813                                         osrfRouterProcessAppRequest( router, msg, omsg );
814                                         break;
815
816                                 default:
817                                         break;
818                         }
819                 }
820         }
821         osrfListClear( router->message_list );
822
823         return;
824 }
825
826 /**
827         @brief Respond to a CONNECT message.
828         @param router Pointer to the current osrfRouter.
829         @param msg Pointer to the transport_message that the osrfMessage came from.
830         @param omsg Pointer to the osrfMessage to be processed.
831
832         An application is trying to connect to the router.  Reply with a STATUS message
833         signifying success.
834
835         "CONNECT" is a bit of a misnomer.  We don't establish a stateful session; i.e. we
836         don't retain any memory of this message.  We just confirm that the router is alive,
837         and that the client has a good address for it.
838 */
839 static void osrfRouterRespondConnect( osrfRouter* router, const transport_message* msg,
840                 const osrfMessage* omsg ) {
841         if(!(router && msg && omsg))
842                 return;
843
844         osrfLogDebug( OSRF_LOG_MARK, "router received a CONNECT message from %s", msg->sender );
845
846         // Build a success message
847         osrfMessage* success = osrf_message_init( STATUS, omsg->thread_trace, omsg->protocol );
848         osrf_message_set_status_info(
849                 success, "osrfConnectStatus", "Connection Successful", OSRF_STATUS_OK );
850
851         // Translate the success message into JSON,
852         // and then package the JSON in a transport message
853         char* data = osrf_message_serialize(success);
854         osrfMessageFree( success );
855         transport_message* return_msg = message_init(
856                 data,           // message payload, translated from osrfMessage
857                 "",             // no subject
858                 msg->thread,    // same thread
859                 msg->sender,    // destination (client's Jabber ID)
860                 ""              // don't send our address; client already has it
861         );
862         free( data );
863
864         client_send_message( router->connection, return_msg );
865         message_free( return_msg );
866 }
867
868
869 /**
870         @brief Respond to a REQUEST message.
871         @param router Pointer to the current osrfRouter.
872         @param msg Pointer to the transport_message that the osrfMessage came from.
873         @param omsg Pointer to the osrfMessage to be processed.
874
875         Respond to an information request from an application.  Most types of request involve
876         one or more counts of messages successfully routed.
877
878         Request types currently supported:
879         - "opensrf.router.info.class.list" -- list of class names.
880         - "opensrf.router.info.stats.class.summary" -- total count for a specified class.
881         - "opensrf.router.info.stats.class" -- count for every node of a specified class.
882         - "opensrf.router.info.stats.class.all" -- count for every node of every class.
883         - "opensrf.router.info.stats.class.node.all" -- total count for every class.
884 */
885 static void osrfRouterProcessAppRequest( osrfRouter* router, const transport_message* msg,
886                 const osrfMessage* omsg ) {
887
888         if(!(router && msg && omsg && omsg->method_name))
889                 return;
890
891         osrfLogInfo( OSRF_LOG_MARK, "Router received app request: %s", omsg->method_name );
892
893         // Branch on the request type.  Build a jsonObject as an answer to the request.
894         jsonObject* jresponse = NULL;
895         if(!strcmp( omsg->method_name, ROUTER_REQUEST_CLASS_LIST )) {
896
897                 // Prepare an array of class names.
898                 int i;
899                 jresponse = jsonNewObjectType(JSON_ARRAY);
900
901                 osrfStringArray* keys = osrfHashKeys( router->classes );
902                 for( i = 0; i != keys->size; i++ )
903                         jsonObjectPush( jresponse, jsonNewObject(osrfStringArrayGetString( keys, i )) );
904                 osrfStringArrayFree(keys);
905
906         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_SUMMARY )) {
907
908                 // Prepare a count of all the messages successfully routed for a given class.
909                 int count = 0;
910
911                 // class name is the first parameter
912                 const char* classname = jsonObjectGetString( jsonObjectGetIndex( omsg->_params, 0 ) );
913                 if (!classname)
914                         return;
915
916                 osrfRouterClass* class = osrfHashGet(router->classes, classname);
917
918                 // For each node: add the count to the total.
919                 osrfRouterNode* node;
920                 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
921                 while( (node = osrfHashIteratorNext(node_itr)) ) {
922                         count += node->count;
923                         // jsonObjectSetKey( class_res, node->remoteId, 
924                         //       jsonNewNumberObject( (double) node->count ) );
925                 }
926                 osrfHashIteratorFree(node_itr);
927
928                 jresponse = jsonNewNumberObject( (double) count );
929
930         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS )) {
931
932                 // Prepare a hash for a given class.  Key: the remoteId of a node.  Datum: the
933                 // number of messages successfully routed for that node.
934
935                 // class name is the first parameter
936                 const char* classname = jsonObjectGetString( jsonObjectGetIndex( omsg->_params, 0 ) );
937                 if (!classname)
938                         return;
939
940                 jresponse = jsonNewObjectType(JSON_HASH);
941                 osrfRouterClass* class = osrfHashGet(router->classes, classname);
942
943                 // For each node: get the count and store it in the hash.
944                 osrfRouterNode* node;
945                 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
946                 while( (node = osrfHashIteratorNext(node_itr)) ) {
947                         jsonObjectSetKey( jresponse, node->remoteId,
948                                         jsonNewNumberObject( (double) node->count ) );
949                 }
950                 osrfHashIteratorFree(node_itr);
951
952         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_FULL )) {
953
954                 // Prepare a hash of hashes, giving the message counts for each node for each class.
955
956                 osrfRouterClass* class;
957                 osrfRouterNode* node;
958                 jresponse = jsonNewObjectType(JSON_HASH);  // Key: class name.
959
960                 // Traverse the list of classes.
961                 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
962                 while( (class = osrfHashIteratorNext(class_itr)) ) {
963
964                         jsonObject* class_res = jsonNewObjectType(JSON_HASH);  // Key: remoteId of node.
965                         const char* classname = osrfHashIteratorKey(class_itr);
966
967                         // Traverse the list of nodes for the current class.
968                         osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
969                         while( (node = osrfHashIteratorNext(node_itr)) ) {
970                                 jsonObjectSetKey( class_res, node->remoteId,
971                                                 jsonNewNumberObject( (double) node->count ) );
972                         }
973                         osrfHashIteratorFree(node_itr);
974
975                         jsonObjectSetKey( jresponse, classname, class_res );
976                 }
977
978                 osrfHashIteratorFree(class_itr);
979
980         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_NODE_FULL )) {
981
982                 // Prepare a hash. Key: class name.  Datum: total number of successfully routed
983                 // messages routed for nodes of that class.
984
985                 osrfRouterClass* class;
986                 osrfRouterNode* node;
987                 jresponse = jsonNewObjectType(JSON_HASH);
988
989                 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
990                 while( (class = osrfHashIteratorNext(class_itr)) ) {  // For each class
991
992                         int count = 0;
993                         const char* classname = osrfHashIteratorKey(class_itr);
994
995                         osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
996                         while( (node = osrfHashIteratorNext(node_itr)) ) {  // For each node
997                                 count += node->count;
998                         }
999                         osrfHashIteratorFree(node_itr);
1000
1001                         jsonObjectSetKey( jresponse, classname, jsonNewNumberObject( (double) count ) );
1002                 }
1003
1004                 osrfHashIteratorFree(class_itr);
1005
1006         } else {  // None of the above
1007
1008                 osrfRouterHandleMethodNFound( router, msg, omsg );
1009                 return;
1010         }
1011
1012         // Send the result back to the requester.
1013         osrfRouterSendAppResponse( router, msg, omsg, jresponse );
1014         jsonObjectFree(jresponse);
1015 }
1016
1017
1018 /**
1019         @brief Respond to an invalid REQUEST message.
1020         @param router Pointer to the current osrfRouter.
1021         @param msg Pointer to the transport_message that contained the REQUEST message.
1022         @param omsg Pointer to the osrfMessage that contained the REQUEST.
1023 */
1024 static void osrfRouterHandleMethodNFound( osrfRouter* router,
1025                 const transport_message* msg, const osrfMessage* omsg ) {
1026
1027         // Create an exception message
1028         osrfMessage* err = osrf_message_init( STATUS, omsg->thread_trace, 1 );
1029         osrf_message_set_status_info( err,
1030                         "osrfMethodException", "Router method not found", OSRF_STATUS_NOTFOUND );
1031
1032         // Translate it into JSON
1033         char* data =  osrf_message_serialize(err);
1034         osrfMessageFree( err );
1035
1036         // Wrap the JSON up in a transport_message
1037         transport_message* tresponse = message_init(
1038                         data, "", msg->thread, msg->sender, msg->recipient );
1039         free(data);
1040
1041         // Send it
1042         client_send_message( router->connection, tresponse );
1043         message_free( tresponse );
1044 }
1045
1046
1047 /**
1048         @brief Send a response to a router REQUEST message.
1049         @param router Pointer to the current osrfRouter.
1050         @param msg Pointer to the transport_message that contained the REQUEST message.
1051         @param omsg Pointer to the osrfMessage that contained the REQUEST.
1052         @param response Pointer to the jsonObject that constitutes the response.
1053 */
1054 static void osrfRouterSendAppResponse( osrfRouter* router, const transport_message* msg,
1055                 const osrfMessage* omsg, const jsonObject* response ) {
1056
1057         if( response ) { /* send the response message */
1058
1059                 // Turn the jsonObject into JSON, and load it into an osrfMessage
1060                 osrfMessage* oresponse = osrf_message_init(
1061                                 RESULT, omsg->thread_trace, omsg->protocol );
1062
1063                 char* json = jsonObjectToJSON(response);
1064                 osrf_message_set_result_content( oresponse, json );
1065                 free(json);
1066
1067                 // Package the osrfMessage into a transport_message, and send it
1068                 char* data =  osrf_message_serialize(oresponse);
1069                 osrfMessageFree(oresponse);
1070                 osrfLogDebug( OSRF_LOG_MARK,  "Responding to client app request with data: \n%s\n", data );
1071
1072                 transport_message* tresponse = message_init(
1073                                 data, "", msg->thread, msg->sender, msg->recipient );
1074                 free(data);
1075
1076                 client_send_message(router->connection, tresponse );
1077                 message_free(tresponse);
1078         }
1079
1080         /* now send the 'request complete' message */
1081         osrfMessage* status = osrf_message_init( STATUS, omsg->thread_trace, 1);
1082         osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
1083                         OSRF_STATUS_COMPLETE );
1084         char* statusdata = osrf_message_serialize(status);
1085         osrfMessageFree(status);
1086
1087         transport_message* sresponse = message_init(
1088                         statusdata, "", msg->thread, msg->sender, msg->recipient );
1089         free(statusdata);
1090
1091         client_send_message(router->connection, sresponse );
1092         message_free(sresponse);
1093 }