/* $Id: events.c 1105 2011-05-25 21:34:50Z wojtekka $ */ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> * Robert J. Woźny <speedy@ziew.org> * Arkadiusz Miśkiewicz <arekm@pld-linux.org> * Adam Wysocki <gophi@ekg.chmurka.net> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version * 2.1 as published by the Free Software Foundation. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, #include "libgadu-config.h" #include "libgadu-internal.h" #include "libgadu-debug.h" #ifdef GG_CONFIG_HAVE_GNUTLS # include <gnutls/gnutls.h> # include <gnutls/x509.h> #ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/x509.h> * Zwalnia pamięć zajmowaną przez informację o zdarzeniu. * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci * \param e Struktura zdarzenia void gg_event_free(struct gg_event *e) gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); case GG_EVENT_MULTILOGON_MSG: free(e->event.msg.message); free(e->event.msg.formats); free(e->event.msg.recipients); free(e->event.msg.xhtml_message); for (i = 0; e->event.notify60[i].uin; i++) free(e->event.notify60[i].descr); free(e->event.status60.descr); free(e->event.status.descr); case GG_EVENT_NOTIFY_DESCR: free(e->event.notify_descr.notify); free(e->event.notify_descr.descr); case GG_EVENT_DCC_VOICE_DATA: free(e->event.dcc_voice_data.data); case GG_EVENT_PUBDIR50_SEARCH_REPLY: case GG_EVENT_PUBDIR50_READ: case GG_EVENT_PUBDIR50_WRITE: gg_pubdir50_free(e->event.pubdir50); free(e->event.userlist.reply); case GG_EVENT_IMAGE_REPLY: free(e->event.image_reply.filename); free(e->event.image_reply.image); free(e->event.xml_event.data); for (i = 0; i < e->event.user_data.user_count; i++) { for (j = 0; j < e->event.user_data.users[i].attr_count; j++) { free(e->event.user_data.users[i].attrs[j].key); free(e->event.user_data.users[i].attrs[j].value); free(e->event.user_data.users[i].attrs); free(e->event.user_data.users); case GG_EVENT_MULTILOGON_INFO: for (i = 0; i < e->event.multilogon_info.count; i++) free(e->event.multilogon_info.sessions[i].name); free(e->event.multilogon_info.sessions); case GG_EVENT_USERLIST100_REPLY: free(e->event.userlist100_reply.reply); * \internal Usuwa obrazek z kolejki do wysłania. * \param s Struktura sesji * \param q Struktura obrazka * \param freeq Flaga zwolnienia elementu kolejki * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) struct gg_image_queue *qq; for (qq = s->images; qq; qq = qq->next) { * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji. * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free(). * \param sess Struktura sesji * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd struct gg_event *gg_watch_fd(struct gg_session *sess) gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); if (!(e = (void*) calloc(1, sizeof(*e)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left); res = write(sess->fd, sess->send_buf, sess->send_left); if (res == -1 && errno != EAGAIN) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); if (sess->state == GG_STATE_READING_REPLY) e->event.failure = GG_FAILURE_CONNECTING; if (res == sess->send_left) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n"); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res); memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); sess->resolver_cleanup(&sess->resolver, 0); /* jeśli jesteśmy w resolverze i mamy ustawiony port * proxy, znaczy, że resolvowaliśmy proxy. zatem sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do * bezpośredniego połączenia, jeśli hub leży) if (sess->proxy_addr && sess->proxy_port) sess->server_addr = sess->hub_addr = addr.s_addr; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); /* łączymy się albo z hubem, albo z proxy, zależnie * od tego, co resolvowaliśmy. */ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { /* jeśli w trybie asynchronicznym gg_connect() * zwróci błąd, nie ma sensu próbować dalej. */ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); /* jeśli podano serwer i łączmy się przez proxy, * jest to bezpośrednie połączenie, inaczej jest if (sess->proxy_addr && sess->proxy_port && sess->server_addr) { sess->state = GG_STATE_CONNECTING_GG; sess->state = GG_STATE_CONNECTING_HUB; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; case GG_STATE_CONNECTING_HUB: char buf[1024], *client, *auth; socklen_t res_size = sizeof(res); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił * przypadkiem jakiś błąd. */ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to %s failed (errno=%d, %s)\n", (sess->proxy_addr && sess->proxy_port) ? "proxy" : "hub", res, strerror(res)); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (sess->client_version != NULL && isdigit(sess->client_version[0])) client = gg_urlencode(sess->client_version); client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) host = "http://" GG_APPMSG_HOST; #if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL) snprintf(buf, sizeof(buf) - 1, "GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n" "Host: " GG_APPMSG_HOST "\r\n" "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); snprintf(buf, sizeof(buf) - 1, "GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n" "Host: " GG_APPMSG_HOST "\r\n" "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); /* zapytanie jest krótkie, więc zawsze zmieści się * do bufora gniazda. jeśli write() zwróci mniej, * stało się coś złego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); sess->state = GG_STATE_READING_DATA; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; case GG_STATE_READING_DATA: char buf[1024], *tmp, *host; int port = GG_DEFAULT_PORT; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); /* czytamy linię z gniazda i obcinamy \r\n. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); /* sprawdzamy, czy wszystko w porządku. */ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n"); /* ignorujemy resztę nagłówka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); /* czytamy pierwszą linię danych. */ if (gg_read_line(sess->fd, buf, sizeof(buf) - 1) == NULL) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read error\n"); /* jeśli pierwsza liczba w linii nie jest równa zeru, * oznacza to, że mamy wiadomość systemową. */ char tmp[1024], *foo, *sysmsg_buf = NULL; while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); e->event.msg.msgclass = atoi(buf); e->event.msg.message = (unsigned char*) sysmsg_buf; gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ while (*tmp && *tmp != ' ') while (*tmp && *tmp == ' ') while (*tmp && *tmp != ' ') while (*tmp && *tmp == ' ') while (*tmp && *tmp != ' ') if ((tmp = strchr(host, ':'))) { if (strcmp(host, "") == 0) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid response\n"); e->event.failure = GG_FAILURE_HUB; if (!strcmp(host, "notoperating")) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); e->event.failure = GG_FAILURE_UNAVAILABLE; addr.s_addr = inet_addr(host); sess->server_addr = addr.s_addr; if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { /* jeśli mamy proxy, łączymy się z nim. */ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { /* nie wyszło? trudno. */ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); e->event.failure = GG_FAILURE_PROXY; sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; /* Jeśli podano nazwę, nie adres serwera... */ if (sess->server_addr == INADDR_NONE) { if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); sess->state = GG_STATE_RESOLVING_GG; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; /* łączymy się z właściwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; /* nie wyszło? próbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { /* ostatnia deska ratunku zawiodła? * w takim razie zwijamy manatki. */ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); e->event.failure = GG_FAILURE_CONNECTING; sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; case GG_STATE_RESOLVING_GG: gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); sess->resolver_cleanup(&sess->resolver, 0); e->event.failure = GG_FAILURE_RESOLVING; sess->server_addr = addr.s_addr; /* łączymy się z właściwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; /* nie wyszło? próbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { /* ostatnia deska ratunku zawiodła? * w takim razie zwijamy manatki. */ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); e->event.failure = GG_FAILURE_CONNECTING; sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; case GG_STATE_CONNECTING_GG: socklen_t res_size = sizeof(res); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); /* jeśli wystąpił błąd podczas łączenia się... */ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { /* jeśli nie udało się połączenie z proxy, * nie mamy czego próbować więcej. */ if (sess->proxy_addr && sess->proxy_port) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); e->event.failure = GG_FAILURE_PROXY; #if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL) /* jeśli logujemy się po TLS, nie próbujemy * się łączyć już z niczym innym w przypadku * błędu. nie dość, że nie ma sensu, to i * trzeba by się bawić w tworzenie na nowo gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); e->event.failure = GG_FAILURE_CONNECTING; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); if (sess->port == GG_HTTPS_PORT) { e->event.failure = GG_FAILURE_CONNECTING; sess->port = GG_HTTPS_PORT; /* próbujemy na port 443. */ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); e->event.failure = GG_FAILURE_CONNECTING; sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); /* jeśli mamy proxy, wyślijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); addr.s_addr = sess->server_addr; addr.s_addr = sess->hub_addr; snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); /* wysyłamy zapytanie. jest ono na tyle krótkie, * że musi się zmieścić w buforze gniazda. jeśli * write() zawiedzie, stało się coś złego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); e->event.failure = GG_FAILURE_PROXY; gg_debug_session(sess, GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); e->event.failure = GG_FAILURE_PROXY; if (write(sess->fd, "\r\n", 2) < 2) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); e->event.failure = GG_FAILURE_PROXY; #if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL) #ifdef GG_CONFIG_HAVE_GNUTLS gnutls_transport_set_ptr(GG_SESSION_GNUTLS(sess), (gnutls_transport_ptr_t) sess->fd); #ifdef GG_CONFIG_HAVE_OPENSSL SSL_set_fd(sess->ssl, sess->fd); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; #ifdef GG_CONFIG_HAVE_GNUTLS case GG_STATE_TLS_NEGOTIATION: gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); res = gnutls_handshake(GG_SESSION_GNUTLS(sess)); if (res == GNUTLS_E_AGAIN) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n"); sess->state = GG_STATE_TLS_NEGOTIATION; if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0) sess->check = GG_CHECK_READ; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; if (res == GNUTLS_E_INTERRUPTED) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n"); goto gnutls_handshake_repeat; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake error %d\n", res); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n"); gg_debug_session(sess, GG_DEBUG_MISC, "// cipher: VERS-%s:%s:%s:%s:COMP-%s\n", gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))), gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))), gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))), gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))), gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess)))); if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) { const gnutls_datum_t *peers; if (gnutls_x509_crt_init(&cert) >= 0) { peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count); if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) { gnutls_x509_crt_get_dn(cert, buf, &size); gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf); gnutls_x509_crt_get_issuer_dn(cert, buf, &size); gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; #ifdef GG_CONFIG_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); if ((res = SSL_connect(sess->ssl)) <= 0) { int err = SSL_get_error(sess->ssl, res); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; if (err == SSL_ERROR_WANT_READ) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; } else if (err == SSL_ERROR_WANT_WRITE) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); peer = SSL_get_peer_certificate(sess->ssl); gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; case GG_STATE_READING_KEY: case GG_STATE_READING_REPLY: case GG_STATE_DISCONNECTING: if (sess->state == GG_STATE_READING_KEY) gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); else if (sess->state == GG_STATE_READING_REPLY) gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); else if (sess->state == GG_STATE_CONNECTED) gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); else if (sess->state == GG_STATE_DISCONNECTING) gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_DISCONNECTING\n"); /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie * się tekstu wrzucanego przez proxy. */ if (sess->state == GG_STATE_READING_KEY && sess->proxy_addr && sess->proxy_port) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); while (strcmp(buf, "")) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_debug_session(sess, GG_DEBUG_MISC, "// %s\n", buf); /* XXX niech czeka jeszcze raz w tej samej * fazie. głupio, ale działa. */ sess->last_event = time(NULL); gh = gg_recv_packet(sess); gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) { sess->check = GG_CHECK_READ; if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) sess->check |= GG_CHECK_WRITE; e->event.failure = GG_FAILURE_PROXY; e->event.failure = GG_FAILURE_HUB; sess->resolver_cleanup(&sess->resolver, 1); sess->state = GG_STATE_IDLE; if (e->event.failure != 0) { e->type = GG_EVENT_CONN_FAILED; * c-indentation-style: k&r * indent-tabs-mode: notnil