////////////////////////////////////////////////////////////////////////// // Miranda lan functions #include "stdafx.h" #define MCODE_SND_STATUS 1 #define MCODE_SND_NAME 2 #define MCODE_REQ_STATUS 3 #define MCODE_SND_MESSAGE 4 #define MCODE_ACK_MESSAGE 5 #define MCODE_SND_VERSION 6 #define MCODE_REQ_AWAYMSG 7 #define MCODE_SND_AWAYMSG 8 #define MCODE_SND_URL 9 #define MCODE_ACK_URL 10 #define FCODE_SND_ACCEPT 1 #define FCODE_SND_FILEREQ 2 #define FCODE_SND_FILESKIP 3 #define FCODE_SND_NEXTFILE 4 #define FCODE_SND_FILEDATA 5 enum enuLEXT { LEXT_SENDMESSAGE, LEXT_SEARCH, LEXT_GETAWAYMSG, LEXT_SENDURL, }; CMLan::CMLan() { m_RequiredIp = 0; m_UseHostName = true; m_mirStatus = ID_STATUS_OFFLINE; m_pRootContact = 0; m_pRootContact = NULL; m_hCheckThread = NULL; m_handleId = 1; m_amesAway = NULL; m_amesNa = NULL; m_amesOccupied = NULL; m_amesDnd = NULL; m_amesFfc = NULL; m_pFileConnectionList = NULL; LoadSettings(); InitializeCriticalSection(&m_csAccessClass); InitializeCriticalSection(&m_csReceiveThreadLock); InitializeCriticalSection(&m_csAccessAwayMes); InitializeCriticalSection(&m_csFileConnectionList); SetAllOffline(); //m_hookIcqMsgReq = CreateHookableEvent(ME_ICQ_STATUSMSGREQ); } CMLan::~CMLan() { m_mirStatus = ID_STATUS_OFFLINE; StopChecking(); DeleteCache(); StopListen(); Shutdown(); DeleteCriticalSection(&m_csFileConnectionList); DeleteCriticalSection(&m_csAccessAwayMes); DeleteCriticalSection(&m_csReceiveThreadLock); DeleteCriticalSection(&m_csAccessClass); delete[] m_amesAway; delete[] m_amesNa; delete[] m_amesOccupied; delete[] m_amesDnd; delete[] m_amesFfc; } void CMLan::DeleteCache() { TContact* pCont = m_pRootContact; m_pRootContact = NULL; while (pCont) { delete[] pCont->m_nick; TContact* pPrev = pCont->m_prev; delete pCont; pCont = pPrev; } } int CMLan::GetMirandaStatus() { if (GetMode()!=LM_LISTEN) return ID_STATUS_OFFLINE; return m_mirStatus; } void CMLan::SetMirandaStatus(u_int status) { if (status==ID_STATUS_INVISIBLE) { ProtoBroadcastAck(PROTONAME,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)m_mirStatus, m_mirStatus); return; } u_int old_status = m_mirStatus; m_mirStatus = status; if (old_status==ID_STATUS_OFFLINE && m_mirStatus!=ID_STATUS_OFFLINE) { StartChecking(); } else if (old_status!=ID_STATUS_OFFLINE && m_mirStatus==ID_STATUS_OFFLINE) { StopChecking(); } else if (m_mirStatus!=ID_STATUS_OFFLINE && m_mirStatus!=old_status) { RequestStatus(false); } ProtoBroadcastAck(PROTONAME,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS,(HANDLE)old_status,m_mirStatus); } void CMLan::SetAllOffline() { for (MCONTACT hContact = db_find_first(PROTONAME); hContact; hContact = db_find_next(hContact, PROTONAME)) { db_set_w(hContact, PROTONAME, "Status", ID_STATUS_OFFLINE); db_unset(hContact, PROTONAME, "IP"); } DeleteCache(); } void CMLan::StartChecking() { if (m_hCheckThread) return; TContact* cont = m_pRootContact; while (cont) { cont->m_time = MLAN_CHECK + MLAN_TIMEOUT; cont = cont->m_prev; } DWORD threadId; m_hCheckThread = CreateThread(NULL, 0, CheckProc, (LPVOID)this, 0, &threadId); StartListen(); RequestStatus(true); } void CMLan::StopChecking() { EnterCriticalSection(&m_csAccessClass); if (m_hCheckThread) { TerminateThread(m_hCheckThread, 0); m_hCheckThread = NULL; } LeaveCriticalSection(&m_csAccessClass); EnterCriticalSection(&m_csReceiveThreadLock); m_mirStatus = ID_STATUS_OFFLINE; RequestStatus(false); StopListen(); LeaveCriticalSection(&m_csReceiveThreadLock); TFileConnection* fc = m_pFileConnectionList; while (fc) { fc->Terminate(); fc = fc->m_pNext; } while (m_pFileConnectionList) Sleep(10); SetAllOffline(); } DWORD WINAPI CMLan::CheckProc(LPVOID lpParameter) { CMLan* lan = (CMLan*)lpParameter; lan->Check(); return 0; } void CMLan::Check() { while(1) { Sleep(MLAN_SLEEP); EnterCriticalSection(&m_csAccessClass); TContact* cont = m_pRootContact; while (cont) { if (cont->m_status != ID_STATUS_OFFLINE) { if (cont->m_time) cont->m_time--; if (cont->m_time==MLAN_TIMEOUT) RequestStatus(true, cont->m_addr.S_un.S_addr); if (!cont->m_time) { cont->m_status = ID_STATUS_OFFLINE; MCONTACT hContact = FindContact(cont->m_addr, cont->m_nick, false, false, false); if (hContact) { db_set_w(hContact,PROTONAME,"Status",ID_STATUS_OFFLINE); } } } cont = cont->m_prev; } LeaveCriticalSection(&m_csAccessClass); } } void CMLan::RequestStatus(bool answer, u_long addr) { TPacket pak; ZeroMemory(&pak, sizeof(pak)); pak.flReqStatus = answer; pak.strName = m_name; SendPacketExt(pak, addr); } void CMLan::SendPacketExt(TPacket& pak, u_long addr) { int pakLen; u_char* buf = CreatePacket(pak, &pakLen); in_addr _addr; _addr.S_un.S_addr = addr; SendPacket(_addr, (u_char*)buf, pakLen); delete[] buf; } MCONTACT CMLan::FindContact(in_addr addr, const char* nick, bool add_to_list, bool make_permanent, bool make_visible, u_int status) { for (MCONTACT res = db_find_first(PROTONAME); res; res = db_find_next(res, PROTONAME)) { u_long caddr = db_get_dw(res, PROTONAME, "ipaddr", -1); if (caddr==addr.S_un.S_addr) { if (make_permanent) db_unset(res,"CList","NotOnList"); if (make_visible) db_unset(res,"CList","Hidden"); return res; } } if (add_to_list) { MCONTACT res=(MCONTACT)CallService(MS_DB_CONTACT_ADD,0,0); CallService(MS_PROTO_ADDTOCONTACT,(WPARAM)res,(LPARAM)PROTONAME); db_set_dw(res,PROTONAME, "ipaddr", addr.S_un.S_addr); db_set_s(res,PROTONAME, "Nick", nick); if (!make_permanent) db_set_b(res,"CList","NotOnList",1); if (!make_visible) db_set_b(res,"CList","Hidden",1); db_set_w(res,PROTONAME, "Status", status); return res; } return NULL; } void CMLan::OnRecvPacket(u_char* mes, int len, in_addr from) { EnterCriticalSection(&m_csReceiveThreadLock); if (len) { TPacket pak; ParsePacket(pak, mes, len); if (pak.idVersion!=0) { TContact* cont = m_pRootContact; while (cont) { if (cont->m_addr.S_un.S_addr == from.S_un.S_addr) break; cont = cont->m_prev; } if (pak.idStatus) { EnterCriticalSection(&m_csAccessClass); if (!cont) { if (!pak.strName) pak.strName = "Unknown"; cont = new TContact; cont->m_addr = from; cont->m_prev = m_pRootContact; cont->m_status = ID_STATUS_OFFLINE; int nlen = (int)strlen(pak.strName); cont->m_nick = new char[nlen+1]; CopyMemory(cont->m_nick, pak.strName, nlen+1); m_pRootContact = cont; } else { if (pak.strName && strcmp(pak.strName, cont->m_nick)!=0) { delete[] cont->m_nick; int nlen = (int)strlen(pak.strName); cont->m_nick = new char[nlen+1]; CopyMemory(cont->m_nick, pak.strName, nlen+1); } } cont->m_time = MLAN_CHECK + MLAN_TIMEOUT; cont->m_ver = pak.idVersion; u_int old_status = cont->m_status; cont->m_status = pak.idStatus; MCONTACT hContact = FindContact(cont->m_addr, cont->m_nick, false, false, false); if (hContact) { db_set_w(hContact,PROTONAME, "Status", cont->m_status); if (db_get_dw(hContact,PROTONAME, "RemoteVersion", 0)!=cont->m_ver) db_set_dw(hContact,PROTONAME, "RemoteVersion", cont->m_ver); if (old_status == ID_STATUS_OFFLINE) { u_int rip = cont->m_addr.S_un.S_addr; int tip = (rip<<24)|((rip&0xff00)<<8)|((rip&0xff0000)>>8)|(rip>>24); db_set_dw(hContact, PROTONAME, "IP", tip); // HOSTENT* host = gethostbyaddr((const char*)&rip, sizeof(rip), AF_INET); // if (host) // db_set_s(hContact, PROTONAME, "UID", host->h_name); } } LeaveCriticalSection(&m_csAccessClass); } if (pak.flReqStatus) RequestStatus(false, from.S_un.S_addr); if (pak.strMessage) { if (!cont) RequestStatus(true, from.S_un.S_addr); else { PROTORECVEVENT pre = { 0 }; pre.timestamp = get_time(); pre.szMessage = pak.strMessage; ProtoChainRecv( FindContact(cont->m_addr, cont->m_nick, true, false, false, cont->m_status), pak.flIsUrl ? PSR_URL : PSR_MESSAGE, 0, (LPARAM)&pre ); TPacket npak; ZeroMemory(&npak, sizeof(npak)); npak.idAckMessage = pak.idMessage; npak.flIsUrl = pak.flIsUrl; SendPacketExt(npak, from.S_un.S_addr); } } if (pak.idAckMessage && cont) { MCONTACT hContact = FindContact(cont->m_addr, cont->m_nick, false, false, false); if (hContact) ProtoBroadcastAck(PROTONAME, hContact, pak.flIsUrl?ACKTYPE_URL:ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)pak.idAckMessage, 0); } if (pak.strAwayMessage && cont) { PROTORECVEVENT pre = { 0 }; pre.timestamp = get_time(); pre.szMessage = pak.strAwayMessage; pre.lParam = pak.idAckAwayMessage; ProtoChainRecv( FindContact(cont->m_addr, cont->m_nick, true, false, false, cont->m_status), PSR_AWAYMSG, 0, (LPARAM)&pre); } if (pak.idReqAwayMessage && cont) { MCONTACT hContact = FindContact(cont->m_addr, cont->m_nick, true, false, false); // Removed - it causes that whoisreadingawaymessage plugin was not working // if (hContact) // { // int IcqStatus = 0; // switch (m_mirStatus) // { // case ID_STATUS_AWAY: IcqStatus = ICQ_MSGTYPE_GETAWAYMSG; break; // case ID_STATUS_NA: IcqStatus = ICQ_MSGTYPE_GETNAMSG; break; // case ID_STATUS_OCCUPIED: IcqStatus = ICQ_MSGTYPE_GETOCCUMSG; break; // case ID_STATUS_DND: IcqStatus = ICQ_MSGTYPE_GETDNDMSG; break; // case ID_STATUS_FREECHAT: IcqStatus = ICQ_MSGTYPE_GETFFCMSG; break; // } // // HACK: this is a real hack // db_set_dw(hContact, "ICQ", "UIN", 1/*0xffffffff*/); // NotifyEventHooks(m_hookIcqMsgReq, IcqStatus, 1/*0xffffffff*/); // db_unset(hContact, "ICQ", "UIN"); // } EnterCriticalSection(&m_csAccessAwayMes); char* mesAway = NULL; switch (m_mirStatus) { case ID_STATUS_AWAY: mesAway = m_amesAway; break; case ID_STATUS_NA: mesAway = m_amesNa; break; case ID_STATUS_OCCUPIED: mesAway = m_amesOccupied; break; case ID_STATUS_DND: mesAway = m_amesDnd; break; case ID_STATUS_FREECHAT: mesAway = m_amesFfc; break; } if (mesAway) { TPacket npak; ZeroMemory(&npak, sizeof(npak)); npak.idAckAwayMessage = pak.idReqAwayMessage; npak.strAwayMessage = mesAway; SendPacketExt(npak, cont->m_addr.S_un.S_addr); } LeaveCriticalSection(&m_csAccessAwayMes); } } } LeaveCriticalSection(&m_csReceiveThreadLock); } void CMLan::RecvMessageUrl(CCSDATA* ccs) { DBEVENTINFO dbei; PROTORECVEVENT *pre=(PROTORECVEVENT*)ccs->lParam; ZeroMemory(&dbei,sizeof(dbei)); if (!lstrcmpA(ccs->szProtoService, PSR_MESSAGE)) dbei.eventType = EVENTTYPE_MESSAGE; else dbei.eventType = EVENTTYPE_URL; dbei.cbSize = sizeof(dbei); dbei.szModule = PROTONAME; dbei.timestamp = pre->timestamp; dbei.flags = pre->flags&PREF_CREATEREAD?DBEF_READ:0; dbei.cbBlob = lstrlen(pre->szMessage)+1; if (!lstrcmpA(ccs->szProtoService, PSR_URL)) { dbei.cbBlob += 2+lstrlen(pre->szMessage+dbei.cbBlob+1); } dbei.pBlob = (PBYTE)pre->szMessage; db_unset(ccs->hContact,"CList","Hidden"); db_event_add(ccs->hContact, &dbei); } int CMLan::AddToContactList(u_int flags, EMPSEARCHRESULT* psr) { if (psr->hdr.cbSize!=sizeof(EMPSEARCHRESULT)) return (int)(HANDLE)NULL; in_addr addr; addr.S_un.S_addr = psr->ipaddr; bool TempAdd = flags&PALF_TEMPORARY; MCONTACT contact = FindContact(addr, psr->hdr.nick, true, !TempAdd, !TempAdd, psr->stat); if (contact != NULL) { db_set_w(contact,PROTONAME,"Status", psr->stat ); db_set_w(contact,PROTONAME,"RemoteVersion", psr->ver ); } return (int)contact; } int CMLan::SendMessageUrl(CCSDATA* ccs, bool isUrl) { DWORD th_id; int cid = GetRandomProcId(); int len; if (isUrl) { len = lstrlen((char*)ccs->lParam); ((char*)ccs->lParam)[len] = 1; } TDataHolder* hold = new TDataHolder(ccs, cid, isUrl?LEXT_SENDURL:LEXT_SENDMESSAGE, this); if (isUrl) { ((char*)ccs->lParam)[len] = 0; hold->msg[len] = 0; } CloseHandle(CreateThread(NULL,0,LaunchExt,(LPVOID)hold ,0,&th_id)); return cid; } int CMLan::Search(const char* name) { DWORD th_id; int cid = GetRandomProcId(); CloseHandle(CreateThread(NULL,0,LaunchExt,(LPVOID)new TDataHolder(name, cid, LEXT_SEARCH, this),0,&th_id)); return cid; } int CMLan::GetAwayMsg(CCSDATA* ccs) { DWORD th_id; int cid = GetRandomProcId(); CloseHandle(CreateThread(NULL,0,LaunchExt,(LPVOID)new TDataHolder(ccs, cid, LEXT_GETAWAYMSG, this),0,&th_id)); return cid; } int CMLan::RecvAwayMsg(CCSDATA* ccs) { PROTORECVEVENT *pre=(PROTORECVEVENT*)ccs->lParam; ProtoBroadcastAck(PROTONAME, ccs->hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)pre->lParam,(LPARAM)pre->szMessage); return 0; } DWORD WINAPI CMLan::LaunchExt(LPVOID lpParameter) { TDataHolder* hold = (TDataHolder*)lpParameter; switch (hold->op) { case LEXT_SENDMESSAGE: case LEXT_SENDURL: hold->lan->SendMessageExt(hold); break; case LEXT_SEARCH: hold->lan->SearchExt(hold); break; case LEXT_GETAWAYMSG: hold->lan->GetAwayMsgExt(hold); break; } return 0; } void CMLan::SearchExt(TDataHolder* hold) { // TODO: Normal search must be added Sleep(0); EMPSEARCHRESULT psr; memset(&psr,0,sizeof(psr)); psr.hdr.cbSize=sizeof(psr); TContact* cont = m_pRootContact; while (cont) { if (strcmp(hold->msg, cont->m_nick)==0 || strcmp(hold->msg, "*")==0) { char buf[MAX_HOSTNAME_LEN]; lstrcpy(buf, cont->m_nick); int len = lstrlen(buf); buf[len] = '@'; lstrcpy(buf+len+1, inet_ntoa(cont->m_addr)); psr.hdr.nick = cont->m_nick; psr.hdr.firstName=""; psr.hdr.lastName=""; psr.hdr.email=buf; psr.ipaddr = cont->m_addr.S_un.S_addr; psr.stat = cont->m_status; psr.ver = cont->m_ver; ProtoBroadcastAck(PROTONAME, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)hold->id, (LPARAM)&psr); } cont = cont->m_prev; } ProtoBroadcastAck(PROTONAME, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)hold->id, 0); delete hold; } void CMLan::SendMessageExt(TDataHolder* hold) { Sleep(0); if (db_get_w((MCONTACT)hold->hContact, PROTONAME, "Status", ID_STATUS_OFFLINE)==ID_STATUS_OFFLINE) { Sleep(20); ProtoBroadcastAck(PROTONAME, hold->hContact, (hold->op==LEXT_SENDURL)?ACKTYPE_URL:ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)hold->id, 0); } else { TPacket pak; ZeroMemory(&pak, sizeof(pak)); u_long addr = db_get_dw((MCONTACT)hold->hContact, PROTONAME, "ipaddr", 0); pak.strMessage = hold->msg; pak.idMessage = hold->id; if (hold->op==LEXT_SENDURL) pak.flIsUrl = true; SendPacketExt(pak, addr); } delete hold; } void CMLan::GetAwayMsgExt(TDataHolder* hold) { // TODO: check all other params (offline user, offline protocol) Sleep(0); TPacket pak; ZeroMemory(&pak, sizeof(pak)); pak.idReqAwayMessage = hold->id; u_long addr = db_get_dw((MCONTACT)hold->hContact, PROTONAME, "ipaddr", 0); SendPacketExt(pak, addr); ProtoBroadcastAck(PROTONAME, hold->hContact, ACKTYPE_AWAYMSG, ACKRESULT_SENTREQUEST, (HANDLE)hold->id, 0); delete hold; } int CMLan::SetAwayMsg(u_int status, char* msg) { char** ppMsg; switch (status) { case ID_STATUS_AWAY: ppMsg = &m_amesAway; break; case ID_STATUS_NA: ppMsg = &m_amesNa; break; case ID_STATUS_OCCUPIED: ppMsg = &m_amesOccupied; break; case ID_STATUS_DND: ppMsg = &m_amesDnd; break; case ID_STATUS_FREECHAT: ppMsg = &m_amesFfc; break; default: return 1; } EnterCriticalSection(&m_csAccessAwayMes); delete[] *ppMsg; if (msg) *ppMsg = _strdup(msg); else *ppMsg = NULL; LeaveCriticalSection(&m_csAccessAwayMes); return 0; } ////////////////////////////////////////////////////////////////////////// // Packets u_char* CMLan::CreatePacket(TPacket& pak, int* pBufLen) { int len = 1; if (pak.idVersion != -1) pak.idVersion = __FILEVERSION_DWORD; else pak.idVersion = 0; if (pak.idStatus != -1) pak.idStatus = m_mirStatus; else pak.idStatus = -1; // Searching for packet len if (pak.idVersion) len += 1+1+4; if (pak.idStatus) len += 1+1+2; int nameLen; if (pak.strName) { nameLen = lstrlen(pak.strName); len += 1+1+nameLen+1; } if (pak.flReqStatus) len += 1+1; int mesLen = 0; if (pak.strMessage) { mesLen = lstrlen(pak.strMessage); if (pak.flIsUrl) mesLen += 1+lstrlen(pak.strMessage+mesLen+1); len += 3+1+4+mesLen+1; } if (pak.idAckMessage) len += 1+1+4; if (pak.idReqAwayMessage) len += 1+1+4; int awayLen = 0; if (pak.strAwayMessage) { awayLen = lstrlen(pak.strAwayMessage); len += 3+1+4+awayLen+1; } // Creating packet u_char* buf = new u_char[len]; u_char* pb = buf; if (pak.idVersion) { *pb++ = 1+4; *pb++ = MCODE_SND_VERSION; *((u_int*)pb) = pak.idVersion; pb += sizeof(u_int); } if (pak.idStatus) { *pb++ = 3; *pb++ = MCODE_SND_STATUS; *((u_short*)pb) = pak.idStatus; pb += sizeof(u_short); } if (pak.strName) { *pb++ = 1+nameLen+1; *pb++ = MCODE_SND_NAME; CopyMemory(pb, pak.strName, nameLen); pb += nameLen; *pb++ = 0; } if (pak.flReqStatus) { *pb++ = 2; *pb++ = MCODE_REQ_STATUS; } if (pak.strMessage) { *pb++ = 255; *((u_short*)pb) = 1+4+mesLen+1; pb += sizeof(u_short); if (pak.flIsUrl) *pb++ = MCODE_SND_URL; else *pb++ = MCODE_SND_MESSAGE; *((u_int*)pb) = pak.idMessage; pb += sizeof(u_int); if (mesLen) CopyMemory(pb, pak.strMessage, mesLen); pb += mesLen; *pb++ = 0; } if (pak.idAckMessage) { *pb++ = 1+4; if (pak.flIsUrl) *pb++ = MCODE_ACK_URL; else *pb++ = MCODE_ACK_MESSAGE; *((u_int*)pb) = pak.idAckMessage; pb += sizeof(u_int); } if (pak.idReqAwayMessage) { *pb++ = 1+4; *pb++ = MCODE_REQ_AWAYMSG; *((u_int*)pb) = pak.idReqAwayMessage; pb += sizeof(u_int); } if (pak.strAwayMessage) { *pb++ = 255; *((u_short*)pb) = 1+4+awayLen+1; pb += sizeof(u_short); *pb++ = MCODE_SND_AWAYMSG; *((u_int*)pb) = pak.idAckAwayMessage; pb += sizeof(u_int); if (awayLen) CopyMemory(pb, pak.strAwayMessage, awayLen); pb += awayLen; *pb++ = 0; } *pb++ = 0; if (pBufLen) *pBufLen = len; return buf; } void CMLan::ParsePacket(TPacket& pak, u_char* buf, int len) { ZeroMemory(&pak, sizeof(pak)); u_char* buf_end = buf+len; while (*buf && bufFileRemoveFromList(this); } delete[] m_szDescription; if (m_szFiles) { char** cp = m_szFiles; while (*cp) { delete[] *cp; cp++; } delete[] m_szFiles; } delete[] m_buf; delete[] m_szDir; delete[] m_szRenamedFile; DeleteCriticalSection(&m_csAccess); } int CMLan::TFileConnection::Recv(bool halt) { // It is supposed that we're having not less then 2 bytes buffer size :) EMLOG("Checking for data"); while (1) { u_long len; if (ioctlsocket(m_socket, FIONREAD, &len)!=0) { EMLOGERR(); return FCS_TERMINATE; } if (len>=3) break; if (!halt) { EMLOG("No data - halting Recv (only " << len << " bytes)"); m_recSize = -1; delete[] m_buf; m_buf = NULL; return FCS_OK; } Sleep(10); if (m_state==FCS_TERMINATE) { EMLOG("Terminate requested, exiting recv"); return FCS_TERMINATE; } } u_short size; int res; EMLOG("Receiving packet size"); res = recv(m_socket, (char*)&size, 3, 0); if (res==SOCKET_ERROR) { EMLOGERR(); return FCS_TERMINATE; } if (size==0) { EMLOG("Connection was gracefully closed - size is 0"); delete m_buf; m_buf = NULL; m_recSize = 0; return FCS_OK; } Lock(); delete[] m_buf; m_buf = new u_char[size]; m_recSize = size; Unlock(); EMLOG("Waiting for the whole packet (" << size << " bytes)"); int csize = 0; while (csize!=size) { while(1) { u_long len; if (ioctlsocket(m_socket, FIONREAD, &len) != 0) { EMLOGERR(); return FCS_TERMINATE; } if (len >= min(size,FILE_MIN_BLOCK)) break; Sleep(10); if (m_state == FCS_TERMINATE) { EMLOG("Terminate requested, exiting recv"); return FCS_TERMINATE; } } EMLOG("Getting data (approx " << size << " bytes)"); Lock(); res = recv(m_socket, (char*)m_buf+csize, size-csize, 0); Unlock(); EMLOGERR(); EMLOGIF("Connection was gracefully closed", res==0); if (res==0 || res==SOCKET_ERROR) return FCS_TERMINATE; EMLOG("Received " << res << " bytes"); csize += res; } EMLOG("Data recv OK"); return FCS_OK; } int CMLan::TFileConnection::SendRaw(u_char* buf, int size) { while (size>0) { if (m_state==FCS_TERMINATE) { EMLOG("Terminate requested, exiting sendraw"); return FCS_TERMINATE; } int err = send(m_socket, (char*)buf, size, 0); if (err==SOCKET_ERROR) { EMLOGERR(); return FCS_TERMINATE; } size -= err; buf += err; EMLOGIF("Send " << err << " bytes", size==0); if (size>0) { EMLOG("Partial send (only " << err << " bytes"); Sleep(10); } } return FCS_OK; } int CMLan::TFileConnection::Send(u_char* buf, int size) { if (m_state==FCS_TERMINATE) { EMLOG("Terminate requested, exiting send"); return FCS_TERMINATE; } EMLOG("Sending 3 bytes of packet size (" << size << ")"); if ( SendRaw((u_char*)&size, 3) != FCS_OK ) return FCS_TERMINATE; if ( SendRaw(buf, size) != FCS_OK ) return FCS_TERMINATE; return FCS_OK; } void CMLan::FileAddToList(TFileConnection* conn) { EnterCriticalSection(&m_csFileConnectionList); conn->Lock(); conn->m_pNext = m_pFileConnectionList; conn->m_pPrev = NULL; if (m_pFileConnectionList) m_pFileConnectionList->m_pPrev = conn; m_pFileConnectionList = conn; conn->m_pLan = this; conn->Unlock(); LeaveCriticalSection(&m_csFileConnectionList); } void CMLan::FileRemoveFromList(TFileConnection* conn) { EnterCriticalSection(&m_csFileConnectionList); conn->Lock(); if (conn->m_pPrev) conn->m_pPrev->m_pNext = conn->m_pNext; else m_pFileConnectionList = conn->m_pNext; if (conn->m_pNext) conn->m_pNext->m_pPrev = conn->m_pPrev; conn->m_pLan = NULL; conn->m_pPrev = NULL; conn->m_pNext = NULL; conn->Unlock(); LeaveCriticalSection(&m_csFileConnectionList); } void CMLan::RecvFile(CCSDATA* ccs) { PROTORECVEVENT *pre = (PROTORECVEVENT *)ccs->lParam; char *szDesc, *szFile; db_unset(ccs->hContact, "CList", "Hidden"); szFile = pre->szMessage + sizeof(DWORD); szDesc = szFile + strlen(szFile) + 1; DBEVENTINFO dbei = { sizeof(dbei) }; dbei.szModule = PROTONAME; dbei.timestamp = pre->timestamp; dbei.flags = pre->flags & (PREF_CREATEREAD ? DBEF_READ : 0); dbei.eventType = EVENTTYPE_FILE; dbei.cbBlob = DWORD(sizeof(DWORD) + strlen(szFile) + strlen(szDesc) + 2); dbei.pBlob = (PBYTE)pre->szMessage; db_event_add(ccs->hContact, &dbei); } void CMLan::OnInTCPConnection(u_long addr, SOCKET in_sock) { EMLOG("Received IN TCP connection"); TContact* cont = m_pRootContact; while (cont && cont->m_addr.S_un.S_addr!=addr) cont = cont->m_prev; // There is no such user in cached list - can not identify him if (cont==NULL) return; EMLOG("Passed contact search (cont is not NULL)"); TFileConnection* conn = new TFileConnection(); conn->m_socket = in_sock; conn->m_cid = GetRandomProcId(); if (conn->Recv() || conn->m_recSize==0 || conn->m_buf[0] != FCODE_SND_FILEREQ) { EMLOG("Not passed synchro data"); EMLOGIF("Rec size is 0", conn->m_recSize==0); EMLOGIF("Wrong data in packet", conn->m_buf[0] != FCODE_SND_FILEREQ); delete conn; return; } EMLOG("File added to connectionn list"); FileAddToList(conn); PROTORECVEVENT pre; int rcTotalSize = *((int*)(conn->m_buf+1)); int rcTotalFiles = *((int*)(conn->m_buf+1+4)); pre.szMessage = new char[conn->m_recSize+rcTotalFiles]; *((int*)pre.szMessage) = conn->m_cid; char* pf_to = pre.szMessage+4; char* pf_fr = (char*)conn->m_buf+1+4+4; conn->m_szFiles = new char* [rcTotalFiles+1]; conn->m_szFiles[rcTotalFiles] = NULL; for (int i=0; im_szFiles[i] = _strdup(pf_fr); if (i) *pf_to++ = ' '; while (*pf_fr) *pf_to++ = *pf_fr++; pf_fr++; *pf_to++ = ';'; } *pf_to++ = 0; while (*pf_fr) *pf_to++ = *pf_fr++; *pf_to++ = *pf_fr++; conn->m_hContact = FindContact(cont->m_addr, cont->m_nick, true, false, false, cont->m_status); pre.flags = 0; pre.timestamp = get_time(); pre.lParam = 0; ProtoChainRecv(conn->m_hContact, PSR_FILE, 0, (LPARAM)&pre); delete[] pre.szMessage; while (!conn->m_state) Sleep(10); if (conn->m_state!=TFileConnection::FCS_ALLOW) { conn->Send(NULL, 0); delete conn; return; } conn->Lock(); conn->m_state = TFileConnection::FCS_OK; conn->Unlock(); u_char buf = FCODE_SND_ACCEPT; if (conn->Send(&buf, 1)) { ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Connection aborted"); delete conn; return; } // Getting current directory char path[MAX_PATH]; char* pathpart; GetFullPathName(conn->m_szDir, MAX_PATH, path, &pathpart); if (!SetCurrentDirectory(path)) { if (rcTotalFiles==1) conn->m_szRenamedFile = _strdup(pathpart); *pathpart = 0; if (!SetCurrentDirectory(path)) { conn->Send(NULL, 0); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't open output directory"); delete conn; return; } } //Starting from next file ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, (HANDLE)conn->m_cid, 0); PROTOFILETRANSFERSTATUS fts; fts.cbSize = sizeof(fts); fts.totalBytes = rcTotalSize; fts.totalFiles = rcTotalFiles; fts.totalProgress = 0; fts.szWorkingDir = conn->m_szDir; fts.flags = false; fts.hContact = conn->m_hContact; fts.pszFiles = conn->m_szFiles; bool err = false; for (int fileNo=0; fileNoRecv() || conn->m_recSize==0 || conn->m_buf[0] != FCODE_SND_NEXTFILE) { err = true; break; } EMLOG("Ok"); fts.szCurrentFile = fts.pszFiles[fileNo]; fts.currentFileNumber = fileNo; fts.currentFileProgress = 0; fts.currentFileSize = *((int*)(conn->m_buf+1)); fts.currentFileTime = get_time(); EMLOG("Waiting for ACCEPT"); if (!ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, (HANDLE)conn->m_cid, (LPARAM)&fts)) { conn->Lock(); conn->m_state = TFileConnection::FCS_OVERWRITE; conn->Unlock(); } else { while(!conn->m_state) Sleep(10); } EMLOG("Ok"); EMLOG("Checking if we're terminated"); if (conn->m_state==TFileConnection::FCS_TERMINATE) { err = true; break; } EMLOG("Still working"); u_char snd_buf[5]; EMLOG("Checking if we're skipping file"); if (conn->m_state==TFileConnection::FCS_SKIP) { EMLOG("Skipped"); conn->Lock(); conn->m_state = TFileConnection::FCS_OK; conn->Unlock(); snd_buf[0] = FCODE_SND_FILESKIP; if (conn->Send(snd_buf, 1)) { EMLOG("Error during sending 'skip' code'"); err = true; break; } continue; } EMLOG("Still processing"); char* filename = conn->m_szRenamedFile; if (!filename) filename = conn->m_szFiles[fileNo]; int mode_open = CREATE_ALWAYS; if (conn->m_state==TFileConnection::FCS_RESUME) mode_open = OPEN_ALWAYS; conn->Lock(); conn->m_state = TFileConnection::FCS_OK; conn->Unlock(); EMLOG("Creating file"); HANDLE hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, mode_open, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile==INVALID_HANDLE_VALUE) { EMLOG("Can't create file"); conn->Send(NULL, 0); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't create file"); delete conn; return; } EMLOG("Ok"); snd_buf[0] = FCODE_SND_ACCEPT; int fsize = GetFileSize(hFile, NULL); *((int*)(snd_buf+1)) = fsize; SetFilePointer(hFile, 0, NULL, FILE_END); fts.currentFileProgress = fsize; fts.totalProgress += fsize; EMLOG("Sending ack"); if (conn->Send(snd_buf, 5)) { EMLOG("Error sending ACK"); CloseHandle(hFile); err = true; break; } EMLOG("Ok"); EMLOG("Broadcast ack internally"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)conn->m_cid, (LPARAM)&fts); EMLOG("Ok"); int refr = 0; while (fts.currentFileProgressRecv(); if (isErr || conn->m_recSize==0 || conn->m_buf[0]!=FCODE_SND_FILEDATA) { EMLOGIF("Error conn->Recv()", isErr); EMLOGIF("Error conn->m_recSize!=0", conn->m_recSize==0); EMLOGIF("Error conn->m_buf[0]==FCODE_SND_FILEDATA", conn->m_buf[0]!=FCODE_SND_FILEDATA); EMLOG("Error"); err = true; break; } EMLOG("Received"); DWORD written; EMLOG("Writing to file"); WriteFile(hFile, conn->m_buf+1, conn->m_recSize-1, &written, NULL); EMLOG("Ok"); fts.currentFileProgress += conn->m_recSize-1; fts.totalProgress += conn->m_recSize-1; refr += conn->m_recSize-1; if (refr>=FILE_INFO_REFRESH) { EMLOG("Refreshing progress bar"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)conn->m_cid, (LPARAM)&fts); refr = 0; } } if (!err) ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)conn->m_cid, (LPARAM)&fts); EMLOG("Closing file handle"); CloseHandle(hFile); if (err) break; delete[] conn->m_szRenamedFile; conn->m_szRenamedFile = NULL; } if (err) { ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Connection aborted"); delete conn; return; } ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, (HANDLE)conn->m_cid, 0); delete conn; } void CMLan::OnOutTCPConnection(u_long addr, SOCKET out_socket, LPVOID lpParameter) { EMLOG("Sending OUT TCP connection"); TFileConnection* conn = (TFileConnection*)lpParameter; if (out_socket == INVALID_SOCKET) { EMLOG("Can't create OUT socket"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't initiate transfer"); delete conn; return; } conn->m_socket = out_socket; EMLOG("Socket is created"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_CONNECTED, (HANDLE)conn->m_cid, 0); EMLOG("Added to list"); FileAddToList(conn); u_char buf[FILE_SEND_BLOCK+1]; char name[MAX_PATH+8]; buf[0] = FCODE_SND_FILEREQ; int len = 1+4+4; int size = 0; int filecount = 0; char** pf = conn->m_szFiles; while (*pf) { // TODO: FIX IT ! EMLOG("Opening file"); HANDLE hFile = CreateFile(*pf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile==INVALID_HANDLE_VALUE) { EMLOG("Can't open file for reading"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't open one of the files"); delete conn; return; } size += GetFileSize(hFile, NULL); filecount++; CloseHandle(hFile); char* filepart; GetFullPathName(*pf, MAX_PATH, (char*)name, &filepart); delete[] *pf; *pf = _strdup(name); strcpy((char*)buf+len, filepart); len += (int)strlen(filepart)+1; pf++; } strcpy((char*)buf+len, conn->m_szDescription); len += (int)strlen(conn->m_szDescription)+1; *((int*)(buf+1)) = size; *((int*)(buf+1+4)) = filecount; GetCurrentDirectory(MAX_PATH, name); conn->m_szDir = _strdup(name); PROTOFILETRANSFERSTATUS fts; fts.cbSize = sizeof(fts); fts.totalBytes = size; fts.totalFiles = filecount; fts.totalProgress = 0; fts.szWorkingDir = conn->m_szDir; fts.flags = PFTS_SENDING; fts.hContact = conn->m_hContact; fts.pszFiles = conn->m_szFiles; EMLOG("Sending file size"); if (conn->Send(buf, len)) { EMLOG("Failed"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Connection aborted"); delete conn; return; } EMLOG("Waiting for ACK"); if (conn->Recv() || conn->m_recSize==0 || conn->m_buf[0]!=FCODE_SND_ACCEPT) { EMLOG("Failed"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)conn->m_cid, 0); delete conn; return; } bool err = false; for (int fileNo=0; fileNom_szFiles[fileNo] , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile==INVALID_HANDLE_VALUE) { EMLOG("Failed"); conn->Send(NULL, 0); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't open file"); delete conn; return; } EMLOG("Sending broadcast NEXT FILE"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, (HANDLE)conn->m_cid, 0); EMLOG("Ok"); u_char snd_buf[5]; snd_buf[0] = FCODE_SND_NEXTFILE; int fsize = GetFileSize(hFile, NULL); *((int*)(snd_buf+1)) = fsize; EMLOG("Sending file size"); if (conn->Send(snd_buf, 5)) { CloseHandle(hFile); err = true; break; } EMLOG("Ok"); EMLOG("Waiting for ACK"); if (conn->Recv() || conn->m_recSize==0 || (conn->m_buf[0]!=FCODE_SND_ACCEPT && conn->m_buf[0]!=FCODE_SND_FILESKIP)) { CloseHandle(hFile); err = true; break; } EMLOG("Ok"); if (conn->m_buf[0]!=FCODE_SND_FILESKIP) { EMLOG("File is not skipped"); int filepos = *((int*)(conn->m_buf+1)); SetFilePointer(hFile, filepos, NULL, FILE_BEGIN); fts.szCurrentFile = fts.pszFiles[fileNo]; fts.currentFileTime = get_time(); fts.currentFileNumber = fileNo; fts.currentFileProgress = filepos; fts.totalProgress += filepos; fts.currentFileSize = fsize; EMLOG("Starting data transfer"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)conn->m_cid, (LPARAM)&fts); EMLOG("Ok"); int refr = 0; fsize -= filepos; while (fsize>0) { DWORD readbytes; int tosend = FILE_SEND_BLOCK; if (tosend>fsize) tosend = fsize; EMLOG("Reading file data"); ReadFile(hFile, buf+1, tosend, &readbytes, NULL); EMLOG("Ok"); buf[0] = FCODE_SND_FILEDATA; if (readbytes!=tosend) { EMLOG("Error during reading file. File was changed"); CloseHandle(hFile); conn->Send(NULL, 0); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Can't read file"); delete conn; return; } EMLOG("Sending data buffer"); if (conn->Send(buf, tosend+1)) { //CloseHandle(hFile); err = true; break; } EMLOG("Ok"); fts.currentFileProgress += tosend; fts.totalProgress += tosend; fsize -= tosend; refr += tosend; if (refr>=FILE_INFO_REFRESH || fsize<=0) { EMLOG("Refreshing file info"); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)conn->m_cid, (LPARAM)&fts); refr = 0; EMLOG("Checking for 'abort'"); if (conn->Recv(false) || conn->m_recSize!=-1) { EMLOG("Aborted"); //CloseHandle(hFile); err = true; break; } EMLOG("Ok"); } if (conn->m_state) { EMLOG("Interrupted by user"); conn->Send(NULL, 0); //CloseHandle(hFile); err = true; break; } } } if (err) break; CloseHandle(hFile); } if (err) { EMLOG("There was error during file transfering"); conn->Send(NULL, 0); ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)conn->m_cid, (LPARAM)"Connection aborted"); delete conn; return; } ProtoBroadcastAck(PROTONAME, conn->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, (HANDLE)conn->m_cid, 0); delete conn; } int CMLan::SendFile(CCSDATA* ccs) { int cid = GetRandomProcId(); TFileConnection* conn = new TFileConnection(); conn->m_cid = cid; conn->m_hContact = ccs->hContact; conn->m_szDescription = _strdup((char*)ccs->wParam); int files = 0; char** ppszFiles = (char**)ccs->lParam; while (ppszFiles[files]) files++; conn->m_szFiles = new char* [files+1]; for (int i=0; im_szFiles[i] = _strdup(ppszFiles[i]); conn->m_szFiles[files] = NULL; u_long addr = db_get_dw(ccs->hContact, PROTONAME, "ipaddr", 0); CreateTCPConnection(addr, (LPVOID)conn); return cid; } int CMLan::FileAllow(CCSDATA* ccs) { int cid = (int)ccs->wParam; TFileConnection* conn = m_pFileConnectionList; while (conn) { if (conn->m_cid == cid) break; conn = conn->m_pNext; } if (!conn) return 0; conn->Lock(); conn->m_state = TFileConnection::FCS_ALLOW; conn->m_szDir = _strdup((char*)ccs->lParam); conn->Unlock(); return cid; } int CMLan::FileDeny(CCSDATA* ccs) { int cid = (int)ccs->wParam; TFileConnection* conn = m_pFileConnectionList; while (conn) { if (conn->m_cid == cid) break; conn = conn->m_pNext; } if (!conn) return 0; conn->Lock(); conn->m_state = TFileConnection::FCS_TERMINATE; conn->Unlock(); return 0; } int CMLan::FileCancel(CCSDATA* ccs) { int cid = (int)ccs->wParam; TFileConnection* conn = m_pFileConnectionList; while (conn) { if (conn->m_cid == cid) break; conn = conn->m_pNext; } if (!conn) return 0; conn->Lock(); conn->m_state = TFileConnection::FCS_TERMINATE; conn->Unlock(); return 0; } int CMLan::FileResume(int cid, PROTOFILERESUME* pfr) { //int cid = (int)ccs->wParam; //PROTOFILERESUME* pfr = (PROTOFILERESUME*)ccs->lParam; TFileConnection* conn = m_pFileConnectionList; while (conn) { if (conn->m_cid == cid) break; conn = conn->m_pNext; } if (!conn) return 0; conn->Lock(); switch (pfr->action) { case FILERESUME_OVERWRITE: conn->m_state = TFileConnection::FCS_OVERWRITE; break; case FILERESUME_RESUME: conn->m_state = TFileConnection::FCS_RESUME; break; case FILERESUME_RENAME: conn->m_state = TFileConnection::FCS_RENAME; delete[] conn->m_szRenamedFile; conn->m_szRenamedFile = _strdup(pfr->szFilename); break; case FILERESUME_SKIP: conn->m_state = TFileConnection::FCS_SKIP; break; } conn->Unlock(); return 0; }