#include "skype.h" #include "skype_chat.h" CSkypeProto::CSkypeProto(const char* protoName, const TCHAR* userName) : PROTO(protoName, userName), Skype(1), skypeKitPort(8963), chatRooms(5) { this->rememberPassword = false; ::InitializeCriticalSection(&this->contact_search_lock); this->SetAllContactStatus(ID_STATUS_OFFLINE); DBEVENTTYPEDESCR dbEventType = { sizeof(dbEventType) }; dbEventType.module = this->m_szModuleName; dbEventType.flags = DETF_HISTORY | DETF_MSGWINDOW; dbEventType.eventType = SKYPE_DB_EVENT_TYPE_EMOTE; dbEventType.descr = "Skype emote"; ::CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType); dbEventType.eventType = SKYPE_DB_EVENT_TYPE_CONTACTS; dbEventType.descr = "Skype contacts"; dbEventType.eventIcon = CSkypeProto::GetSkinIconHandle("sendContacts"); ::CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType); dbEventType.eventType = SKYPE_DB_EVENT_TYPE_CALL; dbEventType.descr = "Skype call"; dbEventType.eventIcon = CSkypeProto::GetSkinIconHandle("call"); ::CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType); this->InitInstanceServiceList(); } CSkypeProto::~CSkypeProto() { ::DeleteCriticalSection(&this->contact_search_lock); ::mir_free(this->login); if (this->password) { ::mir_free(this->password); this->password = NULL; } this->chatRooms.destroy(); } HANDLE __cdecl CSkypeProto::AddToList(int flags, PROTOSEARCHRESULT* psr) { CContact::Ref contact; this->GetContact((char *)mir_ptr(::mir_utf8encodeW(psr->id)), contact); return this->AddContact(contact); } HANDLE __cdecl CSkypeProto::AddToListByEvent(int flags, int iContact, HANDLE hDbEvent) { DBEVENTINFO dbei = {0}; dbei.cbSize = sizeof(dbei); /*if ((dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0)) != -1) { dbei.pBlob = (PBYTE)alloca(dbei.cbBlob); if (CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei) == 0 && !strcmp(dbei.szModule, m_szModuleName) && (dbei.eventType == EVENTTYPE_AUTHREQUEST || dbei.eventType == EVENTTYPE_CONTACTS)) { char *nick = (char*)(dbei.pBlob + sizeof(DWORD) * 2); char *firstName = nick + strlen(nick) + 1; char *lastName = firstName + strlen(firstName) + 1; char *skypeName = lastName + strlen(lastName) + 1; return AddContactBySkypeName(::mir_a2u(skypeName), ::mir_a2u(nick), 0); } }*/ return 0; } int __cdecl CSkypeProto::Authorize(HANDLE hDbEvent) { if (this->IsOnline() && hDbEvent) { HANDLE hContact = this->GetContactFromAuthEvent(hDbEvent); if (hContact == INVALID_HANDLE_VALUE) return 1; return CSkypeProto::GrantAuth((WPARAM)hContact, NULL); } return 1; } int __cdecl CSkypeProto::AuthDeny(HANDLE hDbEvent, const TCHAR* szReason) { if (this->IsOnline()) { HANDLE hContact = this->GetContactFromAuthEvent(hDbEvent); if (hContact == INVALID_HANDLE_VALUE) return 1; return CSkypeProto::RevokeAuth((WPARAM)hContact, NULL); } return 1; } int __cdecl CSkypeProto::AuthRecv(HANDLE hContact, PROTORECVEVENT* pre) { DWORD flags = 0; if (pre->flags & PREF_CREATEREAD) flags |= DBEF_READ; if (pre->flags & PREF_UTF) flags |= DBEF_UTF; this->AddDBEvent( hContact, EVENTTYPE_AUTHREQUEST, pre->timestamp, flags, pre->lParam, (PBYTE)pre->szMessage); return 0; } int __cdecl CSkypeProto::AuthRequest(HANDLE hContact, const TCHAR* szMessage) { if (this->IsOnline() && hContact) { CContact::Ref contact; SEString sid(_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID))); if (this->GetContact(sid, contact)) { contact->SetBuddyStatus(Contact::AUTHORIZED_BY_ME); contact->SendAuthRequest(::mir_utf8encodeW(szMessage)); } return 0; } return 1; } HANDLE __cdecl CSkypeProto::ChangeInfo( int iInfoType, void* pInfoData ) { return 0; } HANDLE __cdecl CSkypeProto::FileAllow( HANDLE hContact, HANDLE hTransfer, const TCHAR* szPath ) { uint oid = (uint)hTransfer; this->debugLogW(L"Incoming file transfer is accepted"); bool success; wchar_t fullPath[MAX_PATH] = {0}; SEString data; MessageRef msgRef(oid); TransferRefs transfers; msgRef->GetTransfers(transfers); for (uint i = 0; i < transfers.size(); i++) { auto transfer = transfers[i]; transfer->GetPropFilename(data); ptrW name(::mir_utf8decodeW(data)); ::mir_sntprintf(fullPath, MAX_PATH, L"%s%s", szPath, name); auto fOid = transfer->getOID(); FileTransferParam ftp = this->transferts[oid]; //PROTOFILETRANSFERSTATUS pfts = { sizeof(pfts) }; //pfts.hContact = hContact; //pfts.flags = PFTS_UNICODE | PFTS_RECEIVING; //pfts.ptszFiles = ftp.pfts.ptszFiles; //pfts.totalFiles = ftp.pfts.totalFiles; //pfts.currentFileNumber = i; //pfts.totalBytes = ftp.files[fOid].size; //pfts.totalProgress = ftp.files[fOid].transfered; //pfts.tszWorkingDir = mir_wstrdup(szPath); //pfts.currentFileNumber = 0; //pfts.tszCurrentFile = mir_wstrdup(fullPath); ////pfts.tszCurrentFile = ::mir_utf8decodeW(data); //pfts.currentFileSize = ftp.files[fOid].size; //pfts.currentFileProgress = ftp.files[fOid].transfered; //if ( !ProtoBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, (HANDLE)oid, (LPARAM)&pfts)) if ( !transfer->Accept((char *)ptrA(::mir_utf8encodeW(fullPath)), success) || !success) { this->debugLogW(L"Cannot accept file transfer"); this->transferList.remove_val(transfer); } } return hTransfer; } int __cdecl CSkypeProto::FileCancel( HANDLE hContact, HANDLE hTransfer ) { uint oid = (uint)hTransfer; MessageRef msgRef(oid); TransferRefs transfers; Transfer::STATUS transferStatus; msgRef->GetTransfers(transfers); for (uint i = 0; i < transfers.size(); i++) { auto transfer = transfers[i]; transfer->GetPropStatus(transferStatus); if (transferStatus <= Transfer::CANCELLED && this->transferList.contains(transfer)) { if ( !transfer->Cancel()) this->debugLogW(L"Incoming file transfer is cancelled"); this->transferList.remove_val(transfer); } } this->transferts.erase(this->transferts.find(oid)); return 1; } int __cdecl CSkypeProto::FileDeny( HANDLE hContact, HANDLE hTransfer, const TCHAR* szReason ) { uint oid = (uint)hTransfer; MessageRef msgRef(oid); TransferRefs transfers; Transfer::STATUS transferStatus; msgRef->GetTransfers(transfers); for (uint i = 0; i < transfers.size(); i++) { auto transfer = transfers[i]; transfer->GetPropStatus(transferStatus); if (transferStatus <= Transfer::CANCELLED && this->transferList.contains(transfer)) { if ( !transfer->Cancel()) this->debugLogW(L"Incoming file transfer is denied"); this->transferList.remove_val(transfer); } } this->transferts.erase(this->transferts.find(oid)); return 1; } int __cdecl CSkypeProto::FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename ) { if ( !this->IsOnline()) return 1; uint oid = (uint)hTransfer; //auto fOid = transfers[i]->getOID(); FileTransferParam ftp = this->transferts[oid]; MessageRef msgRef(oid); TransferRefs transfers; msgRef->GetTransfers(transfers); for (uint i = 0; i < transfers.size(); i++){} switch (*action) { case FILERESUME_SKIP: /*if (ft->p2p_appID != 0) p2p_sendStatus(ft, 603); else msnftp_sendAcceptReject (ft, false);*/ break; case FILERESUME_RESUME: //replaceStrT(ft->std.tszCurrentFile, *szFilename); break; case FILERESUME_RENAME: //replaceStrT(ft->std.tszCurrentFile, *szFilename); break; default: /*bool fcrt = ft->create() != -1; if (ft->p2p_appID != 0) { if (fcrt) p2p_sendFeedStart(ft); p2p_sendStatus(ft, fcrt ? 200 : 603); } else msnftp_sendAcceptReject (ft, fcrt);*/ //ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ft, 0); break; } return 0; } DWORD_PTR __cdecl CSkypeProto:: GetCaps(int type, HANDLE hContact) { switch(type) { case PFLAGNUM_1: return PF1_IM | PF1_FILE | PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_AUTHREQ | PF1_CHAT | PF1_SERVERCLIST | PF1_CONTACT/* | PF1_ADDSEARCHRES*/; case PFLAGNUM_2: case PFLAGNUM_3: return PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_INVISIBLE | PF2_ONTHEPHONE; case PFLAGNUM_4: return PF4_FORCEAUTH | PF4_FORCEADDED | PF4_SUPPORTTYPING | PF4_AVATARS | /*PF4_OFFLINEFILES | */PF4_IMSENDUTF | PF4_IMSENDOFFLINE | PF4_NOAUTHDENYREASON; case PFLAGNUM_5: return PF2_ONTHEPHONE; case PFLAG_UNIQUEIDTEXT: return (DWORD_PTR)::Translate("Skype name"); case PFLAG_MAXCONTACTSPERPACKET: return 1024; case PFLAG_UNIQUEIDSETTING: return (DWORD_PTR)SKYPE_SETTINGS_SID; default: return 0; } } int __cdecl CSkypeProto::GetInfo( HANDLE hContact, int infoType ) { return 0; } HANDLE __cdecl CSkypeProto::SearchBasic(const TCHAR* id) { if ( !this->IsOnline()) return 0; this->ForkThread(&CSkypeProto::SearchBySidAsync, ::mir_tstrdup(id)); return (HANDLE)SKYPE_SEARCH_BYSID; } HANDLE __cdecl CSkypeProto::SearchByEmail(const TCHAR* email) { if ( !this->IsOnline()) return 0; this->ForkThread(&CSkypeProto::SearchByEmailAsync, ::mir_tstrdup(email)); return (HANDLE)SKYPE_SEARCH_BYEMAIL; } HANDLE __cdecl CSkypeProto::SearchByName(const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName) { PROTOSEARCHRESULT *psr = new PROTOSEARCHRESULT(); psr->cbSize = sizeof(psr); psr->flags = PSR_TCHAR; psr->nick = ::mir_wstrdup(nick); psr->firstName = ::mir_wstrdup(firstName); psr->lastName = ::mir_wstrdup(lastName); this->ForkThread(&CSkypeProto::SearchByNamesAsync, psr); return (HANDLE)SKYPE_SEARCH_BYNAMES; } HWND __cdecl CSkypeProto::SearchAdvanced( HWND owner ) { return 0; } HWND __cdecl CSkypeProto::CreateExtendedSearchUI( HWND owner ){ return 0; } int __cdecl CSkypeProto::RecvContacts( HANDLE hContact, PROTORECVEVENT* pre) { this->debugLogW(L"Incoming contacts"); ::db_unset(hContact, "CList", "Hidden"); return (INT_PTR)this->AddDBEvent( hContact, EVENTTYPE_CONTACTS, pre->timestamp, DBEF_UTF | ((pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0), pre->lParam, (PBYTE)pre->szMessage); } int __cdecl CSkypeProto::RecvFile( HANDLE hContact, PROTORECVFILET* pre) { this->debugLogW(L"Incoming file transfer"); ::db_unset(hContact, "CList", "Hidden"); return ::Proto_RecvFile(hContact, pre); } int __cdecl CSkypeProto::RecvMsg(HANDLE hContact, PROTORECVEVENT* pre) { this->debugLogW(L"Incoming message"); ::db_unset(hContact, "CList", "Hidden"); ReadMessageParam *param = (ReadMessageParam*)pre->lParam; char *message = (char *)pre->szMessage; size_t msgLen = ::strlen(message) + 1; message = (char *)::mir_realloc(message, msgLen + param->guid.size()); ::memcpy((char *)&message[msgLen], param->guid.data(), param->guid.size()); return (INT_PTR)this->AddDBEvent( hContact, param->msgType == CMessage::POSTED_TEXT ? EVENTTYPE_MESSAGE : SKYPE_DB_EVENT_TYPE_EMOTE, pre->timestamp, DBEF_UTF | ((pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0), DWORD(msgLen + param->guid.size()), (PBYTE)message); } int __cdecl CSkypeProto::RecvUrl(HANDLE hContact, PROTORECVEVENT *) { return 0; } int __cdecl CSkypeProto::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { if (this->IsOnline() && hContact && hContactsList) { this->debugLogW(L"Outcoming contacts"); ConversationRef conversation; if ( !this->isChatRoom(hContact)) { SEStringList targets; targets.append((char *)_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID))); this->GetConversationByParticipants(targets, conversation); } else { this->GetConversationByIdentity((char *)_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID)), conversation); } if ( !conversation) return 0; ContactRefs contacts; for (int i = 0; i < nContacts; i++) { CContact::Ref contact; ptrW csid(::db_get_wsa(hContactsList[i], this->m_szModuleName, SKYPE_SETTINGS_SID)); this->GetContact((char *)ptrA(::mir_utf8encodeW(csid)), contact); contacts.append(contact); } time_t timestamp = time(NULL); if ( !conversation->PostContacts(contacts)) return 0; // todo: bad hack CMessage::Refs msgs; this->GetMessageListByType(Message::POSTED_CONTACTS, true, msgs, timestamp); if (msgs.size() == 0) return 0; CMessage::Ref lastMsg = msgs[msgs.size() - 1]; return lastMsg->getOID(); } return 0; } HANDLE __cdecl CSkypeProto::SendFile(HANDLE hContact, const TCHAR *szDescription, TCHAR **ppszFiles) { if (this->IsOnline() && hContact && ppszFiles) { this->debugLogW(L"Outcoming file transfer"); ConversationRef conversation; if ( !this->isChatRoom(hContact)) { SEStringList targets; targets.append((char *)_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID))); this->GetConversationByParticipants(targets, conversation); } else { this->GetConversationByIdentity((char *)_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID)), conversation); } if ( !conversation) return 0; FileTransferParam ftp; ftp.pfts.flags = PFTS_SENDING | PFTS_UNICODE; ftp.pfts.hContact = hContact; for (ftp.pfts.totalFiles = 0; ppszFiles[ftp.pfts.totalFiles]; ftp.pfts.totalFiles++); ftp.pfts.ptszFiles = new wchar_t*[ftp.pfts.totalFiles + 1]; wchar_t *wd = new wchar_t[wcslen(ppszFiles[0]) + 1]; wcscpy(wd, ppszFiles[0]); PathRemoveFileSpec(wd); ftp.pfts.tszWorkingDir = wd; SEFilenameList fileList; for (int i = 0; ppszFiles[i]; i++) { ftp.pfts.ptszFiles[i] = ::mir_wstrdup(ppszFiles[i]); fileList.append((char *)ptrA(::mir_utf8encodeW(ppszFiles[i]))); } auto error = TRANSFER_OPEN_SUCCESS; SEFilename errFile; MessageRef msgRef; if ( !conversation->PostFiles(fileList, " ", error, errFile, msgRef) || error) return 0; SEString data; TransferRefs transfers; if (msgRef->GetTransfers(transfers)) { for (uint i = 0; i < transfers.size(); i++) { transfers[i].fetch(); this->transferList.append(transfers[i]); transfers[i]->GetPropFilesize(data); Sid::uint64 size = data.toUInt64(); ftp.files.insert(std::make_pair(transfers[i]->getOID(), FileParam(size))); ftp.pfts.totalBytes += size; } } auto oid = msgRef->getOID(); this->transferts.insert(std::make_pair(oid, ftp)); return (HANDLE)oid; } return 0; } int __cdecl CSkypeProto::SendMsg(HANDLE hContact, int flags, const char *msg) { this->debugLogW(L"Outcoming message"); SEStringList targets; targets.append((char *)_T2A(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID))); CConversation::Ref conversation; this->GetConversationByParticipants(targets, conversation); if (conversation) { CMessage::Ref message; if (!conversation->PostText(msg, message)) return 0; return message->getOID(); } return 0; } int __cdecl CSkypeProto::SendUrl(HANDLE hContact, int flags, const char *url) { return 0; } int __cdecl CSkypeProto::SetApparentMode(HANDLE hContact, int mode) { return 0; } int CSkypeProto::SetStatus(int new_status) { switch (new_status) { case ID_STATUS_OCCUPIED: new_status = ID_STATUS_DND; break; case ID_STATUS_FREECHAT: new_status = ID_STATUS_ONLINE; break; case ID_STATUS_ONTHEPHONE: case ID_STATUS_OUTTOLUNCH: case ID_STATUS_NA: new_status = ID_STATUS_AWAY; break; } if (new_status == this->m_iDesiredStatus) return 0; int old_status = this->m_iStatus; this->m_iDesiredStatus = new_status; if (new_status == ID_STATUS_OFFLINE) { this->LogOut(); this->m_iStatus = this->m_iDesiredStatus = ID_STATUS_OFFLINE; this->SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); if ( !::Miranda_Terminated()) { this->SetAllContactStatus(ID_STATUS_OFFLINE); this->CloseAllChatSessions(); } return 0; } else { if (old_status == ID_STATUS_OFFLINE && !this->IsOnline()) { this->m_iStatus = ID_STATUS_CONNECTING; if ( !this->LogIn()) return 0; } else { if ( this->account->IsOnline()) { SetServerStatus(new_status); return 0; } this->SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); return 0; } } this->SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); return 0; } HANDLE __cdecl CSkypeProto::GetAwayMsg(HANDLE hContact) { return 0; } int __cdecl CSkypeProto::RecvAwayMsg(HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 0; } int __cdecl CSkypeProto::SetAwayMsg(int m_iStatus, const TCHAR *msg) { return 0; } int __cdecl CSkypeProto::UserIsTyping(HANDLE hContact, int type) { if (hContact && this->IsOnline() && this->m_iStatus != ID_STATUS_INVISIBLE) { ptrW sid(::db_get_wsa(hContact, this->m_szModuleName, SKYPE_SETTINGS_SID)); if (sid != NULL && ::wcsicmp(sid, this->login) != 0) { SEStringList targets; targets.append((char *)ptrA(::mir_utf8encodeW(sid))); CConversation::Ref conversation; this->GetConversationByParticipants(targets, conversation); if (conversation) { switch (type) { case PROTOTYPE_SELFTYPING_ON: conversation->SetMyTextStatusTo(Participant::WRITING); return 0; case PROTOTYPE_SELFTYPING_OFF: conversation->SetMyTextStatusTo(Participant::READING); //todo: mb TEXT_UNKNOWN? return 0; } } } } return 1; } int __cdecl CSkypeProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam) { switch (eventType) { case EV_PROTO_ONLOAD: return this->OnProtoModulesLoaded(wParam, lParam); case EV_PROTO_ONEXIT: return this->OnPreShutdown(wParam, lParam); case EV_PROTO_ONOPTIONS: return this->OnOptionsInit(wParam,lParam); case EV_PROTO_ONCONTACTDELETED: return this->OnContactDeleted(wParam, lParam); case EV_PROTO_ONMENU: this->OnInitStatusMenu(); break; } return 1; }