]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/router/osrf_router.c
31459662636705390344ff8af0b949ba7b20ef07
[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
207                 // Wait indefinitely for an incoming message
208                 if( (selectret = select(maxfd + 1, &set, NULL, NULL, NULL)) < 0 ) {
209                         if( EINTR == errno ) {
210                                 if( router->stop ) {
211                                         osrfLogWarning( OSRF_LOG_MARK, "Top level select call interrupted by signal" );
212                                         break;
213                                 }
214                                 else
215                                         continue;    // Irrelevant signal; ignore it
216                         } else {
217                                 osrfLogWarning( OSRF_LOG_MARK, "Top level select call failed with errno %d: %s",
218                                                 errno, strerror( errno ) );
219                                 break;
220                         }
221                 }
222
223                 /* see if there is a top level router message */
224                 if( FD_ISSET(routerfd, &set) ) {
225                         osrfLogDebug( OSRF_LOG_MARK, "Top router socket is active: %d", routerfd );
226                         osrfRouterHandleIncoming( router );
227                 }
228
229                 /* Check each of the connected classes and see if they have data to route */
230                 osrfRouterClass* class;
231                 osrfHashIterator* itr = router->class_itr;  // remove a layer of indirection
232                 osrfHashIteratorReset( itr );
233
234                 while( (class = osrfHashIteratorNext(itr)) ) {   // for each class
235
236                         const char* classname = osrfHashIteratorKey(itr);
237
238                         if( classname ) {
239
240                                 osrfLogDebug( OSRF_LOG_MARK, "Checking %s for activity...", classname );
241
242                                 int sockfd = client_sock_fd( class->connection );
243                                 if(FD_ISSET( sockfd, &set )) {
244                                         osrfLogDebug( OSRF_LOG_MARK, "Socket is active: %d", sockfd );
245                                         osrfRouterClassHandleIncoming( router, classname, class );
246                                 }
247                         }
248                 } // end while
249         } // end while
250 }
251
252
253 /**
254         @brief Handle incoming requests to the router.
255         @param router Pointer to the osrfRouter.
256
257         Read all available input messages from the top-level transport_client.  For each one:
258         if the domain of the sender's Jabber id is on the list of approved domains, pass the
259         message to osrfRouterHandleCommand() (if there's a message) or osrfRouterHandleAppRequest()
260         (if there isn't).  If the domain is @em not on the approved list, log a warning and
261         discard the message.
262 */
263 static void osrfRouterHandleIncoming( osrfRouter* router ) {
264         if(!router) return;
265
266         transport_message* msg = NULL;
267
268         while( (msg = client_recv( router->connection, 0 )) ) {  // for each message
269
270                 if( msg->sender ) {
271
272                         osrfLogDebug(OSRF_LOG_MARK,
273                                 "osrfRouterHandleIncoming(): investigating message from %s", msg->sender);
274
275                         /* if the server is not on a trusted domain, drop the message */
276                         int len = strlen(msg->sender) + 1;
277                         char domain[len];
278                         jid_get_domain( msg->sender, domain, len - 1 );
279
280                         if(osrfStringArrayContains( router->trustedServers, domain)) {
281
282                                 // If there's a command, obey it.  Otherwise, treat
283                                 // the message as an app session level request.
284                                 if( msg->router_command && *msg->router_command )
285                                         osrfRouterHandleCommand( router, msg );
286                                 else
287                                         osrfRouterHandleAppRequest( router, msg );
288                         }
289                         else
290                                 osrfLogWarning( OSRF_LOG_MARK, 
291                                                 "Received message from un-trusted server domain %s", msg->sender);
292                 }
293
294                 message_free(msg);
295         }
296 }
297
298 /**
299         @brief Handle all available incoming messages for a router class.
300         @param router Pointer to the osrfRouter.
301         @param classname Class name.
302         @param class Pointer to the osrfRouterClass.
303
304         For each message: if the sender is on a trusted domain, process the message.
305 */
306 static void osrfRouterClassHandleIncoming( osrfRouter* router, const char* classname,
307                 osrfRouterClass* class ) {
308         if(!(router && class)) return;
309
310         transport_message* msg;
311         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleIncoming()");
312
313         // For each incoming message for this class:
314         while( (msg = client_recv( class->connection, 0 )) ) {
315
316                 // Save the transaction id so that we can incorporate it
317                 // into any relevant messages
318                 osrfLogSetXid(msg->osrf_xid);
319
320                 if( msg->sender ) {
321
322                         osrfLogDebug(OSRF_LOG_MARK,
323                                 "osrfRouterClassHandleIncoming(): investigating message from %s", msg->sender);
324
325                         /* if the client is not from a trusted domain, drop the message */
326                         int len = strlen(msg->sender) + 1;
327                         char domain[len];
328                         jid_get_domain( msg->sender, domain, len - 1 );
329
330                         if(osrfStringArrayContains( router->trustedClients, domain )) {
331
332                                 if( msg->is_error )  {
333
334                                         transport_message* bouncedMessage = osrfRouterClassHandleBounce(
335                                                         router, classname, class, msg );
336                                         /* handle bounced message */
337                                         if( !bouncedMessage ) {
338                                                 /* we have no one to send the requested message to */
339                                                 message_free( msg );
340                                                 osrfLogClearXid();
341                                                 continue;
342                                         }
343                                         osrfRouterClassHandleMessage( router, class, bouncedMessage );
344                                         message_free( bouncedMessage );
345                                 } else
346                                         osrfRouterClassHandleMessage( router, class, msg );
347
348                         } else {
349                                 osrfLogWarning( OSRF_LOG_MARK, 
350                                                 "Received client message from untrusted client domain %s", domain );
351                         }
352                 }
353
354                 message_free( msg );
355                 osrfLogClearXid();  // We're done with this transaction id
356         }
357 }
358
359 /**
360         @brief Handle a top level router command.
361         @param router Pointer to the osrfRouter.
362         @param msg Pointer to the transport_message to be handled.
363
364         Currently supported commands:
365         - "register" -- Add a server class and/or a server node to our lists.
366         - "unregister" -- Remove a node from a class, and the class as well if no nodes are
367         left for it.
368 */
369 static void osrfRouterHandleCommand( osrfRouter* router, transport_message* msg ) {
370         if(!(router && msg && msg->router_class)) return;
371
372         if( !strcmp( msg->router_command, ROUTER_REGISTER ) ) {
373
374                 osrfLogInfo( OSRF_LOG_MARK, "Registering class %s", msg->router_class );
375
376                 // Add the server class to the list, if it isn't already there
377                 osrfRouterClass* class = osrfRouterFindClass( router, msg->router_class );
378                 if(!class)
379                         class = osrfRouterAddClass( router, msg->router_class );
380
381                 // Add the node to the osrfRouterClass's list, if it isn't already there
382                 if(class && ! osrfRouterClassFindNode( class, msg->sender ) )
383                         osrfRouterClassAddNode( class, msg->sender );
384
385         } else if( !strcmp( msg->router_command, ROUTER_UNREGISTER ) ) {
386
387                 if( msg->router_class && *msg->router_class ) {
388                         osrfLogInfo( OSRF_LOG_MARK, "Unregistering router class %s", msg->router_class );
389                         osrfRouterClassRemoveNode( router, msg->router_class, msg->sender );
390                 }
391         }
392 }
393
394
395 /**
396         @brief Adds an osrfRouterClass to a router, and open a connection for it.
397         @param router Pointer to the osrfRouter.
398         @param classname The name of the class this node handles.
399         @return A pointer to the new osrfRouterClass, or NULL upon error.
400
401         Open a Jabber session to be used for this server class.  The Jabber ID incorporates the
402         class name as the resource name.
403 */
404 static osrfRouterClass* osrfRouterAddClass( osrfRouter* router, const char* classname ) {
405         if(!(router && router->classes && classname)) return NULL;
406
407         osrfRouterClass* class = safe_malloc(sizeof(osrfRouterClass));
408         class->nodes = osrfNewHash();
409         class->itr = osrfNewHashIterator(class->nodes);
410         osrfHashSetCallback(class->nodes, &osrfRouterNodeFree);
411         class->router = router;
412
413         class->connection = client_init( router->domain, router->port, NULL, 0 );
414
415         if(!client_connect( class->connection, router->name,
416                         router->password, classname, 10, AUTH_DIGEST ) ) {
417                 // Cast away the constness of classname.  Though ugly, this
418                 // cast is benign because osrfRouterClassFree doesn't actually
419                 // write through the pointer.  We can't readily change its
420                 // signature because it is used for a function pointer, and
421                 // we would have to change other signatures the same way.
422                 osrfRouterClassFree( (char *) classname, class );
423                 return NULL;
424         }
425
426         osrfHashSet( router->classes, class, classname );
427         return class;
428 }
429
430
431 /**
432         @brief Add a new server node to an osrfRouterClass.
433         @param rclass Pointer to the osrfRouterClass to add the node to.
434         @param remoteId The remote login of the osrfRouterNode.
435 */
436 static void osrfRouterClassAddNode( osrfRouterClass* rclass, const char* remoteId ) {
437         if(!(rclass && rclass->nodes && remoteId)) return;
438
439         osrfLogInfo( OSRF_LOG_MARK, "Adding router node for remote id %s", remoteId );
440
441         osrfRouterNode* node = safe_malloc(sizeof(osrfRouterNode));
442         node->count = 0;
443         node->lastMessage = NULL;
444         node->remoteId = strdup(remoteId);
445
446         osrfHashSet( rclass->nodes, node, remoteId );
447 }
448
449 /* copy off the lastMessage, remove the offending node, send error if it's the last node
450         ? return NULL if it's the last node ?
451 */
452
453 /* handles case where router node is not longer reachable.  copies over the
454         data from the last sent message and returns a newly crafted suitable for treating
455         as a newly inconing message.  Removes the dead node and If there are no more
456         nodes to send the new message to, returns NULL.
457 */
458 /**
459         @brief Handle an input message representing a Jabber error stanza.
460         @param router Pointer to the current osrfRouter.
461         @param classname Name of the class to which the error stanza was sent.
462         @param rclass Pointer to the osrfRouterClass to which the error stanza was sent.
463         @param msg Pointer to the transport_message representing the error stanza.
464         @return Pointer to a newly allocated transport_message; or NULL (see remarks).
465
466         The presumption is that the relevant node is dead.  If another node is available for
467         the same class, then remove the dead one, create a clone of the message to be sent
468         elsewhere, and return a pointer to it.  If there is no other node for the same class,
469         send a cancel message back to Jabber, and return NULL.  If we can't even do that because
470         the entire class is dead, log a message to that effect and return NULL.
471 */
472 static transport_message* osrfRouterClassHandleBounce( osrfRouter* router,
473                 const char* classname, osrfRouterClass* rclass, transport_message* msg ) {
474
475         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleBounce()");
476
477         osrfLogInfo( OSRF_LOG_MARK, "Received network layer error message from %s", msg->sender );
478         osrfRouterNode* node = osrfRouterClassFindNode( rclass, msg->sender );
479         if( ! node ) {
480                 osrfLogInfo( OSRF_LOG_MARK,
481                         "network error occurred after we removed the class.. ignoring");
482                 return NULL;
483         }
484
485         if( osrfHashGetCount(rclass->nodes) == 1 ) { /* the last node is dead */
486
487                 if( node->lastMessage ) {
488                         osrfLogWarning( OSRF_LOG_MARK,
489                                         "We lost the last node in the class, responding with error and removing...");
490
491                         transport_message* error = message_init(
492                                 node->lastMessage->body, node->lastMessage->subject,
493                                 node->lastMessage->thread, node->lastMessage->router_from,
494                                 node->lastMessage->recipient );
495                         message_set_osrf_xid(error, node->lastMessage->osrf_xid);
496                         set_msg_error( error, "cancel", 501 );
497
498                         /* send the error message back to the original sender */
499                         client_send_message( rclass->connection, error );
500                         message_free( error );
501                 }
502
503                 /* remove the dead node */
504                 osrfRouterClassRemoveNode( router, classname, msg->sender);
505                 return NULL;
506
507         } else {
508
509                 transport_message* lastSent = NULL;
510
511                 if( node->lastMessage ) {
512                         osrfLogDebug( OSRF_LOG_MARK, "Cloning lastMessage so next node can send it");
513                         lastSent = message_init( node->lastMessage->body,
514                                 node->lastMessage->subject, node->lastMessage->thread, "",
515                                 node->lastMessage->router_from );
516                         message_set_router_info( lastSent, node->lastMessage->router_from,
517                                 NULL, NULL, NULL, 0 );
518                         message_set_osrf_xid( lastSent, node->lastMessage->osrf_xid );
519                 }
520
521                 /* remove the dead node */
522                 osrfRouterClassRemoveNode( router, classname, msg->sender);
523                 return lastSent;
524         }
525 }
526
527
528 /*
529         Handles class level requests
530         If we get a regular message, we send it to the next node in the list of nodes
531         if we get an error, it's a bounce back from a previous attempt.  We take the
532         body and thread from the last sent on the node that had the bounced message
533         and propogate them on to the new message being sent
534         @return 0 on success
535 */
536 static void osrfRouterClassHandleMessage(
537                 osrfRouter* router, osrfRouterClass* rclass, transport_message* msg ) {
538         if(!(router && rclass && msg)) return;
539
540         osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleMessage()");
541
542         osrfRouterNode* node = osrfHashIteratorNext( rclass->itr );
543         if(!node) {
544                 osrfHashIteratorReset(rclass->itr);
545                 node = osrfHashIteratorNext( rclass->itr );
546         }
547
548         if(node) {
549
550                 transport_message* new_msg= message_init( msg->body,
551                                 msg->subject, msg->thread, node->remoteId, msg->sender );
552                 message_set_router_info( new_msg, msg->sender, NULL, NULL, NULL, 0 );
553                 message_set_osrf_xid( new_msg, msg->osrf_xid );
554
555                 osrfLogInfo( OSRF_LOG_MARK,  "Routing message:\nfrom: [%s]\nto: [%s]",
556                                 new_msg->router_from, new_msg->recipient );
557
558                 message_free( node->lastMessage );
559                 node->lastMessage = new_msg;
560
561                 if ( client_send_message( rclass->connection, new_msg ) == 0 )
562                         node->count++;
563
564                 else {
565                         message_prepare_xml(new_msg);
566                         osrfLogWarning( OSRF_LOG_MARK, "Error sending message from %s to %s\n%s",
567                                         new_msg->sender, new_msg->recipient, new_msg->msg_xml );
568                 }
569
570         }
571 }
572
573
574 /**
575         @brief Remove a given osrfRouterClass from an osrfRouter
576         @param router Pointer to the osrfRouter.
577         @param classname The name of the class to be removed.
578
579         A callback function, installed in the osrfHash, frees the osrfRouterClass and any
580         associated nodes.
581 */
582 static void osrfRouterRemoveClass( osrfRouter* router, const char* classname ) {
583         if( router && router->classes && classname ) {
584                 osrfLogInfo( OSRF_LOG_MARK, "Removing router class %s", classname );
585                 osrfHashRemove( router->classes, classname );
586         }
587 }
588
589
590 /*
591         Removes the given node from the class.  Also, if this is that last node in the set,
592         removes the class from the router
593         @return 0 on successful removal with no class removal
594         @return 1 on successful remove with class removal
595         @return -1 error on removal
596 */
597 static int osrfRouterClassRemoveNode(
598                 osrfRouter* router, const char* classname, const char* remoteId ) {
599
600         if(!(router && router->classes && classname && remoteId)) return 0;
601
602         osrfLogInfo( OSRF_LOG_MARK, "Removing router node %s", remoteId );
603
604         osrfRouterClass* class = osrfRouterFindClass( router, classname );
605
606         if( class ) {
607
608                 osrfHashRemove( class->nodes, remoteId );
609                 if( osrfHashGetCount(class->nodes) == 0 ) {
610                         osrfRouterRemoveClass( router, classname );
611                         return 1;
612                 }
613
614                 return 0;
615         }
616
617         return -1;
618 }
619
620
621 /**
622         @brief Free a router class object.
623         @param classname Class name.
624         @param c Pointer to the osrfRouterClass, cast to a void pointer.
625
626         This function is designated to the osrfHash as a callback.
627 */
628 static void osrfRouterClassFree( char* classname, void* c ) {
629         if(!(classname && c)) return;
630         osrfRouterClass* rclass = (osrfRouterClass*) c;
631         client_disconnect( rclass->connection );
632         client_free( rclass->connection );
633
634         osrfHashIteratorReset( rclass->itr );
635         osrfRouterNode* node;
636
637         while( (node = osrfHashIteratorNext(rclass->itr)) )
638                 osrfHashRemove( rclass->nodes, node->remoteId );
639
640         osrfHashIteratorFree(rclass->itr);
641         osrfHashFree(rclass->nodes);
642
643         free(rclass);
644 }
645
646 /**
647         @brief Free an osrfRouterNode.
648         @param remoteId Name of router (not used).
649         @param n Pointer to the osrfRouterNode to be freed, cast to a void pointer.
650
651         This is a callback installed in an osrfHash (the nodes member of an osrfRouterClass).
652 */
653 static void osrfRouterNodeFree( char* remoteId, void* n ) {
654         if(!n) return;
655         osrfRouterNode* node = (osrfRouterNode*) n;
656         free(node->remoteId);
657         message_free(node->lastMessage);
658         free(node);
659 }
660
661
662 void osrfRouterFree( osrfRouter* router ) {
663         if(!router) return;
664
665         osrfHashIteratorFree( router->class_itr);
666         osrfHashFree(router->classes);
667         free(router->domain);
668         free(router->name);
669         free(router->resource);
670         free(router->password);
671
672         osrfStringArrayFree( router->trustedClients );
673         osrfStringArrayFree( router->trustedServers );
674
675         client_free( router->connection );
676         free(router);
677 }
678
679
680
681 /*
682         Finds the class associated with the given class name in the list of classes
683 */
684 static osrfRouterClass* osrfRouterFindClass( osrfRouter* router, const char* classname ) {
685         if(!( router && router->classes && classname )) return NULL;
686         return (osrfRouterClass*) osrfHashGet( router->classes, classname );
687 }
688
689
690 /*
691         Finds the router node within this class with the given remote id
692 */
693 static osrfRouterNode* osrfRouterClassFindNode( osrfRouterClass* rclass,
694                 const char* remoteId ) {
695         if(!(rclass && remoteId))  return NULL;
696         return (osrfRouterNode*) osrfHashGet( rclass->nodes, remoteId );
697 }
698
699
700 /*
701         Clears and populates the provided fd_set* with file descriptors
702         from the router's top level connection as well as each of the
703         router class connections
704         @return The largest file descriptor found in the filling process
705 */
706 /**
707         @brief Fill an fd_set with all the sockets owned by the osrfRouter.
708         @param router Pointer to the osrfRouter whose sockets are to be used.
709         @param set Pointer to the fd_set that is to be filled.
710         @return The largest file descriptor loaded into the fd_set; or -1 upon error.
711
712         There's one socket for the osrfRouter as a whole, and one for each osrfRouterClass
713         that belongs to it.  We load them all.
714 */
715 static int _osrfRouterFillFDSet( osrfRouter* router, fd_set* set ) {
716         if(!(router && router->classes && set)) return -1;
717
718         FD_ZERO(set);
719         int maxfd = client_sock_fd( router->connection );
720         FD_SET(maxfd, set);
721
722         int sockid;
723
724         osrfRouterClass* class = NULL;
725         osrfHashIterator* itr = router->class_itr;
726         osrfHashIteratorReset( itr );
727
728         while( (class = osrfHashIteratorNext(itr)) ) {
729                 const char* classname = osrfHashIteratorKey(itr);
730
731                 if( classname && (class = osrfRouterFindClass( router, classname )) ) {
732                         sockid = client_sock_fd( class->connection );
733
734                         if( osrfUtilsCheckFileDescriptor( sockid ) ) {
735
736                                 osrfLogWarning(OSRF_LOG_MARK,
737                                         "Removing router class '%s' because of a bad top-level file descriptor [%d]",
738                                         classname, sockid );
739                                 osrfRouterRemoveClass( router, classname );
740
741                         } else {
742                                 if( sockid > maxfd ) maxfd = sockid;
743                                 FD_SET(sockid, set);
744                         }
745                 }
746         }
747
748         return maxfd;
749 }
750
751 /*
752         handles messages that don't have a 'router_command' set.  They are assumed to
753         be app request messages
754 */
755 static void osrfRouterHandleAppRequest( osrfRouter* router, transport_message* msg ) {
756
757         int T = 32;
758         osrfMessage* arr[T];
759         memset(arr, 0, sizeof(arr));
760
761         int num_msgs = osrf_message_deserialize( msg->body, arr, T );
762         osrfMessage* omsg = NULL;
763
764         int i;
765         for( i = 0; i != num_msgs; i++ ) {
766
767                 if( !(omsg = arr[i]) ) continue;
768
769                 switch( omsg->m_type ) {
770
771                         case CONNECT:
772                                 osrfRouterRespondConnect( router, msg, omsg );
773                                 break;
774
775                         case REQUEST:
776                                 osrfRouterProcessAppRequest( router, msg, omsg );
777                                 break;
778
779                         default: break;
780                 }
781
782                 osrfMessageFree( omsg );
783         }
784
785         return;
786 }
787
788 static int osrfRouterRespondConnect( osrfRouter* router, transport_message* msg,
789                 osrfMessage* omsg ) {
790         if(!(router && msg && omsg)) return -1;
791
792         osrfMessage* success = osrf_message_init( STATUS, omsg->thread_trace, omsg->protocol );
793
794         osrfLogDebug( OSRF_LOG_MARK, "router received a CONNECT message from %s", msg->sender );
795
796         osrf_message_set_status_info(
797                 success, "osrfConnectStatus", "Connection Successful", OSRF_STATUS_OK );
798
799         char* data = osrf_message_serialize(success);
800
801         transport_message* return_m = message_init(
802                 data, "", msg->thread, msg->sender, "" );
803
804         client_send_message(router->connection, return_m);
805
806         free(data);
807         osrfMessageFree(success);
808         message_free(return_m);
809
810         return 0;
811 }
812
813
814
815 static int osrfRouterProcessAppRequest( osrfRouter* router, transport_message* msg,
816                 osrfMessage* omsg ) {
817
818         if(!(router && msg && omsg && omsg->method_name)) return -1;
819
820         osrfLogInfo( OSRF_LOG_MARK, "Router received app request: %s", omsg->method_name );
821
822         jsonObject* jresponse = NULL;
823         if(!strcmp( omsg->method_name, ROUTER_REQUEST_CLASS_LIST )) {
824
825                 int i;
826                 jresponse = jsonNewObjectType(JSON_ARRAY);
827
828                 osrfStringArray* keys = osrfHashKeys( router->classes );
829                 for( i = 0; i != keys->size; i++ )
830                         jsonObjectPush( jresponse, jsonNewObject(osrfStringArrayGetString( keys, i )) );
831                 osrfStringArrayFree(keys);
832
833
834         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_SUMMARY )) {
835
836                 osrfRouterClass* class;
837                 osrfRouterNode* node;
838                 int count = 0;
839
840                 char* classname = jsonObjectToSimpleString( jsonObjectGetIndex( omsg->_params, 0 ) );
841
842                 if (!classname)
843                         return -1;
844
845                 class = osrfHashGet(router->classes, classname);
846                 free(classname);
847
848                 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
849                 while( (node = osrfHashIteratorNext(node_itr)) ) {
850                         count += node->count;
851                         //jsonObjectSetKey( class_res, node->remoteId, jsonNewNumberObject( (double) node->count ) );
852                 }
853                 osrfHashIteratorFree(node_itr);
854
855                 jresponse = jsonNewNumberObject( (double) count );
856
857         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS )) {
858
859                 osrfRouterClass* class;
860                 osrfRouterNode* node;
861
862                 char* classname = jsonObjectToSimpleString( jsonObjectGetIndex( omsg->_params, 0 ) );
863
864                 if (!classname)
865                         return -1;
866
867                 jresponse = jsonNewObjectType(JSON_HASH);
868                 class = osrfHashGet(router->classes, classname);
869                 free(classname);
870
871                 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
872                 while( (node = osrfHashIteratorNext(node_itr)) ) {
873                         jsonObjectSetKey( jresponse, node->remoteId,
874                                         jsonNewNumberObject( (double) node->count ) );
875                 }
876                 osrfHashIteratorFree(node_itr);
877
878         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_FULL )) {
879
880                 osrfRouterClass* class;
881                 osrfRouterNode* node;
882                 jresponse = jsonNewObjectType(JSON_HASH);
883
884                 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
885                 while( (class = osrfHashIteratorNext(class_itr)) ) {
886
887                         jsonObject* class_res = jsonNewObjectType(JSON_HASH);
888                         const char* classname = osrfHashIteratorKey(class_itr);
889
890                         osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
891                         while( (node = osrfHashIteratorNext(node_itr)) ) {
892                                 jsonObjectSetKey( class_res, node->remoteId,
893                                                 jsonNewNumberObject( (double) node->count ) );
894                         }
895                         osrfHashIteratorFree(node_itr);
896
897                         jsonObjectSetKey( jresponse, classname, class_res );
898                 }
899
900                 osrfHashIteratorFree(class_itr);
901
902         } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_NODE_FULL )) {
903
904                 osrfRouterClass* class;
905                 osrfRouterNode* node;
906                 int count;
907                 jresponse = jsonNewObjectType(JSON_HASH);
908
909                 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
910                 while( (class = osrfHashIteratorNext(class_itr)) ) {
911
912                         count = 0;
913                         const char* classname = osrfHashIteratorKey(class_itr);
914
915                         osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
916                         while( (node = osrfHashIteratorNext(node_itr)) ) {
917                                 count += node->count;
918                         }
919                         osrfHashIteratorFree(node_itr);
920
921                         jsonObjectSetKey( jresponse, classname, jsonNewNumberObject( (double) count ) );
922                 }
923
924                 osrfHashIteratorFree(class_itr);
925
926         } else {
927
928                 return osrfRouterHandleMethodNFound( router, msg, omsg );
929         }
930
931
932         osrfRouterHandleAppResponse( router, msg, omsg, jresponse );
933         jsonObjectFree(jresponse);
934
935         return 0;
936
937 }
938
939
940 static int osrfRouterHandleMethodNFound(
941                 osrfRouter* router, transport_message* msg, osrfMessage* omsg ) {
942
943         osrfMessage* err = osrf_message_init( STATUS, omsg->thread_trace, 1);
944                 osrf_message_set_status_info( err,
945                                 "osrfMethodException", "Router method not found", OSRF_STATUS_NOTFOUND );
946
947                 char* data =  osrf_message_serialize(err);
948
949                 transport_message* tresponse = message_init(
950                                 data, "", msg->thread, msg->sender, msg->recipient );
951
952                 client_send_message(router->connection, tresponse );
953
954                 free(data);
955                 osrfMessageFree( err );
956                 message_free(tresponse);
957                 return 0;
958 }
959
960
961 static int osrfRouterHandleAppResponse( osrfRouter* router,
962         transport_message* msg, osrfMessage* omsg, const jsonObject* response ) {
963
964         if( response ) { /* send the response message */
965
966                 osrfMessage* oresponse = osrf_message_init(
967                                 RESULT, omsg->thread_trace, omsg->protocol );
968
969                 char* json = jsonObjectToJSON(response);
970                 osrf_message_set_result_content( oresponse, json);
971
972                 char* data =  osrf_message_serialize(oresponse);
973                 osrfLogDebug( OSRF_LOG_MARK,  "Responding to client app request with data: \n%s\n", data );
974
975                 transport_message* tresponse = message_init(
976                                 data, "", msg->thread, msg->sender, msg->recipient );
977
978                 client_send_message(router->connection, tresponse );
979
980                 osrfMessageFree(oresponse);
981                 message_free(tresponse);
982                 free(json);
983                 free(data);
984         }
985
986         /* now send the 'request complete' message */
987         osrfMessage* status = osrf_message_init( STATUS, omsg->thread_trace, 1);
988         osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
989                         OSRF_STATUS_COMPLETE );
990
991         char* statusdata = osrf_message_serialize(status);
992
993         transport_message* sresponse = message_init(
994                         statusdata, "", msg->thread, msg->sender, msg->recipient );
995         client_send_message(router->connection, sresponse );
996
997         free(statusdata);
998         osrfMessageFree(status);
999         message_free(sresponse);
1000
1001         return 0;
1002 }
1003
1004
1005
1006