root/trunk/src/sessions.c

Revision 699, 29.8 kB (checked in by jajcus, 2 months ago)

- remove old timers on re-login (thx smoku)

Line 
1 /* $Id: sessions.c,v 1.105 2004/06/11 07:19:11 smoku Exp $ */
2
3 /*
4  *  (C) Copyright 2002-2006 Jacek Konieczny [jajcus(a)jajcus,net]
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License Version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include "ggtrans.h"
21 #include <libgadu.h>
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25 #include <ctype.h>
26 #include "sessions.h"
27 #include "iq.h"
28 #include "presence.h"
29 #include "users.h"
30 #include "jid.h"
31 #include "message.h"
32 #include "conf.h"
33 #include "status.h"
34 #include "requests.h"
35 #include "encoding.h"
36 #include "debug.h"
37
38
39 static int conn_timeout=30;
40 static int pong_timeout=30;
41 static int disconnect_delay=2;
42 static int ping_interval=10;
43 static int reconnect=0;
44 static GList *gg_servers=NULL;
45 GHashTable *sessions_jid;
46
47 static int session_try_login(Session *s);
48 static int session_destroy(Session *s);
49 static void resource_remove(Resource *r,int kill_session);
50 static int session_io_handler(Session *s);
51
52 static void session_stream_destroyed(gpointer key,gpointer value,gpointer user_data){
53 Session *s=(Session *)value;
54 Stream *stream=(Stream *)user_data;
55
56         if (s->s==stream) s->s=NULL;
57 }
58
59 static void sessions_stream_destroyed(struct stream_s *stream){
60
61         g_hash_table_foreach(sessions_jid,session_stream_destroyed,stream);
62 }
63
64 typedef struct {
65         GSource g_src;
66         Session *session;
67 }Source;
68
69 static gboolean session_g_source_prepare(GSource *source, gint* timeout_){
70
71         *timeout_=-1;
72
73         return FALSE;
74 }
75 static gboolean session_g_source_check(GSource *source){
76 Source *src=(Source *)(gpointer)source;
77
78         return src->session->g_pollfd.revents != 0;
79 }
80 static gboolean session_g_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data){
81 Source *src=(Source *)(gpointer)source;
82
83         session_io_handler(src->session);
84         return TRUE;
85 }
86
87 static GSourceFuncs source_funcs = {
88         session_g_source_prepare,
89         session_g_source_check,
90         session_g_source_dispatch,
91         NULL
92 };
93
94 static void session_setup_g_source(Session *s) {
95
96         if (!s->ggs) return;
97        
98         if (s->g_source && s->g_pollfd.fd != s->ggs->fd) {
99                 g_source_remove_poll(s->g_source, &s->g_pollfd);
100         }
101
102         if (!s->g_source) {
103                 s->g_source = g_source_new(&source_funcs, sizeof(Source));
104                 ((Source *)(gpointer)s->g_source)->session=s;
105                 g_source_attach(s->g_source,g_main_loop_get_context(main_loop));
106         }
107        
108         s->g_pollfd.events=G_IO_ERR|G_IO_HUP|G_IO_NVAL;
109         if (s->ggs->check&GG_CHECK_READ) s->g_pollfd.events|=G_IO_IN;
110         if (s->ggs->check&GG_CHECK_WRITE) s->g_pollfd.events|=G_IO_OUT;
111
112         if (s->g_pollfd.fd != s->ggs->fd) {
113                 s->g_pollfd.fd=s->ggs->fd;
114                 if (s->g_pollfd.fd!=-1) g_source_add_poll(s->g_source, &s->g_pollfd);
115         }
116 }
117
118 static void session_remove_g_source(Session *s) {
119
120         if (!s->g_source) return;
121
122         if (s->g_pollfd.fd!=-1) g_source_remove_poll(s->g_source, &s->g_pollfd);
123         g_source_destroy(s->g_source);
124         s->g_source=NULL;
125 }
126
127
128 int sessions_init(){
129 char *proxy_ip,*proxy_username,*proxy_password,*proxy_http_only;
130 char *p,*r;
131 int port;
132 int i;
133 xmlnode parent,tag;
134 GgServer *server;
135
136         stream_add_destroy_handler(sessions_stream_destroyed);
137
138         sessions_jid=g_hash_table_new(g_str_hash,g_str_equal);
139         if (!sessions_jid) return -1;
140
141         i=config_load_int("conn_timeout",0);
142         if (i>0) conn_timeout=i;
143         i=config_load_int("pong_timeout",0);
144         if (i>0) pong_timeout=i;
145         i=config_load_int("disconnect_delay",0);
146         if (i>0) disconnect_delay=i;
147         i=config_load_int("ping_interval",0);
148         if (i>0) ping_interval=i;
149         i=config_load_int("reconnect",0);
150         if (i>0) reconnect=i;
151
152
153         parent=xmlnode_get_tag(config,"servers");
154         if (parent && xmlnode_has_children(parent)){
155                 gg_servers=NULL;
156                 for(tag=xmlnode_get_firstchild(parent); tag!=NULL;
157                                 tag=xmlnode_get_nextsibling(tag)){
158                         if(xmlnode_get_type(tag) != NTYPE_TAG) continue;
159                         p=xmlnode_get_name(tag);
160                         if (strcmp(p, "hub")==0){
161                                 server=g_new(GgServer, 1);
162                                 server->port=1;
163                                 gg_servers=g_list_append(gg_servers, server);
164                         }
165                         else if (strcmp(p, "server")==0){
166                                 server=g_new(GgServer, 1);
167                                 r=xmlnode_get_attrib(tag, "port");
168                                 if (r)
169                                         server->port=atoi(r);
170                                 else
171                                         server->port=8074;
172                                 r=xmlnode_get_data(tag);
173                                 if(inet_aton(r, &server->addr))
174                                         gg_servers=g_list_append(gg_servers, server);
175                         }
176                         else continue;
177
178                         r=xmlnode_get_attrib(tag, "tls");
179                         if (r && !g_strcasecmp(r,"no"))
180                                 server->tls=0;
181                         else
182                                 server->tls=1;
183                 }
184
185
186         }
187         else{
188                 server=g_new(GgServer, 1);
189                 server->port=1;
190                 server->tls=0;
191                 gg_servers=g_list_append(gg_servers, server);
192
193                 server=g_new(GgServer, 1);
194                 inet_aton("217.17.45.143", &server->addr);
195                 server->port=8074;
196                 server->tls=0;
197                 gg_servers=g_list_append(gg_servers, server);
198         }
199
200         proxy_ip=config_load_string("proxy/ip");
201         if (!proxy_ip) return 0;
202         port=config_load_int("proxy/port",0);
203         if (port<=0) return 0;
204         proxy_username=config_load_string("proxy/username");
205         proxy_password=config_load_string("proxy/password");
206
207         tag=xmlnode_get_tag(config,"proxy");
208         proxy_http_only=xmlnode_get_attrib(tag,"http_only");
209
210         g_message(L_("Using proxy: http://%s:%i"),proxy_ip,port);
211         gg_proxy_enabled=1;
212         gg_proxy_host=proxy_ip;
213         gg_proxy_port=port;
214         if (proxy_username && proxy_password){
215                 gg_proxy_username=proxy_username;
216                 gg_proxy_password=proxy_password;
217         }
218         if (proxy_http_only && strcmp(proxy_http_only,"no")){
219                 gg_proxy_http_only=1;
220         }
221         return 0;
222 }
223
224 static gboolean sessions_hash_remove_func(gpointer key,gpointer value,gpointer udata){
225
226         session_destroy((Session *)value);
227         g_free(key);
228         return TRUE;
229 }
230
231 int sessions_done(){
232 guint s;
233 GList *it;
234
235         s=g_hash_table_size(sessions_jid);
236         debug(L_("%u sessions in hash table"),s);
237
238         g_hash_table_foreach_remove(sessions_jid,sessions_hash_remove_func,NULL);
239         g_hash_table_destroy(sessions_jid);
240
241         for(it=g_list_first(gg_servers);it;it=g_list_next(it))
242                 g_free(it->data);
243         g_list_free(gg_servers);
244
245         stream_del_destroy_handler(sessions_stream_destroyed);
246         return 0;
247 }
248
249 gboolean sessions_reconnect(gpointer data){
250 char *jid;
251
252         jid=(char *)data;
253         presence_send_probe(jabber_stream(),NULL,jid);
254         g_free(jid);
255         return FALSE;
256 }
257
258 void session_schedule_reconnect(Session *s){
259 int t;
260
261         s->current_server=g_list_first(gg_servers);
262         if (!reconnect) return;
263         t=(int)((reconnect*9.0/10.0)+(2.0*reconnect/10.0*rand()/(RAND_MAX+1.0)));
264         debug(L_("Sheduling reconnect in %u seconds"),t);
265         g_timeout_add(t*1000,sessions_reconnect,g_strdup(s->jid));
266 }
267
268 gboolean session_timeout(gpointer data){
269 Session *s;
270
271         g_assert(data!=NULL);
272         s=(Session *)data;
273         user_load_locale(s->user);
274
275         g_warning(N_("Timeout for server %u"),
276                         g_list_position(gg_servers, s->current_server));
277
278         s->current_server=g_list_next(s->current_server);
279         if(s->current_server!=NULL)
280                 if(!session_try_login(s))
281                         return FALSE;
282
283         s->timeout_func=0;
284         g_warning(N_("Session timeout for %s"),s->jid);
285
286         if (s->req_id){
287                 jabber_iq_send_error(s->s,s->jid,NULL,s->req_id,504,_("Remote Server Timeout"));
288         }
289         else{
290                 presence_send(s->s,NULL,s->user->jid,0,NULL,"Connection Timeout",0);
291         }
292
293         session_schedule_reconnect(s);
294         session_remove(s);
295         return FALSE;
296 }
297
298 gboolean session_ping(gpointer data){
299 Session *s;
300
301         debug("Ping...");
302         g_assert(data!=NULL);
303         s=(Session *)data;
304         if (s->waiting_for_pong){
305                 gg_ping(s->ggs); /* send ping, even if server doesn't respond
306                                     this one will be not counted for the ping delay*/
307                 return TRUE;
308         }
309
310         if (!s->ping_timer) s->ping_timer=g_timer_new();
311         else g_timer_reset(s->ping_timer);
312         g_timer_start(s->ping_timer);
313         gg_ping(s->ggs);
314         s->waiting_for_pong=TRUE;
315         if (s->timeout_func) g_source_remove(s->timeout_func);
316         s->timeout_func=g_timeout_add(pong_timeout*1000,session_timeout,s);
317         return TRUE;
318 }
319
320 int session_event_status(Session *s,int status,uin_t uin,char *desc,
321                                 int more,uint32_t ip,uint16_t port,uint32_t version){
322 int oldstat;
323 int available;
324 char *ujid;
325 char *show;
326 Contact *c;
327
328         c=user_get_contact(s->user,uin,FALSE);
329         if (c==NULL) {
330                 debug(L_("%s got notification from unknown contact %i, ignoring.."),s->user->jid,uin);
331                 return 0;
332         }
333         if (!c->got_probe && c->subscribe!=SUB_TO && c->subscribe!=SUB_BOTH) {
334                 debug(L_("%s got notification from contact %i whose presence was not requested, ignoring.."),s->user->jid,uin);
335                 return 0;
336         }
337
338         available=status_gg_to_jabber(status,&show,&desc);
339         oldstat=user_get_contact_status(s->user,uin);
340         user_set_contact_status(s->user,status,uin,desc,more,ip,port,version);
341
342         if (!available && oldstat==-1)
343                 ujid=jid_build(uin);
344         else
345                 ujid=jid_build_full(uin);
346
347         presence_send(s->s,ujid,s->user->jid,available,show,desc,0);
348         g_free(ujid);
349         return 0;
350 }
351
352
353 int session_event_notify(Session *s,struct gg_event *event){
354 int i;
355
356         for(i=0;event->event.notify[i].uin;i++)
357                 session_event_status(s,event->event.notify[i].status,
358                                 event->event.notify[i].uin,
359                                 NULL,
360                                 1,
361                                 event->event.notify[i].remote_ip,
362                                 event->event.notify[i].remote_port,
363                                 event->event.notify[i].version);
364         return 0;
365 }
366
367 int session_event_notify_descr(Session *s,struct gg_event *event){
368 int i;
369
370         for(i=0;event->event.notify_descr.notify[i].uin;i++)
371                 session_event_status(s,event->event.notify_descr.notify[i].status,
372                                 event->event.notify_descr.notify[i].uin,
373                                 event->event.notify_descr.descr,
374                                 1,
375                                 event->event.notify[i].remote_ip,
376                                 event->event.notify[i].remote_port,
377                                 event->event.notify[i].version);
378         return 0;
379 }
380
381 int session_event_notify60(Session *s,struct gg_event *event){
382 int i;
383
384         for(i=0;event->event.notify60[i].uin;i++)
385                 session_event_status(s,event->event.notify60[i].status,
386                                 event->event.notify60[i].uin,
387                                 event->event.notify60[i].descr,
388                                 1,
389                                 event->event.notify60[i].remote_ip,
390                                 event->event.notify60[i].remote_port,
391                                 event->event.notify60[i].version);
392         return 0;
393 }
394
395 void session_broken(Session *s){
396
397         if (s->req_id){
398                 jabber_iq_send_error(s->s,s->jid,NULL,s->req_id,502,_("Remote Server Error"));
399         }
400         else{
401                 GList *it;
402                 presence_send(s->s,NULL,s->user->jid,0,NULL,"Connection broken",0);
403                 for(it=s->user->contacts;it;it=it->next){
404                         Contact *c=(Contact *)it->data;
405
406                         if (!GG_S_NA(c->status) && c->status!=-1){
407                                 char *ujid;
408                                 ujid=jid_build_full(c->uin);
409                                 presence_send(s->s,ujid,s->user->jid,0,NULL,"Transport disconnected",0);
410                                 g_free(ujid);
411                         }
412                 }
413         }
414         s->connected=0;
415         session_schedule_reconnect(s);
416         session_remove(s);
417 }
418
419
420 int session_io_handler(Session *s){
421 struct gg_event *event;
422 char *jid,*str;
423 int chat;
424 GIOCondition condition=s->g_pollfd.revents;
425 time_t timestamp;
426
427         user_load_locale(s->user);
428         debug(L_("Checking error conditions..."));
429         if (condition&(G_IO_ERR|G_IO_NVAL)){
430                 if (condition&G_IO_ERR) g_warning(N_("Error on connection for %s ,[GGnumber: %i]"),s->jid,s->ggs->uin);
431                 if (condition&G_IO_HUP){
432                         g_warning(N_("Hangup on connection for %s ,[GGnumber: %i]"),s->jid,s->ggs->uin);
433                         s->current_server=g_list_next(s->current_server);
434                         if(!s->connected && s->current_server!=NULL){
435                                 session_try_login(s);
436                                 return FALSE;
437                         }
438                 }
439                 if (condition&G_IO_NVAL) g_warning(N_("Invalid channel on connection for %s"),s->jid);
440
441                 session_broken(s);
442                 return FALSE;
443         }
444
445         debug(L_("watching fd (gg_debug_level=%i)..."),gg_debug_level);
446         event=gg_watch_fd(s->ggs);
447         if (!event){
448                 g_warning(N_("Connection broken. Session of %s ,[GGnumber: %i]"),s->jid,s->ggs->uin);
449                 session_broken(s);
450                 return FALSE;
451         }
452
453         switch(event->type){
454                 case GG_EVENT_DISCONNECT:
455                         g_warning(N_("Server closed connection of %s, [GGnumber: %i]"),s->jid,s->ggs->uin);
456                         session_broken(s);
457                         gg_event_free(event);
458                         return FALSE;
459                 case GG_EVENT_CONN_FAILED:
460                         g_warning(N_("Login failed for %s ,[GGnumber: %i]"),s->jid,s->ggs->uin);
461                         if (s->req_id)
462                                 jabber_iq_send_error(s->s,s->jid,NULL,s->req_id,401,_("Unauthorized"));
463                         else presence_send(s->s,NULL,s->user->jid,0,NULL,"Login failed",0);
464                         if (!s->req_id)
465                                 session_schedule_reconnect(s);
466                         session_remove(s);
467                         gg_event_free(event);
468                         return FALSE;
469                 case GG_EVENT_CONN_SUCCESS:
470                         g_message(L_("Login succeed for %s ,[GGnumber: %i]"),s->jid,s->ggs->uin);
471                         if (s->req_id)
472                                 jabber_iq_send_result(s->s,s->jid,NULL,s->req_id,NULL);
473                         if (s->req_id){
474                                 free(s->req_id);
475                                 s->req_id=NULL;
476                         }
477                         if (s->query){
478                                 xmlnode_free(s->query);
479                                 s->query=NULL;
480                         }
481                         if (!s->user->confirmed){
482                                 s->user->confirmed=1;
483                                 user_save(s->user);
484                         }
485                         s->connected=1;
486                         session_send_status(s);
487                         session_send_notify(s);
488                         presence_send(s->s,NULL,s->user->jid,s->user->invisible?-1:1,NULL,s->gg_status_descr,0);
489
490                         if (s->timeout_func) g_source_remove(s->timeout_func);
491                         s->ping_timeout_func=
492                                 g_timeout_add(ping_interval*1000,session_ping,s);
493                         if (s->pubdir_change){
494                                 add_request(RT_CHANGE,s->jid,NULL,s->req_id,
495                                                         NULL,s->pubdir_change,s->s);
496                                 gg_pubdir50_free(s->pubdir_change);
497                                 s->pubdir_change=NULL;
498                         }
499                         if (s->get_roster){
500                                 gg_userlist_request(s->ggs, GG_USERLIST_GET, NULL);
501                         }
502                         break;
503                 case GG_EVENT_NOTIFY:
504                         session_event_notify(s,event);
505                         break;
506                 case GG_EVENT_NOTIFY_DESCR:
507                         session_event_notify_descr(s,event);
508                         break;
509                 case GG_EVENT_NOTIFY60:
510                         session_event_notify60(s,event);
511                         break;
512                 case GG_EVENT_STATUS:
513                         session_event_status(s,
514                                         event->event.status.status,
515                                         event->event.status.uin,
516                                         event->event.status.descr,
517                                         0,0,0,0);
518                         break;
519                 case GG_EVENT_STATUS60:
520                         session_event_status(s,
521                                         event->event.status60.status,
522                                         event->event.status60.uin,
523                                         event->event.status60.descr,
524                                         1,
525                                         event->event.status60.remote_ip,
526                                         event->event.status60.remote_port,
527                                         event->event.status60.version);
528                         break;
529                 case GG_EVENT_MSG:
530                         if (event->event.msg.recipients_count>1){
531                                 debug(L_("Dropped conference message: sender: %i class: %i time: %lu"),
532                                                         event->event.msg.sender,
533                                                         event->event.msg.msgclass,
534                                                         (unsigned long)event->event.msg.time);
535                                 break;
536                         }
537                         gg_messages_in++;
538                         debug(L_("Message: sender: %i class: %i time: %lu"),
539                                                         event->event.msg.sender,
540                                                         event->event.msg.msgclass,
541                                                         (unsigned long)event->event.msg.time);
542                        
543                         if (event->event.msg.sender==0){
544                                 if (!user_sys_msg_received(s->user,event->event.msg.msgclass)) break;
545                                 jid=jid_my_registered();
546                                 timestamp=event->event.msg.time;
547                                 str=g_strdup_printf(_("GG System message #%i"),
548                                                         event->event.msg.msgclass);
549                                 message_send_subject(s->s,jid,s->user->jid,str,
550                                                 to_utf8(event->event.msg.message),timestamp);
551                                 g_free(str);
552                                 jid=jid_my_registered();
553                                 break;
554                         }
555                         else{
556                                 Contact *c=user_get_contact(s->user,
557                                                 event->event.msg.sender,0);
558                                 if ((!c && s->user->ignore_unknown)
559                                                 || (c && c->ignored)) {
560                                         debug(L_("Ignoring the message."));
561                                         break;
562                                 }
563                                 jid=jid_build_full(event->event.msg.sender);
564                                 if ((event->event.msg.msgclass&GG_CLASS_CHAT)!=0) chat=1;
565                                 else chat=0;
566                         }
567                         if ((event->event.msg.msgclass&GG_CLASS_QUEUED)!=0){
568                                 timestamp=event->event.msg.time;
569                         }
570                         else timestamp=0;
571                         message_send(s->s,jid,s->user->jid,chat,
572                                         to_utf8(event->event.msg.message),timestamp);
573                         g_free(jid);
574                         break;
575                 case GG_EVENT_PONG:
576                         s->waiting_for_pong=FALSE;
577                         if (s->ping_timer){
578                                 g_timer_stop(s->ping_timer);
579                                 debug(L_("Pong! ping time: %fs"),
580                                                 g_timer_elapsed(s->ping_timer,NULL));
581                         }
582                         if (s->timeout_func) g_source_remove(s->timeout_func);
583                         break;
584                 case GG_EVENT_PUBDIR50_SEARCH_REPLY:
585                         request_response_search(event);
586                         break;
587                 case GG_EVENT_PUBDIR50_WRITE:
588                         request_response_write(event);
589                         break;
590                 case GG_EVENT_ACK:
591                         debug("GG_EVENT_ACK");
592                         break;
593                 case GG_EVENT_NONE:
594                         debug("GG_EVENT_NONE");
595                         break;
596                 case GG_EVENT_USERLIST:
597                         if(event->event.userlist.type==GG_USERLIST_GET_REPLY)
598                                 get_roster_done(s,event);
599                         else
600                                 g_warning(N_("Wrong gg userlist type: %i"),event->event.userlist.type);
601                         break;
602                 default:
603                         g_warning(N_("Unknown GG event: %i"),event->type);
604                         break;
605         }
606
607         session_setup_g_source(s);
608
609         gg_event_free(event);
610         debug(L_("io handler done..."));
611
612         return FALSE;
613 }
614
615 /* destroys Session object */
616 static int session_destroy(Session *s){
617 GList *it;
618
619         g_message(L_("Deleting session for '%s'"),s->jid);
620         if (s->ping_timeout_func) g_source_remove(s->ping_timeout_func);
621         if (s->timeout_func) g_source_remove(s->timeout_func);
622         if (s->ping_timer) g_timer_destroy(s->ping_timer);
623         session_remove_g_source(s);
624         while(s->resources) resource_remove((Resource *)s->resources->data,0);
625         if (s->ggs){
626                 if (s->connected){
627                         debug("gg_logoff(%p)",s->ggs);
628                         gg_logoff(s->ggs);
629                 }
630                 gg_free_session(s->ggs);
631         }
632         if (s->connected && s->s && s->jid){
633                 for(it=s->user->contacts;it;it=it->next){
634                         Contact *c=(Contact *)it->data;
635
636                         if (!GG_S_NA(c->status) && c->status!=-1){
637                                 char *ujid;
638                                 ujid=jid_build_full(c->uin);
639                                 presence_send(s->s,ujid,s->user->jid,0,NULL,"Transport disconnected",0);
640                                 g_free(ujid);
641                         }
642                 }
643         }
644         if (s->query) xmlnode_free(s->query);
645         if (s->user) user_unref(s->user);
646         if (s->gg_status_descr) g_free(s->gg_status_descr);
647         g_free(s);
648         return 0;
649 }
650
651 int session_remove(Session *s){
652 gpointer key,value;
653 char *njid;
654
655         if (s==NULL) return 1;
656         g_assert(sessions_jid!=NULL);
657         njid=jid_normalized(s->jid,0);
658         g_assert(njid!=NULL);
659         if (g_hash_table_lookup_extended(sessions_jid,(gpointer)njid,&key,&value)){
660                 g_hash_table_remove(sessions_jid,(gpointer)njid);
661                 g_free(key);
662         }
663         g_free(njid);
664         session_destroy(s);
665
666         return 0;
667 }
668
669 /* returns: -1 on error, 1 on status change, 0 on no change */
670 static int session_do_make_status(Session *s, Resource *r, gboolean send_presence){
671 int status;
672 char *status_descr;
673
674         if (!r) {
675                 if (send_presence) presence_send(s->s,NULL,s->user->jid,FALSE,NULL,s->gg_status_descr,0);
676                 s->gg_status=status_jabber_to_gg(0,NULL,s->gg_status_descr);
677                 return -1;
678         }
679         status_descr=s->user->status?s->user->status:r->status;
680         status=status_jabber_to_gg(r->available,r->show,status_descr);
681         if (s->user->invisible || r->available==-1){
682                 if(status_descr){
683                         status=GG_STATUS_INVISIBLE_DESCR;
684                 }else{
685                         status=GG_STATUS_INVISIBLE;
686                 }
687         }
688         else if (s->user->friends_only) status|=GG_STATUS_FRIENDS_MASK;
689
690         if (status==s->gg_status){
691                 if (status_descr!=NULL && s->gg_status_descr!=NULL
692                                 && !strcmp(status_descr,s->gg_status_descr)) return 0;
693                 if (status_descr==NULL && s->gg_status_descr==NULL) return 0;
694         }
695         g_free(s->gg_status_descr);
696         s->gg_status_descr=g_strdup(status_descr);
697         s->gg_status=status;
698         if (send_presence) {
699                 presence_send(s->s,NULL,s->user->jid,s->user->invisible?-1:r->available,r->show,s->gg_status_descr,0);
700         }
701         return 1;
702 }
703
704 Resource *session_get_cur_resource(Session *s){
705 GList *it;
706 Resource *r=NULL;
707 int maxprio;
708
709         maxprio=-129;
710         for(it=g_list_last(s->resources);it;it=it->prev){
711                 Resource *r1=(Resource *)it->data;
712                 if (r1->priority>maxprio){
713                         r=r1;
714                         maxprio=r1->priority;
715                 }
716         }
717         return r;
718 }
719
720 /* returns: -1 on error, 1 on status change, 0 on no change */
721 int session_make_status(Session *s, gboolean send_presence){
722 Resource *r;
723
724         r=session_get_cur_resource(s);
725         return session_do_make_status(s,r,send_presence);
726 }
727
728
729 static int session_try_login(Session *s){
730 struct gg_login_params login_params;
731 GgServer *serv;
732
733         g_warning(N_("Trying to log in on server %u"),
734                         g_list_position(gg_servers, s->current_server));
735
736         if (s->ping_timeout_func) g_source_remove(s->ping_timeout_func);
737         if (s->timeout_func) g_source_remove(s->timeout_func);
738         if (s->ping_timer) g_timer_destroy(s->ping_timer);
739         if (s->ggs) {
740                 gg_free_session(s->ggs);
741                 s->ggs=NULL;
742         }
743         session_remove_g_source(s);
744
745         memset(&login_params,0,sizeof(login_params));
746         login_params.uin=s->user->uin;
747         login_params.password=from_utf8(s->user->password);
748         login_params.async=1;
749         login_params.last_sysmsg=s->user->last_sys_msg;
750         login_params.protocol_version=GG_DEFAULT_PROTOCOL_VERSION;
751         login_params.status=GG_STATUS_INVISIBLE;
752
753         serv=(GgServer*)s->current_server->data;
754         if(serv->port!=1){
755                 login_params.server_addr=serv->addr.s_addr;
756                 login_params.server_port=serv->port;
757         }
758 #ifdef __GG_LIBGADU_HAVE_OPENSSL
759         debug(N_("Turning TLS %s"), serv->tls?"on":"off");
760         login_params.tls=serv->tls;
761 #endif
762
763         s->ggs=gg_login(&login_params);
764         if (!s->ggs){
765                 g_free(s);
766                 return 1;
767         }
768         session_setup_g_source(s);
769
770         s->timeout_func=g_timeout_add(conn_timeout*1000,session_timeout,s);
771         return FALSE;
772 }
773
774 Session * session_create(User *user,const char *jid,const char *req_id,
775                 const xmlnode query,struct stream_s *stream,int delay_login){
776 Session *s;
777 char *njid;
778
779         g_message(L_("Creating session for '%s'"),jid);
780         njid=jid_normalized(jid,0);
781         if (njid==NULL){
782                 g_message(L_("Bad JID: '%s'"),jid);
783                 return NULL;
784         }
785
786         g_assert(user!=NULL);
787
788         if (user->deleted){
789                 g_message(L_("User deleted: '%s'"),user->jid);
790                 return NULL;
791         }
792        
793         s=g_new0(Session,1);
794         s->user=user;
795         user_ref(user);
796         s->gg_status=-1;
797         s->jid=g_strdup(jid);
798         if (req_id) s->req_id=g_strdup(req_id);
799         s->query=xmlnode_dup(query);
800         s->current_server=g_list_first(gg_servers);
801         s->g_pollfd.fd=-1;
802
803         if (!delay_login && session_try_login(s)) return NULL;
804
805         s->s=stream;
806
807         g_assert(sessions_jid!=NULL);
808         g_hash_table_insert(sessions_jid,(gpointer)njid,(gpointer)s);
809         return s;
810 }
811
812 Session *session_get_by_jid(const char *jid,Stream *stream,int delay_login){
813 Session *s;
814 User *u;
815 char *njid;
816 GList *it;
817
818         g_assert(sessions_jid!=NULL);
819         debug(L_("Looking up session for '%s'"),jid);
820         njid=jid_normalized(jid,0);
821         if (njid==NULL){
822                 g_message(L_("Bad JID: '%s'"),jid);
823                 return NULL;
824         }
825
826         debug(L_("Using '%s' as key"),njid);
827         s=(Session *)g_hash_table_lookup(sessions_jid,(gpointer)njid);
828         g_free(njid);
829         if (s) return s;
830         debug(L_("Session not found"));
831         if (!stream) return NULL;