1 /*
2  * Manages the available connections (note the file is misnamed)
3  *
4  * climm Copyright (C) © 2001-2010 Rüdiger Kuhlmann
5  *
6  * climm is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 dated June, 1991.
9  *
10  * climm is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13  * License for more details.
14  *
15  * In addition, as a special exception permission is granted to link the
16  * code of this release of climm with the OpenSSL project's "OpenSSL"
17  * library, and distribute the linked executables.  You must obey the GNU
18  * General Public License in all respects for all of the code used other
19  * than "OpenSSL".  If you modify this file, you may extend this exception
20  * to your version of the file, but you are not obligated to do so.  If you
21  * do not wish to do so, delete this exception statement from your version
22  * of this file.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this package; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  * 02111-1307, USA.
28  *
29  * $Id: connection.c 2864 2010-03-16 22:48:25Z kuhlmann $
30  */
31 
32 #include "climm.h"
33 #include "connection.h"
34 #include "preferences.h"
35 #include "util_ui.h"
36 #include "oscar_dc.h"
37 #if HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #if HAVE_WINSOCK2_H
41 #include <winsock2.h>
42 #endif
43 #include <assert.h>
44 
45 #define ServerListLen      5
46 #define ConnectionListLen 10
47 
48 typedef struct ConnectionList_s ConnectionList;
49 typedef struct ServerList_s     ServerList;
50 
51 struct ConnectionList_s
52 {
53     ConnectionList *more;
54     Connection     *conn[ConnectionListLen];
55 };
56 
57 
58 struct ServerList_s
59 {
60     ServerList *more;
61     Server     *serv[ServerListLen];
62 };
63 
64 
65 static ConnectionList clist = { NULL, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
66 static ServerList     slist = { NULL, {0, 0, 0, 0, 0} };
67 
68 /*
69  * Creates a new session.
70  * Returns NULL if not enough memory available.
71  */
72 #undef ServerC
ServerC(UWORD type DEBUGPARAM)73 Server *ServerC (UWORD type DEBUGPARAM)
74 {
75     Connection *conn;
76     Server *serv;
77     ServerList *cl;
78     int i, j;
79 
80     conn = ConnectionC (type & ~TYPEF_ANY_SERVER);
81     if (!conn)
82         return NULL;
83 
84     cl = &slist;
85     i = j = 0;
86     while (cl->serv[ServerListLen - 1] && cl->more)
87         cl = cl->more, j += ServerListLen;
88     if (cl->serv[ServerListLen - 1])
89     {
90         cl->more = calloc (1, sizeof (ServerList));
91 
92         if (!cl->more)
93             return NULL;
94 
95         cl = cl->more;
96         j += ServerListLen;
97     }
98     while (cl->serv[i])
99         i++, j++;
100 
101     if (!(serv = calloc (1, sizeof (Server))))
102     {
103         ConnectionD (conn);
104         return NULL;
105     }
106     cl->serv[i] = serv;
107 
108     conn->serv = serv;
109     serv->conn = conn;
110 
111     serv->logfd = -1;
112     serv->flags = 0;
113     serv->status = ims_offline;
114     assert (type & TYPEF_ANY_SERVER);
115     serv->type = type;
116 
117     Debug (DEB_CONNECT, "<=S= %p create %d", serv, serv->type);
118     return serv;
119 }
120 
121 /*
122  * Creates a new session.
123  * Returns NULL if not enough memory available.
124  */
125 #undef ConnectionC
ConnectionC(UWORD type DEBUGPARAM)126 Connection *ConnectionC (UWORD type DEBUGPARAM)
127 {
128     ConnectionList *cl;
129     Connection *conn;
130     int i, j;
131 
132     cl = &clist;
133     i = j = 0;
134     assert (~type & TYPEF_ANY_SERVER);
135     while (cl->conn[ConnectionListLen - 1] && cl->more)
136         cl = cl->more, j += ConnectionListLen;
137     if (cl->conn[ConnectionListLen - 1])
138     {
139         cl->more = calloc (1, sizeof (ConnectionList));
140 
141         if (!cl->more)
142             return NULL;
143 
144         cl = cl->more;
145         j += ConnectionListLen;
146     }
147     while (cl->conn[i])
148         i++, j++;
149 
150     if (!(conn = calloc (1, sizeof (Connection))))
151         return NULL;
152     cl->conn[i] = conn;
153 
154     conn->our_local_ip   = 0x7f000001;
155     conn->our_outside_ip = 0x7f000001;
156     conn->sok = -1;
157     conn->type = type;
158 
159     Debug (DEB_CONNECT, "<=== %p[%d] create %d", conn, j, type);
160 
161     return conn;
162 }
163 
164 /*
165  * Clones an existing session, while blanking out some values.
166  * Returns NULL if not enough memory available.
167  */
168 #undef ServerChild
ServerChild(Server * serv,Contact * cont,UWORD type DEBUGPARAM)169 Connection *ServerChild (Server *serv, Contact *cont, UWORD type DEBUGPARAM)
170 {
171     Connection *child;
172 
173     child = ConnectionC (type DEBUGFOR);
174     if (!child)
175         return NULL;
176 
177     child->serv   = serv;
178     child->cont   = cont;
179 
180     Debug (DEB_CONNECT, "<=*= %p (%s) clone from %p (%s)",
181         child, ConnectionStrType (child), serv, ServerStrType (serv));
182 
183     return child;
184 }
185 
186 /*
187  * Returns the n-th connection.
188  */
ConnectionNr(int i)189 Connection *ConnectionNr (int i)
190 {
191     ConnectionList *cl;
192 
193     for (cl = &clist; cl && i >= ConnectionListLen; cl = cl->more)
194         i -= ConnectionListLen;
195 
196     if (!cl || i < 0)
197         return NULL;
198 
199     return cl->conn[i];
200 }
201 
202 /*
203  * Returns the n-th session.
204  */
ServerNr(int i)205 Server *ServerNr (int i)
206 {
207     ServerList *cl;
208 
209     for (cl = &slist; cl && i >= ServerListLen; cl = cl->more)
210         i -= ServerListLen;
211 
212     if (!cl || i < 0)
213         return NULL;
214 
215     return cl->serv[i];
216 }
217 
218 /*
219  * Finds a session of given type and/or given uin.
220  * Actually, you may specify TYPEF_* here that must all be set.
221  * The parent is the session this one has to have as parent.
222  */
ServerFindChild(const Server * parent,const Contact * cont,UWORD type)223 Connection *ServerFindChild (const Server *parent, const Contact *cont, UWORD type)
224 {
225     ConnectionList *cl;
226     Connection *conn;
227     int i;
228 
229     if (parent)
230     {
231         if (cont)
232         {
233             for (cl = &clist; cl; cl = cl->more)
234                 for (i = 0; i < ConnectionListLen; i++)
235                     if ((conn = cl->conn[i]) && (conn->type & type) == type && conn->cont == cont && conn->serv == parent)
236                         return conn;
237         }
238         else
239             for (cl = &clist; cl; cl = cl->more)
240                 for (i = 0; i < ConnectionListLen; i++)
241                     if ((conn = cl->conn[i]) && (conn->type & type) == type && (conn->connect & CONNECT_OK) && conn->serv == parent)
242                         return conn;
243     }
244     else
245     {
246         if (cont)
247         {
248             for (cl = &clist; cl; cl = cl->more)
249                 for (i = 0; i < ConnectionListLen; i++)
250                     if ((conn = cl->conn[i]) && (conn->type & type) == type && conn->cont == cont)
251                         return conn;
252         }
253         else
254         {
255             for (cl = &clist; cl; cl = cl->more)
256                 for (i = 0; i < ConnectionListLen; i++)
257                     if ((conn = cl->conn[i]) && (conn->type & type) == type && (conn->connect & CONNECT_OK))
258                         return conn;
259             for (cl = &clist; cl; cl = cl->more)
260                 for (i = 0; i < ConnectionListLen; i++)
261                     if ((conn = cl->conn[i]) && (conn->type & type) == type)
262                         return conn;
263         }
264     }
265     return NULL;
266 }
267 
268 /*
269  * Finds a session of given type and given screen.
270  * Actually, you may specify TYPEF_* here that must all be set.
271  */
ServerFindScreen(UWORD type,const char * screen)272 Server *ServerFindScreen (UWORD type, const char *screen)
273 {
274     ServerList *cl;
275     Server *serv;
276     int i;
277 
278     assert (type);
279     assert (screen);
280     assert (type & TYPEF_ANY_SERVER);
281 
282     for (cl = &slist; cl; cl = cl->more)
283         for (i = 0; i < ServerListLen; i++)
284             if ((serv = cl->serv[i]) && (serv->type & type) == type
285                 && serv->screen && !strcmp (serv->screen, screen))
286                 return serv;
287     return NULL;
288 }
289 
290 /*
291  * Finds the index of this session.
292  */
ConnectionFindNr(const Connection * conn)293 UDWORD ConnectionFindNr (const Connection *conn)
294 {
295     ConnectionList *cl;
296     int i, j;
297 
298     if (!conn)
299         return -1;
300 
301     for (i = 0, cl = &clist; cl; cl = cl->more)
302         for (j = i; i < j + ConnectionListLen; i++)
303             if (cl->conn[i % ConnectionListLen] == conn)
304                 return i;
305     return -1;
306 }
307 
308 /*
309  * Finds the index of this session.
310  */
ServerFindNr(const Server * serv)311 UDWORD ServerFindNr (const Server *serv)
312 {
313     ServerList *cl;
314     int i, j;
315 
316     if (!serv)
317         return -1;
318 
319     for (i = 0, cl = &slist; cl; cl = cl->more)
320         for (j = i; i < j + ServerListLen; i++)
321             if (cl->serv[i % ServerListLen] == serv)
322                 return i;
323     return -1;
324 }
325 
326 /*
327  * Closes and removes a session.
328  */
329 #undef ServerD
ServerD(Server * serv DEBUGPARAM)330 void ServerD (Server *serv DEBUGPARAM)
331 {
332     ConnectionList *cl;
333     Connection *clc;
334     int j;
335 
336     s_repl (&serv->screen, NULL);
337     for (cl = &clist; cl; cl = cl->more)
338         for (j = 0; j < ConnectionListLen; j++)
339             if ((clc = cl->conn[j]) && clc->serv == serv)
340             {
341                 clc->serv = NULL;
342                 ConnectionD (clc);
343                 cl = &clist;
344                 j = -1;
345             }
346 
347     ConnectionD (serv->conn);
348     serv->conn = NULL;
349     free (serv);
350 
351     Debug (DEB_CONNECT, "=S=> %p closed.", serv);
352 }
353 
354 /*
355  * Closes and removes a session.
356  */
357 #undef ConnectionD
ConnectionD(Connection * conn DEBUGPARAM)358 void ConnectionD (Connection *conn DEBUGPARAM)
359 {
360     ConnectionList *cl;
361     Connection *clc;
362     int i, j, k;
363 
364     if ((i = ConnectionFindNr (conn)) == -1)
365         return;
366 
367     Debug (DEB_CONNECT, "===> %p[%d] (%s) closing...", conn, i, ConnectionStrType (conn));
368 
369     UtilIOClose (conn);
370 
371     if ((i = ConnectionFindNr (conn)) == -1)
372         return;
373 
374     conn->sok     = -1;
375     conn->connect = 0;
376     conn->serv  = NULL;
377     s_free (conn->server);
378     conn->server  = NULL;
379 
380     for (cl = &clist; cl; cl = cl->more)
381         for (j = 0; j < ConnectionListLen; j++)
382             if ((clc = cl->conn[j]) && clc->oscar_file == conn)
383                 clc->oscar_file = NULL;
384 
385     QueueCancel (conn);
386 
387     for (k = 0, cl = &clist; cl; cl = cl->more)
388     {
389         for (i = 0; i < ConnectionListLen; i++, k++)
390             if (cl->conn[i] == conn)
391                 break;
392         if (i < ConnectionListLen)
393             break;
394     }
395 
396     while (cl)
397     {
398         for (j = i; j + 1 < ConnectionListLen; j++)
399             cl->conn[j] = cl->conn[j + 1];
400         cl->conn[j] = cl->more ? cl->more->conn[0] : NULL;
401         i = 0;
402         cl = cl->more;
403     }
404 
405     Debug (DEB_CONNECT, "===> %p[%d] closed.", conn, k);
406     free (conn);
407 }
408 
409 /*
410  * Returns a string describing the session's type.
411  */
ConnectionStrType(Connection * conn)412 const char *ConnectionStrType (Connection *conn)
413 {
414     switch (conn->type) {
415         case TYPE_XMPP_SERVER & ~TYPEF_ANY_SERVER:
416             return i18n (2730, "xmpp main i/o");
417         case TYPE_MSN_SERVER & ~TYPEF_ANY_SERVER:
418             return i18n (2731, "msn main i/o");
419         case TYPE_SERVER & ~TYPEF_ANY_SERVER:
420             return i18n (2732, "icq main i/o");
421         case TYPE_MSN_TEMP:
422             return i18n (2584, "msn temp");
423         case TYPE_MSN_CHAT:
424             return i18n (2586, "msn chat");
425         case TYPE_MSGLISTEN:
426             return i18n (1947, "listener");
427         case TYPE_MSGDIRECT:
428             return i18n (1890, "peer-to-peer");
429         case TYPE_FILELISTEN:
430             return i18n (2089, "file listener");
431         case TYPE_FILEDIRECT:
432             return i18n (2090, "file peer-to-peer");
433         case TYPE_FILE:
434             return i18n (2067, "file io");
435         case TYPE_XMPPDIRECT:
436             return i18n (2733, "xmpp peer-to-peer");
437         case TYPE_FILEXMPP:
438             return i18n (2734, "xmpp file io");
439         case TYPE_REMOTE:
440             return i18n (2225, "scripting");
441         default:
442             return i18n (1745, "unknown");
443     }
444 }
445 
ServerStrType(Server * serv)446 const char *ServerStrType (Server *serv)
447 {
448     switch (serv->type) {
449         case TYPE_XMPP_SERVER:
450             return i18n (2604, "xmpp");
451         case TYPE_MSN_SERVER:
452             return i18n (2735, "msn");
453         case TYPE_SERVER:
454             return i18n (2736, "icq");
455         default:
456             return i18n (1745, "unknown");
457     }
458 }
459 
ConnectionServerType(UWORD type)460 const char *ConnectionServerType (UWORD type)
461 {
462     switch (type) {
463         case TYPE_XMPP_SERVER:
464             return "xmpp";
465         case TYPE_MSN_SERVER:
466             return "msn";
467         case TYPE_SERVER:
468             return "icq8";
469         case TYPE_MSGLISTEN:
470             return "peer";
471         case TYPE_REMOTE:
472             return "remote";
473         default:
474             return "unknown";
475     }
476 }
477 
ConnectionServerNType(const char * type,char del)478 UWORD ConnectionServerNType (const char *type, char del)
479 {
480     if (!strncmp (type, "icq8", 4)   && type[4] == del) return TYPE_SERVER;
481     if (!strncmp (type, "peer", 4)   && type[4] == del) return TYPE_MSGLISTEN;
482     if (!strncmp (type, "remote", 6) && type[6] == del) return TYPE_REMOTE;
483     if (!strncmp (type, "msn", 3)    && type[3] == del) return TYPE_MSN_SERVER;
484     if (!strncmp (type, "jabber", 6) && type[6] == del) return TYPE_XMPP_SERVER;
485     if (!strncmp (type, "xmpp", 4)   && type[4] == del) return TYPE_XMPP_SERVER;
486     return 0;
487 }
488 
489 
490 /*
491  * Query an option for a contact group
492  */
ServerPrefVal(Server * serv,UDWORD flag)493 val_t ServerPrefVal (Server *serv, UDWORD flag)
494 {
495     val_t res = 0;
496     if (serv->contacts && OptGetVal (&serv->copts, flag, &res))
497         return res;
498     if (OptGetVal (&prG->copts, flag, &res))
499         return res;
500     return 0;
501 }
502 
503 /*
504  * Query an option for a contact group
505  */
ServerPrefStr(Server * serv,UDWORD flag)506 const char *ServerPrefStr (Server *serv, UDWORD flag)
507 {
508     const char *res = 0;
509     if (serv->contacts && OptGetStr (&serv->copts, flag, &res))
510         return res;
511     if (OptGetStr (&prG->copts, flag, &res))
512         return res;
513     return 0;
514 }
515 
516