| |
1 /* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */ |
| |
2 |
| |
3 /* |
| |
4 * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> |
| |
5 * Robert J. Woźny <speedy@ziew.org> |
| |
6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org> |
| |
7 * Tomasz Chiliński <chilek@chilan.com> |
| |
8 * |
| |
9 * This program is free software; you can redistribute it and/or modify |
| |
10 * it under the terms of the GNU Lesser General Public License Version |
| |
11 * 2.1 as published by the Free Software Foundation. |
| |
12 * |
| |
13 * This program is distributed in the hope that it will be useful, |
| |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 * GNU Lesser General Public License for more details. |
| |
17 * |
| |
18 * You should have received a copy of the GNU Lesser General Public |
| |
19 * License along with this program; if not, write to the Free Software |
| |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, |
| |
21 * USA. |
| |
22 */ |
| |
23 |
| |
24 #include <sys/types.h> |
| |
25 #ifndef _WIN32 |
| |
26 #include <sys/wait.h> |
| |
27 #include <sys/socket.h> |
| |
28 #include <netinet/in.h> |
| |
29 #include <arpa/inet.h> |
| |
30 #ifdef sun |
| |
31 # include <sys/filio.h> |
| |
32 #endif |
| |
33 #else |
| |
34 #include <io.h> |
| |
35 #include <fcntl.h> |
| |
36 #include <errno.h> |
| |
37 #define SHUT_RDWR SD_BOTH |
| |
38 #endif |
| |
39 |
| |
40 #include "libgadu-config.h" |
| |
41 |
| |
42 #include <errno.h> |
| |
43 #ifndef _WIN32 |
| |
44 #include <netdb.h> |
| |
45 #endif |
| |
46 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
| |
47 # include <pthread.h> |
| |
48 #endif |
| |
49 #include <stdarg.h> |
| |
50 #include <stdio.h> |
| |
51 #include <stdlib.h> |
| |
52 #include <string.h> |
| |
53 #include <unistd.h> |
| |
54 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
55 # include <openssl/err.h> |
| |
56 # include <openssl/rand.h> |
| |
57 #endif |
| |
58 |
| |
59 #include "compat.h" |
| |
60 #include "libgadu.h" |
| |
61 |
| |
62 int gg_debug_level = 0; |
| |
63 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; |
| |
64 |
| |
65 int gg_dcc_port = 0; |
| |
66 unsigned long gg_dcc_ip = 0; |
| |
67 |
| |
68 unsigned long gg_local_ip = 0; |
| |
69 /* |
| |
70 * zmienne opisujące parametry proxy http. |
| |
71 */ |
| |
72 char *gg_proxy_host = NULL; |
| |
73 int gg_proxy_port = 0; |
| |
74 int gg_proxy_enabled = 0; |
| |
75 int gg_proxy_http_only = 0; |
| |
76 char *gg_proxy_username = NULL; |
| |
77 char *gg_proxy_password = NULL; |
| |
78 |
| |
79 #ifndef lint |
| |
80 static char rcsid[] |
| |
81 #ifdef __GNUC__ |
| |
82 __attribute__ ((unused)) |
| |
83 #endif |
| |
84 = "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $"; |
| |
85 #endif |
| |
86 |
| |
87 #ifdef _WIN32 |
| |
88 /** |
| |
89 * Deal with the fact that you can't select() on a win32 file fd. |
| |
90 * This makes it practically impossible to tie into gaim's event loop. |
| |
91 * |
| |
92 * -This is thanks to Tor Lillqvist. |
| |
93 * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. |
| |
94 */ |
| |
95 static int |
| |
96 socket_pipe (int *fds) |
| |
97 { |
| |
98 SOCKET temp, socket1 = -1, socket2 = -1; |
| |
99 struct sockaddr_in saddr; |
| |
100 int len; |
| |
101 u_long arg; |
| |
102 fd_set read_set, write_set; |
| |
103 struct timeval tv; |
| |
104 |
| |
105 temp = socket(AF_INET, SOCK_STREAM, 0); |
| |
106 |
| |
107 if (temp == INVALID_SOCKET) { |
| |
108 goto out0; |
| |
109 } |
| |
110 |
| |
111 arg = 1; |
| |
112 if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { |
| |
113 goto out0; |
| |
114 } |
| |
115 |
| |
116 memset(&saddr, 0, sizeof(saddr)); |
| |
117 saddr.sin_family = AF_INET; |
| |
118 saddr.sin_port = 0; |
| |
119 saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| |
120 |
| |
121 if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { |
| |
122 goto out0; |
| |
123 } |
| |
124 |
| |
125 if (listen(temp, 1) == SOCKET_ERROR) { |
| |
126 goto out0; |
| |
127 } |
| |
128 |
| |
129 len = sizeof(saddr); |
| |
130 if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { |
| |
131 goto out0; |
| |
132 } |
| |
133 |
| |
134 socket1 = socket(AF_INET, SOCK_STREAM, 0); |
| |
135 |
| |
136 if (socket1 == INVALID_SOCKET) { |
| |
137 goto out0; |
| |
138 } |
| |
139 |
| |
140 arg = 1; |
| |
141 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { |
| |
142 goto out1; |
| |
143 } |
| |
144 |
| |
145 if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || |
| |
146 WSAGetLastError() != WSAEWOULDBLOCK) { |
| |
147 goto out1; |
| |
148 } |
| |
149 |
| |
150 FD_ZERO(&read_set); |
| |
151 FD_SET(temp, &read_set); |
| |
152 |
| |
153 tv.tv_sec = 0; |
| |
154 tv.tv_usec = 0; |
| |
155 |
| |
156 if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { |
| |
157 goto out1; |
| |
158 } |
| |
159 |
| |
160 if (!FD_ISSET(temp, &read_set)) { |
| |
161 goto out1; |
| |
162 } |
| |
163 |
| |
164 socket2 = accept(temp, (struct sockaddr *) &saddr, &len); |
| |
165 if (socket2 == INVALID_SOCKET) { |
| |
166 goto out1; |
| |
167 } |
| |
168 |
| |
169 FD_ZERO(&write_set); |
| |
170 FD_SET(socket1, &write_set); |
| |
171 |
| |
172 tv.tv_sec = 0; |
| |
173 tv.tv_usec = 0; |
| |
174 |
| |
175 if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { |
| |
176 goto out2; |
| |
177 } |
| |
178 |
| |
179 if (!FD_ISSET(socket1, &write_set)) { |
| |
180 goto out2; |
| |
181 } |
| |
182 |
| |
183 arg = 0; |
| |
184 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { |
| |
185 goto out2; |
| |
186 } |
| |
187 |
| |
188 arg = 0; |
| |
189 if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { |
| |
190 goto out2; |
| |
191 } |
| |
192 |
| |
193 fds[0] = socket1; |
| |
194 fds[1] = socket2; |
| |
195 |
| |
196 closesocket (temp); |
| |
197 |
| |
198 return 0; |
| |
199 |
| |
200 out2: |
| |
201 closesocket (socket2); |
| |
202 out1: |
| |
203 closesocket (socket1); |
| |
204 out0: |
| |
205 closesocket (temp); |
| |
206 errno = EIO; /* XXX */ |
| |
207 |
| |
208 return -1; |
| |
209 } |
| |
210 #endif |
| |
211 |
| |
212 /* |
| |
213 * gg_libgadu_version() |
| |
214 * |
| |
215 * zwraca wersję libgadu. |
| |
216 * |
| |
217 * - brak |
| |
218 * |
| |
219 * wersja libgadu. |
| |
220 */ |
| |
221 const char *gg_libgadu_version() |
| |
222 { |
| |
223 return GG_LIBGADU_VERSION; |
| |
224 } |
| |
225 |
| |
226 /* |
| |
227 * gg_fix32() |
| |
228 * |
| |
229 * zamienia kolejność bajtów w liczbie 32-bitowej tak, by odpowiadała |
| |
230 * kolejności bajtów w protokole GG. ze względu na LE-owość serwera, |
| |
231 * zamienia tylko na maszynach BE-wych. |
| |
232 * |
| |
233 * - x - liczba do zamiany |
| |
234 * |
| |
235 * liczba z odpowiednią kolejnością bajtów. |
| |
236 */ |
| |
237 uint32_t gg_fix32(uint32_t x) |
| |
238 { |
| |
239 #ifndef __GG_LIBGADU_BIGENDIAN |
| |
240 return x; |
| |
241 #else |
| |
242 return (uint32_t) |
| |
243 (((x & (uint32_t) 0x000000ffU) << 24) | |
| |
244 ((x & (uint32_t) 0x0000ff00U) << 8) | |
| |
245 ((x & (uint32_t) 0x00ff0000U) >> 8) | |
| |
246 ((x & (uint32_t) 0xff000000U) >> 24)); |
| |
247 #endif |
| |
248 } |
| |
249 |
| |
250 /* |
| |
251 * gg_fix16() |
| |
252 * |
| |
253 * zamienia kolejność bajtów w liczbie 16-bitowej tak, by odpowiadała |
| |
254 * kolejności bajtów w protokole GG. ze względu na LE-owość serwera, |
| |
255 * zamienia tylko na maszynach BE-wych. |
| |
256 * |
| |
257 * - x - liczba do zamiany |
| |
258 * |
| |
259 * liczba z odpowiednią kolejnością bajtów. |
| |
260 */ |
| |
261 uint16_t gg_fix16(uint16_t x) |
| |
262 { |
| |
263 #ifndef __GG_LIBGADU_BIGENDIAN |
| |
264 return x; |
| |
265 #else |
| |
266 return (uint16_t) |
| |
267 (((x & (uint16_t) 0x00ffU) << 8) | |
| |
268 ((x & (uint16_t) 0xff00U) >> 8)); |
| |
269 #endif |
| |
270 } |
| |
271 |
| |
272 /* |
| |
273 * gg_login_hash() // funkcja wewnętrzna |
| |
274 * |
| |
275 * liczy hash z hasła i danego seeda. |
| |
276 * |
| |
277 * - password - hasło do hashowania |
| |
278 * - seed - wartość podana przez serwer |
| |
279 * |
| |
280 * hash. |
| |
281 */ |
| |
282 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) |
| |
283 { |
| |
284 unsigned int x, y, z; |
| |
285 |
| |
286 y = seed; |
| |
287 |
| |
288 for (x = 0; *password; password++) { |
| |
289 x = (x & 0xffffff00) | *password; |
| |
290 y ^= x; |
| |
291 y += x; |
| |
292 x <<= 8; |
| |
293 y ^= x; |
| |
294 x <<= 8; |
| |
295 y -= x; |
| |
296 x <<= 8; |
| |
297 y ^= x; |
| |
298 |
| |
299 z = y & 0x1F; |
| |
300 y = (y << z) | (y >> (32 - z)); |
| |
301 } |
| |
302 |
| |
303 return y; |
| |
304 } |
| |
305 |
| |
306 #ifndef _WIN32 |
| |
307 |
| |
308 /* |
| |
309 * gg_resolve() // funkcja wewnętrzna |
| |
310 * |
| |
311 * tworzy potok, forkuje się i w drugim procesie zaczyna resolvować |
| |
312 * podanego hosta. zapisuje w sesji deskryptor potoku. jeśli coś tam |
| |
313 * będzie gotowego, znaczy, że można wczytać struct in_addr. jeśli |
| |
314 * nie znajdzie, zwraca INADDR_NONE. |
| |
315 * |
| |
316 * - fd - wskaźnik gdzie wrzucić deskryptor |
| |
317 * - pid - gdzie wrzucić pid procesu potomnego |
| |
318 * - hostname - nazwa hosta do zresolvowania |
| |
319 * |
| |
320 * 0, -1. |
| |
321 */ |
| |
322 int gg_resolve(int *fd, int *pid, const char *hostname) |
| |
323 { |
| |
324 int pipes[2], res; |
| |
325 struct in_addr a; |
| |
326 int errno2; |
| |
327 |
| |
328 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); |
| |
329 |
| |
330 if (!fd || !pid) { |
| |
331 errno = EFAULT; |
| |
332 return -1; |
| |
333 } |
| |
334 |
| |
335 if (pipe(pipes) == -1) |
| |
336 return -1; |
| |
337 |
| |
338 if ((res = fork()) == -1) { |
| |
339 errno2 = errno; |
| |
340 close(pipes[0]); |
| |
341 close(pipes[1]); |
| |
342 errno = errno2; |
| |
343 return -1; |
| |
344 } |
| |
345 |
| |
346 if (!res) { |
| |
347 if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { |
| |
348 struct in_addr *hn; |
| |
349 |
| |
350 if (!(hn = gg_gethostbyname(hostname))) |
| |
351 a.s_addr = INADDR_NONE; |
| |
352 else { |
| |
353 a.s_addr = hn->s_addr; |
| |
354 free(hn); |
| |
355 } |
| |
356 } |
| |
357 |
| |
358 write(pipes[1], &a, sizeof(a)); |
| |
359 |
| |
360 _exit(0); |
| |
361 } |
| |
362 |
| |
363 close(pipes[1]); |
| |
364 |
| |
365 *fd = pipes[0]; |
| |
366 *pid = res; |
| |
367 |
| |
368 return 0; |
| |
369 } |
| |
370 #endif |
| |
371 |
| |
372 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
| |
373 |
| |
374 struct gg_resolve_pthread_data { |
| |
375 char *hostname; |
| |
376 int fd; |
| |
377 }; |
| |
378 |
| |
379 static void *gg_resolve_pthread_thread(void *arg) |
| |
380 { |
| |
381 struct gg_resolve_pthread_data *d = arg; |
| |
382 struct in_addr a; |
| |
383 |
| |
384 pthread_detach(pthread_self()); |
| |
385 |
| |
386 if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { |
| |
387 struct in_addr *hn; |
| |
388 |
| |
389 if (!(hn = gg_gethostbyname(d->hostname))) |
| |
390 a.s_addr = INADDR_NONE; |
| |
391 else { |
| |
392 a.s_addr = hn->s_addr; |
| |
393 free(hn); |
| |
394 } |
| |
395 } |
| |
396 |
| |
397 write(d->fd, &a, sizeof(a)); |
| |
398 close(d->fd); |
| |
399 |
| |
400 free(d->hostname); |
| |
401 d->hostname = NULL; |
| |
402 |
| |
403 free(d); |
| |
404 |
| |
405 pthread_exit(NULL); |
| |
406 |
| |
407 return NULL; /* żeby kompilator nie marudził */ |
| |
408 } |
| |
409 |
| |
410 /* |
| |
411 * gg_resolve_pthread() // funkcja wewnętrzna |
| |
412 * |
| |
413 * tworzy potok, nowy wątek i w nim zaczyna resolvować podanego hosta. |
| |
414 * zapisuje w sesji deskryptor potoku. jeśli coś tam będzie gotowego, |
| |
415 * znaczy, że można wczytać struct in_addr. jeśli nie znajdzie, zwraca |
| |
416 * INADDR_NONE. |
| |
417 * |
| |
418 * - fd - wskaźnik do zmiennej przechowującej desktyptor resolvera |
| |
419 * - resolver - wskaźnik do wskaźnika resolvera |
| |
420 * - hostname - nazwa hosta do zresolvowania |
| |
421 * |
| |
422 * 0, -1. |
| |
423 */ |
| |
424 int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) |
| |
425 { |
| |
426 struct gg_resolve_pthread_data *d = NULL; |
| |
427 pthread_t *tmp; |
| |
428 int pipes[2], new_errno; |
| |
429 |
| |
430 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); |
| |
431 |
| |
432 if (!resolver || !fd || !hostname) { |
| |
433 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); |
| |
434 errno = EFAULT; |
| |
435 return -1; |
| |
436 } |
| |
437 |
| |
438 if (!(tmp = malloc(sizeof(pthread_t)))) { |
| |
439 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); |
| |
440 return -1; |
| |
441 } |
| |
442 |
| |
443 if (pipe(pipes) == -1) { |
| |
444 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); |
| |
445 free(tmp); |
| |
446 return -1; |
| |
447 } |
| |
448 |
| |
449 if (!(d = malloc(sizeof(*d)))) { |
| |
450 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); |
| |
451 new_errno = errno; |
| |
452 goto cleanup; |
| |
453 } |
| |
454 |
| |
455 d->hostname = NULL; |
| |
456 |
| |
457 if (!(d->hostname = strdup(hostname))) { |
| |
458 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); |
| |
459 new_errno = errno; |
| |
460 goto cleanup; |
| |
461 } |
| |
462 |
| |
463 d->fd = pipes[1]; |
| |
464 |
| |
465 if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { |
| |
466 gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); |
| |
467 new_errno = errno; |
| |
468 goto cleanup; |
| |
469 } |
| |
470 |
| |
471 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); |
| |
472 |
| |
473 *resolver = tmp; |
| |
474 |
| |
475 *fd = pipes[0]; |
| |
476 |
| |
477 return 0; |
| |
478 |
| |
479 cleanup: |
| |
480 if (d) { |
| |
481 free(d->hostname); |
| |
482 free(d); |
| |
483 } |
| |
484 |
| |
485 close(pipes[0]); |
| |
486 close(pipes[1]); |
| |
487 |
| |
488 free(tmp); |
| |
489 |
| |
490 errno = new_errno; |
| |
491 |
| |
492 return -1; |
| |
493 } |
| |
494 |
| |
495 #elif defined _WIN32 |
| |
496 |
| |
497 struct gg_resolve_win32thread_data { |
| |
498 char *hostname; |
| |
499 int fd; |
| |
500 }; |
| |
501 |
| |
502 static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) |
| |
503 { |
| |
504 struct gg_resolve_win32thread_data *d = arg; |
| |
505 struct in_addr a; |
| |
506 |
| |
507 if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { |
| |
508 struct in_addr *hn; |
| |
509 |
| |
510 if (!(hn = gg_gethostbyname(d->hostname))) |
| |
511 a.s_addr = INADDR_NONE; |
| |
512 else { |
| |
513 a.s_addr = hn->s_addr; |
| |
514 free(hn); |
| |
515 } |
| |
516 } |
| |
517 |
| |
518 write(d->fd, &a, sizeof(a)); |
| |
519 close(d->fd); |
| |
520 |
| |
521 free(d->hostname); |
| |
522 d->hostname = NULL; |
| |
523 |
| |
524 free(d); |
| |
525 |
| |
526 return 0; |
| |
527 } |
| |
528 |
| |
529 |
| |
530 int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) |
| |
531 { |
| |
532 struct gg_resolve_win32thread_data *d = NULL; |
| |
533 HANDLE h; |
| |
534 DWORD dwTId; |
| |
535 int pipes[2], new_errno; |
| |
536 |
| |
537 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); |
| |
538 |
| |
539 if (!resolver || !fd || !hostname) { |
| |
540 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); |
| |
541 errno = EFAULT; |
| |
542 return -1; |
| |
543 } |
| |
544 |
| |
545 if (socket_pipe(pipes) == -1) { |
| |
546 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); |
| |
547 return -1; |
| |
548 } |
| |
549 |
| |
550 if (!(d = malloc(sizeof(*d)))) { |
| |
551 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); |
| |
552 new_errno = GetLastError(); |
| |
553 goto cleanup; |
| |
554 } |
| |
555 |
| |
556 d->hostname = NULL; |
| |
557 |
| |
558 if (!(d->hostname = strdup(hostname))) { |
| |
559 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); |
| |
560 new_errno = GetLastError(); |
| |
561 goto cleanup; |
| |
562 } |
| |
563 |
| |
564 d->fd = pipes[1]; |
| |
565 |
| |
566 h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, |
| |
567 d, 0, &dwTId); |
| |
568 |
| |
569 if (h == NULL) { |
| |
570 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); |
| |
571 new_errno = GetLastError(); |
| |
572 goto cleanup; |
| |
573 } |
| |
574 |
| |
575 *resolver = h; |
| |
576 *fd = pipes[0]; |
| |
577 |
| |
578 return 0; |
| |
579 |
| |
580 cleanup: |
| |
581 if (d) { |
| |
582 free(d->hostname); |
| |
583 free(d); |
| |
584 } |
| |
585 |
| |
586 close(pipes[0]); |
| |
587 close(pipes[1]); |
| |
588 |
| |
589 errno = new_errno; |
| |
590 |
| |
591 return -1; |
| |
592 |
| |
593 } |
| |
594 #endif |
| |
595 |
| |
596 /* |
| |
597 * gg_read() // funkcja pomocnicza |
| |
598 * |
| |
599 * czyta z gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy |
| |
600 * połączenie zwykłe czy TLS. |
| |
601 * |
| |
602 * - sess - sesja, |
| |
603 * - buf - bufor, |
| |
604 * - length - ilość bajtów, |
| |
605 * |
| |
606 * takie same wartości jak read(). |
| |
607 */ |
| |
608 int gg_read(struct gg_session *sess, char *buf, int length) |
| |
609 { |
| |
610 int res; |
| |
611 |
| |
612 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
613 if (sess->ssl) { |
| |
614 int err; |
| |
615 |
| |
616 res = SSL_read(sess->ssl, buf, length); |
| |
617 |
| |
618 if (res < 0) { |
| |
619 err = SSL_get_error(sess->ssl, res); |
| |
620 |
| |
621 if (err == SSL_ERROR_WANT_READ) |
| |
622 errno = EAGAIN; |
| |
623 |
| |
624 return -1; |
| |
625 } |
| |
626 } else |
| |
627 #endif |
| |
628 res = read(sess->fd, buf, length); |
| |
629 |
| |
630 return res; |
| |
631 } |
| |
632 |
| |
633 /* |
| |
634 * gg_write() // funkcja pomocnicza |
| |
635 * |
| |
636 * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy |
| |
637 * połączenie zwykłe czy TLS. |
| |
638 * |
| |
639 * - sess - sesja, |
| |
640 * - buf - bufor, |
| |
641 * - length - ilość bajtów, |
| |
642 * |
| |
643 * takie same wartości jak write(). |
| |
644 */ |
| |
645 int gg_write(struct gg_session *sess, const char *buf, int length) |
| |
646 { |
| |
647 int res = 0; |
| |
648 |
| |
649 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
650 if (sess->ssl) { |
| |
651 int err; |
| |
652 |
| |
653 res = SSL_write(sess->ssl, buf, length); |
| |
654 |
| |
655 if (res < 0) { |
| |
656 err = SSL_get_error(sess->ssl, res); |
| |
657 |
| |
658 if (err == SSL_ERROR_WANT_WRITE) |
| |
659 errno = EAGAIN; |
| |
660 |
| |
661 return -1; |
| |
662 } |
| |
663 } else |
| |
664 #endif |
| |
665 { |
| |
666 int written = 0; |
| |
667 |
| |
668 while (written < length) { |
| |
669 res = write(sess->fd, buf + written, length - written); |
| |
670 |
| |
671 if (res == -1) { |
| |
672 if (errno == EAGAIN) |
| |
673 continue; |
| |
674 else |
| |
675 break; |
| |
676 } else { |
| |
677 written += res; |
| |
678 res = written; |
| |
679 } |
| |
680 } |
| |
681 } |
| |
682 |
| |
683 return res; |
| |
684 } |
| |
685 |
| |
686 /* |
| |
687 * gg_recv_packet() // funkcja wewnętrzna |
| |
688 * |
| |
689 * odbiera jeden pakiet i zwraca wskaźnik do niego. pamięć po nim |
| |
690 * należy zwolnić za pomocą free(). |
| |
691 * |
| |
692 * - sess - opis sesji |
| |
693 * |
| |
694 * w przypadku błędu NULL, kod błędu w errno. należy zwrócić uwagę, że gdy |
| |
695 * połączenie jest nieblokujące, a kod błędu wynosi EAGAIN, nie udało się |
| |
696 * odczytać całego pakietu i nie należy tego traktować jako błąd. |
| |
697 */ |
| |
698 void *gg_recv_packet(struct gg_session *sess) |
| |
699 { |
| |
700 struct gg_header h; |
| |
701 char *buf = NULL; |
| |
702 int ret = 0, offset, size = 0; |
| |
703 |
| |
704 gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); |
| |
705 |
| |
706 if (!sess) { |
| |
707 errno = EFAULT; |
| |
708 return NULL; |
| |
709 } |
| |
710 |
| |
711 if (sess->recv_left < 1) { |
| |
712 if (sess->header_buf) { |
| |
713 memcpy(&h, sess->header_buf, sess->header_done); |
| |
714 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); |
| |
715 free(sess->header_buf); |
| |
716 sess->header_buf = NULL; |
| |
717 } else |
| |
718 sess->header_done = 0; |
| |
719 |
| |
720 while (sess->header_done < sizeof(h)) { |
| |
721 ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); |
| |
722 |
| |
723 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); |
| |
724 |
| |
725 if (!ret) { |
| |
726 errno = ECONNRESET; |
| |
727 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); |
| |
728 return NULL; |
| |
729 } |
| |
730 |
| |
731 if (ret == -1) { |
| |
732 if (errno == EINTR) { |
| |
733 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); |
| |
734 continue; |
| |
735 } |
| |
736 |
| |
737 if (errno == EAGAIN) { |
| |
738 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); |
| |
739 |
| |
740 if (!(sess->header_buf = malloc(sess->header_done))) { |
| |
741 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); |
| |
742 return NULL; |
| |
743 } |
| |
744 |
| |
745 memcpy(sess->header_buf, &h, sess->header_done); |
| |
746 |
| |
747 return NULL; |
| |
748 } |
| |
749 |
| |
750 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); |
| |
751 |
| |
752 return NULL; |
| |
753 } |
| |
754 |
| |
755 sess->header_done += ret; |
| |
756 |
| |
757 } |
| |
758 |
| |
759 h.type = gg_fix32(h.type); |
| |
760 h.length = gg_fix32(h.length); |
| |
761 } else |
| |
762 memcpy(&h, sess->recv_buf, sizeof(h)); |
| |
763 |
| |
764 /* jakieś sensowne limity na rozmiar pakietu */ |
| |
765 if (h.length > 65535) { |
| |
766 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); |
| |
767 errno = ERANGE; |
| |
768 return NULL; |
| |
769 } |
| |
770 |
| |
771 if (sess->recv_left > 0) { |
| |
772 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); |
| |
773 size = sess->recv_left; |
| |
774 offset = sess->recv_done; |
| |
775 buf = sess->recv_buf; |
| |
776 } else { |
| |
777 if (!(buf = malloc(sizeof(h) + h.length + 1))) { |
| |
778 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); |
| |
779 return NULL; |
| |
780 } |
| |
781 |
| |
782 memcpy(buf, &h, sizeof(h)); |
| |
783 |
| |
784 offset = 0; |
| |
785 size = h.length; |
| |
786 } |
| |
787 |
| |
788 while (size > 0) { |
| |
789 ret = gg_read(sess, buf + sizeof(h) + offset, size); |
| |
790 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); |
| |
791 if (!ret) { |
| |
792 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); |
| |
793 errno = ECONNRESET; |
| |
794 return NULL; |
| |
795 } |
| |
796 if (ret > -1 && ret <= size) { |
| |
797 offset += ret; |
| |
798 size -= ret; |
| |
799 } else if (ret == -1) { |
| |
800 int errno2 = errno; |
| |
801 |
| |
802 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); |
| |
803 errno = errno2; |
| |
804 |
| |
805 if (errno == EAGAIN) { |
| |
806 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); |
| |
807 sess->recv_buf = buf; |
| |
808 sess->recv_left = size; |
| |
809 sess->recv_done = offset; |
| |
810 return NULL; |
| |
811 } |
| |
812 if (errno != EINTR) { |
| |
813 free(buf); |
| |
814 return NULL; |
| |
815 } |
| |
816 } |
| |
817 } |
| |
818 |
| |
819 sess->recv_left = 0; |
| |
820 |
| |
821 if ((gg_debug_level & GG_DEBUG_DUMP)) { |
| |
822 unsigned int i; |
| |
823 |
| |
824 gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); |
| |
825 for (i = 0; i < sizeof(h) + h.length; i++) |
| |
826 gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); |
| |
827 gg_debug(GG_DEBUG_DUMP, "\n"); |
| |
828 } |
| |
829 |
| |
830 return buf; |
| |
831 } |
| |
832 |
| |
833 /* |
| |
834 * gg_send_packet() // funkcja wewnętrzna |
| |
835 * |
| |
836 * konstruuje pakiet i wysyła go do serwera. |
| |
837 * |
| |
838 * - sock - deskryptor gniazda |
| |
839 * - type - typ pakietu |
| |
840 * - payload_1 - pierwsza część pakietu |
| |
841 * - payload_length_1 - długość pierwszej części |
| |
842 * - payload_2 - druga część pakietu |
| |
843 * - payload_length_2 - długość drugiej części |
| |
844 * - ... - kolejne części pakietu i ich długości |
| |
845 * - NULL - końcowym parametr (konieczny!) |
| |
846 * |
| |
847 * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM, |
| |
848 * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0 |
| |
849 * nie wysłano całego pakietu. |
| |
850 */ |
| |
851 int gg_send_packet(struct gg_session *sess, int type, ...) |
| |
852 { |
| |
853 struct gg_header *h; |
| |
854 char *tmp; |
| |
855 int tmp_length; |
| |
856 void *payload; |
| |
857 unsigned int payload_length; |
| |
858 va_list ap; |
| |
859 int res; |
| |
860 |
| |
861 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); |
| |
862 |
| |
863 tmp_length = sizeof(struct gg_header); |
| |
864 |
| |
865 if (!(tmp = malloc(tmp_length))) { |
| |
866 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); |
| |
867 return -1; |
| |
868 } |
| |
869 |
| |
870 va_start(ap, type); |
| |
871 |
| |
872 payload = va_arg(ap, void *); |
| |
873 |
| |
874 while (payload) { |
| |
875 char *tmp2; |
| |
876 |
| |
877 payload_length = va_arg(ap, unsigned int); |
| |
878 |
| |
879 if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { |
| |
880 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); |
| |
881 free(tmp); |
| |
882 va_end(ap); |
| |
883 return -1; |
| |
884 } |
| |
885 |
| |
886 tmp = tmp2; |
| |
887 |
| |
888 memcpy(tmp + tmp_length, payload, payload_length); |
| |
889 tmp_length += payload_length; |
| |
890 |
| |
891 payload = va_arg(ap, void *); |
| |
892 } |
| |
893 |
| |
894 va_end(ap); |
| |
895 |
| |
896 h = (struct gg_header*) tmp; |
| |
897 h->type = gg_fix32(type); |
| |
898 h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); |
| |
899 |
| |
900 if ((gg_debug_level & GG_DEBUG_DUMP)) { |
| |
901 int i; |
| |
902 |
| |
903 gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); |
| |
904 for (i = 0; i < tmp_length; ++i) |
| |
905 gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); |
| |
906 gg_debug(GG_DEBUG_DUMP, "\n"); |
| |
907 } |
| |
908 |
| |
909 if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { |
| |
910 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); |
| |
911 free(tmp); |
| |
912 return -1; |
| |
913 } |
| |
914 |
| |
915 free(tmp); |
| |
916 return 0; |
| |
917 } |
| |
918 |
| |
919 /* |
| |
920 * gg_session_callback() // funkcja wewnętrzna |
| |
921 * |
| |
922 * wywoływany z gg_session->callback, wykonuje gg_watch_fd() i pakuje |
| |
923 * do gg_session->event jego wynik. |
| |
924 */ |
| |
925 static int gg_session_callback(struct gg_session *s) |
| |
926 { |
| |
927 if (!s) { |
| |
928 errno = EFAULT; |
| |
929 return -1; |
| |
930 } |
| |
931 |
| |
932 return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; |
| |
933 } |
| |
934 |
| |
935 /* |
| |
936 * gg_login() |
| |
937 * |
| |
938 * rozpoczyna procedurę łączenia się z serwerem. resztę obsługuje się przez |
| |
939 * gg_watch_fd(). |
| |
940 * |
| |
941 * UWAGA! program musi obsłużyć SIGCHLD, jeśli łączy się asynchronicznie, |
| |
942 * żeby poprawnie zamknąć proces resolvera. |
| |
943 * |
| |
944 * - p - struktura opisująca początkowy stan. wymagane pola: uin, |
| |
945 * password |
| |
946 * |
| |
947 * w przypadku błędu NULL, jeśli idzie dobrze (async) albo poszło |
| |
948 * dobrze (sync), zwróci wskaźnik do zaalokowanej struct gg_session. |
| |
949 */ |
| |
950 struct gg_session *gg_login(const struct gg_login_params *p) |
| |
951 { |
| |
952 struct gg_session *sess = NULL; |
| |
953 char *hostname; |
| |
954 int port; |
| |
955 |
| |
956 if (!p) { |
| |
957 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p); |
| |
958 errno = EFAULT; |
| |
959 return NULL; |
| |
960 } |
| |
961 |
| |
962 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async); |
| |
963 |
| |
964 if (!(sess = malloc(sizeof(struct gg_session)))) { |
| |
965 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n"); |
| |
966 goto fail; |
| |
967 } |
| |
968 |
| |
969 memset(sess, 0, sizeof(struct gg_session)); |
| |
970 |
| |
971 if (!p->password || !p->uin) { |
| |
972 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n"); |
| |
973 errno = EFAULT; |
| |
974 goto fail; |
| |
975 } |
| |
976 |
| |
977 if (!(sess->password = strdup(p->password))) { |
| |
978 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n"); |
| |
979 goto fail; |
| |
980 } |
| |
981 |
| |
982 if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { |
| |
983 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); |
| |
984 goto fail; |
| |
985 } |
| |
986 |
| |
987 sess->uin = p->uin; |
| |
988 sess->state = GG_STATE_RESOLVING; |
| |
989 sess->check = GG_CHECK_READ; |
| |
990 sess->timeout = GG_DEFAULT_TIMEOUT; |
| |
991 sess->async = p->async; |
| |
992 sess->type = GG_SESSION_GG; |
| |
993 sess->initial_status = p->status; |
| |
994 sess->callback = gg_session_callback; |
| |
995 sess->destroy = gg_free_session; |
| |
996 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); |
| |
997 sess->server_addr = p->server_addr; |
| |
998 sess->external_port = p->external_port; |
| |
999 sess->external_addr = p->external_addr; |
| |
1000 sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; |
| |
1001 if (p->era_omnix) |
| |
1002 sess->protocol_version |= GG_ERA_OMNIX_MASK; |
| |
1003 if (p->has_audio) |
| |
1004 sess->protocol_version |= GG_HAS_AUDIO_MASK; |
| |
1005 sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; |
| |
1006 sess->last_sysmsg = p->last_sysmsg; |
| |
1007 sess->image_size = p->image_size; |
| |
1008 sess->pid = -1; |
| |
1009 |
| |
1010 if (p->tls == 1) { |
| |
1011 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
1012 char buf[1024]; |
| |
1013 |
| |
1014 OpenSSL_add_ssl_algorithms(); |
| |
1015 |
| |
1016 if (!RAND_status()) { |
| |
1017 char rdata[1024]; |
| |
1018 struct { |
| |
1019 time_t time; |
| |
1020 void *ptr; |
| |
1021 } rstruct; |
| |
1022 |
| |
1023 time(&rstruct.time); |
| |
1024 rstruct.ptr = (void *) &rstruct; |
| |
1025 |
| |
1026 RAND_seed((void *) rdata, sizeof(rdata)); |
| |
1027 RAND_seed((void *) &rstruct, sizeof(rstruct)); |
| |
1028 } |
| |
1029 |
| |
1030 sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); |
| |
1031 |
| |
1032 if (!sess->ssl_ctx) { |
| |
1033 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); |
| |
1034 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); |
| |
1035 goto fail; |
| |
1036 } |
| |
1037 |
| |
1038 SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL); |
| |
1039 |
| |
1040 sess->ssl = SSL_new(sess->ssl_ctx); |
| |
1041 |
| |
1042 if (!sess->ssl) { |
| |
1043 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); |
| |
1044 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); |
| |
1045 goto fail; |
| |
1046 } |
| |
1047 #else |
| |
1048 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); |
| |
1049 #endif |
| |
1050 } |
| |
1051 |
| |
1052 if (gg_proxy_enabled) { |
| |
1053 hostname = gg_proxy_host; |
| |
1054 sess->proxy_port = port = gg_proxy_port; |
| |
1055 } else { |
| |
1056 hostname = GG_APPMSG_HOST; |
| |
1057 port = GG_APPMSG_PORT; |
| |
1058 } |
| |
1059 |
| |
1060 if (!p->async) { |
| |
1061 struct in_addr a; |
| |
1062 |
| |
1063 if (!p->server_addr || !p->server_port) { |
| |
1064 if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { |
| |
1065 struct in_addr *hn; |
| |
1066 |
| |
1067 if (!(hn = gg_gethostbyname(hostname))) { |
| |
1068 gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); |
| |
1069 goto fail; |
| |
1070 } else { |
| |
1071 a.s_addr = hn->s_addr; |
| |
1072 free(hn); |
| |
1073 } |
| |
1074 } |
| |
1075 } else { |
| |
1076 a.s_addr = p->server_addr; |
| |
1077 port = p->server_port; |
| |
1078 } |
| |
1079 |
| |
1080 sess->hub_addr = a.s_addr; |
| |
1081 |
| |
1082 if (gg_proxy_enabled) |
| |
1083 sess->proxy_addr = a.s_addr; |
| |
1084 |
| |
1085 if ((sess->fd = gg_connect(&a, port, 0)) == -1) { |
| |
1086 gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); |
| |
1087 goto fail; |
| |
1088 } |
| |
1089 |
| |
1090 if (p->server_addr && p->server_port) |
| |
1091 sess->state = GG_STATE_CONNECTING_GG; |
| |
1092 else |
| |
1093 sess->state = GG_STATE_CONNECTING_HUB; |
| |
1094 |
| |
1095 while (sess->state != GG_STATE_CONNECTED) { |
| |
1096 struct gg_event *e; |
| |
1097 |
| |
1098 if (!(e = gg_watch_fd(sess))) { |
| |
1099 gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n"); |
| |
1100 goto fail; |
| |
1101 } |
| |
1102 |
| |
1103 if (e->type == GG_EVENT_CONN_FAILED) { |
| |
1104 errno = EACCES; |
| |
1105 gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n"); |
| |
1106 gg_event_free(e); |
| |
1107 goto fail; |
| |
1108 } |
| |
1109 |
| |
1110 gg_event_free(e); |
| |
1111 } |
| |
1112 |
| |
1113 return sess; |
| |
1114 } |
| |
1115 |
| |
1116 if (!sess->server_addr || gg_proxy_enabled) { |
| |
1117 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
| |
1118 if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { |
| |
1119 #elif defined _WIN32 |
| |
1120 if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) { |
| |
1121 #else |
| |
1122 if (gg_resolve(&sess->fd, &sess->pid, hostname)) { |
| |
1123 #endif |
| |
1124 gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); |
| |
1125 goto fail; |
| |
1126 } |
| |
1127 } else { |
| |
1128 if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { |
| |
1129 gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno)); |
| |
1130 goto fail; |
| |
1131 } |
| |
1132 sess->state = GG_STATE_CONNECTING_GG; |
| |
1133 sess->check = GG_CHECK_WRITE; |
| |
1134 } |
| |
1135 |
| |
1136 return sess; |
| |
1137 |
| |
1138 fail: |
| |
1139 if (sess) { |
| |
1140 if (sess->password) |
| |
1141 free(sess->password); |
| |
1142 if (sess->initial_descr) |
| |
1143 free(sess->initial_descr); |
| |
1144 free(sess); |
| |
1145 } |
| |
1146 |
| |
1147 return NULL; |
| |
1148 } |
| |
1149 |
| |
1150 /* |
| |
1151 * gg_free_session() |
| |
1152 * |
| |
1153 * próbuje zamknąć połączenia i zwalnia pamięć zajmowaną przez sesję. |
| |
1154 * |
| |
1155 * - sess - opis sesji |
| |
1156 */ |
| |
1157 void gg_free_session(struct gg_session *sess) |
| |
1158 { |
| |
1159 if (!sess) |
| |
1160 return; |
| |
1161 |
| |
1162 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ |
| |
1163 |
| |
1164 if (sess->password) |
| |
1165 free(sess->password); |
| |
1166 |
| |
1167 if (sess->initial_descr) |
| |
1168 free(sess->initial_descr); |
| |
1169 |
| |
1170 if (sess->client_version) |
| |
1171 free(sess->client_version); |
| |
1172 |
| |
1173 if (sess->header_buf) |
| |
1174 free(sess->header_buf); |
| |
1175 |
| |
1176 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
1177 if (sess->ssl) |
| |
1178 SSL_free(sess->ssl); |
| |
1179 |
| |
1180 if (sess->ssl_ctx) |
| |
1181 SSL_CTX_free(sess->ssl_ctx); |
| |
1182 #endif |
| |
1183 |
| |
1184 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
| |
1185 if (sess->resolver) { |
| |
1186 pthread_cancel(*((pthread_t*) sess->resolver)); |
| |
1187 free(sess->resolver); |
| |
1188 sess->resolver = NULL; |
| |
1189 } |
| |
1190 #elif defined _WIN32 |
| |
1191 if (sess->resolver) { |
| |
1192 HANDLE h = sess->resolver; |
| |
1193 TerminateThread(h, 0); |
| |
1194 CloseHandle(h); |
| |
1195 sess->resolver = NULL; |
| |
1196 } |
| |
1197 #else |
| |
1198 if (sess->pid != -1) |
| |
1199 waitpid(sess->pid, NULL, WNOHANG); |
| |
1200 #endif |
| |
1201 |
| |
1202 if (sess->fd != -1) |
| |
1203 close(sess->fd); |
| |
1204 |
| |
1205 while (sess->images) |
| |
1206 gg_image_queue_remove(sess, sess->images, 1); |
| |
1207 |
| |
1208 free(sess); |
| |
1209 } |
| |
1210 |
| |
1211 /* |
| |
1212 * gg_change_status() |
| |
1213 * |
| |
1214 * zmienia status użytkownika. przydatne do /away i /busy oraz /quit. |
| |
1215 * |
| |
1216 * - sess - opis sesji |
| |
1217 * - status - nowy status użytkownika |
| |
1218 * |
| |
1219 * 0, -1. |
| |
1220 */ |
| |
1221 int gg_change_status(struct gg_session *sess, int status) |
| |
1222 { |
| |
1223 struct gg_new_status p; |
| |
1224 |
| |
1225 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); |
| |
1226 |
| |
1227 if (!sess) { |
| |
1228 errno = EFAULT; |
| |
1229 return -1; |
| |
1230 } |
| |
1231 |
| |
1232 if (sess->state != GG_STATE_CONNECTED) { |
| |
1233 errno = ENOTCONN; |
| |
1234 return -1; |
| |
1235 } |
| |
1236 |
| |
1237 p.status = gg_fix32(status); |
| |
1238 |
| |
1239 sess->status = status; |
| |
1240 |
| |
1241 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); |
| |
1242 } |
| |
1243 |
| |
1244 /* |
| |
1245 * gg_change_status_descr() |
| |
1246 * |
| |
1247 * zmienia status użytkownika na opisowy. |
| |
1248 * |
| |
1249 * - sess - opis sesji |
| |
1250 * - status - nowy status użytkownika |
| |
1251 * - descr - opis statusu |
| |
1252 * |
| |
1253 * 0, -1. |
| |
1254 */ |
| |
1255 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) |
| |
1256 { |
| |
1257 struct gg_new_status p; |
| |
1258 |
| |
1259 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); |
| |
1260 |
| |
1261 if (!sess || !descr) { |
| |
1262 errno = EFAULT; |
| |
1263 return -1; |
| |
1264 } |
| |
1265 |
| |
1266 if (sess->state != GG_STATE_CONNECTED) { |
| |
1267 errno = ENOTCONN; |
| |
1268 return -1; |
| |
1269 } |
| |
1270 |
| |
1271 p.status = gg_fix32(status); |
| |
1272 |
| |
1273 sess->status = status; |
| |
1274 |
| |
1275 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); |
| |
1276 } |
| |
1277 |
| |
1278 /* |
| |
1279 * gg_change_status_descr_time() |
| |
1280 * |
| |
1281 * zmienia status użytkownika na opisowy z godziną powrotu. |
| |
1282 * |
| |
1283 * - sess - opis sesji |
| |
1284 * - status - nowy status użytkownika |
| |
1285 * - descr - opis statusu |
| |
1286 * - time - czas w formacie uniksowym |
| |
1287 * |
| |
1288 * 0, -1. |
| |
1289 */ |
| |
1290 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) |
| |
1291 { |
| |
1292 struct gg_new_status p; |
| |
1293 uint32_t newtime; |
| |
1294 |
| |
1295 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); |
| |
1296 |
| |
1297 if (!sess || !descr || !time) { |
| |
1298 errno = EFAULT; |
| |
1299 return -1; |
| |
1300 } |
| |
1301 |
| |
1302 if (sess->state != GG_STATE_CONNECTED) { |
| |
1303 errno = ENOTCONN; |
| |
1304 return -1; |
| |
1305 } |
| |
1306 |
| |
1307 p.status = gg_fix32(status); |
| |
1308 |
| |
1309 sess->status = status; |
| |
1310 |
| |
1311 newtime = gg_fix32(time); |
| |
1312 |
| |
1313 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); |
| |
1314 } |
| |
1315 |
| |
1316 /* |
| |
1317 * gg_logoff() |
| |
1318 * |
| |
1319 * wylogowuje użytkownika i zamyka połączenie, ale nie zwalnia pamięci. |
| |
1320 * |
| |
1321 * - sess - opis sesji |
| |
1322 */ |
| |
1323 void gg_logoff(struct gg_session *sess) |
| |
1324 { |
| |
1325 if (!sess) |
| |
1326 return; |
| |
1327 |
| |
1328 gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); |
| |
1329 |
| |
1330 if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) |
| |
1331 gg_change_status(sess, GG_STATUS_NOT_AVAIL); |
| |
1332 |
| |
1333 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
| |
1334 if (sess->ssl) |
| |
1335 SSL_shutdown(sess->ssl); |
| |
1336 #endif |
| |
1337 |
| |
1338 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
| |
1339 if (sess->resolver) { |
| |
1340 pthread_cancel(*((pthread_t*) sess->resolver)); |
| |
1341 free(sess->resolver); |
| |
1342 sess->resolver = NULL; |
| |
1343 } |
| |
1344 #elif defined _WIN32 |
| |
1345 if (sess->resolver) { |
| |
1346 HANDLE h = sess->resolver; |
| |
1347 TerminateThread(h, 0); |
| |
1348 CloseHandle(h); |
| |
1349 sess->resolver = NULL; |
| |
1350 } |
| |
1351 #else |
| |
1352 if (sess->pid != -1) { |
| |
1353 waitpid(sess->pid, NULL, WNOHANG); |
| |
1354 sess->pid = -1; |
| |
1355 } |
| |
1356 #endif |
| |
1357 |
| |
1358 if (sess->fd != -1) { |
| |
1359 shutdown(sess->fd, SHUT_RDWR); |
| |
1360 close(sess->fd); |
| |
1361 sess->fd = -1; |
| |
1362 } |
| |
1363 } |
| |
1364 |
| |
1365 /* |
| |
1366 * gg_image_request() |
| |
1367 * |
| |
1368 * wysyła żądanie wysłania obrazka o podanych parametrach. |
| |
1369 * |
| |
1370 * - sess - opis sesji |
| |
1371 * - recipient - numer adresata |
| |
1372 * - size - rozmiar obrazka |
| |
1373 * - crc32 - suma kontrolna obrazka |
| |
1374 * |
| |
1375 * 0/-1 |
| |
1376 */ |
| |
1377 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) |
| |
1378 { |
| |
1379 struct gg_send_msg s; |
| |
1380 struct gg_msg_image_request r; |
| |
1381 char dummy = 0; |
| |
1382 int res; |
| |
1383 |
| |
1384 gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); |
| |
1385 |
| |
1386 if (!sess) { |
| |
1387 errno = EFAULT; |
| |
1388 return -1; |
| |
1389 } |
| |
1390 |
| |
1391 if (sess->state != GG_STATE_CONNECTED) { |
| |
1392 errno = ENOTCONN; |
| |
1393 return -1; |
| |
1394 } |
| |
1395 |
| |
1396 if (size < 0) { |
| |
1397 errno = EINVAL; |
| |
1398 return -1; |
| |
1399 } |
| |
1400 |
| |
1401 s.recipient = gg_fix32(recipient); |
| |
1402 s.seq = gg_fix32(0); |
| |
1403 s.msgclass = gg_fix32(GG_CLASS_MSG); |
| |
1404 |
| |
1405 r.flag = 0x04; |
| |
1406 r.size = gg_fix32(size); |
| |
1407 r.crc32 = gg_fix32(crc32); |
| |
1408 |
| |
1409 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); |
| |
1410 |
| |
1411 if (!res) { |
| |
1412 struct gg_image_queue *q = malloc(sizeof(*q)); |
| |
1413 char *buf; |
| |
1414 |
| |
1415 if (!q) { |
| |
1416 gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); |
| |
1417 return -1; |
| |
1418 } |
| |
1419 |
| |
1420 buf = malloc(size); |
| |
1421 if (size && !buf) |
| |
1422 { |
| |
1423 gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); |
| |
1424 free(q); |
| |
1425 return -1; |
| |
1426 } |
| |
1427 |
| |
1428 memset(q, 0, sizeof(*q)); |
| |
1429 |
| |
1430 q->sender = recipient; |
| |
1431 q->size = size; |
| |
1432 q->crc32 = crc32; |
| |
1433 q->image = buf; |
| |
1434 |
| |
1435 if (!sess->images) |
| |
1436 sess->images = q; |
| |
1437 else { |
| |
1438 struct gg_image_queue *qq; |
| |
1439 |
| |
1440 for (qq = sess->images; qq->next; qq = qq->next) |
| |
1441 ; |
| |
1442 |
| |
1443 qq->next = q; |
| |
1444 } |
| |
1445 } |
| |
1446 |
| |
1447 return res; |
| |
1448 } |
| |
1449 |
| |
1450 /* |
| |
1451 * gg_image_reply() |
| |
1452 * |
| |
1453 * wysyła żądany obrazek. |
| |
1454 * |
| |
1455 * - sess - opis sesji |
| |
1456 * - recipient - numer adresata |
| |
1457 * - filename - nazwa pliku |
| |
1458 * - image - bufor z obrazkiem |
| |
1459 * - size - rozmiar obrazka |
| |
1460 * |
| |
1461 * 0/-1 |
| |
1462 */ |
| |
1463 int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size) |
| |
1464 { |
| |
1465 struct gg_msg_image_reply *r; |
| |
1466 struct gg_send_msg s; |
| |
1467 const char *tmp; |
| |
1468 char buf[1910]; |
| |
1469 int res = -1; |
| |
1470 |
| |
1471 gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); |
| |
1472 |
| |
1473 if (!sess || !filename || !image) { |
| |
1474 errno = EFAULT; |
| |
1475 return -1; |
| |
1476 } |
| |
1477 |
| |
1478 if (sess->state != GG_STATE_CONNECTED) { |
| |
1479 errno = ENOTCONN; |
| |
1480 return -1; |
| |
1481 } |
| |
1482 |
| |
1483 if (size < 0) { |
| |
1484 errno = EINVAL; |
| |
1485 return -1; |
| |
1486 } |
| |
1487 |
| |
1488 /* wytnij ścieżki, zostaw tylko nazwę pliku */ |
| |
1489 while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) |
| |
1490 filename = tmp + 1; |
| |
1491 |
| |
1492 if (strlen(filename) < 1 || strlen(filename) > 1024) { |
| |
1493 errno = EINVAL; |
| |
1494 return -1; |
| |
1495 } |
| |
1496 |
| |
1497 s.recipient = gg_fix32(recipient); |
| |
1498 s.seq = gg_fix32(0); |
| |
1499 s.msgclass = gg_fix32(GG_CLASS_MSG); |
| |
1500 |
| |
1501 buf[0] = 0; |
| |
1502 r = (void*) &buf[1]; |
| |
1503 |
| |
1504 r->flag = 0x05; |
| |
1505 r->size = gg_fix32(size); |
| |
1506 r->crc32 = gg_fix32(gg_crc32(0, image, size)); |
| |
1507 |
| |
1508 while (size > 0) { |
| |
1509 size_t buflen, chunklen; |
| |
1510 |
| |
1511 /* \0 + struct gg_msg_image_reply */ |
| |
1512 buflen = sizeof(struct gg_msg_image_reply) + 1; |
| |
1513 |
| |
1514 /* w pierwszym kawałku jest nazwa pliku */ |
| |
1515 if (r->flag == 0x05) { |
| |
1516 strcpy(buf + buflen, filename); |
| |
1517 buflen += strlen(filename) + 1; |
| |
1518 } |
| |
1519 |
| |
1520 chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size; |
| |
1521 |
| |
1522 memcpy(buf + buflen, image, chunklen); |
| |
1523 size -= chunklen; |
| |
1524 image += chunklen; |
| |
1525 |
| |
1526 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); |
| |
1527 |
| |
1528 if (res == -1) |
| |
1529 break; |
| |
1530 |
| |
1531 r->flag = 0x06; |
| |
1532 } |
| |
1533 |
| |
1534 return res; |
| |
1535 } |
| |
1536 |
| |
1537 /* |
| |
1538 * gg_send_message_ctcp() |
| |
1539 * |
| |
1540 * wysyła wiadomość do innego użytkownika. zwraca losowy numer |
| |
1541 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
| |
1542 * |
| |
1543 * - sess - opis sesji |
| |
1544 * - msgclass - rodzaj wiadomości |
| |
1545 * - recipient - numer adresata |
| |
1546 * - message - treść wiadomości |
| |
1547 * - message_len - długość |
| |
1548 * |
| |
1549 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| |
1550 */ |
| |
1551 int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) |
| |
1552 { |
| |
1553 struct gg_send_msg s; |
| |
1554 |
| |
1555 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); |
| |
1556 |
| |
1557 if (!sess) { |
| |
1558 errno = EFAULT; |
| |
1559 return -1; |
| |
1560 } |
| |
1561 |
| |
1562 if (sess->state != GG_STATE_CONNECTED) { |
| |
1563 errno = ENOTCONN; |
| |
1564 return -1; |
| |
1565 } |
| |
1566 |
| |
1567 s.recipient = gg_fix32(recipient); |
| |
1568 s.seq = gg_fix32(0); |
| |
1569 s.msgclass = gg_fix32(msgclass); |
| |
1570 |
| |
1571 return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); |
| |
1572 } |
| |
1573 |
| |
1574 /* |
| |
1575 * gg_send_message() |
| |
1576 * |
| |
1577 * wysyła wiadomość do innego użytkownika. zwraca losowy numer |
| |
1578 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
| |
1579 * |
| |
1580 * - sess - opis sesji |
| |
1581 * - msgclass - rodzaj wiadomości |
| |
1582 * - recipient - numer adresata |
| |
1583 * - message - treść wiadomości |
| |
1584 * |
| |
1585 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| |
1586 */ |
| |
1587 int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) |
| |
1588 { |
| |
1589 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); |
| |
1590 |
| |
1591 return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); |
| |
1592 } |
| |
1593 |
| |
1594 /* |
| |
1595 * gg_send_message_richtext() |
| |
1596 * |
| |
1597 * wysyła kolorową wiadomość do innego użytkownika. zwraca losowy numer |
| |
1598 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
| |
1599 * |
| |
1600 * - sess - opis sesji |
| |
1601 * - msgclass - rodzaj wiadomości |
| |
1602 * - recipient - numer adresata |
| |
1603 * - message - treść wiadomości |
| |
1604 * - format - informacje o formatowaniu |
| |
1605 * - formatlen - długość informacji o formatowaniu |
| |
1606 * |
| |
1607 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| |
1608 */ |
| |
1609 int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) |
| |
1610 { |
| |
1611 struct gg_send_msg s; |
| |
1612 |
| |
1613 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); |
| |
1614 |
| |
1615 if (!sess) { |
| |
1616 errno = EFAULT; |
| |
1617 return -1; |
| |
1618 } |
| |
1619 |
| |
1620 if (sess->state != GG_STATE_CONNECTED) { |
| |
1621 errno = ENOTCONN; |
| |
1622 return -1; |
| |
1623 } |
| |
1624 |
| |
1625 if (!message) { |
| |
1626 errno = EFAULT; |
| |
1627 return -1; |
| |
1628 } |
| |
1629 |
| |
1630 s.recipient = gg_fix32(recipient); |
| |
1631 if (!sess->seq) |
| |
1632 sess->seq = 0x01740000 | (rand() & 0xffff); |
| |
1633 s.seq = gg_fix32(sess->seq); |
| |
1634 s.msgclass = gg_fix32(msgclass); |
| |
1635 sess->seq += (rand() % 0x300) + 0x300; |
| |
1636 |
| |
1637 if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1) |
| |
1638 return -1; |
| |
1639 |
| |
1640 return gg_fix32(s.seq); |
| |
1641 } |
| |
1642 |
| |
1643 /* |
| |
1644 * gg_send_message_confer() |
| |
1645 * |
| |
1646 * wysyła wiadomość do kilku użytkownikow (konferencja). zwraca losowy numer |
| |
1647 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
| |
1648 * |
| |
1649 * - sess - opis sesji |
| |
1650 * - msgclass - rodzaj wiadomości |
| |
1651 * - recipients_count - ilość adresatów |
| |
1652 * - recipients - numerki adresatów |
| |
1653 * - message - treść wiadomości |
| |
1654 * |
| |
1655 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| |
1656 */ |
| |
1657 int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) |
| |
1658 { |
| |
1659 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); |
| |
1660 |
| |
1661 return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); |
| |
1662 } |
| |
1663 |
| |
1664 /* |
| |
1665 * gg_send_message_confer_richtext() |
| |
1666 * |
| |
1667 * wysyła kolorową wiadomość do kilku użytkownikow (konferencja). zwraca |
| |
1668 * losowy numer sekwencyjny, który można zignorować albo wykorzystać do |
| |
1669 * potwierdzenia. |
| |
1670 * |
| |
1671 * - sess - opis sesji |
| |
1672 * - msgclass - rodzaj wiadomości |
| |
1673 * - recipients_count - ilość adresatów |
| |
1674 * - recipients - numerki adresatów |
| |
1675 * - message - treść wiadomości |
| |
1676 * - format - informacje o formatowaniu |
| |
1677 * - formatlen - długość informacji o formatowaniu |
| |
1678 * |
| |
1679 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| |
1680 */ |
| |
1681 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) |
| |
1682 { |
| |
1683 struct gg_send_msg s; |
| |
1684 struct gg_msg_recipients r; |
| |
1685 int i, j, k; |
| |
1686 uin_t *recps; |
| |
1687 |
| |
1688 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); |
| |
1689 |
| |
1690 if (!sess) { |
| |
1691 errno = EFAULT; |
| |
1692 return -1; |
| |
1693 } |
| |
1694 |
| |
1695 if (sess->state != GG_STATE_CONNECTED) { |
| |
1696 errno = ENOTCONN; |
| |
1697 return -1; |
| |
1698 } |
| |
1699 |
| |
1700 if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { |
| |
1701 errno = EINVAL; |
| |
1702 return -1; |
| |
1703 } |
| |
1704 |
| |
1705 r.flag = 0x01; |
| |
1706 r.count = gg_fix32(recipients_count - 1); |
| |
1707 |
| |
1708 if (!sess->seq) |
| |
1709 sess->seq = 0x01740000 | (rand() & 0xffff); |
| |
1710 s.seq = gg_fix32(sess->seq); |
| |
1711 s.msgclass = gg_fix32(msgclass); |
| |
1712 |
| |
1713 recps = malloc(sizeof(uin_t) * recipients_count); |
| |
1714 if (!recps) |
| |
1715 return -1; |
| |
1716 |
| |
1717 for (i = 0; i < recipients_count; i++) { |
| |
1718 |
| |
1719 s.recipient = gg_fix32(recipients[i]); |
| |
1720 |
| |
1721 for (j = 0, k = 0; j < recipients_count; j++) |
| |
1722 if (recipients[j] != recipients[i]) { |
| |
1723 recps[k] = gg_fix32(recipients[j]); |
| |
1724 k++; |
| |
1725 } |
| |
1726 |
| |
1727 if (!i) |
| |
1728 sess->seq += (rand() % 0x300) + 0x300; |
| |
1729 |
| |
1730 if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { |
| |
1731 free(recps); |
| |
1732 return -1; |
| |
1733 } |
| |
1734 } |
| |
1735 |
| |
1736 free(recps); |
| |
1737 |
| |
1738 return gg_fix32(s.seq); |
| |
1739 } |
| |
1740 |
| |
1741 /* |
| |
1742 * gg_ping() |
| |
1743 * |
| |
1744 * wysyła do serwera pakiet ping. |
| |
1745 * |
| |
1746 * - sess - opis sesji |
| |
1747 * |
| |
1748 * 0, -1. |
| |
1749 */ |
| |
1750 int gg_ping(struct gg_session *sess) |
| |
1751 { |
| |
1752 gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); |
| |
1753 |
| |
1754 if (!sess) { |
| |
1755 errno = EFAULT; |
| |
1756 return -1; |
| |
1757 } |
| |
1758 |
| |
1759 if (sess->state != GG_STATE_CONNECTED) { |
| |
1760 errno = ENOTCONN; |
| |
1761 return -1; |
| |
1762 } |
| |
1763 |
| |
1764 return gg_send_packet(sess, GG_PING, NULL); |
| |
1765 } |
| |
1766 |
| |
1767 /* |
| |
1768 * gg_notify_ex() |
| |
1769 * |
| |
1770 * wysyła serwerowi listę kontaktów (wraz z odpowiadającymi im typami userów), |
| |
1771 * dzięki czemu wie, czyj stan nas interesuje. |
| |
1772 * |
| |
1773 * - sess - opis sesji |
| |
1774 * - userlist - wskaźnik do tablicy numerów |
| |
1775 * - types - wskaźnik do tablicy typów użytkowników |
| |
1776 * - count - ilość numerków |
| |
1777 * |
| |
1778 * 0, -1. |
| |
1779 */ |
| |
1780 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) |
| |
1781 { |
| |
1782 struct gg_notify *n; |
| |
1783 uin_t *u; |
| |
1784 char *t; |
| |
1785 int i, res = 0; |
| |
1786 |
| |
1787 gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); |
| |
1788 |
| |
1789 if (!sess) { |
| |
1790 errno = EFAULT; |
| |
1791 return -1; |
| |
1792 } |
| |
1793 |
| |
1794 if (sess->state != GG_STATE_CONNECTED) { |
| |
1795 errno = ENOTCONN; |
| |
1796 return -1; |
| |
1797 } |
| |
1798 |
| |
1799 if (!userlist || !count) |
| |
1800 return gg_send_packet(sess, GG_LIST_EMPTY, NULL); |
| |
1801 |
| |
1802 while (count > 0) { |
| |
1803 int part_count, packet_type; |
| |
1804 |
| |
1805 if (count > 400) { |
| |
1806 part_count = 400; |
| |
1807 packet_type = GG_NOTIFY_FIRST; |
| |
1808 } else { |
| |
1809 part_count = count; |
| |
1810 packet_type = GG_NOTIFY_LAST; |
| |
1811 } |
| |
1812 |
| |
1813 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) |
| |
1814 return -1; |
| |
1815 |
| |
1816 for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { |
| |
1817 n[i].uin = gg_fix32(*u); |
| |
1818 n[i].dunno1 = *t; |
| |
1819 } |
| |
1820 |
| |
1821 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { |
| |
1822 free(n); |
| |
1823 res = -1; |
| |
1824 break; |
| |
1825 } |
| |
1826 |
| |
1827 count -= part_count; |
| |
1828 userlist += part_count; |
| |
1829 types += part_count; |
| |
1830 |
| |
1831 free(n); |
| |
1832 } |
| |
1833 |
| |
1834 return res; |
| |
1835 } |
| |
1836 |
| |
1837 /* |
| |
1838 * gg_notify() |
| |
1839 * |
| |
1840 * wysyła serwerowi listę kontaktów, dzięki czemu wie, czyj stan nas |
| |
1841 * interesuje. |
| |
1842 * |
| |
1843 * - sess - opis sesji |
| |
1844 * - userlist - wskaźnik do tablicy numerów |
| |
1845 * - count - ilość numerków |
| |
1846 * |
| |
1847 * 0, -1. |
| |
1848 */ |
| |
1849 int gg_notify(struct gg_session *sess, uin_t *userlist, int count) |
| |
1850 { |
| |
1851 struct gg_notify *n; |
| |
1852 uin_t *u; |
| |
1853 int i, res = 0; |
| |
1854 |
| |
1855 gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); |
| |
1856 |
| |
1857 if (!sess) { |
| |
1858 errno = EFAULT; |
| |
1859 return -1; |
| |
1860 } |
| |
1861 |
| |
1862 if (sess->state != GG_STATE_CONNECTED) { |
| |
1863 errno = ENOTCONN; |
| |
1864 return -1; |
| |
1865 } |
| |
1866 |
| |
1867 if (!userlist || !count) |
| |
1868 return gg_send_packet(sess, GG_LIST_EMPTY, NULL); |
| |
1869 |
| |
1870 while (count > 0) { |
| |
1871 int part_count, packet_type; |
| |
1872 |
| |
1873 if (count > 400) { |
| |
1874 part_count = 400; |
| |
1875 packet_type = GG_NOTIFY_FIRST; |
| |
1876 } else { |
| |
1877 part_count = count; |
| |
1878 packet_type = GG_NOTIFY_LAST; |
| |
1879 } |
| |
1880 |
| |
1881 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) |
| |
1882 return -1; |
| |
1883 |
| |
1884 for (u = userlist, i = 0; i < part_count; u++, i++) { |
| |
1885 n[i].uin = gg_fix32(*u); |
| |
1886 n[i].dunno1 = GG_USER_NORMAL; |
| |
1887 } |
| |
1888 |
| |
1889 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { |
| |
1890 res = -1; |
| |
1891 free(n); |
| |
1892 break; |
| |
1893 } |
| |
1894 |
| |
1895 free(n); |
| |
1896 |
| |
1897 userlist += part_count; |
| |
1898 count -= part_count; |
| |
1899 } |
| |
1900 |
| |
1901 return res; |
| |
1902 } |
| |
1903 |
| |
1904 /* |
| |
1905 * gg_add_notify_ex() |
| |
1906 * |
| |
1907 * dodaje do listy kontaktów dany numer w trakcie połączenia. |
| |
1908 * dodawanemu użytkownikowi określamy jego typ (patrz protocol.html) |
| |
1909 * |
| |
1910 * - sess - opis sesji |
| |
1911 * - uin - numer |
| |
1912 * - type - typ |
| |
1913 * |
| |
1914 * 0, -1. |
| |
1915 */ |
| |
1916 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) |
| |
1917 { |
| |
1918 struct gg_add_remove a; |
| |
1919 |
| |
1920 gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); |
| |
1921 |
| |
1922 if (!sess) { |
| |
1923 errno = EFAULT; |
| |
1924 return -1; |
| |
1925 } |
| |
1926 |
| |
1927 if (sess->state != GG_STATE_CONNECTED) { |
| |
1928 errno = ENOTCONN; |
| |
1929 return -1; |
| |
1930 } |
| |
1931 |
| |
1932 a.uin = gg_fix32(uin); |
| |
1933 a.dunno1 = type; |
| |
1934 |
| |
1935 return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); |
| |
1936 } |
| |
1937 |
| |
1938 /* |
| |
1939 * gg_add_notify() |
| |
1940 * |
| |
1941 * dodaje do listy kontaktów dany numer w trakcie połączenia. |
| |
1942 * |
| |
1943 * - sess - opis sesji |
| |
1944 * - uin - numer |
| |
1945 * |
| |
1946 * 0, -1. |
| |
1947 */ |
| |
1948 int gg_add_notify(struct gg_session *sess, uin_t uin) |
| |
1949 { |
| |
1950 return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); |
| |
1951 } |
| |
1952 |
| |
1953 /* |
| |
1954 * gg_remove_notify_ex() |
| |
1955 * |
| |
1956 * usuwa z listy kontaktów w trakcie połączenia. |
| |
1957 * usuwanemu użytkownikowi określamy jego typ (patrz protocol.html) |
| |
1958 * |
| |
1959 * - sess - opis sesji |
| |
1960 * - uin - numer |
| |
1961 * - type - typ |
| |
1962 * |
| |
1963 * 0, -1. |
| |
1964 */ |
| |
1965 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) |
| |
1966 { |
| |
1967 struct gg_add_remove a; |
| |
1968 |
| |
1969 gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); |
| |
1970 |
| |
1971 if (!sess) { |
| |
1972 errno = EFAULT; |
| |
1973 return -1; |
| |
1974 } |
| |
1975 |
| |
1976 if (sess->state != GG_STATE_CONNECTED) { |
| |
1977 errno = ENOTCONN; |
| |
1978 return -1; |
| |
1979 } |
| |
1980 |
| |
1981 a.uin = gg_fix32(uin); |
| |
1982 a.dunno1 = type; |
| |
1983 |
| |
1984 return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); |
| |
1985 } |
| |
1986 |
| |
1987 /* |
| |
1988 * gg_remove_notify() |
| |
1989 * |
| |
1990 * usuwa z listy kontaktów w trakcie połączenia. |
| |
1991 * |
| |
1992 * - sess - opis sesji |
| |
1993 * - uin - numer |
| |
1994 * |
| |
1995 * 0, -1. |
| |
1996 */ |
| |
1997 int gg_remove_notify(struct gg_session *sess, uin_t uin) |
| |
1998 { |
| |
1999 return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); |
| |
2000 } |
| |
2001 |
| |
2002 /* |
| |
2003 * gg_userlist_request() |
| |
2004 * |
| |
2005 * wysyła żądanie/zapytanie listy kontaktów na serwerze. |
| |
2006 * |
| |
2007 * - sess - opis sesji |
| |
2008 * - type - rodzaj zapytania/żądania |
| |
2009 * - request - treść zapytania/żądania (może być NULL) |
| |
2010 * |
| |
2011 * 0, -1 |
| |
2012 */ |
| |
2013 int gg_userlist_request(struct gg_session *sess, char type, const char *request) |
| |
2014 { |
| |
2015 int len; |
| |
2016 |
| |
2017 if (!sess) { |
| |
2018 errno = EFAULT; |
| |
2019 return -1; |
| |
2020 } |
| |
2021 |
| |
2022 if (sess->state != GG_STATE_CONNECTED) { |
| |
2023 errno = ENOTCONN; |
| |
2024 return -1; |
| |
2025 } |
| |
2026 |
| |
2027 if (!request) { |
| |
2028 sess->userlist_blocks = 1; |
| |
2029 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); |
| |
2030 } |
| |
2031 |
| |
2032 len = strlen(request); |
| |
2033 |
| |
2034 sess->userlist_blocks = 0; |
| |
2035 |
| |
2036 while (len > 2047) { |
| |
2037 sess->userlist_blocks++; |
| |
2038 |
| |
2039 if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) |
| |
2040 return -1; |
| |
2041 |
| |
2042 if (type == GG_USERLIST_PUT) |
| |
2043 type = GG_USERLIST_PUT_MORE; |
| |
2044 |
| |
2045 request += 2047; |
| |
2046 len -= 2047; |
| |
2047 } |
| |
2048 |
| |
2049 sess->userlist_blocks++; |
| |
2050 |
| |
2051 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); |
| |
2052 } |
| |
2053 |
| |
2054 /* |
| |
2055 * Local variables: |
| |
2056 * c-indentation-style: k&r |
| |
2057 * c-basic-offset: 8 |
| |
2058 * indent-tabs-mode: notnil |
| |
2059 * End: |
| |
2060 * |
| |
2061 * vim: shiftwidth=8: |
| |
2062 */ |