/** * 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); }
/** * 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); }