📄 ed2klink.cpp
字号:
//this file is part of eMule
//Copyright (C)2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include <wininet.h>
#include "resource.h"
#include "ED2KLink.h"
#include "OtherFunctions.h"
#include "SafeFile.h"
#include "StringConversion.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
namespace {
struct autoFree {
autoFree(TCHAR* p) : m_p(p) {}
~autoFree() { free(m_p); }
private:
TCHAR * m_p;
};
inline unsigned int FromHexDigit(TCHAR digit) {
switch (digit) {
case _T('0'): return 0;
case _T('1'): return 1;
case _T('2'): return 2;
case _T('3'): return 3;
case _T('4'): return 4;
case _T('5'): return 5;
case _T('6'): return 6;
case _T('7'): return 7;
case _T('8'): return 8;
case _T('9'): return 9;
case _T('A'): return 10;
case _T('B'): return 11;
case _T('C'): return 12;
case _T('D'): return 13;
case _T('E'): return 14;
case _T('F'): return 15;
case _T('a'): return 10;
case _T('b'): return 11;
case _T('c'): return 12;
case _T('d'): return 13;
case _T('e'): return 14;
case _T('f'): return 15;
default: throw GetResString(IDS_ERR_ILLFORMEDHASH);
}
}
}
CED2KLink::~CED2KLink()
{
}
/////////////////////////////////////////////
// CED2KServerListLink implementation
/////////////////////////////////////////////
CED2KServerListLink::CED2KServerListLink(const TCHAR* address)
{
m_address = address;
}
CED2KServerListLink::~CED2KServerListLink()
{
}
void
CED2KServerListLink::GetLink(CString& lnk)
{
lnk = _T("ed2k://|serverlist|");
lnk += m_address;
lnk += _T("|/");
}
CED2KServerListLink*
CED2KServerListLink::GetServerListLink()
{
return this;
}
CED2KServerLink*
CED2KServerListLink::GetServerLink()
{
return 0;
}
CED2KFileLink*
CED2KServerListLink::GetFileLink()
{
return 0;
}
CED2KLink::LinkType
CED2KServerListLink::GetKind() const
{
return kServerList;
}
/////////////////////////////////////////////
// CED2KServerLink implementation
/////////////////////////////////////////////
CED2KServerLink::CED2KServerLink(const TCHAR* ip,const TCHAR* port)
{
USES_CONVERSION;
m_ip = inet_addr(T2CA(ip));
unsigned long ul = _tcstoul(port,0,10);
if ( ul > 0xFFFF )
throw GetResString(IDS_ERR_BADPORT);
m_port = static_cast<uint16>(ul);
m_defaultName = _T("Server ");
m_defaultName += ip;
m_defaultName += _T(":");
m_defaultName += port;
}
CED2KServerLink::~CED2KServerLink()
{
}
void
CED2KServerLink::GetLink(CString& lnk)
{
TCHAR buffer[32];
lnk = _T("ed2k://|server|");
lnk += ipstr(m_ip);
lnk += _T("|");
_stprintf(buffer,_T("%d"),static_cast<int>(m_port));
lnk += buffer;
lnk += _T("|/");
}
CED2KServerListLink*
CED2KServerLink::GetServerListLink()
{
return 0;
}
CED2KServerLink*
CED2KServerLink::GetServerLink()
{
return this;
}
CED2KFileLink*
CED2KServerLink::GetFileLink()
{
return 0;
}
CED2KLink::LinkType
CED2KServerLink::GetKind() const
{
return kServer;
}
/////////////////////////////////////////////
// CED2KFileLink implementation
/////////////////////////////////////////////
CED2KFileLink::CED2KFileLink(const TCHAR* name,const TCHAR* size, const TCHAR* hash, const CStringArray& astrParams, const TCHAR* sources)
: m_name(name)
, m_size(size)
{
SourcesList = NULL;
m_hashset = NULL;
m_bAICHHashValid = false;
if ( _tcslen(hash) != 32 )
throw GetResString(IDS_ERR_ILLFORMEDHASH);
if (_tstoi64(size)>=4294967295)
throw GetResString(IDS_ERR_TOOLARGEFILE);
if (_tstoi64(size)<=0)
throw GetResString(IDS_ERR_NOTAFILELINK);
for ( int idx = 0 ; idx < 16 ; ++idx) {
m_hash[idx] = FromHexDigit(*hash++)*16;
m_hash[idx] += FromHexDigit(*hash++);
}
bool bError = false;
for (int i = 0; !bError && i < astrParams.GetCount(); i++)
{
const CString& strParam = astrParams.GetAt(i);
ASSERT( !strParam.IsEmpty() );
CString strTok;
int iPos = strParam.Find(_T('='));
if (iPos != -1)
strTok = strParam.Left(iPos);
if (strTok == _T("s"))
{
CString strURL = strParam.Mid(iPos + 1);
if (!strURL.IsEmpty())
{
TCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH];
TCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH];
TCHAR szUrlPath[INTERNET_MAX_PATH_LENGTH];
TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
TCHAR szExtraInfo[INTERNET_MAX_URL_LENGTH];
URL_COMPONENTS Url = {0};
Url.dwStructSize = sizeof(Url);
Url.lpszScheme = szScheme;
Url.dwSchemeLength = ARRSIZE(szScheme);
Url.lpszHostName = szHostName;
Url.dwHostNameLength = ARRSIZE(szHostName);
Url.lpszUserName = szUserName;
Url.dwUserNameLength = ARRSIZE(szUserName);
Url.lpszPassword = szPassword;
Url.dwPasswordLength = ARRSIZE(szPassword);
Url.lpszUrlPath = szUrlPath;
Url.dwUrlPathLength = ARRSIZE(szUrlPath);
Url.lpszExtraInfo = szExtraInfo;
Url.dwExtraInfoLength = ARRSIZE(szExtraInfo);
if (InternetCrackUrl(strURL, 0, 0, &Url) && Url.dwHostNameLength > 0)
{
SUnresolvedHostname* hostname = new SUnresolvedHostname;
hostname->strURL = strURL;
hostname->strHostname = szHostName;
m_HostnameSourcesList.AddTail(hostname);
}
}
else
ASSERT(0);
}
else if (strTok == _T("p"))
{
CString strPartHashs = strParam.Tokenize(_T("="), iPos);
if (m_hashset != NULL){
ASSERT(0);
bError = true;
break;
}
m_hashset = new CSafeMemFile(256);
m_hashset->WriteHash16(m_hash);
m_hashset->WriteUInt16(0);
int iPartHashs = 0;
int iPosPH = 0;
CString strHash = strPartHashs.Tokenize(_T(":"), iPosPH);
while (!strHash.IsEmpty())
{
uchar aucPartHash[16];
if (!strmd4(strHash, aucPartHash)){
bError = true;
break;
}
m_hashset->WriteHash16(aucPartHash);
iPartHashs++;
strHash = strPartHashs.Tokenize(_T(":"), iPosPH);
}
if (bError)
break;
m_hashset->Seek(16, CFile::begin);
m_hashset->WriteUInt16(iPartHashs);
m_hashset->Seek(0, CFile::begin);
}
else if (strTok == _T("h"))
{
CString strHash = strParam.Mid(iPos + 1);
if (!strHash.IsEmpty())
{
if (DecodeBase32(strHash, m_AICHHash.GetRawHash(), CAICHHash::GetHashSize()) == CAICHHash::GetHashSize()){
m_bAICHHashValid = true;
ASSERT( m_AICHHash.GetString().CompareNoCase(strHash) == 0 );
}
else
ASSERT( false );
}
else
ASSERT( false );
}
else
ASSERT(0);
}
if (bError)
{
delete m_hashset;
m_hashset = NULL;
}
if (sources){
TCHAR* pNewString = _tcsdup(sources);
autoFree liberator(pNewString);
TCHAR* pCh = pNewString;
TCHAR* pEnd;
TCHAR* pIP;
TCHAR* pPort;
bool bAllowSources;
TCHAR date[3];
COleDateTime expirationDate;
int nYear,nMonth,nDay;
uint16 nCount = 0;
uint32 dwID;
uint16 nPort;
uint32 dwServerIP = 0;
uint16 nServerPort = 0;
unsigned long ul;
int nInvalid = 0;
pCh = _tcsstr( pCh, _T("sources") );
if( pCh != NULL ) {
pCh = pCh + 7; // point to char after "sources"
pEnd = pCh;
while( *pEnd ) pEnd++; // make pEnd point to the terminating NULL
bAllowSources=true;
// if there's an expiration date...
if( *pCh == _T('@') && (pEnd-pCh) > 7 )
{
pCh++; // after '@'
date[2] = 0; // terminate the two character string
date[0] = *(pCh++); date[1] = *(pCh++);
nYear = _tcstol( date, 0, 10 ) + 2000;
date[0] = *(pCh++); date[1] = *(pCh++);
nMonth = _tcstol( date, 0, 10 );
date[0] = *(pCh++); date[1] = *(pCh++);
nDay = _tcstol( date, 0, 10 );
bAllowSources = ( expirationDate.SetDate(nYear,nMonth,nDay) == 0 );
if (bAllowSources) bAllowSources=(COleDateTime::GetCurrentTime() < expirationDate);
}
// increment pCh to point to the first "ip:port" and check for sources
if ( bAllowSources && ++pCh < pEnd ) {
SourcesList = new CSafeMemFile(256);
SourcesList->WriteUInt16(nCount); // init to 0, we'll fix this at the end.
// for each "ip:port" source string until the end
// limit to prevent overflow (uint16 due to CPartFile::AddClientSources)
while( *pCh != 0 && nCount < MAXSHORT ) {
pIP = pCh;
// find the end of this ip:port string & start of next ip:port string.
if( (pCh = _tcschr(pCh, _T(','))) != NULL ) {
*pCh = 0; // terminate current "ip:port"
pCh++; // point to next "ip:port"
}
else
pCh = pEnd;
// if port is not present for this ip, go to the next ip.
if( (pPort = _tcschr(pIP, _T(':'))) == NULL )
{ nInvalid++; continue; }
*pPort = 0; // terminate ip string
pPort++; // point pPort to port string.
USES_CONVERSION;
dwID = inet_addr(T2A(pIP));
ul = _tcstoul( pPort, 0, 10 );
nPort = static_cast<uint16>(ul);
// skip bad ips / ports
if (ul > 0xFFFF || ul == 0 ) // port
{ nInvalid++; continue; }
if( dwID == INADDR_NONE) { // hostname?
if (_tcslen(pIP) > 512)
{ nInvalid++; continue; }
SUnresolvedHostname* hostname = new SUnresolvedHostname;
hostname->nPort = nPort;
hostname->strHostname = pIP;
m_HostnameSourcesList.AddTail(hostname);
continue;
}
//TODO: This will filter out *.*.*.0 clients. Is there a nice way to fix?
if( IsLowID(dwID) ) // ip
{ nInvalid++; continue; }
SourcesList->WriteUInt32(dwID);
SourcesList->WriteUInt16(nPort);
SourcesList->WriteUInt32(dwServerIP);
SourcesList->WriteUInt16(nServerPort);
nCount++;
}
SourcesList->SeekToBegin();
SourcesList->WriteUInt16(nCount);
SourcesList->SeekToBegin();
if (nCount==0) {
delete SourcesList;
SourcesList=NULL;
}
}
}
}
}
CED2KFileLink::~CED2KFileLink()
{
if (SourcesList){
delete SourcesList;
SourcesList=NULL;
}
while (!m_HostnameSourcesList.IsEmpty())
delete m_HostnameSourcesList.RemoveHead();
delete m_hashset;
}
void
CED2KFileLink::GetLink(CString& lnk)
{
lnk = _T("ed2k://|file|");
lnk += EncodeUrlUtf8(m_name);
lnk += _T("|");
lnk += m_size;
lnk += _T("|");
for (int idx=0; idx != 16 ; ++idx ) {
unsigned int ui1 = m_hash[idx] / 16;
unsigned int ui2 = m_hash[idx] % 16;
lnk+= static_cast<TCHAR>( ui1 > 9 ? (_T('0')+ui1) : (_T('A')+(ui1-10)) );
lnk+= static_cast<TCHAR>( ui2 > 9 ? (_T('0')+ui2) : (_T('A')+(ui2-10)) );
}
lnk += _T("|/");
}
CED2KServerListLink*
CED2KFileLink::GetServerListLink()
{
return 0;
}
CED2KServerLink*
CED2KFileLink::GetServerLink()
{
return 0;
}
CED2KFileLink*
CED2KFileLink::GetFileLink()
{
m_name = OptUtf8ToStr(URLDecode(m_name));
return this;
}
CED2KLink::LinkType
CED2KFileLink::GetKind() const
{
return kFile;
}
//static
CED2KLink*
CED2KLink::CreateLinkFromUrl( const TCHAR * uri)
{
CString strURI(uri);
int iPos = 0;
CString strTok = strURI.Tokenize(_T("|"), iPos);
if (strTok == _T("ed2k://"))
{
strTok = strURI.Tokenize(_T("|"), iPos);
if (strTok == _T("file"))
{
CString strName = strURI.Tokenize(_T("|"), iPos);
if (!strName.IsEmpty())
{
CString strSize = strURI.Tokenize(_T("|"), iPos);
if (!strSize.IsEmpty())
{
CString strHash = strURI.Tokenize(_T("|"), iPos);
if (!strHash.IsEmpty())
{
CStringArray astrEd2kParams;
bool bEmuleExt = false;
CString strEmuleExt;
CString strLastTok;
strTok = strURI.Tokenize(_T("|"), iPos);
while (!strTok.IsEmpty())
{
strLastTok = strTok;
if (strTok == _T("/"))
{
if (bEmuleExt)
break;
bEmuleExt = true;
}
else
{
if (bEmuleExt)
{
if (!strEmuleExt.IsEmpty())
strEmuleExt += _T('|');
strEmuleExt += strTok;
}
else
astrEd2kParams.Add(strTok);
}
strTok = strURI.Tokenize(_T("|"), iPos);
}
if (strLastTok == _T("/"))
return new CED2KFileLink(strName, strSize, strHash, astrEd2kParams, strEmuleExt.IsEmpty() ? (LPCTSTR)NULL : (LPCTSTR)strEmuleExt);
}
}
}
}
else if (strTok == _T("serverlist"))
{
CString strURL = strURI.Tokenize(_T("|"), iPos);
if (!strURL.IsEmpty() && strURI.Tokenize(_T("|"), iPos) == _T("/"))
return new CED2KServerListLink(strURL);
}
else if (strTok == _T("server"))
{
CString strServer = strURI.Tokenize(_T("|"), iPos);
if (!strServer.IsEmpty())
{
CString strPort = strURI.Tokenize(_T("|"), iPos);
if (!strPort.IsEmpty() && strURI.Tokenize(_T("|"), iPos) == _T("/"))
return new CED2KServerLink(strServer, strPort);
}
}
}
throw GetResString(IDS_ERR_NOSLLINK);
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -