connwrap - initialize gnutls session in cw_connect
[centerim.git] / libjabber / jconn.c
blob4fc4d3a7bdb11217a255d4394f286dc7f55fc799
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * Jabber
17 * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
20 #include "jabber.h"
21 #include "connwrap.h"
23 #ifdef HAVE_THREAD
24 #include <pthread.h>
25 #endif
26 /* local macros for launching event handlers */
27 #define STATE_EVT(arg) if(j->on_state) { (j->on_state)(j, (arg) ); }
30 /* prototypes of the local functions */
31 static void startElement(void *userdata, const char *name, const char **attribs);
32 static void endElement(void *userdata, const char *name);
33 static void charData(void *userdata, const char *s, int slen);
37 * jab_new -- initialize a new jabber connection
39 * parameters
40 * user -- jabber id of the user
41 * pass -- password of the user
43 * results
44 * a pointer to the connection structure
45 * or NULL if allocations failed
47 jconn jab_new(char *user, char *pass, char *server, int port, int ssl)
49 pool p;
50 jconn j;
52 if(!user) return(NULL);
54 p = pool_new();
55 if(!p) return(NULL);
56 j = pmalloc_x(p, sizeof(jconn_struct), 0);
57 if(!j) return(NULL);
58 j->p = p;
60 j->user = jid_new(p, user);
61 j->pass = pstrdup(p, pass);
62 j->port = port;
63 j->server = server;
65 j->state = JCONN_STATE_OFF;
66 j->cw_state = 0;
67 j->id = 1;
68 j->fd = -1;
69 j->ssl = ssl;
71 return j;
75 * jab_delete -- free a jabber connection
77 * parameters
78 * j -- connection
81 void jab_delete(jconn j)
83 if(!j) return;
85 jab_stop(j);
86 pool_free(j->p);
90 * jab_state_handler -- set callback handler for state change
92 * parameters
93 * j -- connection
94 * h -- name of the handler function
96 void jab_state_handler(jconn j, jconn_state_h h)
98 if(!j) return;
100 j->on_state = h;
104 * jab_packet_handler -- set callback handler for incoming packets
106 * parameters
107 * j -- connection
108 * h -- name of the handler function
110 void jab_packet_handler(jconn j, jconn_packet_h h)
112 if(!j) return;
114 j->on_packet = h;
117 void jab_logger(jconn j, jconn_logger h)
119 if(!j) return;
121 j->logger = h;
126 * jab_start -- start connection
128 * parameters
129 * j -- connection
132 void jab_start(jconn j)
134 xmlnode x;
135 char *t,*t2;
137 if(!j || (j->state != JCONN_STATE_OFF && j->state != JCONN_STATE_CONNECTING) ) return;
139 if (!(j->cw_state & CW_CONNECT_WANT_SOMETHING)) { /* same as state != JCONN_STATE_CONNECTING */
140 j->parser = XML_ParserCreate(NULL);
141 XML_SetUserData(j->parser, (void *)j);
142 XML_SetElementHandler(j->parser, startElement, endElement);
143 XML_SetCharacterDataHandler(j->parser, charData);
145 if (j->cw_state & CW_CONNECT_BLOCKING)
146 j->fd = make_netsocket(j->port, j->server, NETSOCKET_CLIENT, j->ssl);
147 else
148 j->fd = make_nb_netsocket(j->port, j->server, NETSOCKET_CLIENT, j->ssl, &j->cw_state);
150 if(j->fd < 0) {
151 STATE_EVT(JCONN_STATE_OFF);
152 return;
155 else { /* subsequent calls to cw_nb_connect until it finishes negociation */
156 if (cw_nb_connect(j->fd, 0, 0, j->ssl, &j->cw_state)) {
157 STATE_EVT(JCONN_STATE_OFF);
158 return;
161 if (j->cw_state & CW_CONNECT_WANT_SOMETHING){ /* check if it finished negociation */
162 j->state = JCONN_STATE_CONNECTING;
163 STATE_EVT(JCONN_STATE_CONNECTING);
164 return;
166 change_socket_to_blocking(j->fd);
168 j->state = JCONN_STATE_CONNECTED;
169 STATE_EVT(JCONN_STATE_CONNECTED)
171 /* start stream */
172 x = jutil_header(NS_CLIENT, j->user->server);
173 t = xmlnode2str(x);
174 /* this is ugly, we can create the string here instead of jutil_header */
175 /* what do you think about it? -madcat */
176 t2 = strstr(t,"/>");
177 if (t2 != NULL)
179 *t2++ = '>';
180 *t2 = '\0';
182 jab_send_raw(j,"<?xml version='1.0'?>");
183 jab_send_raw(j,t);
184 xmlnode_free(x);
186 j->state = JCONN_STATE_ON;
187 STATE_EVT(JCONN_STATE_ON)
192 * jab_stop -- stop connection
194 * parameters
195 * j -- connection
197 void jab_stop(jconn j)
199 if(!j || j->state == JCONN_STATE_OFF) return;
201 j->state = JCONN_STATE_OFF;
202 cw_close(j->fd);
203 j->fd = -1;
204 XML_ParserFree(j->parser);
208 * jab_getfd -- get file descriptor of connection socket
210 * parameters
211 * j -- connection
213 * returns
214 * fd of the socket or -1 if socket was not connected
216 int jab_getfd(jconn j)
218 if(j)
219 return j->fd;
220 else
221 return -1;
225 * jab_getjid -- get jid structure of user
227 * parameters
228 * j -- connection
230 jid jab_getjid(jconn j)
232 if(j)
233 return(j->user);
234 else
235 return NULL;
238 /* jab_getsid -- get stream id
239 * This is the id of server's <stream:stream> tag and used for
240 * digest authorization.
242 * parameters
243 * j -- connection
245 char *jab_getsid(jconn j)
247 if(j)
248 return(j->sid);
249 else
250 return NULL;
254 * jab_getid -- get a unique id
256 * parameters
257 * j -- connection
259 char *jab_getid(jconn j)
261 snprintf(j->idbuf, 8, "%d", j->id++);
262 return &j->idbuf[0];
266 * jab_send -- send xml data
268 * parameters
269 * j -- connection
270 * x -- xmlnode structure
272 void jab_send(jconn j, xmlnode x)
274 if (j && j->state != JCONN_STATE_OFF)
276 char *buf = xmlnode2str(x);
277 if (buf) {
278 cw_write(j->fd, buf, strlen(buf), j->ssl);
279 if (j->logger)
280 (j->logger)(j, 0, buf);
283 #ifdef JDEBUG
284 printf ("out: %s\n", buf);
285 #endif
290 * jab_send_raw -- send a string
292 * parameters
293 * j -- connection
294 * str -- xml string
296 void jab_send_raw(jconn j, const char *str)
298 if (j && j->state != JCONN_STATE_OFF) {
299 cw_write(j->fd, str, strlen(str), j->ssl);
301 if (j->logger)
302 (j->logger)(j, 0, str);
305 #ifdef JDEBUG
306 printf ("out: %s\n", str);
307 #endif
311 * jab_recv -- read and parse incoming data
313 * parameters
314 * j -- connection
316 void jab_recv(jconn j)
318 static char buf[32768];
319 int len;
321 if(!j || j->state == JCONN_STATE_OFF)
322 return;
324 len = cw_read(j->fd, buf, sizeof(buf)-1, j->ssl);
325 if(len>0)
327 buf[len] = '\0';
329 if (j->logger)
330 (j->logger)(j, 1, buf);
332 #ifdef JDEBUG
333 printf (" in: %s\n", buf);
334 #endif
335 XML_Parse(j->parser, buf, len, 0);
337 else if(len<=0)
339 STATE_EVT(JCONN_STATE_OFF);
340 jab_stop(j);
345 * jab_poll -- check socket for incoming data
347 * parameters
348 * j -- connection
349 * timeout -- poll timeout
351 void jab_poll(jconn j, int timeout)
353 fd_set fds;
354 struct timeval tv;
355 int r;
357 if (!j || j->state == JCONN_STATE_OFF || j->fd == -1)
358 return;
360 FD_ZERO(&fds);
361 FD_SET(j->fd, &fds);
363 if(timeout <= 0) {
364 r = select(j->fd + 1, &fds, NULL, NULL, NULL);
366 } else {
367 tv.tv_sec = 0;
368 tv.tv_usec = timeout;
369 r = select(j->fd + 1, &fds, NULL, NULL, &tv);
373 if(r > 0) {
374 jab_recv(j);
376 } else if(r) {
377 STATE_EVT(JCONN_STATE_OFF);
378 jab_stop(j);
384 * jab_auth -- authorize user
386 * parameters
387 * j -- connection
389 * returns
390 * id of the iq packet
392 char *jab_auth(jconn j)
394 xmlnode x,y,z;
395 char *hash, *user, *id;
397 if(!j) return(NULL);
399 x = jutil_iqnew(JPACKET__SET, NS_AUTH);
400 id = jab_getid(j);
401 xmlnode_put_attrib(x, "id", id);
402 y = xmlnode_get_tag(x,"query");
404 user = j->user->user;
406 if (user)
408 z = xmlnode_insert_tag(y, "username");
409 xmlnode_insert_cdata(z, user, -1);
412 z = xmlnode_insert_tag(y, "resource");
413 xmlnode_insert_cdata(z, j->user->resource, -1);
415 if (j->sid)
417 z = xmlnode_insert_tag(y, "digest");
418 hash = pmalloc(x->p, strlen(j->sid)+strlen(j->pass)+1);
419 strcpy(hash, j->sid);
420 strcat(hash, j->pass);
421 hash = shahash(hash);
422 xmlnode_insert_cdata(z, hash, 40);
424 else
426 z = xmlnode_insert_tag(y, "password");
427 xmlnode_insert_cdata(z, j->pass, -1);
430 jab_send(j, x);
431 xmlnode_free(x);
432 return id;
436 * jab_reg -- register user
438 * parameters
439 * j -- connection
441 * returns
442 * id of the iq packet
444 char *jab_reg(jconn j)
446 xmlnode x,y,z;
447 char *hash, *user, *id;
449 if (!j) return(NULL);
451 x = jutil_iqnew(JPACKET__SET, NS_REGISTER);
452 id = jab_getid(j);
453 xmlnode_put_attrib(x, "id", id);
454 y = xmlnode_get_tag(x,"query");
456 user = j->user->user;
458 if (user)
460 z = xmlnode_insert_tag(y, "username");
461 xmlnode_insert_cdata(z, user, -1);
464 z = xmlnode_insert_tag(y, "resource");
465 xmlnode_insert_cdata(z, j->user->resource, -1);
467 if (j->pass)
469 z = xmlnode_insert_tag(y, "password");
470 xmlnode_insert_cdata(z, j->pass, -1);
473 jab_send(j, x);
474 xmlnode_free(x);
475 j->state = JCONN_STATE_ON;
476 STATE_EVT(JCONN_STATE_ON)
477 return id;
481 /* local functions */
483 static void startElement(void *userdata, const char *name, const char **attribs)
485 xmlnode x;
486 jconn j = (jconn)userdata;
488 if(j->current)
490 /* Append the node to the current one */
491 x = xmlnode_insert_tag(j->current, name);
492 xmlnode_put_expat_attribs(x, attribs);
494 j->current = x;
496 else
498 x = xmlnode_new_tag(name);
499 xmlnode_put_expat_attribs(x, attribs);
500 if(strcmp(name, "stream:stream") == 0) {
501 /* special case: name == stream:stream */
502 /* id attrib of stream is stored for digest auth */
503 j->sid = xmlnode_get_attrib(x, "id");
504 /* STATE_EVT(JCONN_STATE_AUTH) */
505 } else {
506 j->current = x;
511 static void endElement(void *userdata, const char *name)
513 jconn j = (jconn)userdata;
514 xmlnode x;
515 jpacket p;
517 if(j->current == NULL) {
518 /* we got </stream:stream> */
519 STATE_EVT(JCONN_STATE_OFF)
520 return;
523 x = xmlnode_get_parent(j->current);
525 if(x == NULL)
527 /* it is time to fire the event */
528 p = jpacket_new(j->current);
530 if(j->on_packet)
531 (j->on_packet)(j, p);
532 else
533 xmlnode_free(j->current);
536 j->current = x;
539 static void charData(void *userdata, const char *s, int slen)
541 jconn j = (jconn)userdata;
543 if (j->current)
544 xmlnode_insert_cdata(j->current, s, slen);
547 void cleanup_thread(void *arg)
549 struct pargs_r *argument = (struct pargs_r*)arg;
550 if(argument)
552 close(argument->fd_file);
553 close(argument->sock);
554 if(argument->hash)
555 free(argument->hash);
556 if(argument->rfile)
557 free(argument->rfile);
558 free(argument);
562 void jabber_send_file(jconn j, const char *filename, long int size, struct send_file *file, void *rfile, void (*function)(void *file, long int bytes, long int size, int status, int conn_type), int start_port, int end_port) //returning ip address and port after binding
564 #ifdef HAVE_THREAD
565 int sock;
566 struct sockaddr_in addr;
567 int optval = 1;
569 int fd_file = open(filename, O_RDONLY);
570 if( fd_file < 0 )
571 return;
573 sock = socket(AF_INET, SOCK_STREAM, 0);
574 if(sock < 0)
576 close( fd_file );
577 return;
580 addr.sin_family = AF_INET;
582 if( (start_port == 0) || (end_port == 0))
583 addr.sin_port = htons(0); //
584 else
585 addr.sin_port = htons(next_random(start_port,end_port)); //
587 struct sockaddr_in sa;
588 int sa_len = sizeof( sa );
589 getsockname( j->fd, (struct sockaddr *) &sa, &sa_len ); //geting address for bind
590 addr.sin_addr.s_addr = sa.sin_addr.s_addr;
592 // addr.sin_addr.s_addr = htonl(INADDR_ANY);
593 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)) < 0)
595 close( fd_file );
596 close( sock );
597 return;
599 if ( (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr)) ) < 0 )
603 if( errno == EADDRINUSE ) //select another port
604 addr.sin_port = htons(next_random(start_port, end_port)); //geting another random port
605 else
607 close( fd_file );
608 close( sock );
609 return;
612 while(bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr)) < 0);
614 listen(sock, 5);
615 file->host = strdup(inet_ntoa(sa.sin_addr));
617 getsockname( sock, (struct sockaddr *) &sa, &sa_len );
618 file->port = (int) ntohs(sa.sin_port);
620 struct pargs_r *arg;
621 arg = (struct pargs_r *)calloc(1, sizeof(struct pargs_r));
622 if( arg )
625 arg->sock = sock;
626 arg->fd_file = fd_file;
627 arg->size = size;
628 arg->rfile = rfile;
629 arg->hash = NULL;
630 arg->url = NULL;
631 arg->callback = function;
632 if (pthread_create(&file->thread, NULL, jabber_send_file_fd, (void *)arg ))
634 free( arg );
635 close( fd_file );
636 close( sock );
639 else
641 close( fd_file );
642 close( sock );
644 #endif
648 void *jabber_send_file_fd(void *arg)
650 #ifdef HAVE_THREAD
651 struct pargs_r *argument;
652 int sock;
653 int fd_file;
654 struct sockaddr_in *addr;
655 long int size;
656 argument = (struct pargs_r*)arg;
658 sock = argument->sock;
659 fd_file = argument->fd_file;
660 size = argument->size;
662 pthread_cleanup_push(cleanup_thread, argument);
663 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
665 int client;
666 char buff[SEND_BUF+1];
668 char sbuf[SEND_BUF+1];
670 int n;
671 int addr_size = sizeof( addr );
672 client = accept(sock, (struct sockaddr *) &addr, &addr_size);
673 int counter = 0;
674 if( client > 0 )
676 n = recv(client, buff, SEND_BUF, 0);
677 if( strstr( buff, "GET /" ) )
679 snprintf( sbuf, SEND_BUF, "%d\r\n\r\n", size );
680 strncpy( buff, "HTTP/1.0 200 OK\r\nContent-Type: application/data\r\nContent-Length: ", SEND_BUF );
681 strncat( buff, sbuf, SEND_BUF );
682 int str_len = strlen( buff );
683 send( client, buff, str_len, 0);
684 while( ( n = read( fd_file, buff, SEND_BUF )) > 0 )
686 counter += n;
687 if( send( client, buff, n, 0) != n )
688 break;
689 else
690 argument->callback(argument->rfile, counter, size, 0, 1);
693 close( client );
697 if( counter == size )
698 argument->callback(argument->rfile, counter, size, 1, 1);
699 else
700 argument->callback(argument->rfile, counter, size, 2, 1);
702 pthread_cleanup_pop(1);
704 pthread_exit(0);
705 #endif
708 int next_random( int start_port, int end_port ) //generate random number between two digits
710 srand( time( NULL ) );
711 return (start_port + rand() % (end_port-start_port+1));
714 void jabber_get_file(jconn j, const char *filename, long int size, struct send_file *file, void *rfile, void (*function)(void *file, long int bytes, long int size, int status, int conn_type) ) //returning ip address and port after binding
716 #ifdef HAVE_THREAD
717 int sock;
718 struct sockaddr_in addr;
719 int optval = 1;
720 char *ip_addr = file->host;
721 char *sid_from_to = file->sid_from_to;
722 int port = file->port;
724 int fd_file = open(filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
725 if( fd_file < 0 )
726 return;
728 sock = socket(AF_INET, SOCK_STREAM, 0);
729 if(sock < 0)
731 close( fd_file );
732 return;
735 struct hostent *host = gethostbyname(ip_addr);
736 bcopy(host->h_addr, &addr.sin_addr, host->h_length);
738 addr.sin_family = AF_INET;
739 addr.sin_port = htons( port );
742 if ( connect(sock, (struct sockaddr *) &addr, sizeof(addr) ) < 0 )
744 close( sock );
745 close( fd_file );
746 return;
751 char *hash = NULL;
752 if(sid_from_to)
753 hash = shahash(sid_from_to);
755 struct pargs_r *arg;
756 arg = (struct pargs_r *)calloc(1, sizeof(struct pargs_r));
757 if( arg )
760 arg->sock = sock;
761 arg->fd_file = fd_file;
762 arg->size = size;
763 if( hash )
764 arg->hash = strdup( hash );
765 arg->rfile = rfile;
766 arg->url = file->url;
767 arg->callback = function;
768 if(file->transfer_type == 0)
770 if (pthread_create(&file->thread, NULL, jabber_recieve_file_fd, (void *)arg ))
773 free( arg );
774 close( fd_file );
775 close( sock );
778 else
780 if (pthread_create(&file->thread, NULL, jabber_recieve_file_fd_http, (void *)arg ))
783 free( arg );
784 close( fd_file );
785 close( sock );
789 else
791 close( fd_file );
792 close( sock );
794 #endif
797 void *jabber_recieve_file_fd(void *arg)
799 #ifdef HAVE_THREAD
800 struct pargs_r *argument;
801 int sock;
802 int fd_file;
803 long int size;
804 char *hash;
805 argument = (struct pargs_r*)arg;
807 sock = argument->sock;
808 fd_file = argument->fd_file;
809 size = argument->size;
810 hash = argument->hash;
811 pthread_cleanup_push(cleanup_thread, argument);
812 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
814 char buff[SEND_BUF];
818 buff[0] = 0x05;
819 buff[1] = 0x01;
820 buff[2] = 0x00;
822 if( send( sock, buff, 3, 0 ) != 3 )
824 free(hash);
825 close(sock);
826 close(fd_file);
827 return;
830 recv( sock, buff, SEND_BUF, 0 );
831 if( buff[0] != 0x05 || buff[1] != 0x00 )
833 free(hash);
834 close(sock);
835 close(fd_file);
836 return;
839 //socks5 bytestream packet
840 buff[0] = 0x05;
841 buff[1] = 0x01;
842 buff[2] = 0x00;
843 buff[3] = 0x03;
844 buff[4] = 0x28;
845 strncpy( (char*)(buff + 5), hash, 40 );
846 buff[45] = 0x00;
847 buff[46] = 0x00;
849 if( send( sock, buff, 47, 0 ) != 47 )
851 free(hash);
852 close(sock);
853 close(fd_file);
854 return;
856 recv( sock, buff, 47, 0 );
857 if( buff[0] != 0x05 || buff[3] != 0x03 )
859 free(hash);
860 close(sock);
861 close(fd_file);
862 return;
866 int bytes = 0;
867 int counter = 0;
868 while(1)
870 bytes = recv( sock, buff, SEND_BUF, 0 );
871 if( bytes < 1 )
872 break;
873 else
875 write(fd_file, buff, bytes);
876 counter += bytes;
877 argument->callback(argument->rfile, counter, size, 0, 0);
881 if( counter == size )
882 argument->callback(argument->rfile, counter, size, 1, 0);
883 else
884 argument->callback(argument->rfile, counter, size, 2, 0);
886 pthread_cleanup_pop(1);
888 pthread_exit(0);
891 #endif
895 void *jabber_recieve_file_fd_http(void *arg)
897 #ifdef HAVE_THREAD
898 struct pargs_r *argument;
899 int sock;
900 int fd_file;
901 long int size;
902 char *url;
903 argument = (struct pargs_r*)arg;
905 sock = argument->sock;
906 fd_file = argument->fd_file;
907 size = argument->size;
908 url = argument->url;
909 pthread_cleanup_push(cleanup_thread, argument);
910 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
912 char buff[SEND_BUF];
914 snprintf( buff, SEND_BUF, "GET %s HTTP/1.0\r\n\r\n", url );
915 send( sock, buff, strlen(buff), 0 );
918 int but = recv( sock, buff, SEND_BUF, 0 );
919 if( strstr(buff, "200 OK" ) )
921 char *length = strstr( buff, "Content-Length:" );
922 sscanf( length, "Content-Length: %d", &size );
924 int i = 0;
925 while(i < but-4)
927 if(buff[i] == '\r' && buff[i+1] == '\n' && buff[i+2] == '\r' && buff[i+3] == '\n' )
929 break;
931 i++;
933 i = i + 4;
934 int bytes = 0;
935 int counter = 0;
937 bytes = but-i;
938 write(fd_file, (buff+i), bytes);
939 counter += bytes;
941 while(1)
943 bytes = recv( sock, buff, SEND_BUF, 0 );
944 if( bytes < 1 )
945 break;
946 else
948 write(fd_file, buff, bytes);
949 counter += bytes;
950 argument->callback(argument->rfile, counter, size, 0, 0);
954 if( counter == size )
955 argument->callback(argument->rfile, counter, size, 1, 0);
956 else
957 argument->callback(argument->rfile, counter, size, 2, 0);
960 pthread_cleanup_pop(1);
962 pthread_exit(0);
963 #endif