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