HANDLE GGPROTO::dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath) { struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; char fileName[MAX_PATH], *path = mir_t2a(szPath); int iFtRemoveRes; strncpy(fileName, path, sizeof(fileName)); strncat(fileName, (char*)dcc7->filename, sizeof(fileName) - strlen(fileName)); dcc7->folder = _strdup((char *) path); dcc7->tick = 0; mir_free(path); // Remove transfer from waiting list gg_EnterCriticalSection(&ft_mutex, "dcc7fileallow", 40, "ft_mutex", 1); iFtRemoveRes = list_remove(&transfers, dcc7, 0); gg_LeaveCriticalSection(&ft_mutex, "dcc7fileallow", 40, 1, "ft_mutex", 1); if (iFtRemoveRes == -1) { debugLogA("dcc7fileallow(): File transfer denied."); ProtoBroadcastAck(dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); // Free transfer gg_dcc7_free(dcc7); return 0; } // Open file for appending and check if ok if ((dcc7->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1) { debugLogA("dcc7fileallow(): Failed to create file \"%s\". errno=%d: %s", fileName, errno, strerror(errno)); TCHAR error[512]; mir_sntprintf(error, SIZEOF(error), TranslateT("Cannot create transfer file. ERROR: %d: %s (dcc7)\n%s"), errno, _tcserror(errno), szPath); showpopup(m_tszUserName, error, GG_POPUP_ERROR); gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); ProtoBroadcastAck(dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); // Free transfer gg_dcc7_free(dcc7); return 0; } // Put an offset to the file dcc7->offset = _lseek(dcc7->file_fd, 0, SEEK_END); gg_dcc7_accept(dcc7, dcc7->offset); // Add to watches and start transfer gg_EnterCriticalSection(&ft_mutex, "dcc7fileallow", 41, "ft_mutex", 1); list_add(&watches, dcc7, 0); gg_LeaveCriticalSection(&ft_mutex, "dcc7fileallow", 41, 1, "ft_mutex", 1); debugLogA("dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin); return hTransfer; }
int GGPROTO::dcc7filecancel(HANDLE hTransfer) { struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; if (dcc7->type == GG_SESSION_DCC7_SEND && dcc7->state == GG_STATE_WAITING_FOR_ACCEPT) gg_dcc7_abort(dcc7); // Remove transfer from any list gg_EnterCriticalSection(&ft_mutex, "dcc7filecancel", 45, "ft_mutex", 1); if (watches) list_remove(&watches, dcc7, 0); if (transfers) list_remove(&transfers, dcc7, 0); gg_LeaveCriticalSection(&ft_mutex, "dcc7filecancel", 45, 1, "ft_mutex", 1); // Send failed info ProtoBroadcastAck((UINT_PTR)dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); // Close file if (dcc7->file_fd != -1) { _close(dcc7->file_fd); dcc7->file_fd = -1; } debugLogA("dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); // Free transfer gg_dcc7_free(dcc7); return 0; }
static void gg_dcc7_close_handler(dcc_t *d) { struct gg_dcc7 *g; if (!d || !(g = d->priv)) return; gg_dcc7_free(g); }
int GGPROTO::dcc7filedeny(HANDLE hTransfer) { struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); // Remove transfer from any list gg_EnterCriticalSection(&ft_mutex, "dcc7filedeny", 43, "ft_mutex", 1); if (watches) list_remove(&watches, dcc7, 0); if (transfers) list_remove(&transfers, dcc7, 0); gg_LeaveCriticalSection(&ft_mutex, "dcc7filedeny", 43, 1, "ft_mutex", 1); debugLogA("dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); // Free transfer gg_dcc7_free(dcc7); return 0; }
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; }
DccSocketNotifiers::~DccSocketNotifiers() { gg_dcc7_free(Socket7); Socket7 = 0; }
void __cdecl GGPROTO::dccmainthread(void*) { uin_t uin; gg_event *e; struct timeval tv; fd_set rd, wd; int ret; SOCKET maxfd; DWORD tick; list_t l; char szFilename[MAX_PATH]; // Zero up lists watches = transfers = requests = l = NULL; debugLogA("dccmainthread(): started. DCC Server Thread Starting"); // Readup number if (!(uin = getDword(GG_KEY_UIN, 0))) { debugLogA("dccmainthread(): No Gadu-Gadu number specified. Exiting."); if (hEvent) SetEvent(hEvent); #ifdef DEBUGMODE debugLogA("dccmainthread(): end 1."); #endif return; } // Create listen socket on config direct port if (!(dcc = gg_dcc_socket_create(uin, (uint16_t)getWord(GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)))) { debugLogA("dccmainthread(): Cannot create DCC listen socket. Exiting."); // Signalize mainthread we haven't start if (hEvent) SetEvent(hEvent); #ifdef DEBUGMODE debugLogA("dccmainthread(): end 2."); #endif return; } gg_dcc_port = dcc->port; gg_dcc_ip = inet_addr("255.255.255.255"); debugLogA("dccmainthread(): Listening on port %d.", gg_dcc_port); // Signalize mainthread we started if (hEvent) SetEvent(hEvent); // Add main dcc handler to watches list_add(&watches, dcc, 0); // Do while we are in the main server thread while(pth_dcc.dwThreadId && dcc) { // Timeouts tv.tv_sec = 1; tv.tv_usec = 0; // Prepare descriptiors for select FD_ZERO(&rd); FD_ZERO(&wd); for (maxfd = 0, l = watches; l; l = l->next) { gg_common *w = (gg_common*)l->data; if (!w || w->state == GG_STATE_ERROR || w->state == GG_STATE_IDLE || w->state == GG_STATE_DONE) continue; // Check if it's proper descriptor if (w->fd == -1) continue; if (w->fd > maxfd) maxfd = w->fd; if ((w->check & GG_CHECK_READ)) FD_SET(w->fd, &rd); if ((w->check & GG_CHECK_WRITE)) FD_SET(w->fd, &wd); } // Wait for data on selects ret = select(maxfd + 1, &rd, &wd, NULL, &tv); // Check for select error if (ret == -1) { if (errno == EBADF) debugLogA("dccmainthread(): Bad descriptor on select()."); else if (errno != EINTR) debugLogA("dccmainthread(): Unknown error on select()."); continue; } // Process watches (carefull with l) l = watches; gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); while (l) { struct gg_common *local_c = (gg_common*)l->data; struct gg_dcc *local_dcc = (gg_dcc*)l->data; struct gg_dcc7 *local_dcc7 = (gg_dcc7*)l->data; l = l->next; switch (local_c->type) { default: if (!local_dcc || (!FD_ISSET(local_dcc->fd, &rd) && !FD_ISSET(local_dcc->fd, &wd))) continue; ///////////////////////////////////////////////////////////////// // Process DCC events // Connection broken/closed gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 2, "ft_mutex", 1); if (!(e = gg_dcc_socket_watch_fd(local_dcc))) { debugLogA("dccmainthread(): Socket closed."); // Remove socket and _close gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); list_remove(&watches, local_dcc, 0); gg_dcc_socket_free(local_dcc); // Check if it's main socket if (local_dcc == dcc) dcc = NULL; continue; } else { gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); debugLogA("dccmainthread(): Event: %s", ggdebug_eventtype(e)); } switch(e->type) { // Client connected case GG_EVENT_DCC_NEW: list_add(&watches, e->event.dcc_new, 0); e->event.dcc_new = NULL; break; // case GG_EVENT_NONE: // If transfer in progress do status if (local_dcc->file_fd != -1 && local_dcc->offset > 0 && (((tick = GetTickCount()) - local_dcc->tick) > GGSTATREFRESHEVERY)) { PROTOFILETRANSFERSTATUS pfts; local_dcc->tick = tick; mir_snprintf(szFilename, "%s%s", local_dcc->folder, local_dcc->file_info.filename); memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); pfts.hContact = (UINT_PTR)local_dcc->contact; pfts.flags = (local_dcc->type == GG_SESSION_DCC_SEND); pfts.pszFiles = NULL; pfts.totalFiles = 1; pfts.currentFileNumber = 0; pfts.totalBytes = local_dcc->file_info.size; pfts.totalProgress = local_dcc->offset; pfts.szWorkingDir = local_dcc->folder; pfts.szCurrentFile = szFilename; pfts.currentFileSize = local_dcc->file_info.size; pfts.currentFileProgress = local_dcc->offset; pfts.currentFileTime = 0; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 3, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, local_dcc, (LPARAM)&pfts); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } break; // Connection was successfuly ended case GG_EVENT_DCC_DONE: debugLogA("dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); // Remove from watches list_remove(&watches, local_dcc, 0); // Close file & success if (local_dcc->file_fd != -1) { PROTOFILETRANSFERSTATUS pfts; mir_snprintf(szFilename, "%s%s", local_dcc->folder, local_dcc->file_info.filename); memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); pfts.hContact = (UINT_PTR)local_dcc->contact; pfts.flags = (local_dcc->type == GG_SESSION_DCC_SEND); pfts.pszFiles = NULL; pfts.totalFiles = 1; pfts.currentFileNumber = 0; pfts.totalBytes = local_dcc->file_info.size; pfts.totalProgress = local_dcc->file_info.size; pfts.szWorkingDir = local_dcc->folder; pfts.szCurrentFile = szFilename; pfts.currentFileSize = local_dcc->file_info.size; pfts.currentFileProgress = local_dcc->file_info.size; pfts.currentFileTime = 0; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 4, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, local_dcc, (LPARAM)&pfts); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); _close(local_dcc->file_fd); local_dcc->file_fd = -1; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 5, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, local_dcc, 0); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } // Free dcc gg_free_dcc(local_dcc); if (local_dcc == dcc) dcc = NULL; break; // Client error case GG_EVENT_DCC_ERROR: switch (e->event.dcc_error) { case GG_ERROR_DCC_HANDSHAKE: debugLogA("dccmainthread(): Client: %d, Handshake error.", local_dcc->peer_uin); break; case GG_ERROR_DCC_NET: debugLogA("dccmainthread(): Client: %d, Network error.", local_dcc->peer_uin); break; case GG_ERROR_DCC_FILE: debugLogA("dccmainthread(): Client: %d, File read/write error.", local_dcc->peer_uin); break; case GG_ERROR_DCC_EOF: debugLogA("dccmainthread(): Client: %d, End of file/connection error.", local_dcc->peer_uin); break; case GG_ERROR_DCC_REFUSED: debugLogA("dccmainthread(): Client: %d, Connection refused error.", local_dcc->peer_uin); break; default: debugLogA("dccmainthread(): Client: %d, Unknown error.", local_dcc->peer_uin); } // Don't do anything if it's main socket if (local_dcc == dcc) break; // Remove from watches list_remove(&watches, local_dcc, 0); // Close file & fail if (local_dcc->contact) { _close(local_dcc->file_fd); local_dcc->file_fd = -1; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 6, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, local_dcc, 0); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } // Free dcc gg_free_dcc(local_dcc); if (local_dcc == dcc) dcc = NULL; break; // Need file acknowledgement case GG_EVENT_DCC_NEED_FILE_ACK: debugLogA("dccmainthread(): Client: %d, File ack filename \"%s\" size %d.", local_dcc->peer_uin, local_dcc->file_info.filename, local_dcc->file_info.size); // Do not watch for transfer until user accept it list_remove(&watches, local_dcc, 0); // Add to waiting transfers list_add(&transfers, local_dcc, 0); ////////////////////////////////////////////////// // Add file recv request { // Make new ggtransfer struct local_dcc->contact = (void*)getcontact(local_dcc->peer_uin, 0, 0, NULL); TCHAR* filenameT = mir_utf8decodeT((char*)dcc->file_info.filename); PROTORECVFILET pre = {0}; pre.dwFlags = PRFF_TCHAR; pre.fileCount = 1; pre.timestamp = time(NULL); pre.descr.t = filenameT; pre.files.t = &filenameT; pre.lParam = (LPARAM)local_dcc; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 7, "ft_mutex", 1); ProtoChainRecvFile((UINT_PTR)local_dcc->contact, &pre); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); mir_free(filenameT); } break; // Need client accept case GG_EVENT_DCC_CLIENT_ACCEPT: debugLogA("dccmainthread(): Client: %d, Client accept.", local_dcc->peer_uin); // Check if user is on the list and if it is my uin if (getcontact(local_dcc->peer_uin, 0, 0, NULL) && getDword(GG_KEY_UIN, -1) == local_dcc->uin) break; // Kill unauthorized dcc list_remove(&watches, dcc, 0); gg_free_dcc(local_dcc); if (local_dcc == dcc) dcc = NULL; break; // Client connected as we wished to (callback) case GG_EVENT_DCC_CALLBACK: { int found = 0; debugLogA("dccmainthread(): Callback from client %d.", local_dcc->peer_uin); // Seek for stored callback request for (l = requests; l; l = l->next) { struct gg_dcc *req = (gg_dcc*)l->data; if (req && req->peer_uin == local_dcc->peer_uin) { gg_dcc_set_type(local_dcc, GG_SESSION_DCC_SEND); found = 1; // Copy data req ===> dcc local_dcc->folder = req->folder; local_dcc->contact = req->contact; local_dcc->file_fd = req->file_fd; memcpy(&local_dcc->file_info, &req->file_info, sizeof(struct gg_file_info)); // Copy data back to dcc ===> req memcpy(req, local_dcc, sizeof(struct gg_dcc)); // Remove request list_remove(&requests, req, 0); // Remove dcc from watches list_remove(&watches, local_dcc, 0); // Add request to watches list_add(&watches, req, 0); // Free old dat gg_free_dcc(local_dcc); debugLogA("dccmainthread(): Found stored request to client %d, filename \"%s\" size %d, folder \"%s\".", req->peer_uin, req->file_info.filename, req->file_info.size, req->folder); break; } } if (!found) { debugLogA("dccmainthread(): Unknown request to client %d.", local_dcc->peer_uin); // Kill unauthorized dcc list_remove(&watches, local_dcc, 0); gg_free_dcc(local_dcc); if (local_dcc == dcc) dcc = NULL; } break; } } // Free event gg_free_event(e); break; case GG_SESSION_DCC7_SOCKET: case GG_SESSION_DCC7_GET: case GG_SESSION_DCC7_SEND: case GG_SESSION_DCC7_VOICE: if (!local_dcc7 || (!FD_ISSET(local_dcc7->fd, &rd) && !FD_ISSET(local_dcc7->fd, &wd))) continue; ///////////////////////////////////////////////////////////////// // Process DCC7 events // Connection broken/closed gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 8, "ft_mutex", 1); if (!(e = gg_dcc7_watch_fd(local_dcc7))) { debugLogA("dccmainthread(): Socket closed."); // Remove socket and _close gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); list_remove(&watches, local_dcc7, 0); gg_dcc7_free(local_dcc7); continue; } else { gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); debugLogA("dccmainthread(): Event: %s", ggdebug_eventtype(e)); } switch(e->type) { // case GG_EVENT_NONE: // If transfer in progress do status if (local_dcc7->file_fd != -1 && local_dcc7->offset > 0 && (((tick = GetTickCount()) - local_dcc7->tick) > GGSTATREFRESHEVERY)) { PROTOFILETRANSFERSTATUS pfts; local_dcc7->tick = tick; mir_snprintf(szFilename, "%s%s", local_dcc->folder, local_dcc7->filename); memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); pfts.hContact = (UINT_PTR)local_dcc7->contact; pfts.flags = (local_dcc7->type == GG_SESSION_DCC7_SEND); pfts.pszFiles = NULL; pfts.totalFiles = 1; pfts.currentFileNumber = 0; pfts.totalBytes = local_dcc7->size; pfts.totalProgress = local_dcc7->offset; pfts.szWorkingDir = local_dcc7->folder; pfts.szCurrentFile = szFilename; pfts.currentFileSize = local_dcc7->size; pfts.currentFileProgress = local_dcc7->offset; pfts.currentFileTime = 0; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 9, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, local_dcc7, (LPARAM)&pfts); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } break; // Connection was successfuly ended case GG_EVENT_DCC7_DONE: debugLogA("dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); // Remove from watches list_remove(&watches, local_dcc7, 0); // Close file & success if (local_dcc7->file_fd != -1) { PROTOFILETRANSFERSTATUS pfts; mir_snprintf(szFilename, "%s%s", local_dcc->folder, local_dcc7->filename); memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); pfts.hContact = (UINT_PTR)local_dcc7->contact; pfts.flags = (local_dcc7->type == GG_SESSION_DCC7_SEND); pfts.pszFiles = NULL; pfts.totalFiles = 1; pfts.currentFileNumber = 0; pfts.totalBytes = local_dcc7->size; pfts.totalProgress = local_dcc7->size; pfts.szWorkingDir = local_dcc7->folder; pfts.szCurrentFile = szFilename; pfts.currentFileSize = local_dcc7->size; pfts.currentFileProgress = local_dcc7->size; pfts.currentFileTime = 0; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 10, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, local_dcc7, (LPARAM)&pfts); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); _close(local_dcc7->file_fd); local_dcc7->file_fd = -1; gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 11, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc7->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, local_dcc7, 0); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } // Free dcc gg_dcc7_free(local_dcc7); break; // Client error case GG_EVENT_DCC7_ERROR: switch (e->event.dcc7_error) { case GG_ERROR_DCC7_HANDSHAKE: debugLogA("dccmainthread(): Client: %d, Handshake error.", local_dcc7->peer_uin); break; case GG_ERROR_DCC7_NET: debugLogA("dccmainthread(): Client: %d, Network error.", local_dcc7->peer_uin); break; case GG_ERROR_DCC7_FILE: debugLogA("dccmainthread(): Client: %d, File read/write error.", local_dcc7->peer_uin); break; case GG_ERROR_DCC7_EOF: debugLogA("dccmainthread(): Client: %d, End of file/connection error.", local_dcc7->peer_uin); break; case GG_ERROR_DCC7_REFUSED: debugLogA("dccmainthread(): Client: %d, Connection refused error.", local_dcc7->peer_uin); break; case GG_ERROR_DCC7_RELAY: debugLogA("dccmainthread(): Client: %d, Relay connection error.", local_dcc7->peer_uin); break; default: debugLogA("dccmainthread(): Client: %d, Unknown error.", local_dcc7->peer_uin); } // Remove from watches list_remove(&watches, local_dcc7, 0); // Close file & fail if (local_dcc7->file_fd != -1) { _close(local_dcc7->file_fd); local_dcc7->file_fd = -1; } if (local_dcc7->contact) { gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 12, "ft_mutex", 1); ProtoBroadcastAck((UINT_PTR)local_dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, local_dcc7, 0); gg_EnterCriticalSection(&ft_mutex, "dccmainthread", 37, "ft_mutex", 1); } // Free dcc gg_dcc7_free(local_dcc7); break; } // Free event gg_free_event(e); break; } } gg_LeaveCriticalSection(&ft_mutex, "dccmainthread", 37, 1, "ft_mutex", 1); } // Close all dcc client sockets for (l = watches; l; l = l->next) { struct gg_common *c = (gg_common*)l->data; if (!c) continue; if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) { struct gg_dcc7 *local_dcc7 = (gg_dcc7*)l->data; gg_dcc7_free(local_dcc7); } else { struct gg_dcc *local_dcc = (gg_dcc*)l->data; gg_dcc_socket_free(local_dcc); // Check if it's main socket if (local_dcc == dcc) dcc = NULL; } } // Close all waiting for aknowledgle transfers for (l = transfers; l; l = l->next) { struct gg_common *c = (gg_common*)l->data; if (!c) continue; if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) { struct gg_dcc7 *local_dcc7 = (gg_dcc7*)l->data; gg_dcc7_free(local_dcc7); } else { struct gg_dcc *local_dcc = (gg_dcc*)l->data; gg_dcc_socket_free(local_dcc); } } // Close all waiting dcc requests for (l = requests; l; l = l->next) { struct gg_dcc *local_dcc = (gg_dcc*)l->data; if (local_dcc) gg_free_dcc(local_dcc); } list_destroy(watches, 0); list_destroy(transfers, 0); list_destroy(requests, 0); gg_dcc_port = 0; gg_dcc_ip = 0; debugLogA("dccmainthread(): end. DCC Server Thread Ending"); }
/** * \internal Obsługuje pakiet nowego połączenia bezpośredniego. * * \param sess Struktura sesji * \param e Struktura zdarzenia * \param payload Treść pakietu * \param len Długość pakietu * * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) { struct gg_dcc7_new *p = payload; struct gg_dcc7 *dcc; gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len); switch (gg_fix32(p->type)) { case GG_DCC7_TYPE_FILE: if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n"); return -1; } memset(dcc, 0, sizeof(struct gg_dcc7)); dcc->type = GG_SESSION_DCC7_GET; dcc->dcc_type = GG_DCC7_TYPE_FILE; dcc->fd = -1; dcc->file_fd = -1; dcc->uin = sess->uin; dcc->peer_uin = gg_fix32(p->uin_from); dcc->cid = p->id; dcc->sess = sess; if (gg_dcc7_session_add(sess, dcc) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); gg_dcc7_free(dcc); return -1; } dcc->size = gg_fix32(p->size); strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1); dcc->filename[GG_DCC7_FILENAME_LEN] = 0; memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN); e->type = GG_EVENT_DCC7_NEW; e->event.dcc7_new = dcc; break; case GG_DCC7_TYPE_VOICE: if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n"); return -1; } memset(dcc, 0, sizeof(struct gg_dcc7)); dcc->type = GG_SESSION_DCC7_VOICE; dcc->dcc_type = GG_DCC7_TYPE_VOICE; dcc->fd = -1; dcc->file_fd = -1; dcc->uin = sess->uin; dcc->peer_uin = gg_fix32(p->uin_from); dcc->cid = p->id; dcc->sess = sess; if (gg_dcc7_session_add(sess, dcc) == -1) { gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); gg_dcc7_free(dcc); return -1; } e->type = GG_EVENT_DCC7_NEW; e->event.dcc7_new = dcc; break; default: gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from)); break; } return 0; }
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 fds[2] = { -1, -1 }; if (argc != 2 || atoi(argv[1]) >= TEST_MODE_LAST) { fprintf(stderr, "usage: %s <mode>\n" "\n" "mode: 0 - send file\n" " 1 - send file, simulate NAT\n" " 2 - receive file\n" " 3 - receive file, simulate NAT\n" " 4 - receive file, resume at the end\n" "\n", argv[0]); exit(1); } test_mode = atoi(argv[1]); if (config_read() == -1 || config_peer == 0) { perror("config"); exit(1); } #ifdef _WIN32 gg_win32_init_network(); gg_win32_hook(connect, my_connect, &connect_hook); #else signal(SIGPIPE, SIG_IGN); #endif gg_debug_file = stdout; gg_debug_level = ~0; if (!config_file && socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) == -1) { perror("pipe"); exit(1); } memset(&glp, 0, sizeof(glp)); glp.uin = config_uin; glp.password = config_password; glp.async = 1; glp.client_addr = config_ip; glp.client_port = config_port; glp.protocol_version = GG_PROTOCOL_VERSION_100; if (config_dir && (test_mode == TEST_MODE_RECEIVE || test_mode == TEST_MODE_RECEIVE_NAT || test_mode == TEST_MODE_RECEIVE_RESUME)) { if (chdir(config_dir) == -1) { perror("chdir"); exit(1); } } 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 (fds[1] != -1) { if (fds[1] > maxfd) maxfd = fds[1]; FD_SET(fds[1], &wds); } 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); } if (gd && 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); if (test_mode == TEST_MODE_RECEIVE_NAT) gs->client_addr = INADDR_NONE; ping = time(NULL); break; case GG_EVENT_CONN_FAILED: debug("Connection failed\n"); exit(1); 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_USER_DATA: debug("User data\n"); break; 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 (uin == config_peer && (GG_S_A(status) || GG_S_B(status)) && (test_mode == TEST_MODE_SEND || test_mode == TEST_MODE_SEND_NAT)) { debug("Sending file...\n"); if (config_file) { gd = gg_dcc7_send_file(gs, config_peer, config_file, NULL, NULL); } else { gd = gg_dcc7_send_file_fd(gs, config_peer, fds[0], config_size, "test.bin", "DummySHA1HashOfAAAAA"); } if (!gd) { perror("gg_dcc7_send_file"); exit(1); } } break; case GG_EVENT_DCC7_NEW: debug("Incoming direct connection\n"); if (test_mode == TEST_MODE_RECEIVE || test_mode == TEST_MODE_RECEIVE_NAT || test_mode == TEST_MODE_RECEIVE_RESUME) { gd = ge->event.dcc7_new; if (config_dir) { gd->file_fd = open((char*) gd->filename, O_WRONLY | O_CREAT, 0600); #if 0 lseek(gd->file_fd, gd->size, SEEK_SET); #endif } else gd->file_fd = open("/dev/null", O_WRONLY); if (gd->file_fd == -1) { perror("open"); exit(1); } if (test_mode != TEST_MODE_RECEIVE_RESUME) gg_dcc7_accept(gd, 0); else gg_dcc7_accept(gd, gd->size); } 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); case GG_EVENT_DCC7_PENDING: debug("Pending\n"); break; case GG_EVENT_NONE: break; 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_NONE: break; default: debug("Unsupported event %d\n", ge->type); break; } gg_event_free(ge); } if (fds[1] != -1 && FD_ISSET(fds[1], &wds)) { char buf[4096]; memset(buf, 'A', sizeof(buf)); if (write(fds[1], buf, sizeof(buf)) < 1) { perror("write"); exit(1); } } } return 0; }