2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "ED2KLink.h" // Interface declarations.
30 #include <wx/string.h>
31 #include <wx/regex.h> // Needed for wxRegEx
32 #include <wx/tokenzr.h> // Needed for wxStringTokenizer
34 #include <protocol/ed2k/Constants.h>
36 #include "MemFile.h" // Needed for CMemFile
37 #include "NetworkFunctions.h" // Needed for Uint32toStringIP
38 #include <common/Format.h> // Needed for CFormat
41 CED2KLink::CED2KLink( LinkType type
)
47 CED2KLink::~CED2KLink()
52 CED2KLink::LinkType
CED2KLink::GetKind() const
58 CED2KLink
* CED2KLink::CreateLinkFromUrl(const wxString
& link
)
60 wxRegEx
re_type(wxT("ed2k://\\|(file|server|serverlist)\\|.*/"), wxRE_ICASE
| wxRE_DEFAULT
);
61 { wxCHECK(re_type
.IsValid(), NULL
); }
63 if (!re_type
.Matches(link
)) {
64 throw wxString(wxT("Not a valid ed2k-URI"));
67 wxString type
= re_type
.GetMatch(link
, 1).MakeLower();
68 { wxCHECK(type
.Length(), NULL
); }
70 if (type
== wxT("file")) {
71 return new CED2KFileLink(link
);
72 } else if (type
== wxT("server")) {
73 return new CED2KServerLink(link
);
74 } else if (type
== wxT("serverlist")) {
75 return new CED2KServerListLink(link
);
82 /////////////////////////////////////////////
83 // CED2KServerListLink implementation
84 /////////////////////////////////////////////
85 CED2KServerListLink::CED2KServerListLink(const wxString
& link
)
86 : CED2KLink( kServerList
)
88 wxRegEx
re(wxT("ed2k://\\|serverlist\\|(.*)\\|/"), wxRE_ICASE
| wxRE_DEFAULT
);
89 if (!re
.Matches(link
)) {
90 throw wxString(wxT("Not a valid server-list link."));
93 m_address
= UnescapeHTML(re
.GetMatch(link
, 1));
97 wxString
CED2KServerListLink::GetLink() const
99 return wxT("ed2k://|serverlist|") + m_address
+ wxT("|/");
103 const wxString
& CED2KServerListLink::GetAddress() const
109 /////////////////////////////////////////////
110 // CED2KServerLink implementation
111 /////////////////////////////////////////////
112 CED2KServerLink::CED2KServerLink(const wxString
& link
)
113 : CED2KLink( kServer
)
115 wxRegEx
re(wxT("ed2k://\\|server\\|([^\\|]+)\\|([0-9]+)\\|/"), wxRE_ICASE
| wxRE_DEFAULT
);
116 if (!re
.Matches(link
)) {
117 throw wxString(wxT("Not a valid server link."));
120 wxString ip
= UnescapeHTML(re
.GetMatch(link
, 1));
121 wxString port
= re
.GetMatch(link
, 2);
123 unsigned long ul
= StrToULong(port
);
124 if (ul
> 0xFFFF || ul
== 0) {
125 throw wxString( wxT("Bad port number") );
128 m_port
= static_cast<uint16
>(ul
);
129 m_ip
= StringIPtoUint32(ip
);
133 wxString
CED2KServerLink::GetLink() const
135 return wxString(wxT("ed2k://|server|")) << Uint32toStringIP(m_ip
) << wxT("|") << m_port
<< wxT("|/");
139 uint32
CED2KServerLink::GetIP() const
145 uint16
CED2KServerLink::GetPort() const
151 /////////////////////////////////////////////
152 // CED2KFileLink implementation
153 /////////////////////////////////////////////
154 CED2KFileLink::CED2KFileLink(const wxString
& link
)
155 : CED2KLink( kFile
),
158 m_bAICHHashValid(false)
160 // Start tokenizing after the "ed2k:://|file|" part of the link
161 wxStringTokenizer
tokens(link
, wxT("|"), wxTOKEN_RET_EMPTY_ALL
);
163 // Must at least be ed2k://|file|NAME|SIZE|HASH|/
164 if (tokens
.CountTokens() < 5
165 || tokens
.GetNextToken() != wxT("ed2k://")
166 || tokens
.GetNextToken() != wxT("file")) {
167 throw wxString(wxT("Not a valid file link"));
170 m_name
= UnescapeHTML(tokens
.GetNextToken().Strip(wxString::both
));
171 // We don't want a path in the name.
172 m_name
.Replace(wxT("/"), wxT("_"));
174 // Note that StrToULong returns ULONG_MAX if the value is
175 // too large to be contained in a unsigned long, which means
176 // that this check is valid, as odd as it seems
177 wxString size
= tokens
.GetNextToken().Strip(wxString::both
);
178 m_size
= StrToULongLong(size
);
179 if ((m_size
== 0) || (m_size
> MAX_FILE_SIZE
)) {
180 throw wxString(CFormat(wxT("Invalid file size %i")) % m_size
);
183 if (!m_hash
.Decode(tokens
.GetNextToken().Strip(wxString::both
))) {
184 throw wxString(wxT("Invalid hash"));
187 // Check extra fields (sources, parthashes, masterhashes)
188 while (tokens
.HasMoreTokens()) {
189 wxString field
= tokens
.GetNextToken().MakeLower().Strip(wxString::both
);
191 if (field
.StartsWith(wxT("sources,"))) {
192 wxStringTokenizer
srcTokens(field
, wxT(","));
193 // Skipping the first token ("sources").
194 wxString token
= srcTokens
.GetNextToken();
195 while (srcTokens
.HasMoreTokens()) {
196 token
= srcTokens
.GetNextToken().Strip(wxString::both
);
198 wxStringTokenizer
sourceTokens(token
, wxT(":"));
199 wxString addr
= sourceTokens
.GetNextToken();
200 if (addr
.IsEmpty()) {
201 throw wxString( wxT("Empty address" ) );
204 wxString strport
= sourceTokens
.GetNextToken();
205 if (strport
.IsEmpty()) {
206 throw wxString( wxT("Empty port" ) );
209 unsigned port
= StrToULong(strport
);
212 if ((port
== 0) || (port
> 0xFFFF)) {
213 throw wxString( wxT("Invalid Port" ) );
217 uint8 cryptoptions
=0;
218 wxString strcryptoptions
= sourceTokens
.GetNextToken();
219 if (!strcryptoptions
.IsEmpty()) {
220 cryptoptions
= (uint8
) StrToULong(strcryptoptions
);
221 if ((cryptoptions
& 0x80) > 0) {
222 // Source ready for encryption, hash included.
223 sourcehash
= sourceTokens
.GetNextToken();
224 if (sourcehash
.IsEmpty()) {
225 throw wxString( wxT("Empty sourcehash conflicts with cryptoptions flag 0x80" ) );
230 SED2KLinkSource entry
= { addr
, (uint16
) port
, sourcehash
, cryptoptions
};
232 m_sources
.push_back(entry
);
234 } else if (field
.StartsWith(wxT("p="))) {
235 wxStringTokenizer
hashTokens(field
.AfterFirst(wxT('=')), wxT(":"), wxTOKEN_RET_EMPTY_ALL
);
237 m_hashset
= new CMemFile();
238 m_hashset
->WriteHash(m_hash
);
239 m_hashset
->WriteUInt16(0);
241 while (hashTokens
.HasMoreTokens()) {
243 if (!hash
.Decode(hashTokens
.GetNextToken().Strip(wxString::both
))) {
244 throw wxString(wxT("Invalid hash in part-hashes list"));
247 m_hashset
->WriteHash(hash
);
250 unsigned count
= m_hashset
->GetLength() / 16u - 1u;
253 m_hashset
->Seek( 16, wxFromStart
);
254 m_hashset
->WriteUInt16( count
);
255 m_hashset
->Seek( 0, wxFromStart
);
260 } else if (field
.StartsWith(wxT("h="))) {
261 wxString hash
= field
.AfterFirst(wxT('=')).MakeUpper();
263 size_t decodedSize
= DecodeBase32(hash
, CAICHHash::GetHashSize(), m_AICHHash
.GetRawHash());
264 if ((decodedSize
!= CAICHHash::GetHashSize()) || m_AICHHash
.GetString() != hash
) {
265 throw wxString(wxT("Invalid master-hash"));
268 m_bAICHHashValid
= true;
274 CED2KFileLink::~CED2KFileLink()
281 wxString
CED2KFileLink::GetLink() const
283 return CFormat(wxT("ed2k://|file|%s|%u|%s|/")) % m_name
% m_size
% m_hash
.Encode();
287 wxString
CED2KFileLink::GetName() const
293 uint64
CED2KFileLink::GetSize() const
299 const CMD4Hash
& CED2KFileLink::GetHashKey() const
305 bool CED2KFileLink::HasValidAICHHash() const
307 return m_bAICHHashValid
;
311 const CAICHHash
& CED2KFileLink::GetAICHHash() const
315 // File_checked_for_headers