libpurple/protocols/gg/lib/libgadu.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
equal deleted inserted replaced
38881:25cb836b9cec 38882:bea4cc95b40f
1 /* $Id$ */
2
3 /*
4 * (C) Copyright 2001-2010 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 * Adam Wysocki <gophi@ekg.chmurka.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License Version
12 * 2.1 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
22 * USA.
23 */
24
25 /**
26 * \file libgadu.c
27 *
28 * \brief Główny moduł biblioteki
29 */
30
31 #include "strman.h"
32 #include "network.h"
33 #include "fileio.h"
34
35 #include "libgadu.h"
36 #include "protocol.h"
37 #include "resolver.h"
38 #include "internal.h"
39 #include "encoding.h"
40 #include "debug.h"
41 #include "session.h"
42 #include "message.h"
43 #include "deflate.h"
44 #include "tvbuilder.h"
45 #include "protobuf.h"
46 #include "packets.pb-c.h"
47
48 #include <errno.h>
49 #include <stdarg.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #ifdef GG_CONFIG_HAVE_GNUTLS
54 # include <gnutls/gnutls.h>
55 #endif
56 #ifdef GG_CONFIG_HAVE_OPENSSL
57 # include <openssl/err.h>
58 # include <openssl/rand.h>
59 #endif
60
61 /**
62 * Port gniazda nasłuchującego dla połączeń bezpośrednich.
63 *
64 * \ingroup ip
65 */
66 int gg_dcc_port = 0;
67
68 /**
69 * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
70 *
71 * \ingroup ip
72 */
73 unsigned long gg_dcc_ip = 0;
74
75 /**
76 * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
77 *
78 * \ingroup ip
79 */
80 unsigned long gg_local_ip = 0;
81
82 /**
83 * Flaga włączenia połączeń przez serwer pośredniczący.
84 *
85 * \ingroup proxy
86 */
87 int gg_proxy_enabled = 0;
88
89 /**
90 * Adres serwera pośredniczącego.
91 *
92 * \ingroup proxy
93 */
94 char *gg_proxy_host = NULL;
95
96 /**
97 * Port serwera pośredniczącego.
98 *
99 * \ingroup proxy
100 */
101 int gg_proxy_port = 0;
102
103 /**
104 * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
105 *
106 * \ingroup proxy
107 */
108 int gg_proxy_http_only = 0;
109
110 /**
111 * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
112 *
113 * \ingroup proxy
114 */
115 char *gg_proxy_username = NULL;
116
117 /**
118 * Hasło użytkownika do autoryzacji serwera pośredniczącego.
119 *
120 * \ingroup proxy
121 */
122 char *gg_proxy_password = NULL;
123
124 #ifndef DOXYGEN
125
126 #ifndef lint
127 static char rcsid[] GG_UNUSED = "$Id$";
128 #endif
129
130 #endif /* DOXYGEN */
131
132 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients);
133 static void gg_compat_message_cleanup(struct gg_session *sess);
134
135 #ifdef GG_CONFIG_IS_GPL_COMPLIANT
136 /**
137 * Symbol zdefiniowany tylko dla libgadu zgodnego z licencją GPL.
138 *
139 * Zwracana wartość nie jest istotna, a ponadto może się zmienić w przyszłych
140 * wersjach biblioteki. Istotne jest tylko wywołanie tej funkcji w kodzie, który
141 * ma być zgodny z GPL, aby wymusić jej istnienie.
142 *
143 * \return Wartość 1.
144 *
145 * \ingroup version
146 */
147 int gg_is_gpl_compliant(void)
148 {
149 return 1;
150 }
151 #endif
152
153 /**
154 * Zwraca wersję biblioteki.
155 *
156 * \return Wskaźnik na statyczny bufor z wersją biblioteki.
157 *
158 * \ingroup version
159 */
160 const char *gg_libgadu_version(void)
161 {
162 return GG_LIBGADU_VERSION;
163 }
164
165 void * gg_new0(size_t size)
166 {
167 void *ptr;
168
169 ptr = malloc(size);
170 if (ptr == NULL) {
171 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
172 "//gg_new0(%" GG_SIZE_FMT
173 ") not enough memory\n", size);
174 return NULL;
175 }
176
177 memset(ptr, 0, size);
178 return ptr;
179 }
180
181 int
182 gg_required_proto(struct gg_session *gs, int protocol_version)
183 {
184 if (gs->protocol_version >= protocol_version)
185 return 1;
186
187 gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// requested "
188 "feature requires protocol %#02x, but %#02x is selected\n",
189 protocol_version, gs->protocol_version);
190 return 0;
191 }
192
193 int gg_get_dummy_fd(struct gg_session *sess)
194 {
195 struct gg_session_private *p = sess->private_data;
196
197 if (p->dummyfds_created)
198 return p->dummyfds[0];
199
200 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, p->dummyfds) == -1) {
201 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_get_dummy_fd() "
202 "unable to create pipes (errno=%d, %s)\n",
203 errno, strerror(errno));
204 return -1;
205 }
206
207 p->dummyfds_created = 1;
208 return p->dummyfds[0];
209 }
210
211 /**
212 * \internal Liczy skrót z hasła i ziarna.
213 *
214 * \param password Hasło
215 * \param seed Ziarno podane przez serwer
216 *
217 * \return Wartość skrótu
218 */
219 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
220 {
221 unsigned int x, y, z;
222
223 y = seed;
224
225 for (x = 0; *password; password++) {
226 x = (x & 0xffffff00) | *password;
227 y ^= x;
228 y += x;
229 x <<= 8;
230 y ^= x;
231 x <<= 8;
232 y -= x;
233 x <<= 8;
234 y ^= x;
235
236 z = y & 0x1F;
237 y = (y << z) | (y >> (32 - z));
238 }
239
240 return y;
241 }
242
243 /**
244 * \internal Odbiera od serwera dane binarne.
245 *
246 * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności.
247 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
248 * wywołaniami systemowymi.
249 *
250 * \param sess Struktura sesji
251 * \param buf Bufor na danymi
252 * \param length Długość bufora
253 *
254 * \return To samo co funkcja systemowa \c read
255 */
256 int gg_read(struct gg_session *sess, char *buf, int length)
257 {
258 struct gg_session_private *p = sess->private_data;
259 int res;
260
261 #ifdef GG_CONFIG_HAVE_GNUTLS
262 if (sess->ssl != NULL) {
263 for (;;) {
264 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
265
266 if (res < 0) {
267 if (res == GNUTLS_E_AGAIN)
268 errno = EAGAIN;
269 else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
270 continue;
271 else
272 errno = EINVAL;
273
274 return -1;
275 }
276
277 return res;
278 }
279 }
280 #endif
281
282 #ifdef GG_CONFIG_HAVE_OPENSSL
283 if (sess->ssl != NULL) {
284 for (;;) {
285 int err;
286
287 res = SSL_read(sess->ssl, buf, length);
288
289 if (res < 0) {
290 err = SSL_get_error(sess->ssl, res);
291
292 if (err == SSL_ERROR_SYSCALL && errno == EINTR)
293 continue;
294
295 if (err == SSL_ERROR_WANT_READ)
296 errno = EAGAIN;
297 else if (err != SSL_ERROR_SYSCALL)
298 errno = EINVAL;
299
300 return -1;
301 }
302
303 return res;
304 }
305 }
306 #endif
307
308 if (p->socket_handle != NULL) {
309 if (p->socket_manager.read_cb == NULL) {
310 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
311 "// gg_read() socket_manager.read callback is "
312 "empty\n");
313 errno = EINVAL;
314 return -1;
315 }
316
317 do {
318 res = p->socket_manager.read_cb(
319 p->socket_manager.cb_data, p->socket_handle,
320 (unsigned char*)buf, length);
321 } while (res < 0 && errno == EINTR);
322
323 if (res < 0) {
324 if (errno == EAGAIN)
325 return -1;
326 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
327 "// gg_read() unexpected errno=%d\n", errno);
328 errno = EINVAL;
329 }
330 return res;
331 }
332
333 for (;;) {
334 res = recv(sess->fd, buf, length, 0);
335
336 if (res == -1 && errno == EINTR)
337 continue;
338
339 return res;
340 }
341 }
342
343 /**
344 * \internal Wysyła do serwera dane binarne.
345 *
346 * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności.
347 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
348 * wywołaniami systemowymi.
349 *
350 * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz
351 * gg_write()).
352 *
353 * \param sess Struktura sesji
354 * \param buf Bufor z danymi
355 * \param length Długość bufora
356 *
357 * \return To samo co funkcja systemowa \c write
358 */
359 static int gg_write_common(struct gg_session *sess, const char *buf, int length)
360 {
361 struct gg_session_private *p = sess->private_data;
362 int res;
363
364 #ifdef GG_CONFIG_HAVE_GNUTLS
365 if (sess->ssl != NULL) {
366 for (;;) {
367 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
368
369 if (res < 0) {
370 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
371 continue;
372
373 if (res == GNUTLS_E_AGAIN)
374 errno = EAGAIN;
375 else
376 errno = EINVAL;
377
378 return -1;
379 }
380
381 return res;
382 }
383 }
384 #endif
385
386 #ifdef GG_CONFIG_HAVE_OPENSSL
387 if (sess->ssl != NULL) {
388 for (;;) {
389 int err;
390
391 res = SSL_write(sess->ssl, buf, length);
392
393 if (res < 0) {
394 err = SSL_get_error(sess->ssl, res);
395
396 if (err == SSL_ERROR_SYSCALL && errno == EINTR)
397 continue;
398
399 if (err == SSL_ERROR_WANT_WRITE)
400 errno = EAGAIN;
401 else if (err != SSL_ERROR_SYSCALL)
402 errno = EINVAL;
403
404 return -1;
405 }
406
407 return res;
408 }
409 }
410 #endif
411
412 if (p->socket_handle != NULL) {
413 if (p->socket_manager.write_cb == NULL) {
414 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
415 "// gg_write_common() socket_manager.write "
416 "callback is empty\n");
417 errno = EINVAL;
418 return -1;
419 }
420
421 do {
422 res = p->socket_manager.write_cb(
423 p->socket_manager.cb_data, p->socket_handle,
424 (const unsigned char*)buf, length);
425 } while (res < 0 && errno == EINTR);
426
427 if (res < 0) {
428 if (errno == EAGAIN)
429 return -1;
430 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
431 "// gg_read() unexpected errno=%d\n", errno);
432 errno = EINVAL;
433 }
434
435 return res;
436 }
437
438 for (;;) {
439 res = send(sess->fd, buf, length, 0);
440
441 if (res == -1 && errno == EINTR)
442 continue;
443
444 return res;
445 }
446 }
447
448 /**
449 * \internal Wysyła do serwera dane binarne.
450 *
451 * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
452 *
453 * \param sess Struktura sesji
454 * \param buf Bufor z danymi
455 * \param length Długość bufora
456 *
457 * \return To samo co funkcja systemowa \c write
458 */
459 int gg_write(struct gg_session *sess, const char *buf, int length)
460 {
461 int res = 0;
462
463 if (!sess->async) {
464 int written = 0;
465
466 while (written < length) {
467 res = gg_write_common(sess, buf + written, length - written);
468
469 if (res == -1)
470 return -1;
471
472 written += res;
473 res = written;
474 }
475 } else {
476 if (sess->send_buf == NULL) {
477 res = gg_write_common(sess, buf, length);
478
479 if (res == -1 && errno == EAGAIN)
480 res = 0;
481 if (res == -1)
482 return -1;
483 }
484
485 if (res < length) {
486 char *tmp;
487
488 if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
489 errno = ENOMEM;
490 return -1;
491 }
492
493 sess->send_buf = tmp;
494
495 memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
496
497 sess->send_left += length - res;
498 }
499 }
500
501 return res;
502 }
503
504 void gg_close(struct gg_session *sess)
505 {
506 struct gg_session_private *p = sess->private_data;
507 int errno_copy;
508
509 errno_copy = errno;
510
511 if (!p->socket_is_external) {
512 if (sess->fd != -1)
513 close(sess->fd);
514 } else {
515 assert(p->socket_manager_type !=
516 GG_SOCKET_MANAGER_TYPE_INTERNAL);
517 if (p->socket_handle != NULL) {
518 p->socket_manager.close_cb(p->socket_manager.cb_data,
519 p->socket_handle);
520 }
521 p->socket_is_external = 0;
522 }
523 sess->fd = -1;
524 p->socket_handle = NULL;
525
526 while (p->event_queue) {
527 gg_eventqueue_t *next = p->event_queue->next;
528 gg_event_free(p->event_queue->event);
529 free(p->event_queue);
530 p->event_queue = next;
531 }
532
533 while (p->imgout_queue) {
534 gg_imgout_queue_t *next = p->imgout_queue->next;
535 free(p->imgout_queue);
536 p->imgout_queue = next;
537 }
538
539 if (p->dummyfds_created) {
540 close(p->dummyfds[0]);
541 close(p->dummyfds[1]);
542 p->dummyfds_created = 0;
543 }
544
545 gg_compat_message_cleanup(sess);
546
547 errno = errno_copy;
548 }
549
550 /**
551 * \internal Odbiera pakiet od serwera.
552 *
553 * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
554 * w zaalokowanym buforze.
555 *
556 * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
557 * skompletować całego pakietu -- w takim przypadku zwróci \c NULL, a kodem błędu
558 * będzie \c EAGAIN.
559 *
560 * \param sess Struktura sesji
561 *
562 * \return Wskaźnik do zaalokowanego bufora
563 */
564 void *gg_recv_packet(struct gg_session *sess)
565 {
566 struct gg_header *gh;
567 char *packet;
568 int res;
569 size_t len;
570 uint32_t ghlen = 0;
571
572 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
573
574 if (sess == NULL) {
575 errno = EFAULT;
576 return NULL;
577 }
578
579 for (;;) {
580 if (sess->recv_buf == NULL && sess->recv_done == 0) {
581 sess->recv_buf = malloc(sizeof(struct gg_header) + 1);
582
583 if (sess->recv_buf == NULL) {
584 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
585 return NULL;
586 }
587 }
588
589 gh = (struct gg_header*) sess->recv_buf;
590
591 if ((size_t) sess->recv_done < sizeof(struct gg_header)) {
592 len = sizeof(struct gg_header) - sess->recv_done;
593 gg_debug_session(sess, GG_DEBUG_NET,
594 "// gg_recv_packet() header: %d done, "
595 "%" GG_SIZE_FMT " to go\n",
596 sess->recv_done, len);
597 } else {
598 ghlen = gh ? gg_fix32(gh->length) : 0;
599
600 if (ghlen > 65535) {
601 gg_debug_session(sess, GG_DEBUG_ERROR,
602 "// gg_recv_packet() invalid packet "
603 "length (%d)\n", ghlen);
604 errno = ERANGE;
605 goto fail;
606 }
607
608 if ((size_t) sess->recv_done >= sizeof(struct gg_header) + ghlen) {
609 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n");
610 break;
611 }
612
613 len = sizeof(struct gg_header) + ghlen - sess->recv_done;
614
615 gg_debug_session(sess, GG_DEBUG_NET,
616 "// gg_recv_packet() payload: %d done, "
617 "%u length, %" GG_SIZE_FMT " to go\n",
618 sess->recv_done, ghlen, len);
619 }
620
621 res = gg_read(sess, sess->recv_buf + sess->recv_done, len);
622
623 if (res == 0) {
624 errno = ECONNRESET;
625 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n");
626 goto fail;
627 }
628
629 if (res == -1 && errno == EAGAIN) {
630 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n");
631 goto eagain;
632 }
633
634 if (res == -1) {
635 gg_debug_session(sess, GG_DEBUG_ERROR,
636 "// gg_recv_packet() read failed: errno=%d, "
637 "%s\n", errno, strerror(errno));
638 goto fail;
639 }
640
641 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res);
642
643 if (sess->recv_done + res == sizeof(struct gg_header)) {
644 char *tmp;
645 ghlen = gh ? gg_fix32(gh->length) : 0;
646
647 gg_debug_session(sess, GG_DEBUG_NET,
648 "// gg_recv_packet() header complete, "
649 "payload %d bytes\n", ghlen);
650
651 if (ghlen == 0)
652 break;
653
654 if (ghlen > 65535) {
655 gg_debug_session(sess, GG_DEBUG_ERROR,
656 "// gg_recv_packet() invalid packet "
657 "length (%d)\n", ghlen);
658 errno = ERANGE;
659 goto fail;
660 }
661
662 tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + ghlen + 1);
663
664 if (tmp == NULL) {
665 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
666 goto fail;
667 }
668
669 sess->recv_buf = tmp;
670 }
671
672 sess->recv_done += res;
673 }
674
675 packet = sess->recv_buf;
676 sess->recv_buf = NULL;
677 sess->recv_done = 0;
678
679 if (gh == NULL)
680 goto fail;
681
682 /* Czasami zakładamy, że teksty w pakietach są zakończone zerem */
683 packet[sizeof(struct gg_header) + ghlen] = 0;
684
685 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, "
686 "length=%d)\n", gg_fix32(gh->type), ghlen);
687 gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + ghlen);
688
689 gh->type = gg_fix32(gh->type);
690 gh->length = ghlen;
691
692 return packet;
693
694 fail:
695 free(sess->recv_buf);
696 sess->recv_buf = NULL;
697 sess->recv_done = 0;
698
699 eagain:
700 return NULL;
701 }
702
703 /**
704 * \internal Wysyła pakiet do serwera.
705 *
706 * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
707 * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
708 * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
709 *
710 * \param sess Struktura sesji
711 * \param type Rodzaj pakietu
712 * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
713 * typu \c int) zakończona \c NULL
714 *
715 * \return 0 jeśli się powiodło, -1 w przypadku błędu
716 */
717 int gg_send_packet(struct gg_session *sess, int type, ...)
718 {
719 struct gg_header *h;
720 char *tmp;
721 unsigned int tmp_length;
722 void *payload;
723 unsigned int payload_length;
724 va_list ap;
725 int res;
726
727 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
728
729 tmp_length = sizeof(struct gg_header);
730
731 if (!(tmp = malloc(tmp_length))) {
732 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n");
733 return -1;
734 }
735
736 va_start(ap, type);
737
738 payload = va_arg(ap, void *);
739
740 while (payload) {
741 char *tmp2;
742
743 payload_length = va_arg(ap, unsigned int);
744
745 if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
746 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n");
747 free(tmp);
748 va_end(ap);
749 return -1;
750 }
751
752 tmp = tmp2;
753
754 memcpy(tmp + tmp_length, payload, payload_length);
755 tmp_length += payload_length;
756
757 payload = va_arg(ap, void *);
758 }
759
760 va_end(ap);
761
762 h = (struct gg_header*) tmp;
763 h->type = gg_fix32(type);
764 h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
765
766 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, "
767 "length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
768 gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
769
770 res = gg_write(sess, tmp, tmp_length);
771
772 free(tmp);
773
774 if (res == -1) {
775 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() "
776 "write() failed. res = %d, errno = %d (%s)\n",
777 res, errno, strerror(errno));
778 return -1;
779 }
780
781 if (sess->async) {
782 gg_debug_session(sess, GG_DEBUG_NET, "// gg_send_packet() "
783 "partial write(), %d sent, %d left, %d total left\n",
784 res, tmp_length - res, sess->send_left);
785 }
786
787 if (sess->send_buf)
788 sess->check |= GG_CHECK_WRITE;
789
790 return 0;
791 }
792
793 /**
794 * \internal Funkcja zwrotna sesji.
795 *
796 * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji.
797 * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
798 *
799 * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
800 *
801 * \param sess Struktura sesji
802 *
803 * \return 0 jeśli się powiodło, -1 w przypadku błędu
804 */
805 static int gg_session_callback(struct gg_session *sess)
806 {
807 if (!sess) {
808 errno = EFAULT;
809 return -1;
810 }
811
812 return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
813 }
814
815 /**
816 * Łączy się z serwerem Gadu-Gadu.
817 *
818 * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
819 * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
820 * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca
821 * informacje o zdarzeniach.
822 *
823 * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
824 * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera
825 * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
826 * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
827 * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
828 * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
829 * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
830 * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
831 * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
832 * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
833 *
834 * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
835 * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
836 *
837 * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale
838 * obsługa SSL nie jest wkompilowana.
839 *
840 * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
841 * password, async.
842 *
843 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
844 * w przypadku błędu.
845 *
846 * \ingroup login
847 */
848 struct gg_session *gg_login(const struct gg_login_params *p)
849 {
850 struct gg_session *sess = NULL;
851 struct gg_session_private *sess_private = NULL;
852
853 if (p == NULL) {
854 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
855 errno = EFAULT;
856 return NULL;
857 }
858
859 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
860
861 sess = malloc(sizeof(struct gg_session));
862
863 if (sess == NULL) {
864 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
865 goto fail;
866 }
867
868 memset(sess, 0, sizeof(struct gg_session));
869 sess->fd = -1;
870
871 sess_private = malloc(sizeof(struct gg_session_private));
872
873 if (sess_private == NULL) {
874 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session private data\n");
875 goto fail;
876 }
877
878 memset(sess_private, 0, sizeof(struct gg_session_private));
879 sess->private_data = sess_private;
880
881 if (p->password == NULL || p->uin == 0) {
882 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
883 errno = EFAULT;
884 goto fail;
885 }
886
887 if (!(sess->password = strdup(p->password))) {
888 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
889 goto fail;
890 }
891
892 if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
893 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
894 errno = EFAULT;
895 goto fail;
896 }
897
898 sess->uin = p->uin;
899 sess->state = GG_STATE_RESOLVING;
900 sess->check = GG_CHECK_READ;
901 sess->timeout = GG_DEFAULT_TIMEOUT;
902 sess->async = p->async;
903 sess->type = GG_SESSION_GG;
904 sess->initial_status = p->status;
905 sess->callback = gg_session_callback;
906 sess->destroy = gg_free_session;
907 sess->port = p->server_port;
908 sess->server_addr = p->server_addr;
909 sess->external_port = p->external_port;
910 sess->external_addr = p->external_addr;
911 sess->client_addr = p->client_addr;
912 sess->client_port = p->client_port;
913
914 if (GG_LOGIN_PARAMS_HAS_FIELD(p, compatibility))
915 sess_private->compatibility = p->compatibility;
916
917 if (GG_LOGIN_PARAMS_HAS_FIELD(p, connect_host) && p->connect_host != NULL) {
918 int port = 0;
919 char *colon;
920
921 sess->connect_host = strdup(p->connect_host);
922 if (sess->connect_host == NULL)
923 goto fail;
924
925 colon = strchr(sess->connect_host, ':');
926 if (colon != NULL) {
927 colon[0] = '\0';
928 port = atoi(colon + 1);
929 }
930 if (port > 0)
931 sess->port = port;
932 }
933
934 if (GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager_type) &&
935 GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager) &&
936 p->socket_manager_type != GG_SOCKET_MANAGER_TYPE_INTERNAL)
937 {
938 if ((unsigned int)p->socket_manager_type >
939 GG_SOCKET_MANAGER_TYPE_TLS)
940 {
941 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_login()"
942 " invalid arguments. unknown socket manager "
943 "type (%d)\n", p->socket_manager_type);
944 errno = EFAULT;
945 goto fail;
946 } else {
947 sess_private->socket_manager_type =
948 p->socket_manager_type;
949 memcpy(&sess_private->socket_manager,
950 &p->socket_manager,
951 sizeof(gg_socket_manager_t));
952 }
953 } else {
954 sess_private->socket_manager_type =
955 GG_SOCKET_MANAGER_TYPE_INTERNAL;
956 }
957
958 if (GG_LOGIN_PARAMS_HAS_FIELD(p, host_white_list) &&
959 p->host_white_list != NULL)
960 {
961 sess_private->host_white_list =
962 gg_strarr_dup(p->host_white_list);
963 if (sess_private->host_white_list == NULL)
964 goto fail;
965 }
966
967 if (p->protocol_features == 0) {
968 sess->protocol_features = GG_FEATURE_MSG80 |
969 GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC |
970 GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 |
971 GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK |
972 GG_FEATURE_TYPING_NOTIFICATION;
973 } else {
974 sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
975
976 if (!(p->protocol_features & GG_FEATURE_STATUS77))
977 sess->protocol_features |= GG_FEATURE_STATUS80;
978
979 if (!(p->protocol_features & GG_FEATURE_MSG77))
980 sess->protocol_features |= GG_FEATURE_MSG80;
981 }
982
983 if (!(sess->status_flags = p->status_flags))
984 sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;
985
986 if (!p->protocol_version)
987 sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
988 else if (p->protocol_version < 0x2e) {
989 gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n");
990 sess->protocol_version = 0x2e;
991 } else
992 sess->protocol_version = p->protocol_version;
993
994 if (p->client_version && strcmp(p->client_version, "-") != 0)
995 sess->client_version = strdup(p->client_version);
996 sess->last_sysmsg = p->last_sysmsg;
997 sess->image_size = p->image_size;
998 sess->pid = -1;
999 sess->encoding = p->encoding;
1000
1001 if (gg_session_set_resolver(sess, p->resolver) == -1) {
1002 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. "
1003 "unsupported resolver type (%d)\n", p->resolver);
1004 errno = EFAULT;
1005 goto fail;
1006 }
1007
1008 if (p->status_descr) {
1009 sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
1010
1011 if (!sess->initial_descr) {
1012 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
1013 goto fail;
1014 }
1015
1016 /* XXX pamiętać, żeby nie ciąć w środku znaku utf-8 */
1017
1018 if (strlen(sess->initial_descr) > GG_STATUS_DESCR_MAXSIZE)
1019 sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0;
1020 }
1021
1022 if (p->tls != GG_SSL_DISABLED) {
1023 #if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL)
1024 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
1025
1026 if (p->tls == GG_SSL_REQUIRED) {
1027 errno = ENOSYS;
1028 goto fail;
1029 }
1030 #else
1031 sess->ssl_flag = p->tls;
1032 #endif
1033 }
1034
1035 if (p->hash_type)
1036 sess->hash_type = p->hash_type;
1037 else
1038 sess->hash_type = GG_LOGIN_HASH_SHA1;
1039
1040 if (sess->server_addr == 0 && sess->connect_host == NULL) {
1041 if (gg_proxy_enabled) {
1042 sess->resolver_host = gg_proxy_host;
1043 sess->proxy_port = gg_proxy_port;
1044 sess->state = (sess->async) ?
1045 GG_STATE_RESOLVE_PROXY_HUB_ASYNC :
1046 GG_STATE_RESOLVE_PROXY_HUB_SYNC;
1047 } else {
1048 sess->resolver_host = GG_APPMSG_HOST;
1049 sess->proxy_port = 0;
1050 sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC;
1051 }
1052 } else {
1053 if (sess->connect_host != NULL)
1054 sess->server_addr = 0;
1055 else {
1056 /* XXX inet_ntoa i wielowątkowość */
1057 sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr));
1058 if (sess->connect_host == NULL)
1059 goto fail;
1060 }
1061 sess->connect_index = 0;
1062
1063 if (gg_proxy_enabled) {
1064 sess->resolver_host = gg_proxy_host;
1065 sess->proxy_port = gg_proxy_port;
1066 if (sess->port == 0)
1067 sess->connect_port[0] = GG_HTTPS_PORT;
1068 else
1069 sess->connect_port[0] = sess->port;
1070 sess->connect_port[1] = 0;
1071 sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC;
1072 } else {
1073 sess->resolver_host = sess->connect_host;
1074 if (sess->port == 0) {
1075 if (sess->ssl_flag == GG_SSL_DISABLED) {
1076 sess->connect_port[0] = GG_DEFAULT_PORT;
1077 sess->connect_port[1] = GG_HTTPS_PORT;
1078 } else {
1079 sess->connect_port[0] = GG_HTTPS_PORT;
1080 sess->connect_port[1] = 0;
1081 }
1082 } else {
1083 sess->connect_port[0] = sess->port;
1084 sess->connect_port[1] = 0;
1085 }
1086 sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC;
1087 }
1088 }
1089
1090 /* XXX inaczej gg_watch_fd() wyjdzie z timeoutem */
1091 sess->timeout = GG_DEFAULT_TIMEOUT;
1092
1093 if (!sess->async) {
1094 while (!GG_SESSION_IS_CONNECTED(sess)) {
1095 struct gg_event *ge;
1096
1097 ge = gg_watch_fd(sess);
1098
1099 if (ge == NULL) {
1100 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
1101 goto fail;
1102 }
1103
1104 if (ge->type == GG_EVENT_CONN_FAILED) {
1105 errno = EACCES;
1106 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n");
1107 gg_event_free(ge);
1108 goto fail;
1109 }
1110
1111 gg_event_free(ge);
1112 }
1113 } else {
1114 struct gg_event *ge;
1115
1116 ge = gg_watch_fd(sess);
1117
1118 if (ge == NULL) {
1119 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
1120 goto fail;
1121 }
1122
1123 gg_event_free(ge);
1124 }
1125
1126 return sess;
1127
1128 fail:
1129 gg_free_session(sess);
1130
1131 return NULL;
1132 }
1133
1134 /**
1135 * Wysyła do serwera pakiet utrzymania połączenia.
1136 *
1137 * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
1138 * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
1139 * połączenie.
1140 *
1141 * \param sess Struktura sesji
1142 *
1143 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1144 *
1145 * \ingroup login
1146 */
1147 int gg_ping(struct gg_session *sess)
1148 {
1149 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
1150
1151 if (!sess) {
1152 errno = EFAULT;
1153 return -1;
1154 }
1155
1156 if (sess->state != GG_STATE_CONNECTED) {
1157 errno = ENOTCONN;
1158 return -1;
1159 }
1160
1161 return gg_send_packet(sess, GG_PING, NULL);
1162 }
1163
1164 /**
1165 * Kończy połączenie z serwerem.
1166 *
1167 * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
1168 * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
1169 * wcześniej wywołać funkcję \c gg_change_status_descr() lub
1170 * \c gg_change_status_descr_time().
1171 *
1172 * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
1173 * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one
1174 * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu
1175 * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR
1176 * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie
1177 * \c GG_EVENT_DISCONNECT_ACK.
1178 *
1179 * \param sess Struktura sesji
1180 *
1181 * \ingroup login
1182 */
1183 void gg_logoff(struct gg_session *sess)
1184 {
1185 if (!sess)
1186 return;
1187
1188 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
1189
1190 #ifdef GG_CONFIG_HAVE_GNUTLS
1191 if (sess->ssl != NULL)
1192 gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
1193 #endif
1194
1195 #ifdef GG_CONFIG_HAVE_OPENSSL
1196 if (sess->ssl != NULL)
1197 SSL_shutdown(sess->ssl);
1198 #endif
1199
1200 sess->resolver_cleanup(&sess->resolver, 1);
1201
1202 gg_close(sess);
1203
1204 if (sess->send_buf) {
1205 free(sess->send_buf);
1206 sess->send_buf = NULL;
1207 sess->send_left = 0;
1208 }
1209 }
1210
1211 /**
1212 * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
1213 * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
1214 * systemowych.
1215 *
1216 * \param sess Struktura sesji
1217 *
1218 * \ingroup login
1219 */
1220 void gg_free_session(struct gg_session *sess)
1221 {
1222 struct gg_dcc7 *dcc;
1223 gg_chat_list_t *chat;
1224
1225 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess);
1226
1227 if (sess == NULL)
1228 return;
1229
1230 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
1231
1232 free(sess->resolver_result);
1233 free(sess->connect_host);
1234 free(sess->password);
1235 free(sess->initial_descr);
1236 free(sess->client_version);
1237 free(sess->header_buf);
1238 free(sess->recv_buf);
1239
1240 #ifdef GG_CONFIG_HAVE_GNUTLS
1241 if (sess->ssl != NULL) {
1242 gg_session_gnutls_t *tmp;
1243
1244 tmp = (gg_session_gnutls_t*) sess->ssl;
1245 gnutls_deinit(tmp->session);
1246 gnutls_certificate_free_credentials(tmp->xcred);
1247 gnutls_global_deinit();
1248 free(sess->ssl);
1249 }
1250 #endif
1251
1252 #ifdef GG_CONFIG_HAVE_OPENSSL
1253 if (sess->ssl)
1254 SSL_free(sess->ssl);
1255
1256 if (sess->ssl_ctx)
1257 SSL_CTX_free(sess->ssl_ctx);
1258 #endif
1259
1260 if (sess->resolver_cleanup != NULL)
1261 sess->resolver_cleanup(&sess->resolver, 1);
1262
1263 gg_close(sess);
1264
1265 while (sess->images) {
1266 struct gg_image_queue *next = sess->images->next;
1267
1268 gg_image_queue_remove(sess, sess->images, 1);
1269
1270 /* a fix for false-positive NULL-dereference */
1271 sess->images = next;
1272 }
1273
1274 free(sess->send_buf);
1275
1276 for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
1277 dcc->sess = NULL;
1278
1279 chat = sess->private_data->chat_list;
1280 while (chat != NULL) {
1281 gg_chat_list_t *next = chat->next;
1282 free(chat->participants);
1283 free(chat);
1284 chat = next;
1285 }
1286
1287 gg_strarr_free(sess->private_data->host_white_list);
1288
1289 free(sess->private_data);
1290
1291 free(sess);
1292 }
1293
1294 /**
1295 * Zmienia status użytkownika.
1296 *
1297 * \param sess Struktura sesji
1298 * \param status Nowy status użytkownika
1299 *
1300 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1301 *
1302 * \ingroup status
1303 */
1304 int gg_change_status(struct gg_session *sess, int status)
1305 {
1306 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
1307
1308 return gg_change_status_descr(sess, status, NULL);
1309 }
1310
1311 /**
1312 * Zmienia status użytkownika na status opisowy.
1313 *
1314 * \param sess Struktura sesji
1315 * \param status Nowy status użytkownika
1316 * \param descr Opis statusu użytkownika (lub \c NULL)
1317 *
1318 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1319 *
1320 * \ingroup status
1321 */
1322 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
1323 {
1324 struct gg_new_status80 p;
1325 char *gen_descr = NULL;
1326 int descr_len = 0;
1327 int descr_null_len = 0;
1328 int res;
1329
1330 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
1331
1332 if (!sess) {
1333 errno = EFAULT;
1334 return -1;
1335 }
1336
1337 if (sess->state != GG_STATE_CONNECTED) {
1338 errno = ENOTCONN;
1339 return -1;
1340 }
1341
1342 sess->status = status;
1343
1344 if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
1345 descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
1346
1347 if (!gen_descr)
1348 return -1;
1349 }
1350
1351 if (descr) {
1352 descr_len = strlen(descr);
1353
1354 if (descr_len > GG_STATUS_DESCR_MAXSIZE)
1355 descr_len = GG_STATUS_DESCR_MAXSIZE;
1356
1357 /* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */
1358 } else {
1359 descr = "";
1360 }
1361
1362 p.status = gg_fix32(status);
1363 p.flags = gg_fix32(sess->status_flags);
1364 p.description_size = gg_fix32(descr_len);
1365
1366 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
1367 p.flags = gg_fix32(0x00000014);
1368 descr_null_len = 1;
1369 }
1370
1371 res = gg_send_packet(sess, GG_NEW_STATUS80,
1372 &p, sizeof(p), descr, descr_len,
1373 "\x00", descr_null_len, NULL);
1374
1375 free(gen_descr);
1376
1377 if (GG_S_NA(status)) {
1378 sess->state = GG_STATE_DISCONNECTING;
1379 sess->timeout = GG_TIMEOUT_DISCONNECT;
1380 }
1381
1382 return res;
1383 }
1384
1385 /**
1386 * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
1387 *
1388 * \param sess Struktura sesji
1389 * \param status Nowy status użytkownika
1390 * \param descr Opis statusu użytkownika
1391 * \param ts Czas powrotu w postaci uniksowego znacznika czasu
1392 *
1393 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1394 *
1395 * \ingroup status
1396 */
1397 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int ts)
1398 {
1399 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1400 "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n",
1401 sess, status, descr, ts);
1402
1403 return gg_change_status_descr(sess, status, descr);
1404 }
1405
1406 /**
1407 * Funkcja zmieniająca flagi statusu.
1408 *
1409 * \param sess Struktura sesji
1410 * \param flags Nowe flagi statusu
1411 *
1412 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1413 *
1414 * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą
1415 * funkcji z rodziny \c gg_change_status().
1416 *
1417 * \ingroup status
1418 */
1419 int gg_change_status_flags(struct gg_session *sess, int flags)
1420 {
1421 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);
1422
1423 if (sess == NULL) {
1424 errno = EFAULT;
1425 return -1;
1426 }
1427
1428 sess->status_flags = flags;
1429
1430 return 0;
1431 }
1432
1433 #ifndef DOXYGEN
1434
1435 static int gg_send_message_110(struct gg_session *sess,
1436 uin_t recipient, uint64_t chat_id,
1437 const char *message, int is_html)
1438 {
1439 GG110SendMessage msg = GG110_SEND_MESSAGE__INIT;
1440 int packet_type = recipient ? GG_SEND_MSG110 : GG_CHAT_SEND_MSG;
1441 int seq;
1442 char *html_message_gen = NULL, *plain_message_gen = NULL;
1443 const char *html_message, *plain_message;
1444 int succ = 1;
1445
1446 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1447 "** gg_send_message_110(%p, %u, %" PRIu64 ", %p, %d);\n",
1448 sess, recipient, chat_id, message, is_html);
1449
1450 if (message == NULL)
1451 return -1;
1452
1453 if ((recipient == 0) == (chat_id == 0))
1454 return -1;
1455
1456 if (is_html) {
1457 html_message = message;
1458
1459 if (sess->encoding != GG_ENCODING_UTF8) {
1460 html_message = html_message_gen = gg_encoding_convert(
1461 html_message, sess->encoding, GG_ENCODING_UTF8,
1462 -1, -1);
1463 if (html_message_gen == NULL)
1464 return -1;
1465 }
1466
1467 plain_message = plain_message_gen =
1468 gg_message_html_to_text_110(html_message);
1469 if (plain_message_gen == NULL) {
1470 free(html_message_gen);
1471 return -1;
1472 }
1473 } else {
1474 plain_message = message;
1475
1476 if (sess->encoding != GG_ENCODING_UTF8) {
1477 plain_message = plain_message_gen = gg_encoding_convert(
1478 plain_message, sess->encoding, GG_ENCODING_UTF8,
1479 -1, -1);
1480 if (plain_message_gen == NULL)
1481 return -1;
1482 }
1483
1484 html_message = html_message_gen =
1485 gg_message_text_to_html_110(plain_message, -1);
1486 if (html_message_gen == NULL) {
1487 free(plain_message_gen);
1488 return -1;
1489 }
1490 }
1491
1492 seq = ++sess->seq;
1493
1494 if (recipient) {
1495 msg.has_recipient = 1;
1496 gg_protobuf_set_uin(&msg.recipient, recipient, NULL);
1497 }
1498
1499 msg.seq = seq;
1500
1501 /* rzutujemy z const, ale msg i tak nie będzie modyfikowany */
1502 msg.msg_plain = (char*)plain_message;
1503 msg.msg_xhtml = (char*)html_message;
1504
1505 if (chat_id) {
1506 msg.dummy3 = "";
1507 msg.has_chat_id = 1;
1508 msg.chat_id = chat_id;
1509 }
1510
1511 if (!GG_PROTOBUF_SEND(sess, NULL, packet_type, gg110_send_message, msg))
1512 succ = 0;
1513
1514 free(html_message_gen);
1515 free(plain_message_gen);
1516
1517 return succ ? seq : -1;
1518 }
1519
1520 static char *
1521 gg_message_legacy_text_to_html(const char *src, gg_encoding_t encoding,
1522 const unsigned char *format, size_t format_len)
1523 {
1524 size_t len;
1525 char *dst;
1526
1527 if (format == NULL || format_len <= 3) {
1528 format = NULL;
1529 format_len = 0;
1530 } else {
1531 format += 3;
1532 format_len -= 3;
1533 }
1534
1535 len = gg_message_text_to_html(NULL, src, encoding, format, format_len);
1536
1537 dst = malloc(len + 1);
1538 if (dst == NULL)
1539 return NULL;
1540
1541 gg_message_text_to_html(dst, src, encoding, format, format_len);
1542
1543 return dst;
1544 }
1545
1546 /**
1547 * \internal Wysyła wiadomość.
1548 *
1549 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1550 * do potwierdzenia.
1551 *
1552 * \param sess Struktura sesji
1553 * \param msgclass Klasa wiadomości
1554 * \param recipients_count Liczba adresatów
1555 * \param recipients Wskaźnik do tablicy z numerami adresatów
1556 * \param message Treść wiadomości
1557 * \param format Informacje o formatowaniu
1558 * \param formatlen Długość informacji o formatowaniu
1559 * \param html_message Treść wiadomości HTML
1560 *
1561 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1562 *
1563 * \ingroup messages
1564 */
1565 static int gg_send_message_common(struct gg_session *sess, int msgclass,
1566 int recipients_count, uin_t *recipients, const unsigned char *message,
1567 const unsigned char *format, int formatlen,
1568 const unsigned char *html_message)
1569 {
1570 struct gg_send_msg80 s80;
1571 const char *cp_msg = NULL, *utf_html_msg = NULL;
1572 char *recoded_msg = NULL, *recoded_html_msg = NULL;
1573 unsigned char *generated_format = NULL;
1574 int seq_no = -1;
1575
1576 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common("
1577 "%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass,
1578 recipients_count, recipients, message, format,
1579 formatlen, html_message);
1580
1581 if (!sess) {
1582 errno = EFAULT;
1583 return -1;
1584 }
1585
1586 if (sess->state != GG_STATE_CONNECTED) {
1587 errno = ENOTCONN;
1588 return -1;
1589 }
1590
1591 if ((message == NULL && html_message == NULL) ||
1592 recipients_count <= 0 || recipients_count > 0xffff ||
1593 recipients == NULL || (format == NULL && formatlen != 0))
1594 {
1595 errno = EINVAL;
1596 return -1;
1597 }
1598
1599 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
1600 recipients_count == 1)
1601 {
1602 int is_html = (html_message != NULL);
1603 char *formatted_msg = NULL;
1604
1605 if (formatlen > 3 && !is_html) {
1606 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
1607 "// gg_send_message_common() using legacy "
1608 "formatting with new protocol\n");
1609 formatted_msg = gg_message_legacy_text_to_html(
1610 (const char *)message, sess->encoding,
1611 format, formatlen);
1612 if (formatted_msg == NULL)
1613 goto cleanup;
1614 html_message = (unsigned char*)formatted_msg;
1615 is_html = 1;
1616 }
1617
1618 seq_no = gg_send_message_110(sess, recipients[0], 0,
1619 (const char*)(is_html ? html_message : message),
1620 is_html);
1621 goto cleanup;
1622 }
1623
1624 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
1625 !gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_LEGACY_CONFER))
1626 {
1627 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
1628 "// gg_send_message_common() legacy conferences disabled\n");
1629 errno = EINVAL;
1630 return -1;
1631 }
1632
1633 if (message == NULL) {
1634 char *tmp_msg;
1635 size_t len, fmt_len;
1636 uint16_t fixed_fmt_len;
1637
1638 len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding);
1639
1640 tmp_msg = malloc(len + 1);
1641
1642 if (tmp_msg == NULL)
1643 goto cleanup;
1644
1645 if (fmt_len != 0) {
1646 generated_format = malloc(fmt_len + 3);
1647
1648 if (generated_format == NULL) {
1649 free(tmp_msg);
1650 goto cleanup;
1651 }
1652
1653 generated_format[0] = '\x02';
1654 fixed_fmt_len = gg_fix16(fmt_len);
1655 memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len));
1656 gg_message_html_to_text(tmp_msg, generated_format + 3,
1657 NULL, (const char*)html_message, sess->encoding);
1658
1659 format = generated_format;
1660 formatlen = fmt_len + 3;
1661 } else {
1662 gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding);
1663
1664 format = NULL;
1665 formatlen = 0;
1666 }
1667
1668 if (sess->encoding != GG_ENCODING_CP1250) {
1669 cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1);
1670 free(tmp_msg);
1671
1672 if (cp_msg == NULL)
1673 goto cleanup;
1674 } else {
1675 cp_msg = recoded_msg = tmp_msg;
1676 }
1677 } else {
1678 if (sess->encoding != GG_ENCODING_CP1250) {
1679 cp_msg = recoded_msg = gg_encoding_convert(
1680 (const char*)message, sess->encoding,
1681 GG_ENCODING_CP1250, -1, -1);
1682
1683 if (cp_msg == NULL)
1684 goto cleanup;
1685 } else {
1686 cp_msg = (const char*) message;
1687 }
1688 }
1689
1690 if (html_message == NULL) {
1691 char *formatted_msg;
1692
1693 formatted_msg = gg_message_legacy_text_to_html(
1694 (const char*)message, sess->encoding, format, formatlen);
1695 if (formatted_msg == NULL)
1696 goto cleanup;
1697
1698 if (sess->encoding == GG_ENCODING_UTF8) {
1699 utf_html_msg = recoded_html_msg = formatted_msg;
1700 } else {
1701 utf_html_msg = recoded_html_msg = gg_encoding_convert(
1702 formatted_msg, sess->encoding,
1703 GG_ENCODING_UTF8, -1, -1);
1704 free(formatted_msg);
1705
1706 if (utf_html_msg == NULL)
1707 goto cleanup;
1708 }
1709 } else {
1710 if (sess->encoding == GG_ENCODING_UTF8) {
1711 utf_html_msg = (const char*) html_message;
1712 } else {
1713 utf_html_msg = recoded_html_msg = gg_encoding_convert(
1714 (const char*)html_message, sess->encoding,
1715 GG_ENCODING_UTF8, -1, -1);
1716
1717 if (utf_html_msg == NULL)
1718 goto cleanup;
1719 }
1720 }
1721
1722 /* Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
1723 * wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
1724 * wartość, żeby każda wiadomość miała unikalny numer.
1725 */
1726
1727 seq_no = time(NULL);
1728
1729 if (seq_no <= sess->seq)
1730 seq_no = sess->seq + 1;
1731
1732 sess->seq = seq_no;
1733
1734 s80.seq = gg_fix32(seq_no);
1735 s80.msgclass = gg_fix32(msgclass);
1736 s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1);
1737 s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1);
1738
1739 if (recipients_count > 1) {
1740 struct gg_msg_recipients r;
1741 int i, j, k;
1742 uin_t *recps;
1743
1744 r.flag = GG_MSG_OPTION_CONFERENCE;
1745 r.count = gg_fix32(recipients_count - 1);
1746
1747 recps = malloc(sizeof(uin_t) * (recipients_count - 1));
1748
1749 if (!recps) {
1750 seq_no = -1;
1751 goto cleanup;
1752 }
1753
1754 for (i = 0; i < recipients_count; i++) {
1755 for (j = 0, k = 0; j < recipients_count; j++) {
1756 if (j != i) {
1757 recps[k] = gg_fix32(recipients[j]);
1758 k++;
1759 }
1760 }
1761
1762 s80.recipient = gg_fix32(recipients[i]);
1763
1764 if (gg_send_packet(sess, GG_SEND_MSG80, &s80,
1765 sizeof(s80), utf_html_msg,
1766 strlen(utf_html_msg) + 1, cp_msg,
1767 strlen(cp_msg) + 1, &r, sizeof(r), recps,
1768 (recipients_count - 1) * sizeof(uin_t), format,
1769 formatlen, NULL) == -1)
1770 {
1771 seq_no = -1;
1772 }
1773 }
1774
1775 free(recps);
1776 } else {
1777 s80.recipient = gg_fix32(recipients[0]);
1778
1779 if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80),
1780 utf_html_msg, strlen(utf_html_msg) + 1, cp_msg,
1781 strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
1782 {
1783 seq_no = -1;
1784 }
1785 }
1786
1787 cleanup:
1788 free(recoded_msg);
1789 free(recoded_html_msg);
1790 free(generated_format);
1791
1792 if (seq_no >= 0)
1793 gg_compat_message_sent(sess, seq_no, recipients_count, recipients);
1794
1795 return seq_no;
1796 }
1797
1798 #endif /* DOXYGEN */
1799
1800 /**
1801 * Wysyła wiadomość do użytkownika.
1802 *
1803 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1804 * do potwierdzenia.
1805 *
1806 * \param sess Struktura sesji
1807 * \param msgclass Klasa wiadomości
1808 * \param recipient Numer adresata
1809 * \param message Treść wiadomości
1810 *
1811 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1812 *
1813 * \ingroup messages
1814 */
1815 int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
1816 {
1817 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, "
1818 "%u, %p)\n", sess, msgclass, recipient, message);
1819
1820 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
1821 int seq_no;
1822
1823 seq_no = gg_send_message_110(sess, recipient, 0, (const char*)message, 0);
1824
1825 if (seq_no >= 0)
1826 gg_compat_message_sent(sess, seq_no, 1, &recipient);
1827
1828 return seq_no;
1829 }
1830
1831 return gg_send_message_common(sess, msgclass, 1, &recipient, message,
1832 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
1833 9, NULL);
1834 }
1835
1836 /**
1837 * Wysyła wiadomość formatowaną.
1838 *
1839 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1840 * do potwierdzenia.
1841 *
1842 * \param sess Struktura sesji
1843 * \param msgclass Klasa wiadomości
1844 * \param recipient Numer adresata
1845 * \param message Treść wiadomości
1846 * \param format Informacje o formatowaniu
1847 * \param formatlen Długość informacji o formatowaniu
1848 *
1849 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1850 *
1851 * \ingroup messages
1852 */
1853 int gg_send_message_richtext(struct gg_session *sess, int msgclass,
1854 uin_t recipient, const unsigned char *message,
1855 const unsigned char *format, int formatlen)
1856 {
1857 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext("
1858 "%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient,
1859 message, format, formatlen);
1860
1861 return gg_send_message_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL);
1862 }
1863
1864 /**
1865 * Wysyła formatowaną wiadomość HTML.
1866 *
1867 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1868 * do potwierdzenia.
1869 *
1870 * \param sess Struktura sesji
1871 * \param msgclass Klasa wiadomości
1872 * \param recipient Numer adresata
1873 * \param html_message Treść wiadomości HTML
1874 *
1875 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1876 *
1877 * \ingroup messages
1878 */
1879 int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message)
1880 {
1881 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, "
1882 "%d, %u, %p);\n", sess, msgclass, recipient, html_message);
1883
1884 return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message);
1885 }
1886
1887 /**
1888 * Wysyła wiadomość w ramach konferencji.
1889 *
1890 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1891 * do potwierdzenia.
1892 *
1893 * \param sess Struktura sesji
1894 * \param msgclass Klasa wiadomości
1895 * \param recipients_count Liczba adresatów
1896 * \param recipients Wskaźnik do tablicy z numerami adresatów
1897 * \param message Treść wiadomości
1898 *
1899 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1900 *
1901 * \ingroup messages
1902 */
1903 int gg_send_message_confer(struct gg_session *sess, int msgclass,
1904 int recipients_count, uin_t *recipients, const unsigned char *message)
1905 {
1906 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer("
1907 "%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count,
1908 recipients, message);
1909
1910 return gg_send_message_common(sess, msgclass, recipients_count,
1911 recipients, message,
1912 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
1913 9, NULL);
1914 }
1915
1916 /**
1917 * Wysyła wiadomość formatowaną w ramach konferencji.
1918 *
1919 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1920 * do potwierdzenia.
1921 *
1922 * \param sess Struktura sesji
1923 * \param msgclass Klasa wiadomości
1924 * \param recipients_count Liczba adresatów
1925 * \param recipients Wskaźnik do tablicy z numerami adresatów
1926 * \param message Treść wiadomości
1927 * \param format Informacje o formatowaniu
1928 * \param formatlen Długość informacji o formatowaniu
1929 *
1930 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1931 *
1932 * \ingroup messages
1933 */
1934 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass,
1935 int recipients_count, uin_t *recipients, const unsigned char *message,
1936 const unsigned char *format, int formatlen)
1937 {
1938 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1939 "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, "
1940 "%d);\n", sess, msgclass, recipients_count, recipients, message,
1941 format, formatlen);
1942
1943 return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL);
1944 }
1945
1946 /**
1947 * Wysyła formatowaną wiadomość HTML w ramach konferencji.
1948 *
1949 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1950 * do potwierdzenia.
1951 *
1952 * \param sess Struktura sesji
1953 * \param msgclass Klasa wiadomości
1954 * \param recipients_count Liczba adresatów
1955 * \param recipients Wskaźnik do tablicy z numerami adresatów
1956 * \param html_message Treść wiadomości HTML
1957 *
1958 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1959 *
1960 * \ingroup messages
1961 */
1962 int gg_send_message_confer_html(struct gg_session *sess, int msgclass,
1963 int recipients_count, uin_t *recipients,
1964 const unsigned char *html_message)
1965 {
1966 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1967 "** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess,
1968 msgclass, recipients_count, recipients, html_message);
1969
1970 return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message);
1971 }
1972
1973 /**
1974 * Wysyła wiadomość binarną przeznaczoną dla klienta.
1975 *
1976 * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
1977 * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
1978 * który można zignorować albo wykorzystać do potwierdzenia.
1979 *
1980 * \param sess Struktura sesji
1981 * \param msgclass Klasa wiadomości
1982 * \param recipient Numer adresata
1983 * \param message Treść wiadomości
1984 * \param message_len Długość wiadomości
1985 *
1986 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1987 *
1988 * \ingroup messages
1989 */
1990 int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient,
1991 const unsigned char *message, int message_len)
1992 {
1993 struct gg_send_msg s;
1994
1995 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, "
1996 "%d, %u, ...);\n", sess, msgclass, recipient);
1997
1998 if (!sess) {
1999 errno = EFAULT;
2000 return -1;
2001 }
2002
2003 if (sess->state != GG_STATE_CONNECTED) {
2004 errno = ENOTCONN;
2005 return -1;
2006 }
2007
2008 s.recipient = gg_fix32(recipient);
2009 s.seq = gg_fix32(0);
2010 s.msgclass = gg_fix32(msgclass);
2011
2012 return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
2013 }
2014
2015 /**
2016 * Wysyła żądanie obrazka o podanych parametrach.
2017 *
2018 * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
2019 * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
2020 * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
2021 * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
2022 *
2023 * \param sess Struktura sesji
2024 * \param recipient Numer adresata
2025 * \param size Rozmiar obrazka w bajtach
2026 * \param crc32 Suma kontrola obrazka
2027 *
2028 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2029 *
2030 * \ingroup messages
2031 */
2032 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
2033 {
2034 struct gg_send_msg s;
2035 struct gg_msg_image_request r;
2036 char dummy = 0;
2037 int res;
2038
2039 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, "
2040 "%u, 0x%.4x);\n", sess, recipient, size, crc32);
2041
2042 if (!sess) {
2043 errno = EFAULT;
2044 return -1;
2045 }
2046
2047 if (sess->state != GG_STATE_CONNECTED) {
2048 errno = ENOTCONN;
2049 return -1;
2050 }
2051
2052 if (size < 0) {
2053 errno = EINVAL;
2054 return -1;
2055 }
2056
2057 s.recipient = gg_fix32(recipient);
2058 s.seq = gg_fix32(0);
2059 s.msgclass = gg_fix32(GG_CLASS_MSG);
2060
2061 r.flag = GG_MSG_OPTION_IMAGE_REQUEST;
2062 r.size = gg_fix32(size);
2063 r.crc32 = gg_fix32(crc32);
2064
2065 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
2066
2067 if (!res) {
2068 struct gg_image_queue *q = malloc(sizeof(*q));
2069 char *buf;
2070
2071 if (!q) {
2072 gg_debug_session(sess, GG_DEBUG_MISC,
2073 "// gg_image_request() not enough memory for "
2074 "image queue\n");
2075 return -1;
2076 }
2077
2078 buf = malloc(size);
2079 if (size && !buf) {
2080 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
2081 free(q);
2082 return -1;
2083 }
2084
2085 memset(q, 0, sizeof(*q));
2086
2087 q->sender = recipient;
2088 q->size = size;
2089 q->crc32 = crc32;
2090 q->image = buf;
2091
2092 if (!sess->images)
2093 sess->images = q;
2094 else {
2095 struct gg_image_queue *qq;
2096
2097 for (qq = sess->images; qq->next; qq = qq->next);
2098
2099 qq->next = q;
2100 }
2101 }
2102
2103 return res;
2104 }
2105
2106 /**
2107 * Wysyła żądany obrazek.
2108 *
2109 * \param sess Struktura sesji
2110 * \param recipient Numer adresata
2111 * \param filename Nazwa pliku
2112 * \param image Bufor z obrazkiem
2113 * \param size Rozmiar obrazka
2114 *
2115 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2116 *
2117 * \ingroup messages
2118 */
2119 int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
2120 {
2121 struct gg_session_private *p;
2122 struct gg_msg_image_reply *r;
2123 struct gg_send_msg s;
2124 const char *tmp;
2125 char buf[1910];
2126 gg_imgout_queue_t *queue = NULL, *queue_end = NULL;
2127
2128 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, "
2129 "\"%s\", %p, %d);\n", sess, recipient, filename, image, size);
2130
2131 if (!sess || !filename || !image) {
2132 errno = EFAULT;
2133 return -1;
2134 }
2135
2136 p = sess->private_data;
2137
2138 if (sess->state != GG_STATE_CONNECTED) {
2139 errno = ENOTCONN;
2140 return -1;
2141 }
2142
2143 if (size < 0) {
2144 errno = EINVAL;
2145 return -1;
2146 }
2147
2148 /* wytnij ścieżki, zostaw tylko nazwę pliku */
2149 while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
2150 filename = tmp + 1;
2151
2152 if (strlen(filename) < 1 || strlen(filename) > 1024) {
2153 errno = EINVAL;
2154 return -1;
2155 }
2156
2157 s.recipient = gg_fix32(recipient);
2158 s.seq = gg_fix32(0);
2159 s.msgclass = gg_fix32(GG_CLASS_MSG);
2160
2161 buf[0] = 0;
2162 r = (void*) &buf[1];
2163
2164 r->flag = GG_MSG_OPTION_IMAGE_REPLY;
2165 r->size = gg_fix32(size);
2166 r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size));
2167
2168 while (size > 0) {
2169 gg_imgout_queue_t *it;
2170 size_t buflen, chunklen;
2171
2172 /* \0 + struct gg_msg_image_reply */
2173 buflen = sizeof(struct gg_msg_image_reply) + 1;
2174
2175 /* w pierwszym kawałku jest nazwa pliku */
2176 if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) {
2177 strcpy(buf + buflen, filename);
2178 buflen += strlen(filename) + 1;
2179 }
2180
2181 chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size;
2182
2183 memcpy(buf + buflen, image, chunklen);
2184 size -= chunklen;
2185 image += chunklen;
2186
2187 it = gg_new0(sizeof(gg_imgout_queue_t));
2188 if (!it)
2189 break;
2190 if (queue_end) {
2191 queue_end->next = it;
2192 queue_end = it;
2193 } else {
2194 queue = queue_end = it;
2195 }
2196
2197 memcpy(&it->msg_hdr, &s, sizeof(s));
2198 memcpy(it->buf, buf, buflen + chunklen);
2199 it->buf_len = buflen + chunklen;
2200
2201 r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE;
2202 }
2203
2204 if (p->imgout_queue) {
2205 queue_end = p->imgout_queue;
2206 while (queue_end->next)
2207 queue_end = queue_end->next;
2208 queue_end->next = queue;
2209 } else {
2210 p->imgout_queue = queue;
2211 }
2212 gg_image_sendout(sess);
2213
2214 return 0;
2215 }
2216
2217 void gg_image_sendout(struct gg_session *sess)
2218 {
2219 struct gg_session_private *p = sess->private_data;
2220
2221 while (p->imgout_waiting_ack < GG_IMGOUT_WAITING_MAX && p->imgout_queue) {
2222 gg_imgout_queue_t *it = p->imgout_queue;
2223 int res;
2224
2225 p->imgout_queue = p->imgout_queue->next;
2226 p->imgout_waiting_ack++;
2227
2228 res = gg_send_packet(sess, GG_SEND_MSG,
2229 &it->msg_hdr, sizeof(it->msg_hdr),
2230 it->buf, it->buf_len,
2231 NULL);
2232
2233 free(it);
2234
2235 if (res == -1)
2236 break;
2237 }
2238 }
2239
2240 static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
2241 {
2242 int i = 0;
2243
2244 if (!userlist || !count)
2245 return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL);
2246
2247 while (i < count) {
2248 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2249 gg_tvbuilder_expected_size(tvb, 2100);
2250
2251 while (i < count) {
2252 size_t prev_size = gg_tvbuilder_get_size(tvb);
2253 gg_tvbuilder_write_uin(tvb, userlist[i]);
2254 gg_tvbuilder_write_uint8(tvb,
2255 (types == NULL) ? GG_USER_NORMAL : types[i]);
2256
2257 /* Oryginalny klient wysyła maksymalnie 2048 bajtów
2258 * danych w każdym pakiecie tego typu.
2259 */
2260 if (gg_tvbuilder_get_size(tvb) > 2048) {
2261 gg_tvbuilder_strip(tvb, prev_size);
2262 break;
2263 }
2264 i++;
2265 }
2266
2267 if (!gg_tvbuilder_send(tvb, (i < count) ?
2268 GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST))
2269 {
2270 return -1;
2271 }
2272 }
2273
2274 return 0;
2275 }
2276
2277 /**
2278 * Wysyła do serwera listę kontaktów.
2279 *
2280 * Funkcja informuje serwer o liście kontaktów, których statusy będą
2281 * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count
2282 * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj
2283 * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
2284 *
2285 * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
2286 * jest pusta.
2287 *
2288 * \param sess Struktura sesji
2289 * \param userlist Wskaźnik do tablicy numerów kontaktów
2290 * \param types Wskaźnik do tablicy rodzajów kontaktów. Jeżeli NULL, wszystkie kontakty są typu GG_USER_NORMAL.
2291 * \param count Liczba kontaktów
2292 *
2293 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2294 *
2295 * \ingroup contacts
2296 */
2297 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
2298 {
2299 struct gg_notify *n;
2300 int i, res = 0;
2301
2302 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
2303
2304 if (!sess) {
2305 errno = EFAULT;
2306 return -1;
2307 }
2308
2309 if (sess->state != GG_STATE_CONNECTED) {
2310 errno = ENOTCONN;
2311 return -1;
2312 }
2313
2314 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110)
2315 return gg_notify105_ex(sess, userlist, types, count);
2316
2317 if (!userlist || !count)
2318 return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
2319
2320 while (count > 0) {
2321 int part_count, packet_type;
2322
2323 if (count > 400) {
2324 part_count = 400;
2325 packet_type = GG_NOTIFY_FIRST;
2326 } else {
2327 part_count = count;
2328 packet_type = GG_NOTIFY_LAST;
2329 }
2330
2331 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
2332 return -1;
2333
2334 for (i = 0; i < part_count; i++) {
2335 n[i].uin = gg_fix32(userlist[i]);
2336 if (types == NULL)
2337 n[i].dunno1 = GG_USER_NORMAL;
2338 else
2339 n[i].dunno1 = types[i];
2340 }
2341
2342 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
2343 free(n);
2344 res = -1;
2345 break;
2346 }
2347
2348 count -= part_count;
2349 userlist += part_count;
2350 if (types != NULL)
2351 types += part_count;
2352
2353 free(n);
2354 }
2355
2356 return res;
2357 }
2358
2359 /**
2360 * Wysyła do serwera listę kontaktów.
2361 *
2362 * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
2363 * są rodzaju \c GG_USER_NORMAL.
2364 *
2365 * \param sess Struktura sesji
2366 * \param userlist Wskaźnik do tablicy numerów kontaktów
2367 * \param count Liczba kontaktów
2368 *
2369 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2370 *
2371 * \ingroup contacts
2372 */
2373 int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
2374 {
2375 return gg_notify_ex(sess, userlist, NULL, count);
2376 }
2377
2378 /**
2379 * Dodaje kontakt.
2380 *
2381 * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
2382 * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
2383 * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych.
2384 *
2385 * \param sess Struktura sesji
2386 * \param uin Numer kontaktu
2387 * \param type Rodzaj kontaktu
2388 *
2389 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2390 *
2391 * \ingroup contacts
2392 */
2393 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
2394 {
2395 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
2396
2397 if (!sess) {
2398 errno = EFAULT;
2399 return -1;
2400 }
2401
2402 if (sess->state != GG_STATE_CONNECTED) {
2403 errno = ENOTCONN;
2404 return -1;
2405 }
2406
2407 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
2408 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2409 gg_tvbuilder_expected_size(tvb, 16);
2410
2411 gg_tvbuilder_write_uin(tvb, uin);
2412 gg_tvbuilder_write_uint8(tvb, type);
2413
2414 if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105))
2415 return -1;
2416 return 0;
2417 } else {
2418 struct gg_add_remove a;
2419
2420 a.uin = gg_fix32(uin);
2421 a.dunno1 = type;
2422
2423 return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
2424 }
2425 }
2426
2427 /**
2428 * Dodaje kontakt.
2429 *
2430 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
2431 * kontaktów to \c GG_USER_NORMAL.
2432 *
2433 * \param sess Struktura sesji
2434 * \param uin Numer kontaktu
2435 *
2436 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2437 *
2438 * \ingroup contacts
2439 */
2440 int gg_add_notify(struct gg_session *sess, uin_t uin)
2441 {
2442 return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
2443 }
2444
2445 /**
2446 * Usuwa kontakt.
2447 *
2448 * Usuwa z listy kontaktów dany numer w trakcie połączenia.
2449 *
2450 * \param sess Struktura sesji
2451 * \param uin Numer kontaktu
2452 * \param type Rodzaj kontaktu
2453 *
2454 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2455 *
2456 * \ingroup contacts
2457 */
2458 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
2459 {
2460 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
2461
2462 if (!sess) {
2463 errno = EFAULT;
2464 return -1;
2465 }
2466
2467 if (sess->state != GG_STATE_CONNECTED) {
2468 errno = ENOTCONN;
2469 return -1;
2470 }
2471
2472 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
2473 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2474 gg_tvbuilder_expected_size(tvb, 16);
2475
2476 gg_tvbuilder_write_uin(tvb, uin);
2477 gg_tvbuilder_write_uint8(tvb, type);
2478
2479 if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105))
2480 return -1;
2481 return 0;
2482 } else {
2483 struct gg_add_remove a;
2484
2485 a.uin = gg_fix32(uin);
2486 a.dunno1 = type;
2487
2488 return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
2489 }
2490 }
2491
2492 /**
2493 * Usuwa kontakt.
2494 *
2495 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
2496 * kontaktów to \c GG_USER_NORMAL.
2497 *
2498 * \param sess Struktura sesji
2499 * \param uin Numer kontaktu
2500 *
2501 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2502 *
2503 * \ingroup contacts
2504 */
2505 int gg_remove_notify(struct gg_session *sess, uin_t uin)
2506 {
2507 return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
2508 }
2509
2510 /**
2511 * Wysyła do serwera zapytanie dotyczące listy kontaktów.
2512 *
2513 * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
2514 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
2515 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
2516 * listy kontaktów jest ignorowany przez serwer, ale ze względu na
2517 * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
2518 * formacie co oryginalny klient Gadu-Gadu.
2519 *
2520 * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
2521 * z protokołu -- wysyła i odbiera kompletną listę.
2522 *
2523 * \param sess Struktura sesji
2524 * \param type Rodzaj zapytania
2525 * \param request Treść zapytania (może być równe NULL)
2526 *
2527 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2528 *
2529 * \ingroup importexport
2530 */
2531 int gg_userlist_request(struct gg_session *sess, char type, const char *request)
2532 {
2533 int len;
2534
2535 if (!sess) {
2536 errno = EFAULT;
2537 return -1;
2538 }
2539
2540 if (sess->state != GG_STATE_CONNECTED) {
2541 errno = ENOTCONN;
2542 return -1;
2543 }
2544
2545 if (!request) {
2546 sess->userlist_blocks = 1;
2547 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
2548 }
2549
2550 len = strlen(request);
2551
2552 sess->userlist_blocks = 0;
2553
2554 while (len > 2047) {
2555 sess->userlist_blocks++;
2556
2557 if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
2558 return -1;
2559
2560 if (type == GG_USERLIST_PUT)
2561 type = GG_USERLIST_PUT_MORE;
2562
2563 request += 2047;
2564 len -= 2047;
2565 }
2566
2567 sess->userlist_blocks++;
2568
2569 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
2570 }
2571
2572 /**
2573 * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0).
2574 *
2575 * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
2576 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
2577 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
2578 * listy kontaktów jest jednak weryfikowany przez serwer, który stara się
2579 * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0.
2580 * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym
2581 * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest
2582 * równoznaczne z usunięciem listy kontaktów.
2583 *
2584 * Program nie musi się przejmować kompresją listy kontaktów zgodną
2585 * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem.
2586 *
2587 * \param sess Struktura sesji
2588 * \param type Rodzaj zapytania
2589 * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0
2590 * \param format_type Typ formatu listy kontaktów
2591 * \param request Treść zapytania (może być równe NULL)
2592 *
2593 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2594 *
2595 * \ingroup importexport
2596 */
2597 int gg_userlist100_request(struct gg_session *sess, char type,
2598 unsigned int version, char format_type, const char *request)
2599 {
2600 struct gg_userlist100_request pkt;
2601 unsigned char *zrequest;
2602 size_t zrequest_len;
2603 int ret;
2604
2605 if (!sess) {
2606 errno = EFAULT;
2607 return -1;
2608 }
2609
2610 if (sess->state != GG_STATE_CONNECTED) {
2611 errno = ENOTCONN;
2612 return -1;
2613 }
2614
2615 pkt.type = type;
2616 pkt.version = gg_fix32(version);
2617 pkt.format_type = format_type;
2618 pkt.unknown1 = 0x01;
2619
2620 if (request == NULL)
2621 return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);
2622
2623 zrequest = gg_deflate(request, &zrequest_len);
2624
2625 if (zrequest == NULL) {
2626 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n");
2627 return -1;
2628 }
2629
2630 ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);
2631
2632 free(zrequest);
2633
2634 return ret;
2635 }
2636
2637 /**
2638 * Informuje rozmówcę o pisaniu wiadomości.
2639 *
2640 * \param sess Struktura sesji
2641 * \param recipient Numer adresata
2642 * \param length Długość wiadomości lub 0 jeśli jest pusta
2643 *
2644 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2645 *
2646 * \ingroup messages
2647 */
2648 int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
2649 struct gg_typing_notification pkt;
2650 uin_t uin;
2651
2652 pkt.length = gg_fix16(length);
2653 uin = gg_fix32(recipient);
2654 memcpy(&pkt.uin, &uin, sizeof(uin_t));
2655
2656 return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
2657 }
2658
2659 /**
2660 * Rozłącza inną sesję multilogowania.
2661 *
2662 * \param gs Struktura sesji
2663 * \param conn_id Sesja do rozłączenia
2664 *
2665 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2666 *
2667 * \ingroup login
2668 */
2669 int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
2670 {
2671 struct gg_multilogon_disconnect pkt;
2672
2673 pkt.conn_id = conn_id;
2674
2675 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
2676 }
2677
2678 /**
2679 * Tworzy nową konferencję (11.0).
2680 *
2681 * \param gs Struktura sesji
2682 *
2683 * \return Numer sekwencyjny (ten sam, co w \c gg_event_chat_created), lub -1
2684 * w przypadku błędu
2685 *
2686 * \ingroup chat
2687 */
2688 int gg_chat_create(struct gg_session *gs)
2689 {
2690 struct gg_chat_create pkt;
2691 int seq;
2692
2693 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2694 return -1;
2695
2696 seq = ++gs->seq;
2697
2698 pkt.seq = gg_fix32(seq);
2699 pkt.dummy = 0;
2700
2701 if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1)
2702 return -1;
2703
2704 return seq;
2705 }
2706
2707 /**
2708 * Zaprasza nowych użytkowników do konferencji (11.0).
2709 *
2710 * \param gs Struktura sesji
2711 * \param id Identyfikator konferencji
2712 * \param participants Lista użytkowników do zaproszenia
2713 * \param participants_count Liczba użytkowników
2714 *
2715 * \return Numer sekwencyjny w przypadku powodzenia (ten sam, co w
2716 * \c gg_event_chat_invite_ack), lub -1 w przypadku błędu
2717 *
2718 * \ingroup chat
2719 */
2720 int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants,
2721 unsigned int participants_count)
2722 {
2723 struct gg_chat_invite pkt;
2724 int seq, ret;
2725 unsigned int i;
2726 struct gg_chat_participant
2727 {
2728 uint32_t uin;
2729 uint32_t dummy;
2730 } GG_PACKED;
2731 struct gg_chat_participant *participants_list;
2732 size_t participants_list_size;
2733
2734 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2735 return -1;
2736
2737 if (participants_count == 0 || participants_count >=
2738 ~(unsigned int)0 / sizeof(struct gg_chat_participant))
2739 {
2740 return -1;
2741 }
2742
2743 participants_list_size = sizeof(struct gg_chat_participant) *
2744 participants_count;
2745 participants_list = malloc(participants_list_size);
2746 if (participants_list == NULL)
2747 return -1;
2748
2749 seq = ++gs->seq;
2750 pkt.id = gg_fix64(id);
2751 pkt.seq = gg_fix32(seq);
2752 pkt.participants_count = gg_fix32(participants_count);
2753
2754 for (i = 0; i < participants_count; i++) {
2755 participants_list[i].uin = gg_fix32(participants[i]);
2756 participants_list[i].dummy = gg_fix32(0x1e);
2757 }
2758
2759 ret = gg_send_packet(gs, GG_CHAT_INVITE,
2760 &pkt, sizeof(pkt),
2761 participants_list, participants_list_size,
2762 NULL);
2763 free(participants_list);
2764
2765 if (ret == -1)
2766 return -1;
2767 return seq;
2768 }
2769
2770 /**
2771 * Opuszcza konferencję (11.0).
2772 *
2773 * \param gs Struktura sesji
2774 * \param id Identyfikator konferencji
2775 *
2776 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2777 *
2778 * \ingroup chat
2779 */
2780 int gg_chat_leave(struct gg_session *gs, uint64_t id)
2781 {
2782 struct gg_chat_leave pkt;
2783 int seq;
2784
2785 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2786 return -1;
2787
2788 seq = ++gs->seq;
2789 pkt.id = gg_fix64(id);
2790 pkt.seq = gg_fix32(seq);
2791
2792 if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1)
2793 return -1;
2794
2795 return seq;
2796 }
2797
2798 /**
2799 * Wysyła wiadomość w ramach konferencji (11.0).
2800 *
2801 * \param gs Struktura sesji
2802 * \param id Identyfikator konferencji
2803 * \param message Wiadomość
2804 * \param is_html 1, jeżeli wiadomość jest zapisana jako HTML, 0 w p.p.
2805 *
2806 * \return Numer sekwencyjny (taki sam, jak w \c gg_event_chat_send_msg_ack)
2807 * jeśli się powiodło, -1 w przypadku błędu
2808 *
2809 * \ingroup chat
2810 */
2811 int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html)
2812 {
2813 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2814 return -1;
2815
2816 return gg_send_message_110(gs, 0, id, message, is_html);
2817 }
2818
2819 /* @} */
2820
2821 /**
2822 * Sprawdza czy biblioteka obsługuje daną funkcję.
2823 *
2824 * \param feature Identyfikator funkcji.
2825 *
2826 * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
2827 *
2828 * \ingroup version
2829 */
2830 int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
2831 {
2832 switch (feature)
2833 {
2834 case GG_LIBGADU_FEATURE_SSL:
2835 #if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
2836 return 1;
2837 #else
2838 return 0;
2839 #endif
2840
2841 case GG_LIBGADU_FEATURE_PTHREAD:
2842 #ifdef GG_CONFIG_HAVE_PTHREAD
2843 return 1;
2844 #else
2845 return 0;
2846 #endif
2847
2848 case GG_LIBGADU_FEATURE_USERLIST100:
2849 #ifdef GG_CONFIG_HAVE_ZLIB
2850 return 1;
2851 #else
2852 return 0;
2853 #endif
2854
2855 /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
2856
2857 }
2858
2859 return 0;
2860 }
2861
2862 static void gg_socket_manager_error(struct gg_session *sess, enum gg_failure_t failure)
2863 {
2864 int pipes[2];
2865 uint8_t dummy = 0;
2866 struct gg_session_private *p = sess->private_data;
2867
2868 p->socket_failure = failure;
2869
2870 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
2871 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
2872 " create pipes (errno=%d, %s)\n", errno,
2873 strerror(errno));
2874 return;
2875 }
2876
2877 p->socket_is_external = 0;
2878 sess->fd = pipes[1];
2879 sess->check = GG_CHECK_READ;
2880 sess->state = GG_STATE_ERROR;
2881 if (send(pipes[0], &dummy, sizeof(dummy), 0) != sizeof(dummy)) {
2882 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
2883 " send via pipe (errno=%d, %s)\n", errno,
2884 strerror(errno));
2885 return;
2886 }
2887 close(pipes[0]);
2888 }
2889
2890 int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature)
2891 {
2892 gg_compat_t level;
2893
2894 if (sess == NULL)
2895 return 0;
2896
2897 level = sess->private_data->compatibility;
2898
2899 switch (feature) {
2900 case GG_COMPAT_FEATURE_ACK_EVENT:
2901 case GG_COMPAT_FEATURE_LEGACY_CONFER:
2902 return (level < GG_COMPAT_1_12_0);
2903 }
2904
2905 return 0;
2906 }
2907
2908 static gg_msg_list_t * gg_compat_find_sent_message(struct gg_session *sess, int seq, int remove)
2909 {
2910 struct gg_session_private *p = sess->private_data;
2911 gg_msg_list_t *it, *previous = NULL;
2912
2913 for (it = p->sent_messages; it; it = it->next) {
2914 if (it->seq == seq)
2915 break;
2916 else
2917 previous = it;
2918 }
2919
2920 if (remove && it) {
2921 if (previous == NULL)
2922 p->sent_messages = it->next;
2923 else
2924 previous->next = it->next;
2925 }
2926
2927 return it;
2928 }
2929
2930 static void gg_compat_message_cleanup(struct gg_session *sess)
2931 {
2932 struct gg_session_private *p = sess->private_data;
2933
2934 while (p->sent_messages) {
2935 gg_msg_list_t *next = p->sent_messages->next;
2936 free(p->sent_messages->recipients);
2937 free(p->sent_messages);
2938 p->sent_messages = next;
2939 }
2940 }
2941
2942 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients)
2943 {
2944 struct gg_session_private *p = sess->private_data;
2945 gg_msg_list_t *sm;
2946 uin_t *new_recipients;
2947 size_t old_count, i;
2948
2949 if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
2950 return;
2951
2952 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
2953 return;
2954
2955 sm = gg_compat_find_sent_message(sess, seq, 0);
2956 if (!sm) {
2957 sm = gg_new0(sizeof(gg_msg_list_t));
2958 if (!sm)
2959 return;
2960 sm->next = p->sent_messages;
2961 p->sent_messages = sm;
2962 }
2963
2964 sm->seq = seq;
2965 old_count = sm->recipients_count;
2966 sm->recipients_count += recipients_count;
2967
2968 new_recipients = realloc(sm->recipients, sizeof(uin_t) * sm->recipients_count);
2969 if (new_recipients == NULL) {
2970 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
2971 "// gg_compat_message_sent() not enough memory\n");
2972 return;
2973 }
2974 sm->recipients = new_recipients;
2975
2976 for (i = 0; i < recipients_count; i++)
2977 sm->recipients[old_count + i] = recipients[i];
2978 }
2979
2980 void gg_compat_message_ack(struct gg_session *sess, int seq)
2981 {
2982 gg_msg_list_t *sm;
2983 size_t i;
2984
2985 if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
2986 return;
2987
2988 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
2989 return;
2990
2991 sm = gg_compat_find_sent_message(sess, seq, 1);
2992 if (!sm)
2993 return;
2994
2995 for (i = 0; i < sm->recipients_count; i++) {
2996 struct gg_event *qev;
2997
2998 qev = gg_eventqueue_add(sess);
2999
3000 qev->type = GG_EVENT_ACK;
3001 qev->event.ack.status = GG_ACK_DELIVERED;
3002 qev->event.ack.recipient = sm->recipients[i];
3003 qev->event.ack.seq = seq;
3004 }
3005
3006 free(sm->recipients);
3007 free(sm);
3008 }
3009
3010 /**
3011 * Odbiera nowo utworzone gniazdo TCP/TLS.
3012 *
3013 * Po wywołaniu tej funkcji należy zacząć obserwować deskryptor sesji (nawet
3014 * w przypadku niepowodzenia).
3015 *
3016 * Jeżeli gniazdo nie zostanie obsłużone, należy je zniszczyć.
3017 *
3018 * \param handle Uchwyt gniazda
3019 * \param priv Dane prywatne biblioteki libgadu
3020 * \param fd Deskryptor nowo utworzonego gniazda, lub -1 w przypadku błędu
3021 *
3022 * \return Wartość różna od zera, jeżeli gniazdo zostało obsłużone, 0 w przeciwnym przypadku
3023 *
3024 * \ingroup socketmanager
3025 */
3026 int gg_socket_manager_connected(void *handle, void *priv, int fd)
3027 {
3028 struct gg_session *sess = priv;
3029 struct gg_session_private *p = sess->private_data;
3030
3031 if (p->socket_handle != handle) {
3032 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3033 "// gg_socket_manager_connected() invalid handle\n");
3034 return 0;
3035 }
3036
3037 sess->fd = -1;
3038
3039 if (fd < 0) {
3040 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3041 "// gg_socket_manager_connected() connection error\n");
3042 p->socket_handle = NULL;
3043 gg_socket_manager_error(sess, GG_FAILURE_CONNECTING);
3044 return 0;
3045 }
3046
3047 if (p->socket_next_state == GG_STATE_TLS_NEGOTIATION) {
3048 if (gg_session_init_ssl(sess) == -1) {
3049 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3050 "// gg_socket_manager_connected() couldn't "
3051 "initialize ssl\n");
3052 p->socket_handle = NULL;
3053 gg_socket_manager_error(sess, GG_FAILURE_TLS);
3054 return 0;
3055 }
3056 }
3057
3058 p->socket_is_external = 1;
3059 sess->fd = fd;
3060 sess->timeout = GG_DEFAULT_TIMEOUT;
3061 sess->state = p->socket_next_state;
3062
3063 gg_debug_session(sess, GG_DEBUG_MISC, "// next state=%s\n",
3064 gg_debug_state(p->socket_next_state));
3065
3066 if (p->socket_next_state == GG_STATE_READING_KEY)
3067 sess->check = GG_CHECK_READ;
3068 else
3069 sess->check = GG_CHECK_WRITE;
3070
3071 return 1;
3072 }
3073
3074 /*
3075 * Local variables:
3076 * c-indentation-style: k&r
3077 * c-basic-offset: 8
3078 * indent-tabs-mode: notnil
3079 * End:
3080 *
3081 * vim: shiftwidth=8:
3082 */

mercurial