/** * Pobiera pole z odpowiedzi katalogu publicznego. * * \param res Odpowiedź * \param num Numer wyniku odpowiedzi * \param field Nazwa pola (wielkość liter nie ma znaczenia) * * \return Wartość pola lub \c NULL jeśli nie znaleziono * * \ingroup pubdir50 */ const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field) { char *value = NULL; int i; gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field); if (!res || num < 0 || !field) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n"); errno = EINVAL; return NULL; } for (i = 0; i < res->entries_count; i++) { if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) { value = res->entries[i].value; break; } } return value; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu * znajdzie się w polu \c error. * * \param h Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_pubdir_watch_fd(struct gg_http *h) { struct gg_pubdir *p; char *tmp; if (!h) { errno = EFAULT; return -1; } if (h->state == GG_STATE_ERROR) { gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n"); errno = EINVAL; return -1; } if (h->state != GG_STATE_PARSING) { if (gg_http_watch_fd(h) == -1) { gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n"); errno = EINVAL; return -1; } } if (h->state != GG_STATE_PARSING) return 0; h->state = GG_STATE_DONE; if (!(h->data = p = (gg_pubdir*)malloc(sizeof(struct gg_pubdir)))) { gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n"); return -1; } p->success = 0; p->uin = 0; gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body); if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) { p->success = 1; p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0); gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin); } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { p->success = 1; if (tmp[7] == ':') p->uin = strtol(tmp + 8, NULL, 0); gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin); } else gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n"); return 0; }
/** * \internal Rozpoczyna połączenie bezpośrednie z danym klientem. * * \param ip Adres IP odbiorcy * \param port Port odbiorcy * \param my_uin Własny numer * \param peer_uin Numer odbiorcy * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET) * * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu */ static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) { struct gg_dcc *d = NULL; struct in_addr addr; addr.s_addr = ip; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); errno = EINVAL; return NULL; } if (!(d = (void*) calloc(1, sizeof(*d)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n"); return NULL; } d->check = GG_CHECK_WRITE; d->state = GG_STATE_CONNECTING; d->type = type; d->timeout = GG_DEFAULT_TIMEOUT; d->file_fd = -1; d->active = 1; d->fd = -1; d->uin = my_uin; d->peer_uin = peer_uin; if ((d->fd = gg_connect(&addr, port, 1)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n"); free(d); return NULL; } return d; }
/** * Zwalnia zasoby używane przez połączenie bezpośrednie. * * \param d Struktura połączenia * * \ingroup dcc6 */ void gg_dcc_free(struct gg_dcc *d) { gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); if (!d) return; if (d->fd != -1) close(d->fd); if (d->file_fd != -1) gg_file_close(d->file_fd); free(d->chunk_buf); free(d); }
/* * python_unload() * * usuwa z pamiêci podany skrypt. * * - name - nazwa skryptu, * - quiet. * * 0/-1 */ int python_unload(const char *name, int quiet) { list_t l; if (!name) { printq("python_need_name"); return -1; } for (l = modules; l; l = l->next) { struct module *m = l->data; if (strcmp(m->name, name)) continue; gg_debug(GG_DEBUG_MISC, "m->deinit = %p, hmm?\n", m->deinit); if (m->deinit) { PyObject *res = PyObject_CallFunction(m->deinit, "()"); Py_XDECREF(res); Py_XDECREF(m->deinit); } Py_XDECREF(m->handle_msg); Py_XDECREF(m->handle_msg_own); Py_XDECREF(m->handle_connect); Py_XDECREF(m->handle_disconnect); Py_XDECREF(m->handle_status); Py_XDECREF(m->handle_status_own); Py_XDECREF(m->handle_redraw_header); Py_XDECREF(m->handle_redraw_statusbar); Py_XDECREF(m->handle_keypress); Py_XDECREF(m->handle_command_line); Py_XDECREF(m->module); list_remove(&modules, m, 1); printq("python_removed"); return 0; } printq("python_not_found", name); return -1; }
/** * \internal Rozwiązuje nazwę serwera w osobnym procesie. * * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania * nazwy serwera. W tym celu tworzona jest para gniazd, nowy proces i dopiero * w nim przeprowadzane jest rozwiązywanie nazwy. Deskryptor gniazda do odczytu * zapisuje się w strukturze sieci i czeka na dane w postaci struktury * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE. * * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik * do numeru procesu potomnego rozwiązującego nazwę * \param hostname Nazwa serwera do rozwiązania * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname) { struct gg_resolver_fork_data *data = NULL; int pipes[2], new_errno; gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); if (fd == NULL || priv_data == NULL || hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n"); errno = EFAULT; return -1; } data = malloc(sizeof(struct gg_resolver_fork_data)); if (data == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n"); return -1; } if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable " "to create pipes (errno=%d, %s)\n", errno, strerror(errno)); free(data); return -1; } data->pid = fork(); if (data->pid == -1) { new_errno = errno; goto cleanup; } if (data->pid == 0) { int status; close(pipes[0]); status = (gg_resolver_run(pipes[1], hostname, 0) == -1) ? 1 : 0; #ifdef HAVE__EXIT _exit(status); #else exit(status); #endif } close(pipes[1]); gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); *fd = pipes[0]; *priv_data = data; return 0; cleanup: free(data); close(pipes[0]); close(pipes[1]); errno = new_errno; return -1; }
/** * Rozpoczyna wysyłanie pliku. * * \param ip Adres IP odbiorcy * \param port Port odbiorcy * \param my_uin Własny numer * \param peer_uin Numer odbiorcy * * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu * * \ingroup dcc6 */ struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n"); return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); }
int main(int argc, char **argv) { struct gg_session *gs; struct gg_login_params glp; struct gg_dcc7 *gd = NULL; time_t ping = 0, last = 0; int once = 0; if (argc != 2) usage(argv[0]); /* strtol() ? */ if (!(argv[1][0] >= '0' && argv[1][0] <= '9')) usage(argv[0]); if (atoi(argv[1]) >= TEST_MODE_LAST) usage(argv[0]); test_mode = atoi(argv[1]); signal(SIGPIPE, SIG_IGN); gg_debug_file = stdout; gg_debug_level = ~0; if (config_read() == -1 || config_peer == 0) { perror("config"); exit(1); } memset(&glp, 0, sizeof(glp)); glp.uin = config_uin; glp.password = config_password; glp.async = 1; glp.status = GG_STATUS_AVAIL; #if 0 glp.client_addr = config_ip; glp.client_port = config_port; #endif glp.protocol_version = 0x2a; glp.has_audio = 1; glp.last_sysmsg = -1; gg_dcc_ip = config_ip; debug("Connecting...\n"); if (!(gs = gg_login(&glp))) { perror("gg_login"); exit(1); } for (;;) { fd_set rds, wds; struct timeval tv; time_t now; int res, maxfd = -1; FD_ZERO(&rds); FD_ZERO(&wds); tv.tv_sec = 1; tv.tv_usec = 0; maxfd = gs->fd; if ((gs->check & GG_CHECK_READ)) FD_SET(gs->fd, &rds); if ((gs->check & GG_CHECK_WRITE)) FD_SET(gs->fd, &wds); if (gd && gd->fd != -1) { if (gd->fd > maxfd) maxfd = gd->fd; if ((gd->check & GG_CHECK_READ)) FD_SET(gd->fd, &rds); if ((gd->check & GG_CHECK_WRITE)) FD_SET(gd->fd, &wds); } if (voice_fd != -1) { FD_SET(voice_fd, &rds); if (voice_fd > maxfd) maxfd = voice_fd; } if ((res = select(maxfd + 1, &rds, &wds, NULL, &tv)) == -1) { if (errno == EINTR) continue; perror("select"); exit(1); } now = time(NULL); if (last != now) { if (gs->timeout != -1 && gs->timeout-- == 0 && !gs->soft_timeout) { debug("Timeout\n"); exit(1); } /* vvvv XXX */ if (gd && gd->timeout && gd->timeout != -1 && gd->timeout-- == 0 && !gd->soft_timeout) { debug("Timeout\n"); exit(1); } last = now; } if (gs->state == GG_STATE_CONNECTED && ping && now - ping > 60) { ping = now; gg_ping(gs); } if (FD_ISSET(gs->fd, &rds) || FD_ISSET(gs->fd, &wds) || (gs->timeout == 0 && gs->soft_timeout)) { struct gg_event *ge; uin_t uin; int status; if (!(ge = gg_watch_fd(gs))) { debug("Connection broken\n"); exit(1); } switch (ge->type) { case GG_EVENT_CONN_SUCCESS: debug("Connected\n"); connected = 1; gg_notify(gs, &config_peer, 1); ping = time(NULL); break; case GG_EVENT_CONN_FAILED: debug("Connection failed\n"); exit(1); case GG_EVENT_NONE: break; case GG_EVENT_MSG: debug("Message from %d: %s\n", ge->event.msg.sender, ge->event.msg.message); break; case GG_EVENT_DISCONNECT: debug("Forced to disconnect\n"); exit(1); case GG_EVENT_NOTIFY60: uin = ge->event.notify60[0].uin; status = ge->event.notify60[0].status; /* fall-through */ case GG_EVENT_STATUS60: if (ge->type == GG_EVENT_STATUS60) { uin = ge->event.status60.uin; status = ge->event.status60.status; } if (!once && uin == config_peer && (GG_S_A(status) || GG_S_B(status)) && test_mode == TEST_MODE_SEND) { debug("Sending voice request...\n"); if (voice_open_ext("/dev/dsp", 8000, 16, 2, EKG_CODEC_GSM) == -1) { printf("voice_open_ext('/dev/dsp', " "8000, 16, 2, CODEC_GSM) failed\n"); exit(1); } printf("+OK\n"); gd = gg_dcc7_voice_chat(gs, config_peer, 0x00); if (!gd) { perror("gg_dcc7_voice_chat"); exit(1); } once = 1; } gg_change_status(gs, GG_STATUS_AVAIL); /* XXX, libgadu sobie nie radzi */ break; case GG_EVENT_DCC7_NEW: debug("Incoming direct connection\n"); if (test_mode == TEST_MODE_RECEIVE) { gd = ge->event.dcc7_new; if (voice_open_ext("/dev/dsp", 8000, 16, 2, EKG_CODEC_GSM) == -1) { printf("voice_open_ext('/dev/dsp', " "8000, 16, 2, CODEC_GSM) failed\n"); exit(1); } printf("+OK\n"); gg_dcc7_accept_voice(gd, 0x00); } break; case GG_EVENT_DCC7_ERROR: debug("Direct connection error\n"); exit(1); case GG_EVENT_DCC7_ACCEPT: debug("Accepted\n"); break; case GG_EVENT_DCC7_REJECT: debug("Rejected\n"); exit(1); default: debug("Unsupported event %d\n", ge->type); break; } gg_event_free(ge); } if (gd && gd->fd != -1 && (FD_ISSET(gd->fd, &rds) || FD_ISSET(gd->fd, &wds) || (gd->timeout == 0 && gd->soft_timeout))) { struct gg_event *ge; if (!(ge = gg_dcc7_watch_fd(gd))) { debug("Direct connection broken\n"); exit(1); } switch (ge->type) { case GG_EVENT_DCC7_ERROR: debug("Direct connection error\n"); exit(1); case GG_EVENT_DCC7_CONNECTED: debug("Direct connection established\n"); break; case GG_EVENT_DCC7_DONE: debug("Finished"); gg_event_free(ge); gg_dcc7_free(gd); gg_free_session(gs); config_free(); exit(1); case GG_EVENT_DCC7_VOICE_DATA: gg_debug(GG_DEBUG_MISC, "## GG_EVENT_DCC7_VOICE_DATA [%u]\n", ge->event.dcc7_voice_data.length); printf("## GG_EVENT_DCC7_VOICE_DATA [%u]\n", ge->event.dcc7_voice_data.length); if (voice_fd == -1) { printf("voice_fd == -1\n"); exit(1); } if (ge->event.dcc7_voice_data.length == GG_DCC7_VOICE_FRAME_GSM_LENGTH) voice_play(ge->event.dcc7_voice_data.data, ge->event.dcc7_voice_data.length, EKG_CODEC_GSM); else if (ge->event.dcc7_voice_data.length == GG_DCC7_VOICE_FRAME_SPEEX_LENGTH) voice_play(ge->event.dcc7_voice_data.data, ge->event.dcc7_voice_data.length, EKG_CODEC_SPEEX); else if (ge->event.dcc7_voice_data.length == GG_DCC7_VOICE_FRAME_MELP_LENGTH) voice_play(ge->event.dcc7_voice_data.data, ge->event.dcc7_voice_data.length, EKG_CODEC_MELP); break; case GG_EVENT_NONE: break; default: debug("Unsupported event %d\n", ge->type); break; } gg_event_free(ge); } if (voice_fd != -1 && FD_ISSET(voice_fd, &rds)) { char buf[GG_DCC_VOICE_FRAME_LENGTH]; /* dłuższy z buforów */ int length = GG_DCC_VOICE_FRAME_LENGTH; if (gd) { if (gd->state == GG_STATE_READING_VOICE_DATA) { /* XXX, implementowac speex */ length = GG_DCC7_VOICE_FRAME_GSM_LENGTH; voice_record(buf, length, EKG_CODEC_GSM); if (1) gg_dcc7_voice_send(gd, buf, length); else { /* ten pakiet mamy wysylac co 1s */ gg_dcc7_voice_mic_off(gd); } } else voice_record(buf, length, EKG_CODEC_NONE); } else voice_record(buf, length, EKG_CODEC_NONE); } } if (gg_debug_file != stdout) /* w sumie stdout, tez moglibysmy zamknac.. czemu nie. */ fclose(gg_debug_file); return 0; }
/** * Wysyła hasło użytkownika na e-mail. * * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). * * \param uin Numer Gadu-Gadu * \param email Adres e-mail (podany przy rejestracji) * \param tokenid Identyfikator tokenu * \param tokenval Zawartość tokenu * \param async Flaga połączenia asynchronicznego * * \return Struktura \c gg_http lub \c NULL w przypadku błędu * * \ingroup remind */ struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async) { struct gg_http *h; char *form, *query, *__tokenid, *__tokenval, *__email; if (!tokenid || !tokenval || !email) { gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n"); errno = EFAULT; return NULL; } __tokenid = gg_urlencode(tokenid); __tokenval = gg_urlencode(tokenval); __email = gg_urlencode(email); if (!__tokenid || !__tokenval || !__email) { gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); free(__tokenid); free(__tokenval); free(__email); return NULL; } if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) { gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); free(__tokenid); free(__tokenval); free(__email); return NULL; } free(__tokenid); free(__tokenval); free(__email); gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form); query = gg_saprintf( "Host: " GG_REMIND_HOST "\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "User-Agent: " GG_HTTP_USERAGENT "\r\n" "Content-Length: %d\r\n" "Pragma: no-cache\r\n" "\r\n" "%s", (int) strlen(form), form); free(form); if (!query) { gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n"); return NULL; } if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) { gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n"); free(query); return NULL; } h->type = GG_SESSION_REMIND; free(query); h->callback = gg_pubdir_watch_fd; h->destroy = gg_pubdir_free; if (!async) gg_pubdir_watch_fd(h); return h; }
/* * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik * w strukturze \c gg_event. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param packet Pakiet odpowiedzi * \param length Długość pakietu odpowiedzi * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length) { const char *end = packet + length, *p; struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; gg_pubdir50_t res; int num = 0; gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length); if (!sess || !e || !packet) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); errno = EFAULT; return -1; } if (length < 5) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n"); errno = EINVAL; return -1; } if (!(res = gg_pubdir50_new(r->type))) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n"); return -1; } e->event.pubdir50 = res; res->seq = gg_fix32(r->seq); switch (res->type) { case GG_PUBDIR50_READ: e->type = GG_EVENT_PUBDIR50_READ; break; case GG_PUBDIR50_WRITE: e->type = GG_EVENT_PUBDIR50_WRITE; break; default: e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY; break; } /* brak wyników? */ if (length == 5) return 0; /* pomiń początek odpowiedzi */ p = packet + 5; while (p < end) { const char *field, *value; field = p; /* sprawdź, czy nie mamy podziału na kolejne pole */ if (!*field) { num++; field++; } value = NULL; for (p = field; p < end; p++) { /* jeśli mamy koniec tekstu... */ if (!*p) { /* ...i jeszcze nie mieliśmy wartości pola to * wiemy, że po tym zerze jest wartość... */ if (!value) value = p + 1; else /* ...w przeciwym wypadku koniec * wartości i możemy wychodzić * grzecznie z pętli */ break; } } /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów * przez \0 */ if (p == end) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n"); goto failure; } p++; /* jeśli dostaliśmy namier na następne wyniki, to znaczy że * mamy koniec wyników i nie jest to kolejna osoba. */ if (!strcasecmp(field, "nextstart")) { res->next = atoi(value); num--; } else { if (sess->encoding == GG_ENCODING_CP1250) { if (gg_pubdir50_add_n(res, num, field, value) == -1) goto failure; } else { char *tmp; tmp = gg_cp_to_utf8(value); if (tmp == NULL) goto failure; if (gg_pubdir50_add_n(res, num, field, tmp) == -1) { free(tmp); goto failure; } free(tmp); } } } res->count = num + 1; return 0; failure: gg_pubdir50_free(res); return -1; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * Operacja będzie zakończona, gdy pole \c state będzie równe * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia, * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się * w polu \c error. * * \param h Struktura połączenia * * \return \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup http */ int gg_http_watch_fd(struct gg_http *h) { gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h); if (!h) { gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n"); errno = EFAULT; return -1; } if (h->state == GG_STATE_RESOLVING) { struct in_addr a; gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n"); if (gg_sock_read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) { gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n"); gg_http_error(GG_ERROR_RESOLVING); } gg_sock_close(h->fd); h->fd = -1; h->resolver_cleanup(&h->resolver, 0); gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port); if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) { gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno)); gg_http_error(GG_ERROR_CONNECTING); } h->state = GG_STATE_CONNECTING; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; return 0; } if (h->state == GG_STATE_CONNECTING) { int res = 0; unsigned int res_size = sizeof(res); if (h->async && (gg_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno)); gg_sock_close(h->fd); h->fd = -1; h->state = GG_STATE_ERROR; h->error = GG_ERROR_CONNECTING; if (res) errno = res; return 0; } gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n"); h->state = GG_STATE_SENDING_QUERY; } if (h->state == GG_STATE_SENDING_QUERY) { int res; if ((res = gg_sock_write(h->fd, h->query, strlen(h->query))) < 1) { gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno); gg_http_error(GG_ERROR_WRITING); } if (res < (int)strlen(h->query)) { gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res); memmove(h->query, h->query + res, strlen(h->query) - res + 1); h->state = GG_STATE_SENDING_QUERY; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; } else { gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query)); free(h->query); h->query = NULL; h->state = GG_STATE_READING_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; } return 0; } if (h->state == GG_STATE_READING_HEADER) { char buf[1024], *tmp; int res; if ((res = gg_sock_read(h->fd, buf, sizeof(buf))) == -1) { gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno); if (h->header) { free(h->header); h->header = NULL; } gg_http_error(GG_ERROR_READING); } if (!res) { gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n"); if (h->header) { free(h->header); h->header = NULL; } gg_http_error(GG_ERROR_READING); } gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res); if (!(tmp = realloc(h->header, h->header_size + res + 1))) { gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n"); free(h->header); h->header = NULL; gg_http_error(GG_ERROR_READING); } h->header = tmp; memcpy(h->header + h->header_size, buf, res); h->header_size += res; gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size); h->header[h->header_size] = 0; if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) { int sep_len = (*tmp == '\r') ? 4 : 2; unsigned int left; char *line; left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len); gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left); /* HTTP/1.1 200 OK */ if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) { gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n"); free(h->header); h->header = NULL; gg_http_error(GG_ERROR_CONNECTING); } h->body_size = 0; line = h->header; *tmp = 0; gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); while (line) { if (!strncasecmp(line, "Content-length: ", 16)) { h->body_size = atoi(line + 16); } line = strchr(line, '\n'); if (line) line++; } if (h->body_size <= 0) { gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n"); h->body_size = left; } if (left > h->body_size) { gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left); h->body_size = left; } gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size); if (!(h->body = malloc(h->body_size + 1))) { gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1); free(h->header); h->header = NULL; gg_http_error(GG_ERROR_READING); } if (left) { memcpy(h->body, tmp + sep_len, left); h->body_done = left; } h->body[left] = 0; h->state = GG_STATE_READING_DATA; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; } return 0; } if (h->state == GG_STATE_READING_DATA) { char buf[1024]; int res; if ((res = gg_sock_read(h->fd, buf, sizeof(buf))) == -1) { gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno); if (h->body) { free(h->body); h->body = NULL; } gg_http_error(GG_ERROR_READING); } if (!res) { if (h->body_done >= h->body_size) { gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n"); h->state = GG_STATE_PARSING; gg_sock_close(h->fd); h->fd = -1; } else { gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size); if (h->body) { free(h->body); h->body = NULL; } gg_http_error(GG_ERROR_READING); } return 0; } gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res); if (h->body_done + res > h->body_size) { char *tmp; gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size); if (!(tmp = realloc(h->body, h->body_done + res + 1))) { gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1); free(h->body); h->body = NULL; gg_http_error(GG_ERROR_READING); } h->body = tmp; h->body_size = h->body_done + res; } h->body[h->body_done + res] = 0; memcpy(h->body + h->body_done, buf, res); h->body_done += res; gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size); return 0; } if (h->fd != -1) gg_sock_close(h->fd); h->fd = -1; h->state = GG_STATE_ERROR; h->error = 0; return -1; }
struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) { gg_debug(GG_DEBUG_MISC, "// gg_change_passwd3() is obsolete. use gg_change_passwd4() instead!\n"); errno = EINVAL; return NULL; }
/** * \internal Przekazuje zawartość pakietu do odpluskwiania. * * \param prefix Prefiks informacji * \param fd Deskryptor gniazda * \param buf Bufor z danumi * \param size Rozmiar bufora z danymi */ static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) { gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); gg_debug_dump(NULL, GG_DEBUG_DUMP, buf, size); gg_debug(GG_DEBUG_MISC, "\n"); }
struct gg_http *gg_register(const char *email, const char *password, int async) { gg_debug(GG_DEBUG_MISC, "// gg_register() is obsolete. use gg_register3() instead!\n"); errno = EINVAL; return NULL; }
struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) { gg_debug(GG_DEBUG_MISC, "// gg_unregister2() is obsolete. use gg_unregister3() instead!\n"); errno = EINVAL; return NULL; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu * znajdzie się w polu \c error. * * \param h Struktura połączenia * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup token */ int gg_token_watch_fd(struct gg_http *h) { if (!h) { errno = EFAULT; return -1; } if (h->state == GG_STATE_ERROR) { gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n"); errno = EINVAL; return -1; } if (h->state != GG_STATE_PARSING) { if (gg_http_watch_fd(h) == -1) { gg_debug(GG_DEBUG_MISC, "=> token, http failure\n"); errno = EINVAL; return -1; } } if (h->state != GG_STATE_PARSING) return 0; /* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego, * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający * na pobieraniu tokenu. */ if (!h->data) { int width, height, length; char *url = NULL, *tokenid = NULL, *path, *headers; const char *host; struct gg_http *h2; struct gg_token *t; gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body); if (h->body && (!(url = (char*)malloc(strlen(h->body) + 1)) || !(tokenid = (char*)malloc(strlen(h->body) + 1)))) { gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n"); free(url); return -1; } if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) { gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n"); free(url); free(tokenid); errno = EINVAL; return -1; } /* dostaliśmy tokenid i wszystkie niezbędne informacje, * więc pobierzmy obrazek z tokenem */ if (strncmp(url, "http://", 7)) { path = gg_saprintf("%s?tokenid=%s", url, tokenid); host = GG_REGISTER_HOST; } else { char *slash = strchr(url + 7, '/'); if (slash) { path = gg_saprintf("%s?tokenid=%s", slash, tokenid); *slash = 0; host = url + 7; } else { gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n"); free(url); free(tokenid); errno = EINVAL; return -1; } } if (!path) { gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); free(url); free(tokenid); return -1; } if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) { gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); free(path); free(url); free(tokenid); return -1; } if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) { gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n"); free(headers); free(url); free(path); free(tokenid); return -1; } free(headers); free(path); free(url); gg_http_free_fields(h); memcpy(h, h2, sizeof(struct gg_http)); free(h2); h->type = GG_SESSION_TOKEN; h->callback = gg_token_watch_fd; h->destroy = gg_token_free; if (!h->async) gg_token_watch_fd(h); if (!(h->data = t = (struct gg_token*)malloc(sizeof(struct gg_token)))) { gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n"); free(tokenid); return -1; } t->width = width; t->height = height; t->length = length; t->tokenid = tokenid; } else { /* obrazek mamy w h->body */ h->state = GG_STATE_DONE; } return 0; }
/** * Rejestruje nowego użytkownika. * * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). * * \param email Adres e-mail * \param password Hasło * \param tokenid Identyfikator tokenu * \param tokenval Zawartość tokenu * \param async Flaga połączenia asynchronicznego * * \return Struktura \c gg_http lub \c NULL w przypadku błędu * * \ingroup register */ struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async) { struct gg_http *h; char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query; if (!email || !password || !tokenid || !tokenval) { gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n"); errno = EFAULT; return NULL; } __pwd = gg_urlencode(password); __email = gg_urlencode(email); __tokenid = gg_urlencode(tokenid); __tokenval = gg_urlencode(tokenval); if (!__pwd || !__email || !__tokenid || !__tokenval) { gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n"); free(__pwd); free(__email); free(__tokenid); free(__tokenval); return NULL; } form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, password)); free(__pwd); free(__email); free(__tokenid); free(__tokenval); if (!form) { gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n"); return NULL; } gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form); query = gg_saprintf( "Host: " GG_REGISTER_HOST "\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "User-Agent: " GG_HTTP_USERAGENT "\r\n" "Content-Length: %d\r\n" "Pragma: no-cache\r\n" "\r\n" "%s", (int) strlen(form), form); free(form); if (!query) { gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n"); return NULL; } if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n"); free(query); return NULL; } h->type = GG_SESSION_REGISTER; free(query); h->callback = gg_pubdir_watch_fd; h->destroy = gg_pubdir_free; if (!async) gg_pubdir_watch_fd(h); return h; }
/** * \internal Rozwiązuje nazwę serwera w osobnym wątku. * * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą, * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas * kompilacji włączono odpowiednią opcję. * * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik * do prywatnych danych wątku rozwiązującego nazwę * \param hostname Nazwa serwera do rozwiązania * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname) { struct gg_resolver_pthread_data *data = NULL; struct gg_resolver_pthread_params *params = NULL; int pipes[2], pipe_ready = 0, new_errno; pthread_barrier_t init_barrier; gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); if (fd == NULL || priv_data == NULL || hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n"); errno = EFAULT; return -1; } data = malloc(sizeof(struct gg_resolver_pthread_data)); params = malloc(sizeof(struct gg_resolver_pthread_params)); if (data == NULL || params == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n"); goto cleanup; } if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable " "to create pipes (errno=%d, %s)\n", errno, strerror(errno)); goto cleanup; } pipe_ready = 1; params->wfd = pipes[1]; params->hostname = strdup(hostname); if (params->hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n"); goto cleanup; } if (pthread_barrier_init(&init_barrier, NULL, 2) != 0) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() " "can't create barrier\n"); goto cleanup; } params->init_barrier = &init_barrier; if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, params)) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n"); pthread_barrier_destroy(&init_barrier); goto cleanup; } gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data); /* Poczekaj, aż wątek rozwiązywania nazw potwierdzi odebranie * parametrów, aby mieć pewność, że go nie zabijemy zanim je zwolni. */ pthread_barrier_wait(&init_barrier); pthread_barrier_destroy(&init_barrier); *fd = pipes[0]; *priv_data = data; return 0; cleanup: new_errno = errno; free(data); if (params != NULL) free(params->hostname); free(params); if (pipe_ready) { close(pipes[0]); close(pipes[1]); } errno = new_errno; return -1; }
/** * Rozpoczyna połączenie głosowe. * * \param ip Adres IP odbiorcy * \param port Port odbiorcy * \param my_uin Własny numer * \param peer_uin Numer odbiorcy * * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu * * \ingroup dcc6 */ struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n"); return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); }
struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) { gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd2() is obsolete. use gg_remind_passwd3() instead!\n"); errno = EINVAL; return NULL; }
/** * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich. * * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP. * * \param uin Własny numer * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego) * * \note Ze względu na możliwość podania wartości -1 do parametru będącego * 16-bitową liczbą bez znaku, port 65535 nie jest dostępny. * * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu * * \ingroup dcc6 */ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) { struct gg_dcc *c; int sock, bound = 0, errno2; gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); if (!uin) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); errno = EINVAL; return NULL; } if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno)); return NULL; } if (port == 0 || port == (uint16_t)-1) port = GG_DEFAULT_DCC_PORT; while (!bound) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) bound = 1; else { if (++port == 65535) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n"); close(sock); return NULL; } } } if (listen(sock, 10)) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno)); errno2 = errno; close(sock); errno = errno2; return NULL; } gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); if (!(c = malloc(sizeof(*c)))) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n"); close(sock); return NULL; } memset(c, 0, sizeof(*c)); c->port = c->id = port; c->fd = sock; c->file_fd = -1; c->type = GG_SESSION_DCC_SOCKET; c->uin = uin; c->timeout = -1; c->state = GG_STATE_LISTENING; c->check = GG_CHECK_READ; c->callback = gg_dcc_callback; c->destroy = gg_dcc_free; return c; }
struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) { gg_debug(GG_DEBUG_MISC, "// gg_change_info() is obsolete. use gg_pubdir50() instead\n"); errno = EINVAL; return NULL; }
/** * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * * 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 h Struktura połączenia * * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd * * \ingroup dcc6 */ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) { struct gg_event *e; int foo; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); errno = EINVAL; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); return NULL; } e->type = GG_EVENT_NONE; if (h->type == GG_SESSION_DCC_SOCKET) { struct sockaddr_in sin; struct gg_dcc *c; int fd; #ifdef FIONBIO int one = 1; #endif socklen_t sin_len = sizeof(sin); if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); return e; } gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); #ifdef FIONBIO if (ioctl(fd, FIONBIO, &one) == -1) { #else if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { #endif gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno)); close(fd); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } if (!(c = (void*) calloc(1, sizeof(*c)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); free(e); close(fd); return NULL; } c->fd = fd; c->check = GG_CHECK_READ; c->state = GG_STATE_READING_UIN_1; c->type = GG_SESSION_DCC; c->timeout = GG_DEFAULT_TIMEOUT; c->file_fd = -1; c->remote_addr = sin.sin_addr.s_addr; c->remote_port = ntohs(sin.sin_port); e->type = GG_EVENT_DCC_NEW; e->event.dcc_new = c; return e; } else { struct gg_dcc_tiny_packet tiny_pkt; struct gg_dcc_small_packet small_pkt; struct gg_dcc_big_packet big_pkt; int size, tmp, res; unsigned int utmp; socklen_t res_size = sizeof(res); char buf[1024], ack[] = "UDAG"; struct gg_dcc_file_info_packet { struct gg_dcc_big_packet big; struct gg_file_info file_info; } GG_PACKED; struct gg_dcc_file_info_packet file_info_packet; switch (h->state) { case GG_STATE_READING_UIN_1: case GG_STATE_READING_UIN_2: { uin_t uin; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); gg_dcc_read(h->fd, &uin, sizeof(uin)); if (h->state == GG_STATE_READING_UIN_1) { h->state = GG_STATE_READING_UIN_2; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->peer_uin = gg_fix32(uin); } else { h->state = GG_STATE_SENDING_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->uin = gg_fix32(uin); e->type = GG_EVENT_DCC_CLIENT_ACCEPT; } return e; } case GG_STATE_SENDING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); gg_dcc_write(h->fd, ack, 4); h->state = GG_STATE_READING_TYPE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_READING_TYPE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); switch (small_pkt.type) { case 0x0003: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); h->type = GG_SESSION_DCC_SEND; h->state = GG_STATE_SENDING_FILE_INFO; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_CALLBACK; break; case 0x0002: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); h->type = GG_SESSION_DCC_GET; h->state = GG_STATE_READING_REQUEST; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->incoming = 1; break; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small_pkt.type, h->peer_uin); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); switch (small_pkt.type) { case 0x0001: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); h->state = GG_STATE_READING_FILE_INFO; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; break; case 0x0003: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); h->state = GG_STATE_SENDING_VOICE_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; h->type = GG_SESSION_DCC_VOICE; e->type = GG_EVENT_DCC_NEED_VOICE_ACK; break; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small_pkt.type, h->peer_uin); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet)); memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); h->file_info.mode = gg_fix32(h->file_info.mode); h->file_info.size = gg_fix32(h->file_info.size); h->state = GG_STATE_SENDING_FILE_ACK; h->check = GG_CHECK_WRITE; h->timeout = GG_DCC_TIMEOUT_FILE_ACK; e->type = GG_EVENT_DCC_NEED_FILE_ACK; return e; case GG_STATE_SENDING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); big_pkt.type = gg_fix32(0x0006); /* XXX */ big_pkt.dunno1 = gg_fix32(h->offset); big_pkt.dunno2 = 0; gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); h->state = GG_STATE_READING_FILE_HEADER; h->chunk_size = sizeof(big_pkt); h->chunk_offset = 0; if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); free(e); return NULL; } h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_SENDING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); tiny_pkt.type = 0x01; /* XXX */ gg_dcc_write(h->fd, &tiny_pkt, sizeof(tiny_pkt)); h->state = GG_STATE_READING_VOICE_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->offset = 0; return e; case GG_STATE_READING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); tmp = recv(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); h->chunk_offset += tmp; if (h->chunk_offset < h->chunk_size) return e; memcpy(&big_pkt, h->chunk_buf, sizeof(big_pkt)); free(h->chunk_buf); h->chunk_buf = NULL; big_pkt.type = gg_fix32(big_pkt.type); h->chunk_size = gg_fix32(big_pkt.dunno1); h->chunk_offset = 0; if (big_pkt.type == 0x0005) { /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_REFUSED; return e; } if (h->chunk_size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); e->type = GG_EVENT_DCC_DONE; return e; } h->state = GG_STATE_GETTING_FILE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; return e; case GG_STATE_READING_VOICE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); switch (tiny_pkt.type) { case 0x03: /* XXX */ h->state = GG_STATE_READING_VOICE_SIZE; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; break; case 0x04: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); /* XXX zwracać odpowiedni event */ default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } return e; case GG_STATE_READING_VOICE_SIZE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); small_pkt.type = gg_fix32(small_pkt.type); if (small_pkt.type < 16 || small_pkt.type > sizeof(buf)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->chunk_size = small_pkt.type; h->chunk_offset = 0; if (!(h->voice_buf = malloc(h->chunk_size))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); free(e); return NULL; } h->state = GG_STATE_READING_VOICE_DATA; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_READING_VOICE_DATA: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); tmp = recv(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); if (tmp < 1) { if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed (errno=%d, %s)\n", errno, strerror(errno)); } else { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed, connection broken\n"); } e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); h->chunk_offset += tmp; if (h->chunk_offset >= h->chunk_size) { e->type = GG_EVENT_DCC_VOICE_DATA; e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf; e->event.dcc_voice_data.length = h->chunk_size; h->state = GG_STATE_READING_VOICE_HEADER; h->voice_buf = NULL; } h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_CONNECTING: { uin_t uins[2]; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); res = 0; if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); uins[0] = gg_fix32(h->uin); uins[1] = gg_fix32(h->peer_uin); gg_dcc_write(h->fd, uins, sizeof(uins)); h->state = GG_STATE_READING_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; } case GG_STATE_READING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); gg_dcc_read(h->fd, buf, 4); if (strncmp(buf, ack, 4)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->state = GG_STATE_SENDING_REQUEST; return e; case GG_STATE_SENDING_VOICE_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); small_pkt.type = gg_fix32(0x0003); gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); h->state = GG_STATE_READING_VOICE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; return e; case GG_STATE_SENDING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); small_pkt.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); switch (h->type) { case GG_SESSION_DCC_GET: h->state = GG_STATE_READING_REQUEST; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; break; case GG_SESSION_DCC_SEND: h->state = GG_STATE_SENDING_FILE_INFO; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; if (h->file_fd == -1) e->type = GG_EVENT_DCC_NEED_FILE_INFO; break; case GG_SESSION_DCC_VOICE: h->state = GG_STATE_SENDING_VOICE_REQUEST; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; break; } return e; case GG_STATE_SENDING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); if (h->file_fd == -1) { e->type = GG_EVENT_DCC_NEED_FILE_INFO; return e; } small_pkt.type = gg_fix32(0x0001); /* XXX */ gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ file_info_packet.big.dunno1 = 0; file_info_packet.big.dunno2 = 0; memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); /* zostają teraz u nas, więc odwracamy z powrotem */ h->file_info.size = gg_fix32(h->file_info.size); h->file_info.mode = gg_fix32(h->file_info.mode); gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet)); h->state = GG_STATE_READING_FILE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DCC_TIMEOUT_FILE_ACK; return e; case GG_STATE_READING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); gg_dcc_read(h->fd, &big_pkt, sizeof(big_pkt)); /* XXX sprawdzać wynik */ h->offset = gg_fix32(big_pkt.dunno1); h->state = GG_STATE_SENDING_FILE_HEADER; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_ACK; return e; case GG_STATE_READING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); if (tiny_pkt.type != 0x01) { gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny_pkt.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_REFUSED; return e; } h->state = GG_STATE_READING_VOICE_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_ACK; return e; case GG_STATE_SENDING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); h->chunk_offset = 0; if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { h->chunk_size = 4096; big_pkt.type = gg_fix32(0x0003); /* XXX */ } else big_pkt.type = gg_fix32(0x0002); /* XXX */ big_pkt.dunno1 = gg_fix32(h->chunk_size); big_pkt.dunno2 = 0; gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); h->state = GG_STATE_SENDING_FILE; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; return e; case GG_STATE_SENDING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); /* koniec pliku? */ if (h->file_info.size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n"); e->type = GG_EVENT_DCC_DONE; return e; } if (h->offset >= h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC_DONE; return e; } lseek(h->file_fd, h->offset, SEEK_SET); size = read(h->file_fd, buf, utmp); /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_FILE; return e; } /* koniec pliku? */ if (size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_EOF; return e; } /* jeśli wczytaliśmy więcej, utnijmy. */ if (h->offset + size > h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); size = h->file_info.size - h->offset; if (size < 1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n"); e->type = GG_EVENT_DCC_DONE; return e; } } tmp = send(h->fd, buf, size, 0); if (tmp == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() failed (%s)\n", strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } if (tmp == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() failed (connection reset)\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->offset += tmp; if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } h->chunk_offset += tmp; if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_SENDING_FILE_HEADER; h->timeout = GG_DEFAULT_TIMEOUT; } else { h->state = GG_STATE_SENDING_FILE; h->timeout = GG_DCC_TIMEOUT_SEND; } h->check = GG_CHECK_WRITE; return e; case GG_STATE_GETTING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); if (h->offset >= h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); e->type = GG_EVENT_DCC_DONE; return e; } size = recv(h->fd, buf, utmp, 0); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, recv()=%d\n", h->offset, h->file_info.size, size); /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed. (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } /* koniec? */ if (size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() reached eof\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_EOF; return e; } tmp = write(h->file_fd, buf, size); if (tmp == -1 || tmp < size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; return e; } h->offset += size; if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } h->chunk_offset += size; if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_READING_FILE_HEADER; h->timeout = GG_DEFAULT_TIMEOUT; h->chunk_offset = 0; h->chunk_size = sizeof(big_pkt); if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); free(e); return NULL; } } else { h->state = GG_STATE_GETTING_FILE; h->timeout = GG_DCC_TIMEOUT_GET; } h->check = GG_CHECK_READ; return e; default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; return e; } } return e; } /** * Zwalnia zasoby używane przez połączenie bezpośrednie. * * \param d Struktura połączenia * * \ingroup dcc6 */ void gg_dcc_free(struct gg_dcc *d) { gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); if (!d) return; if (d->fd != -1) close(d->fd); if (d->file_fd != -1) gg_file_close(d->file_fd); free(d->chunk_buf); free(d); }
/* * gg_watch_fd() * * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê stanie z obserwowanym * deskryptorem. zwraca klientowi informacjê o tym, co siê dzieje. * * - sess - opis sesji * * wska¼nik do struktury gg_event, któr± trzeba zwolniæ pó¼niej * za pomoc± gg_event_free(). jesli rodzaj zdarzenia jest równy * GG_EVENT_NONE, nale¿y je zignorowaæ. je¶li zwróci³o NULL, * sta³o siê co¶ niedobrego -- albo zabrak³o pamiêci albo zerwa³o * po³±czenie. */ struct gg_event *gg_watch_fd(struct gg_session *sess) { struct gg_event *e; int res = 0; int port = 0; int errno2 = 0; gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); if (!sess) { errno = EFAULT; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); return NULL; } e->type = GG_EVENT_NONE; switch (sess->state) { case GG_STATE_RESOLVING: { struct in_addr addr; int failed = 0; gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); failed = 1; errno2 = errno; } close(sess->fd); sess->fd = -1; #ifndef __GG_LIBGADU_HAVE_PTHREAD waitpid(sess->pid, NULL, 0); sess->pid = -1; #else if (sess->resolver) { gg_resolve_pthread_cleanup(sess->resolver, 0); sess->resolver = NULL; } #endif if (failed) { errno = errno2; goto fail_resolving; } /* je¶li jeste¶my w resolverze i mamy ustawiony port * proxy, znaczy, ¿e resolvowali¶my proxy. zatem * wpiszmy jego adres. */ if (sess->proxy_port) sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do * bezpo¶redniego po³±czenia, je¶li hub le¿y) * z resolvera. */ if (sess->proxy_addr && sess->proxy_port) port = sess->proxy_port; else { sess->server_addr = sess->hub_addr = addr.s_addr; port = GG_APPMSG_PORT; } gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } /* je¶li podano serwer i ³±czmy siê przez proxy, * jest to bezpo¶rednie po³±czenie, inaczej jest * do huba. */ sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_CONNECTING_HUB: { char buf[1024], *client, *auth; int res = 0, res_size = sizeof(res); const char *host, *appmsg; gg_debug(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)) { /* no tak, nie uda³o siê po³±czyæ z proxy. nawet * nie próbujemy dalej. */ if (sess->proxy_addr && sess->proxy_port) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); close(sess->fd); if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { /* przy asynchronicznych, gg_connect() * zwraca -1 przy b³êdach socket(), * ioctl(), braku routingu itd. dlatego * nawet nie próbujemy dalej. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); goto fail_connecting; } if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) host = "http://" GG_APPMSG_HOST; else host = ""; #ifdef __GG_LIBGADU_HAVE_OPENSSL if (sess->ssl) appmsg = "appmsg3.asp"; else #endif appmsg = "appmsg2.asp"; auth = gg_proxy_auth(); snprintf(buf, sizeof(buf) - 1, "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" "Host: " GG_APPMSG_HOST "\r\n" "User-Agent: " GG_HTTP_USERAGENT "\r\n" "Pragma: no-cache\r\n" "%s" "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); if (auth) free(auth); free(client); /* zwolnij pamiêæ po wersji klienta. */ if (sess->client_version) { free(sess->client_version); sess->client_version = NULL; } gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; sess->state = GG_STATE_IDLE; close(sess->fd); sess->fd = -1; break; } sess->state = GG_STATE_READING_DATA; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_READING_DATA: { char buf[1024], *tmp, *host; int port = GG_DEFAULT_PORT; struct in_addr addr; gg_debug(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_chomp(buf); gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); close(sess->fd); /* je¶li otrzymali¶my jakie¶ dziwne informacje, * próbujemy siê ³±czyæ z pominiêciem huba. */ if (sess->proxy_addr && sess->proxy_port) { if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { /* trudno. nie wysz³o. */ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } sess->port = GG_DEFAULT_PORT; /* ³±czymy siê na port 8074 huba. */ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; /* ³±czymy siê na port 443. */ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } /* ignorujemy resztê nag³ówka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); /* czytamy pierwsz± liniê danych. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); /* je¶li pierwsza liczba w linii nie jest równa zeru, * oznacza to, ¿e mamy wiadomo¶æ systemow±. */ if (atoi(buf)) { char tmp[1024], *foo, *sysmsg_buf = NULL; int len = 0; while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); break; } sysmsg_buf = foo; if (!len) strcpy(sysmsg_buf, tmp); else strcat(sysmsg_buf, tmp); len += strlen(tmp); } e->type = GG_EVENT_MSG; e->event.msg.msgclass = atoi(buf); e->event.msg.sender = 0; e->event.msg.message = sysmsg_buf; } close(sess->fd); gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ tmp = buf; while (*tmp && *tmp != ' ') tmp++; while (*tmp && *tmp == ' ') tmp++; host = tmp; while (*tmp && *tmp != ' ') tmp++; *tmp = 0; if ((tmp = strchr(host, ':'))) { *tmp = 0; port = atoi(tmp + 1); } if (!strcmp(host, "notoperating")) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); sess->fd = -1; goto fail_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(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } sess->port = port; /* ³±czymy siê z w³a¶ciwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } case GG_STATE_CONNECTING_GG: { int res = 0, res_size = sizeof(res); gg_debug(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(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } close(sess->fd); sess->fd = -1; #ifdef ETIMEDOUT if (sess->timeout == 0) errno = ETIMEDOUT; #endif #ifdef __GG_LIBGADU_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 * SSL i SSL_CTX. */ if (sess->ssl) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } #endif gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); 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(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); if (gg_proxy_http_only) sess->proxy_port = 0; /* je¶li mamy proxy, wy¶lijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); struct in_addr addr; if (sess->server_addr) addr.s_addr = sess->server_addr; else 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(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(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); if (auth) free(auth); goto fail_connecting; } if (auth) { gg_debug(GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); free(auth); goto fail_connecting; } free(auth); } if (write(sess->fd, "\r\n", 2) < 2) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); goto fail_connecting; } } #ifdef __GG_LIBGADU_HAVE_OPENSSL if (sess->ssl) { SSL_set_fd(sess->ssl, sess->fd); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #endif sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #ifdef __GG_LIBGADU_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: { int res; X509 *peer; gg_debug(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); if (res == 0) { gg_debug(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; close(sess->fd); sess->fd = -1; break; } if (err == SSL_ERROR_WANT_READ) { gg_debug(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; break; } else if (err == SSL_ERROR_WANT_WRITE) { gg_debug(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; break; } else { char buf[1024]; ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); gg_debug(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; close(sess->fd); sess->fd = -1; break; } } gg_debug(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); if (!peer) gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); else { char buf[1024]; X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } sess->state = GG_STATE_READING_KEY; sess->check = GG_CHECK_READ; sess->timeout = GG_DEFAULT_TIMEOUT; break; } #endif case GG_STATE_READING_KEY: { struct gg_header *h; struct gg_welcome *w; struct gg_login60 l; unsigned int hash; unsigned char *password = sess->password; int ret; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); memset(&l, 0, sizeof(l)); l.dunno2 = 0xbe; /* XXX bardzo, bardzo, bardzo g³upi pomys³ na pozbycie * siê tekstu wrzucanego przez proxy. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100]; strcpy(buf, ""); gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); gg_debug(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_chomp(buf); if (strcmp(buf, "")) gg_debug(GG_DEBUG_MISC, "// %s\n", buf); } /* XXX niech czeka jeszcze raz w tej samej * fazie. g³upio, ale dzia³a. */ sess->proxy_port = 0; break; } /* czytaj pierwszy pakiet. */ if (!(h = gg_recv_packet(sess))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; break; } if (h->type != GG_WELCOME) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); free(h); close(sess->fd); sess->fd = -1; errno = EINVAL; e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_INVALID; sess->state = GG_STATE_IDLE; break; } w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); w->key = gg_fix32(w->key); hash = gg_login_hash(password, w->key); gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); free(h); free(sess->password); sess->password = NULL; { struct in_addr dcc_ip; dcc_ip.s_addr = gg_dcc_ip; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); } if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { struct sockaddr_in sin; int sin_len = sizeof(sin); gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); l.local_ip = sin.sin_addr.s_addr; } else { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); l.local_ip = 0; } } else l.local_ip = gg_dcc_ip; l.uin = gg_fix32(sess->uin); l.hash = gg_fix32(hash); l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); l.version = gg_fix32(sess->protocol_version); l.local_port = gg_fix16(gg_dcc_port); l.image_size = sess->image_size; if (sess->external_addr && sess->external_port > 1023) { l.external_ip = sess->external_addr; l.external_port = gg_fix16(sess->external_port); } gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); free(sess->initial_descr); sess->initial_descr = NULL; if (ret == -1) { gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; sess->state = GG_STATE_IDLE; break; } sess->state = GG_STATE_READING_REPLY; break; } case GG_STATE_READING_REPLY: { struct gg_header *h; gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); if (!(h = gg_recv_packet(sess))) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; break; } if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); e->type = GG_EVENT_CONN_SUCCESS; sess->state = GG_STATE_CONNECTED; sess->timeout = -1; sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; free(h); break; } if (h->type == GG_LOGIN_FAILED) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); e->event.failure = GG_FAILURE_PASSWORD; errno = EACCES; } else if (h->type == GG_DISCONNECTING) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); e->event.failure = GG_FAILURE_INTRUDER; errno = EACCES; } else { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); e->event.failure = GG_FAILURE_INVALID; errno = EINVAL; } e->type = GG_EVENT_CONN_FAILED; sess->state = GG_STATE_IDLE; errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; free(h); break; } case GG_STATE_CONNECTED: { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); sess->last_event = time(NULL); if ((res = gg_watch_fd_connected(sess, e)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); if (errno == EAGAIN) { e->type = GG_EVENT_NONE; res = 0; } else res = -1; } break; } } done: if (res == -1) { free(e); e = NULL; } return e; fail_connecting: if (sess->fd != -1) { errno2 = errno; close(sess->fd); errno = errno2; sess->fd = -1; } e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_CONNECTING; sess->state = GG_STATE_IDLE; goto done; fail_resolving: e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_RESOLVING; sess->state = GG_STATE_IDLE; goto done; fail_unavailable: e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_UNAVAILABLE; sess->state = GG_STATE_IDLE; goto done; }
int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread) { #ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R char *buf = NULL; char *new_buf = NULL; struct hostent he; struct hostent *he_ptr = NULL; size_t buf_len = 1024; int result = -1; int h_errnop; int ret = 0; #ifdef GG_CONFIG_HAVE_PTHREAD int old_state; #endif #ifdef GG_CONFIG_HAVE_PTHREAD pthread_cleanup_push(gg_gethostbyname_cleaner, &buf); if (pthread) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); #endif buf = malloc(buf_len); #ifdef GG_CONFIG_HAVE_PTHREAD if (pthread) pthread_setcancelstate(old_state, NULL); #endif if (buf != NULL) { #ifndef sun while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) { #else while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) { #endif buf_len *= 2; #ifdef GG_CONFIG_HAVE_PTHREAD if (pthread) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); #endif new_buf = realloc(buf, buf_len); if (new_buf != NULL) buf = new_buf; #ifdef GG_CONFIG_HAVE_PTHREAD if (pthread) pthread_setcancelstate(old_state, NULL); #endif if (new_buf == NULL) { ret = ENOMEM; break; } } if (ret == 0 && he_ptr != NULL) { memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr)); result = 0; } #ifdef GG_CONFIG_HAVE_PTHREAD if (pthread) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); #endif free(buf); buf = NULL; #ifdef GG_CONFIG_HAVE_PTHREAD if (pthread) pthread_setcancelstate(old_state, NULL); #endif } #ifdef GG_CONFIG_HAVE_PTHREAD pthread_cleanup_pop(1); #endif return result; #else struct hostent *he; he = gethostbyname(hostname); if (he == NULL) return -1; memcpy(addr, he->h_addr, sizeof(struct in_addr)); return 0; #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ } struct in_addr *gg_gethostbyname(const char *hostname) { struct in_addr *addr; if (!(addr = malloc(sizeof(struct in_addr)))) return NULL; if (gg_gethostbyname_real(hostname, addr, 0)) { free(addr); return NULL; } return addr; } /** * \internal Struktura przekazywana do wątku rozwiązującego nazwę. */ struct gg_resolver_fork_data { int pid; /*< Identyfikator procesu */ }; static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *hostname) { struct gg_resolver_fork_data *data = NULL; struct in_addr addr; int new_errno; SOCKET pipes[2]; gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); if (fd == NULL || priv_data == NULL || hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n"); errno = EFAULT; return -1; } data = malloc(sizeof(struct gg_resolver_fork_data)); if (data == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n"); return -1; } if (pipe(pipes) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); free(data); return -1; } data->pid = fork(); if (data->pid == -1) { new_errno = errno; goto cleanup; } if (data->pid == 0) { gg_sock_close(pipes[0]); if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { /* W przypadku błędu gg_gethostbyname_real() zwróci -1 * i nie zmieni &addr. Tam jest już INADDR_NONE, * więc nie musimy robić nic więcej. */ gg_gethostbyname_real(hostname, &addr, 0); } if (gg_sock_write(pipes[1], &addr, sizeof(addr)) != sizeof(addr)) exit(1); exit(0); } gg_sock_close(pipes[1]); gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); *fd = pipes[0]; *priv_data = data; return 0; cleanup: free(data); gg_sock_close(pipes[0]); gg_sock_close(pipes[1]); errno = new_errno; return -1; } static void gg_resolver_fork_cleanup(void **priv_data, int force) { struct gg_resolver_fork_data *data; if (priv_data == NULL || *priv_data == NULL) return; data = (struct gg_resolver_fork_data*) *priv_data; *priv_data = NULL; if (force) kill(data->pid, SIGKILL); waitpid(data->pid, NULL, WNOHANG); free(data); } #ifdef GG_CONFIG_HAVE_PTHREAD /** * \internal Struktura przekazywana do wątku rozwiązującego nazwę. */ struct gg_resolver_pthread_data { pthread_t thread; /*< Identyfikator wątku */ char *hostname; /*< Nazwa serwera */ SOCKET rfd; /*< Deskryptor do odczytu */ SOCKET wfd; /*< Deskryptor do zapisu */ }; static void gg_resolver_pthread_cleanup(void **priv_data, int force) { struct gg_resolver_pthread_data *data; if (priv_data == NULL || *priv_data == NULL) return; data = (struct gg_resolver_pthread_data *) *priv_data; *priv_data = NULL; if (force) { pthread_cancel(&data->thread); pthread_join(&data->thread, NULL); } free(data->hostname); data->hostname = NULL; if (data->wfd != -1) { gg_sock_close(data->wfd); data->wfd = -1; } free(data); } static void *__stdcall gg_resolver_pthread_thread(void *arg) { struct gg_resolver_pthread_data *data = arg; struct in_addr addr; pthread_detach(pthread_self()); if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) { /* W przypadku błędu gg_gethostbyname_real() zwróci -1 * i nie zmieni &addr. Tam jest już INADDR_NONE, * więc nie musimy robić nic więcej. */ gg_gethostbyname_real(data->hostname, &addr, 1); } if (gg_sock_write(data->wfd, &addr, sizeof(addr)) == sizeof(addr)) pthread_exit(NULL); else pthread_exit((void*) -1); return NULL; /* żeby kompilator nie marudził */ } static int gg_resolver_pthread_start(SOCKET *fd, void **priv_data, const char *hostname) { struct gg_resolver_pthread_data *data = NULL; int new_errno; SOCKET pipes[2]; gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); if (fd == NULL || priv_data == NULL || hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n"); errno = EFAULT; return -1; } data = malloc(sizeof(struct gg_resolver_pthread_data)); if (data == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n"); return -1; } if (pipe(pipes) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); free(data); return -1; } data->hostname = strdup(hostname); if (data->hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n"); new_errno = errno; goto cleanup; } data->rfd = pipes[0]; data->wfd = pipes[1]; if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n"); new_errno = errno; goto cleanup; } gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data); *fd = pipes[0]; *priv_data = data; return 0; cleanup: if (data) { free(data->hostname); free(data); } gg_sock_close(pipes[0]); gg_sock_close(pipes[1]); errno = new_errno; return -1; } #endif /* GG_CONFIG_HAVE_PTHREAD */ int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) { if (gs == NULL) { errno = EINVAL; return -1; } if (type == GG_RESOLVER_DEFAULT) { if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { gs->resolver_type = gg_global_resolver_type; gs->resolver_start = gg_global_resolver_start; gs->resolver_cleanup = gg_global_resolver_cleanup; return 0; } #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) type = GG_RESOLVER_FORK; #else type = GG_RESOLVER_PTHREAD; #endif } switch (type) { case GG_RESOLVER_FORK: gs->resolver_type = type; gs->resolver_start = gg_resolver_fork_start; gs->resolver_cleanup = gg_resolver_fork_cleanup; return 0; #ifdef GG_CONFIG_HAVE_PTHREAD case GG_RESOLVER_PTHREAD: gs->resolver_type = type; gs->resolver_start = gg_resolver_pthread_start; gs->resolver_cleanup = gg_resolver_pthread_cleanup; return 0; #endif default: errno = EINVAL; return -1; } } gg_resolver_t gg_session_get_resolver(struct gg_session *gs) { if (gs == NULL) { errno = EINVAL; return GG_RESOLVER_INVALID; } return gs->resolver_type; } int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)) { if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) { errno = EINVAL; return -1; } gs->resolver_type = GG_RESOLVER_CUSTOM; gs->resolver_start = resolver_start; gs->resolver_cleanup = resolver_cleanup; return 0; } int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type) { if (gh == NULL) { errno = EINVAL; return -1; } if (type == GG_RESOLVER_DEFAULT) { if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { gh->resolver_type = gg_global_resolver_type; gh->resolver_start = gg_global_resolver_start; gh->resolver_cleanup = gg_global_resolver_cleanup; return 0; } #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) type = GG_RESOLVER_FORK; #else type = GG_RESOLVER_PTHREAD; #endif } switch (type) { case GG_RESOLVER_FORK: gh->resolver_type = type; gh->resolver_start = gg_resolver_fork_start; gh->resolver_cleanup = gg_resolver_fork_cleanup; return 0; #ifdef GG_CONFIG_HAVE_PTHREAD case GG_RESOLVER_PTHREAD: gh->resolver_type = type; gh->resolver_start = gg_resolver_pthread_start; gh->resolver_cleanup = gg_resolver_pthread_cleanup; return 0; #endif default: errno = EINVAL; return -1; } } gg_resolver_t gg_http_get_resolver(struct gg_http *gh) { if (gh == NULL) { errno = EINVAL; return GG_RESOLVER_INVALID; } return gh->resolver_type; } int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)) { if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) { errno = EINVAL; return -1; } gh->resolver_type = GG_RESOLVER_CUSTOM; gh->resolver_start = resolver_start; gh->resolver_cleanup = resolver_cleanup; return 0; } int gg_global_set_resolver(gg_resolver_t type) { switch (type) { case GG_RESOLVER_DEFAULT: gg_global_resolver_type = type; gg_global_resolver_start = NULL; gg_global_resolver_cleanup = NULL; return 0; case GG_RESOLVER_FORK: gg_global_resolver_type = type; gg_global_resolver_start = gg_resolver_fork_start; gg_global_resolver_cleanup = gg_resolver_fork_cleanup; return 0; #ifdef GG_CONFIG_HAVE_PTHREAD case GG_RESOLVER_PTHREAD: gg_global_resolver_type = type; gg_global_resolver_start = gg_resolver_pthread_start; gg_global_resolver_cleanup = gg_resolver_pthread_cleanup; return 0; #endif default: errno = EINVAL; return -1; } } gg_resolver_t gg_global_get_resolver(void) { return gg_global_resolver_type; } int gg_global_set_custom_resolver(int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)) { if (resolver_start == NULL || resolver_cleanup == NULL) { errno = EINVAL; return -1; } gg_global_resolver_type = GG_RESOLVER_CUSTOM; gg_global_resolver_start = resolver_start; gg_global_resolver_cleanup = resolver_cleanup; return 0; }
struct gg_http *gg_search(const struct gg_search_request *r, int async) { gg_debug(GG_DEBUG_MISC, "// gg_search() is obsolete. use gg_search50() instead!\n"); errno = EINVAL; return NULL; }
/** * Rozpoczyna połączenie HTTP. * * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym, * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się * nie powiedzie. * * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd(). * * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej * chwili za pomocą \c gg_http_stop(). * * \param hostname Adres serwera * \param port Port serwera * \param async Flaga asynchronicznego połączenia * \param method Metoda HTTP * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/') * \param header Nagłówek zapytania plus ewentualne dane dla POST * * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd. * * \ingroup http */ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header) { struct gg_http *h; if (!hostname || !port || !method || !path || !header) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n"); errno = EFAULT; return NULL; } if (!(h = malloc(sizeof(*h)))) return NULL; memset(h, 0, sizeof(*h)); h->async = async; h->port = port; h->fd = -1; h->type = GG_SESSION_HTTP; gg_http_set_resolver(h, GG_RESOLVER_DEFAULT); if (gg_proxy_enabled) { char *auth = gg_proxy_auth(); h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s", method, hostname, port, path, (auth) ? auth : "", header); hostname = gg_proxy_host; h->port = port = gg_proxy_port; free(auth); } else { h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s", method, path, header); } if (!h->query) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n"); free(h); errno = ENOMEM; return NULL; } gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query); if (async) { if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n"); gg_http_free(h); errno = ENOENT; return NULL; } gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver); h->state = GG_STATE_RESOLVING; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; } else { struct in_addr addr; if (gg_gethostbyname(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n"); gg_http_free(h); errno = ENOENT; return NULL; } if (!(h->fd = gg_connect(&addr, port, 0)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno)); gg_http_free(h); return NULL; } h->state = GG_STATE_CONNECTING; while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) { if (gg_http_watch_fd(h) == -1) break; } if (h->state != GG_STATE_PARSING) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n"); gg_http_free(h); return NULL; } } h->callback = gg_http_watch_fd; h->destroy = gg_http_free; return h; }
/** * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. * * \param d Struktura połączenia * \param filename Nazwa pliku zapisywana w strukturze * \param local_filename Nazwa pliku w lokalnym systemie plików * * \return 0 jeśli się powiodło, -1 w przypadku błędu * * \ingroup dcc6 */ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) { struct stat st; const char *name, *ext, *p; unsigned char *q; int i, j; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); if (!d || d->type != GG_SESSION_DCC_SEND) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); errno = EINVAL; return -1; } if (stat(local_filename, &st) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno)); return -1; } if ((st.st_mode & S_IFDIR)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); errno = EINVAL; return -1; } if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); return -1; } memset(&d->file_info, 0, sizeof(d->file_info)); if (!(st.st_mode & S_IWUSR)) d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); d->file_info.size = gg_fix32(st.st_size); d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ if (!(name = strrchr(filename, '/'))) name = filename; else name++; if (!(ext = strrchr(name, '.'))) ext = name + strlen(name); for (i = 0, p = name; i < 8 && p < ext; i++, p++) d->file_info.short_filename[i] = toupper(name[i]); if (i == 8 && p < ext) { d->file_info.short_filename[6] = '~'; d->file_info.short_filename[7] = '1'; } if (strlen(ext) > 0) { for (j = 0; *ext && j < 4; j++, p++) d->file_info.short_filename[i + j] = toupper(ext[j]); } for (q = d->file_info.short_filename; *q; q++) { if (*q == 185) { *q = 165; } else if (*q == 230) { *q = 198; } else if (*q == 234) { *q = 202; } else if (*q == 179) { *q = 163; } else if (*q == 241) { *q = 209; } else if (*q == 243) { *q = 211; } else if (*q == 156) { *q = 140; } else if (*q == 159) { *q = 143; } else if (*q == 191) { *q = 175; } } gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1); return 0; }
struct gg_http *gg_userlist_remove(uin_t uin, const char *passwd, int async) { gg_debug(GG_DEBUG_MISC, "// gg_userlist_remove() is obsolete. use gg_userlist_request() instead!\n"); errno = EINVAL; return NULL; }
/** * \internal Rozwiązuje nazwę serwera w osobnym wątku. * * Funkcja działa analogicznie do \c gg_resolver_pthread_start(), z tą różnicą, * że działa na wątkach Win32. Jest dostępna wyłącznie przy kompilacji dla * systemu Windows. * * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik * do prywatnych danych wątku rozwiązującego nazwę * \param hostname Nazwa serwera do rozwiązania * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_resolver_win32_start(int *fd, void **priv_data, const char *hostname) { struct gg_resolver_win32_data *data = NULL; int pipes[2], new_errno; CRITICAL_SECTION *mutex = NULL; gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_win32_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); if (fd == NULL || priv_data == NULL || hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() invalid arguments\n"); errno = EFAULT; return -1; } data = malloc(sizeof(struct gg_resolver_win32_data)); if (data == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory for resolver data\n"); return -1; } data->orphan = 0; data->finished = 0; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to " "create pipes (errno=%d, %s)\n", errno, strerror(errno)); free(data); return -1; } data->hostname = strdup(hostname); if (data->hostname == NULL) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory\n"); new_errno = errno; goto cleanup; } data->wfd = pipes[1]; mutex = &data->mutex; InitializeCriticalSection(mutex); data->thread = CreateThread(NULL, 0, gg_resolver_win32_thread, data, 0, NULL); if (!data->thread) { gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to create thread\n"); new_errno = errno; goto cleanup; } gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() %p\n", data); *fd = pipes[0]; *priv_data = data; return 0; cleanup: if (data) { free(data->hostname); free(data); } close(pipes[0]); close(pipes[1]); if (mutex) DeleteCriticalSection(mutex); errno = new_errno; return -1; }