]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/transport_session.c
366f14cb2a971adf99285aadf4e27a1e2e95beff
[OpenSRF.git] / src / libopensrf / transport_session.c
1 #include <opensrf/transport_session.h>
2
3 #ifndef HOST_NAME_MAX
4 #define HOST_NAME_MAX 256
5 #endif
6
7 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
8 static int reset_session_buffers( transport_session* session );
9 static char* get_xml_attr( const xmlChar** atts, const char* attr_name );
10
11 // ---------------------------------------------------------------------------------
12 // returns a built and allocated transport_session object.
13 // This codes does no network activity, only memory initilization
14 // ---------------------------------------------------------------------------------
15 transport_session* init_transport(  const char* server, 
16         int port, const char* unix_path, void* user_data, int component ) {
17
18         /* create the session struct */
19         transport_session* session = 
20                 (transport_session*) safe_malloc( sizeof(transport_session) );
21
22         session->user_data = user_data;
23
24         session->component = component;
25
26         /* initialize the data buffers */
27         session->body_buffer                    = buffer_init( JABBER_BODY_BUFSIZE );
28         session->subject_buffer         = buffer_init( JABBER_SUBJECT_BUFSIZE );
29         session->thread_buffer          = buffer_init( JABBER_THREAD_BUFSIZE );
30         session->from_buffer                    = buffer_init( JABBER_JID_BUFSIZE );
31         session->status_buffer          = buffer_init( JABBER_STATUS_BUFSIZE );
32         session->recipient_buffer       = buffer_init( JABBER_JID_BUFSIZE );
33         session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
34         session->session_id                     = buffer_init( 64 );
35
36         session->message_error_code = 0;
37
38         /* for OpenSRF extensions */
39         session->router_to_buffer               = buffer_init( JABBER_JID_BUFSIZE );
40         session->router_from_buffer     = buffer_init( JABBER_JID_BUFSIZE );
41         session->osrf_xid_buffer        = buffer_init( JABBER_JID_BUFSIZE );
42         session->router_class_buffer    = buffer_init( JABBER_JID_BUFSIZE );
43         session->router_command_buffer  = buffer_init( JABBER_JID_BUFSIZE );
44
45         session->router_broadcast   = 0;
46
47         /* initialize the jabber state machine */
48         session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
49         session->state_machine->connected        = 0;
50         session->state_machine->connecting       = 0;
51         session->state_machine->in_message       = 0;
52         session->state_machine->in_message_body  = 0;
53         session->state_machine->in_thread        = 0;
54         session->state_machine->in_subject       = 0;
55         session->state_machine->in_error         = 0;
56         session->state_machine->in_message_error = 0;
57         session->state_machine->in_iq            = 0;
58         session->state_machine->in_presence      = 0;
59         session->state_machine->in_status        = 0;
60
61         /* initialize the sax push parser */
62         session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
63
64         /* initialize the transport_socket structure */
65         session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
66
67         session->sock_mgr->data_received = &grab_incoming;
68         session->sock_mgr->on_socket_closed = NULL;
69         session->sock_mgr->socket = NULL;
70         session->sock_mgr->blob = session;
71         
72         session->port = port;
73         session->server = strdup(server);
74         if(unix_path)   
75                 session->unix_path = strdup(unix_path);
76         else session->unix_path = NULL;
77
78         session->sock_id = 0;
79         session->message_callback = NULL;
80
81         return session;
82 }
83
84
85
86 /* XXX FREE THE BUFFERS */
87 int session_free( transport_session* session ) {
88         if( ! session ) { return 0; }
89
90         if(session->sock_mgr)
91                 socket_manager_free(session->sock_mgr);
92
93         if( session->state_machine ) free( session->state_machine );
94         if( session->parser_ctxt) {
95                 xmlFreeDoc( session->parser_ctxt->myDoc );
96                 xmlFreeParserCtxt(session->parser_ctxt);
97         }
98
99         xmlCleanupCharEncodingHandlers();
100         xmlDictCleanup();
101         xmlCleanupParser();
102
103         buffer_free(session->body_buffer);
104         buffer_free(session->subject_buffer);
105         buffer_free(session->thread_buffer);
106         buffer_free(session->from_buffer);
107         buffer_free(session->recipient_buffer);
108         buffer_free(session->status_buffer);
109         buffer_free(session->message_error_type);
110         buffer_free(session->router_to_buffer);
111         buffer_free(session->router_from_buffer);
112         buffer_free(session->osrf_xid_buffer);
113         buffer_free(session->router_class_buffer);
114         buffer_free(session->router_command_buffer);
115         buffer_free(session->session_id);
116
117         free(session->server);
118         free(session->unix_path);
119
120         free( session );
121         return 1;
122 }
123
124
125 int session_wait( transport_session* session, int timeout ) {
126         if( ! session || ! session->sock_mgr ) {
127                 return 0;
128         }
129
130         int ret =  socket_wait( session->sock_mgr, timeout, session->sock_id );
131
132         if( ret ) {
133                 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
134                 session->state_machine->connected = 0;
135         }
136         return ret;
137 }
138
139 int session_send_msg( 
140                 transport_session* session, transport_message* msg ) {
141
142         if( ! session ) { return -1; }
143
144         if( ! session->state_machine->connected ) {
145                 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
146                 return -1;
147         }
148
149         message_prepare_xml( msg );
150         return socket_send( session->sock_id, msg->msg_xml );
151
152 }
153
154
155 /* connects to server and connects to jabber */
156 int session_connect( transport_session* session, 
157                 const char* username, const char* password, 
158                 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
159
160         int size1 = 0;
161         int size2 = 0;
162
163         if( ! session ) { 
164                 osrfLogWarning(OSRF_LOG_MARK,  "session is null in connect" );
165                 return 0; 
166         }
167
168
169         char* server = session->server;
170
171         if( ! session->sock_id ) {
172
173                 if(session->port > 0) {
174                         if( (session->sock_id = socket_open_tcp_client(
175                                 session->sock_mgr, session->port, session->server)) <= 0 ) 
176                         return 0;
177
178                 } else if(session->unix_path != NULL) {
179                         if( (session->sock_id = socket_open_unix_client(
180                                 session->sock_mgr, session->unix_path)) <= 0 ) 
181                         return 0;
182                 }
183                 else {
184                         osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
185                         return 0;
186                 }
187         }
188
189         if( session->component ) {
190
191                 /* the first Jabber connect stanza */
192                 char our_hostname[HOST_NAME_MAX + 1] = "";
193                 gethostname(our_hostname, sizeof(our_hostname) );
194                 our_hostname[HOST_NAME_MAX] = '\0';
195                 size1 = 150 + strlen( server );
196                 char stanza1[ size1 ]; 
197                 snprintf( stanza1, sizeof(stanza1),
198                                 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
199                                 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
200                                 username, our_hostname );
201
202                 /* send the first stanze */
203                 session->state_machine->connecting = CONNECTING_1;
204
205                 if( socket_send( session->sock_id, stanza1 ) ) {
206                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
207                         return 0;
208                 }
209         
210                 /* wait for reply */
211                 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
212         
213                 /* server acknowledges our existence, now see if we can login */
214                 if( session->state_machine->connecting == CONNECTING_2 ) {
215         
216                         int ss = session->session_id->n_used + strlen(password) + 5;
217                         char hashstuff[ss];
218                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
219
220                         char* hash = shahash( hashstuff );
221                         size2 = 100 + strlen( hash );
222                         char stanza2[ size2 ];
223                         snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
224         
225                         if( socket_send( session->sock_id, stanza2 )  ) {
226                                 osrfLogWarning(OSRF_LOG_MARK, "error sending");
227                                 return 0;
228                         }
229                 }
230
231         } else { /* we're not a component */
232
233                 /* the first Jabber connect stanza */
234                 size1 = 100 + strlen( server );
235                 char stanza1[ size1 ]; 
236                 snprintf( stanza1, sizeof(stanza1), 
237                                 "<stream:stream to='%s' xmlns='jabber:client' "
238                                 "xmlns:stream='http://etherx.jabber.org/streams'>",
239                         server );
240         
241
242                 /* send the first stanze */
243                 session->state_machine->connecting = CONNECTING_1;
244                 if( socket_send( session->sock_id, stanza1 ) ) {
245                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
246                         return 0;
247                 }
248
249
250                 /* wait for reply */
251                 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
252
253                 if( auth_type == AUTH_PLAIN ) {
254
255                         /* the second jabber connect stanza including login info*/
256                         size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
257                         char stanza2[ size2 ];
258                         snprintf( stanza2, sizeof(stanza2), 
259                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
260                                         "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
261                                         username, password, resource );
262         
263                         /* server acknowledges our existence, now see if we can login */
264                         if( session->state_machine->connecting == CONNECTING_2 ) {
265                                 if( socket_send( session->sock_id, stanza2 )  ) {
266                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
267                                         return 0;
268                                 }
269                         }
270
271                 } else if( auth_type == AUTH_DIGEST ) {
272
273                         int ss = session->session_id->n_used + strlen(password) + 5;
274                         char hashstuff[ss];
275                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
276
277                         char* hash = shahash( hashstuff );
278
279                         /* the second jabber connect stanza including login info*/
280                         size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
281                         char stanza2[ size2 ];
282                         snprintf( stanza2, sizeof(stanza2), 
283                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
284                                         "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
285                                         username, hash, resource );
286         
287                         /* server acknowledges our existence, now see if we can login */
288                         if( session->state_machine->connecting == CONNECTING_2 ) {
289                                 if( socket_send( session->sock_id, stanza2 )  ) {
290                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
291                                         return 0;
292                                 }
293                         }
294
295                 }
296
297         } // not component
298
299
300         /* wait for reply */
301         socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
302
303         if( session->state_machine->connected ) {
304                 /* yar! */
305                 return 1;
306         }
307
308         return 0;
309 }
310
311 // ---------------------------------------------------------------------------------
312 // TCP data callback.  Takes data from the socket handler and pushes it directly
313 // into the push parser
314 // ---------------------------------------------------------------------------------
315 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
316         transport_session* ses = (transport_session*) blob;
317         if( ! ses ) { return; }
318         xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
319 }
320
321
322 void startElementHandler(
323         void *session, const xmlChar *name, const xmlChar **atts) {
324
325         transport_session* ses = (transport_session*) session;
326         if( ! ses ) { return; }
327
328         
329         if( strcmp( (char*) name, "message" ) == 0 ) {
330                 ses->state_machine->in_message = 1;
331                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
332                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
333                 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
334                 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
335                 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
336                 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
337                 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
338                 char* broadcast = get_xml_attr( atts, "broadcast" );
339                 if( broadcast )
340                         ses->router_broadcast = atoi( broadcast );
341
342                 return;
343         }
344
345         if( ses->state_machine->in_message ) {
346
347                 if( strcmp( (char*) name, "body" ) == 0 ) {
348                         ses->state_machine->in_message_body = 1;
349                         return;
350                 }
351         
352                 if( strcmp( (char*) name, "subject" ) == 0 ) {
353                         ses->state_machine->in_subject = 1;
354                         return;
355                 }
356         
357                 if( strcmp( (char*) name, "thread" ) == 0 ) {
358                         ses->state_machine->in_thread = 1;
359                         return;
360                 }
361
362         }
363
364         if( strcmp( (char*) name, "presence" ) == 0 ) {
365                 ses->state_machine->in_presence = 1;
366                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
367                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
368                 return;
369         }
370
371         if( strcmp( (char*) name, "status" ) == 0 ) {
372                 ses->state_machine->in_status = 1;
373                 return;
374         }
375
376
377         if( strcmp( (char*) name, "stream:error" ) == 0 ) {
378                 ses->state_machine->in_error = 1;
379                 ses->state_machine->connected = 0;
380                 osrfLogWarning(  OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
381                 return;
382         }
383
384
385         /* first server response from a connect attempt */
386         if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
387                 if( ses->state_machine->connecting == CONNECTING_1 ) {
388                         ses->state_machine->connecting = CONNECTING_2;
389                         buffer_add( ses->session_id, get_xml_attr(atts, "id") );
390                 }
391         }
392
393         if( strcmp( (char*) name, "handshake" ) == 0 ) {
394                 ses->state_machine->connected = 1;
395                 ses->state_machine->connecting = 0;
396                 return;
397         }
398
399
400         if( strcmp( (char*) name, "error" ) == 0 ) {
401                 ses->state_machine->in_message_error = 1;
402                 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
403                 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
404                 osrfLogInfo( OSRF_LOG_MARK,  "Received <error> message with type %s and code %s", 
405                         get_xml_attr( atts, "type"), get_xml_attr( atts, "code") );
406                 return;
407         }
408
409         if( strcmp( (char*) name, "iq" ) == 0 ) {
410                 ses->state_machine->in_iq = 1;
411
412                 if( strcmp( get_xml_attr(atts, "type"), "result") == 0 
413                                 && ses->state_machine->connecting == CONNECTING_2 ) {
414                         ses->state_machine->connected = 1;
415                         ses->state_machine->connecting = 0;
416                         return;
417                 }
418
419                 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
420                         osrfLogWarning( OSRF_LOG_MARK,  "Error connecting to jabber" );
421                         return;
422                 }
423         }
424 }
425
426 // ------------------------------------------------------------------
427 // Returns the value of the given XML attribute
428 // The xmlChar** construct is commonly returned from SAX event
429 // handlers.  Pass that in with the name of the attribute you want
430 // to retrieve.
431 // ------------------------------------------------------------------
432 static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
433         int i;
434         if (atts != NULL) {
435                 for(i = 0;(atts[i] != NULL);i++) {
436                         if( strcmp( (char*) atts[i++], attr_name ) == 0 ) {
437                                 if( atts[i] != NULL ) {
438                                         return (char*) atts[i];
439                                 }
440                         }
441                 }
442         }
443         return NULL;
444 }
445
446
447 // ------------------------------------------------------------------
448 // See which tags are ending
449 // ------------------------------------------------------------------
450 void endElementHandler( void *session, const xmlChar *name) {
451         transport_session* ses = (transport_session*) session;
452         if( ! ses ) { return; }
453
454         if( strcmp( (char*) name, "message" ) == 0 ) {
455
456
457                 /* pass off the message info the callback */
458                 if( ses->message_callback ) {
459
460                         /* here it's ok to pass in the raw buffers because
461                                 message_init allocates new space for the chars 
462                                 passed in */
463                         transport_message* msg =  message_init( 
464                                 ses->body_buffer->buf, 
465                                 ses->subject_buffer->buf,
466                                 ses->thread_buffer->buf, 
467                                 ses->recipient_buffer->buf, 
468                                 ses->from_buffer->buf );
469
470                         message_set_router_info( msg, 
471                                 ses->router_from_buffer->buf, 
472                                 ses->router_to_buffer->buf, 
473                                 ses->router_class_buffer->buf,
474                                 ses->router_command_buffer->buf,
475                                 ses->router_broadcast );
476
477          message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
478
479                         if( ses->message_error_type->n_used > 0 ) {
480                                 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
481                         }
482
483                         if( msg == NULL ) { return; }
484                         ses->message_callback( ses->user_data, msg );
485                 }
486
487                 ses->state_machine->in_message = 0;
488                 reset_session_buffers( session );
489                 return;
490         }
491         
492         if( strcmp( (const char*) name, "body" ) == 0 ) {
493                 ses->state_machine->in_message_body = 0;
494                 return;
495         }
496
497         if( strcmp( (const char*) name, "subject" ) == 0 ) {
498                 ses->state_machine->in_subject = 0;
499                 return;
500         }
501
502         if( strcmp( (const char*) name, "thread" ) == 0 ) {
503                 ses->state_machine->in_thread = 0;
504                 return;
505         }
506         
507         if( strcmp( (const char*) name, "iq" ) == 0 ) {
508                 ses->state_machine->in_iq = 0;
509                 if( ses->message_error_code > 0 ) {
510                         osrfLogWarning( OSRF_LOG_MARK,  "Error in IQ packet: code %d",  ses->message_error_code );
511                         osrfLogWarning( OSRF_LOG_MARK,  "Error 401 means not authorized" );
512                 }
513                 reset_session_buffers( session );
514                 return;
515         }
516
517         if( strcmp( (const char*) name, "presence" ) == 0 ) {
518                 ses->state_machine->in_presence = 0;
519                 /*
520                 if( ses->presence_callback ) {
521                         // call the callback with the status, etc.
522                 }
523                 */
524                 reset_session_buffers( session );
525                 return;
526         }
527
528         if( strcmp( (const char*) name, "status" ) == 0 ) {
529                 ses->state_machine->in_status = 0;
530                 return;
531         }
532
533         if( strcmp( (const char*) name, "error" ) == 0 ) {
534                 ses->state_machine->in_message_error = 0;
535                 return;
536         }
537
538         if( strcmp( (const char*) name, "error:error" ) == 0 ) {
539                 ses->state_machine->in_error = 0;
540                 return;
541         }
542 }
543
544 static int reset_session_buffers( transport_session* ses ) {
545         buffer_reset( ses->body_buffer );
546         buffer_reset( ses->subject_buffer );
547         buffer_reset( ses->thread_buffer );
548         buffer_reset( ses->from_buffer );
549         buffer_reset( ses->recipient_buffer );
550         buffer_reset( ses->router_from_buffer );
551         buffer_reset( ses->osrf_xid_buffer );
552         buffer_reset( ses->router_to_buffer );
553         buffer_reset( ses->router_class_buffer );
554         buffer_reset( ses->router_command_buffer );
555         buffer_reset( ses->message_error_type );
556         buffer_reset( ses->session_id );
557
558         return 1;
559 }
560
561 // ------------------------------------------------------------------
562 // takes data out of the body of the message and pushes it into
563 // the appropriate buffer
564 // ------------------------------------------------------------------
565 void characterHandler(
566                 void *session, const xmlChar *ch, int len) {
567
568         const char* p = (const char*) ch;
569
570         transport_session* ses = (transport_session*) session;
571         if( ! ses ) { return; }
572
573         /* set the various message parts */
574         if( ses->state_machine->in_message ) {
575
576                 if( ses->state_machine->in_message_body ) {
577                         buffer_add_n( ses->body_buffer, p, len );
578                 }
579
580                 if( ses->state_machine->in_subject ) {
581                         buffer_add_n( ses->subject_buffer, p, len );
582                 }
583
584                 if( ses->state_machine->in_thread ) {
585                         buffer_add_n( ses->thread_buffer, p, len );
586                 }
587         }
588
589         /* set the presence status */
590         if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
591                 buffer_add_n( ses->status_buffer, p, len );
592         }
593
594         if( ses->state_machine->in_error ) {
595                 /* for now... */
596                 osrfLogWarning( OSRF_LOG_MARK,  "ERROR XML fragment: %s\n", ch );
597         }
598
599 }
600
601 /* XXX change to warning handlers */
602 void  parseWarningHandler( void *session, const char* msg, ... ) {
603
604         va_list args;
605         va_start(args, msg);
606         fprintf(stdout, "transport_session XML WARNING");
607         vfprintf(stdout, msg, args);
608         va_end(args);
609         fprintf(stderr, "XML WARNING: %s\n", msg ); 
610 }
611
612 void  parseErrorHandler( void *session, const char* msg, ... ){
613
614         va_list args;
615         va_start(args, msg);
616         fprintf(stdout, "transport_session XML ERROR");
617         vfprintf(stdout, msg, args);
618         va_end(args);
619         fprintf(stderr, "XML ERROR: %s\n", msg ); 
620
621 }
622
623 int session_disconnect( transport_session* session ) {
624         if( session == NULL ) { return 0; }
625         socket_send(session->sock_id, "</stream:stream>");
626         socket_disconnect(session->sock_mgr, session->sock_id);
627         return 0;
628 }
629