1// 2// This file is part of the aMule Project. 3// 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 ) 6// 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 9// respective authors. 10// 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. 15// 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. 20// 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 24// 25 26#include <wx/wx.h> 27 28#include "ED2KLink.h" // Interface declarations. 29 30#include <wx/string.h> 31#include <wx/regex.h> // Needed for wxRegEx 32#include <wx/tokenzr.h> // Needed for wxStringTokenizer 33 34#include <protocol/ed2k/Constants.h> 35 36#include "MemFile.h" // Needed for CMemFile 37#include "NetworkFunctions.h" // Needed for Uint32toStringIP 38#include <common/Format.h> // Needed for CFormat 39 40 41CED2KLink::CED2KLink( LinkType type ) 42 : m_type( type ) 43{ 44} 45 46 47CED2KLink::~CED2KLink() 48{ 49} 50 51 52CED2KLink::LinkType CED2KLink::GetKind() const 53{ 54 return m_type; 55} 56 57 58CED2KLink* CED2KLink::CreateLinkFromUrl(const wxString& link) 59{ 60 wxRegEx re_type(wxT("ed2k://\\|(file|server|serverlist)\\|.*/"), wxRE_ICASE | wxRE_DEFAULT); 61 { wxCHECK(re_type.IsValid(), NULL); } 62 63 if (!re_type.Matches(link)) { 64 throw wxString(wxT("Not a valid ed2k-URI")); 65 } 66 67 wxString type = re_type.GetMatch(link, 1).MakeLower(); 68 { wxCHECK(type.Length(), NULL); } 69 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); 76 } else { 77 wxCHECK(false, NULL); 78 } 79} 80 81 82///////////////////////////////////////////// 83// CED2KServerListLink implementation 84///////////////////////////////////////////// 85CED2KServerListLink::CED2KServerListLink(const wxString& link) 86 : CED2KLink( kServerList ) 87{ 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.")); 91 } 92 93 m_address = UnescapeHTML(re.GetMatch(link, 1)); 94} 95 96 97wxString CED2KServerListLink::GetLink() const 98{ 99 return wxT("ed2k://|serverlist|") + m_address + wxT("|/"); 100} 101 102 103const wxString& CED2KServerListLink::GetAddress() const 104{ 105 return m_address; 106} 107 108 109///////////////////////////////////////////// 110// CED2KServerLink implementation 111///////////////////////////////////////////// 112CED2KServerLink::CED2KServerLink(const wxString& link) 113 : CED2KLink( kServer ) 114{ 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.")); 118 } 119 120 wxString ip = UnescapeHTML(re.GetMatch(link, 1)); 121 wxString port = re.GetMatch(link, 2); 122 123 unsigned long ul = StrToULong(port); 124 if (ul > 0xFFFF || ul == 0) { 125 throw wxString( wxT("Bad port number") ); 126 } 127 128 m_port = static_cast<uint16>(ul); 129 m_ip = StringIPtoUint32(ip); 130} 131 132 133wxString CED2KServerLink::GetLink() const 134{ 135 return wxString(wxT("ed2k://|server|")) << Uint32toStringIP(m_ip) << wxT("|") << m_port << wxT("|/"); 136} 137 138 139uint32 CED2KServerLink::GetIP() const 140{ 141 return m_ip; 142} 143 144 145uint16 CED2KServerLink::GetPort() const 146{ 147 return m_port; 148} 149 150 151///////////////////////////////////////////// 152// CED2KFileLink implementation 153///////////////////////////////////////////// 154CED2KFileLink::CED2KFileLink(const wxString& link) 155 : CED2KLink( kFile ), 156 m_hashset(NULL), 157 m_size(0), 158 m_bAICHHashValid(false) 159{ 160 // Start tokenizing after the "ed2k:://|file|" part of the link 161 wxStringTokenizer tokens(link, wxT("|"), wxTOKEN_RET_EMPTY_ALL); 162 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")); 168 } 169 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("_")); 173 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); 181 } 182 183 if (!m_hash.Decode(tokens.GetNextToken().Strip(wxString::both))) { 184 throw wxString(wxT("Invalid hash")); 185 } 186 187 // Check extra fields (sources, parthashes, masterhashes) 188 while (tokens.HasMoreTokens()) { 189 wxString field = tokens.GetNextToken().MakeLower().Strip(wxString::both); 190 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); 197 198 wxStringTokenizer sourceTokens(token, wxT(":")); 199 wxString addr = sourceTokens.GetNextToken(); 200 if (addr.IsEmpty()) { 201 throw wxString( wxT("Empty address" ) ); 202 } 203 204 wxString strport = sourceTokens.GetNextToken(); 205 if (strport.IsEmpty()) { 206 throw wxString( wxT("Empty port" ) ); 207 } 208 209 unsigned port = StrToULong(strport); 210 211 // Sanity checking 212 if ((port == 0) || (port > 0xFFFF)) { 213 throw wxString( wxT("Invalid Port" ) ); 214 } 215 216 wxString sourcehash; 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" ) ); 226 } 227 } 228 } 229 230 SED2KLinkSource entry = { addr, port, sourcehash, cryptoptions }; 231 232 m_sources.push_back(entry); 233 } 234 } else if (field.StartsWith(wxT("p="))) { 235 wxStringTokenizer hashTokens(field.AfterFirst(wxT('=')), wxT(":"), wxTOKEN_RET_EMPTY_ALL); 236 237 m_hashset = new CMemFile(); 238 m_hashset->WriteHash(m_hash); 239 m_hashset->WriteUInt16(0); 240 241 while (hashTokens.HasMoreTokens()) { 242 CMD4Hash hash; 243 if (!hash.Decode(hashTokens.GetNextToken().Strip(wxString::both))) { 244 throw wxString(wxT("Invalid hash in part-hashes list")); 245 } 246 247 m_hashset->WriteHash(hash); 248 } 249 250 unsigned count = m_hashset->GetLength() / 16u - 1u; 251 252 if (count) { 253 m_hashset->Seek( 16, wxFromStart); 254 m_hashset->WriteUInt16( count ); 255 m_hashset->Seek( 0, wxFromStart); 256 } else { 257 delete m_hashset; 258 m_hashset = NULL; 259 } 260 } else if (field.StartsWith(wxT("h="))) { 261 wxString hash = field.AfterFirst(wxT('=')).MakeUpper(); 262 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")); 266 } 267 268 m_bAICHHashValid = true; 269 } 270 } 271} 272 273 274CED2KFileLink::~CED2KFileLink() 275{ 276 delete m_hashset; 277 m_hashset = NULL; 278} 279 280 281wxString CED2KFileLink::GetLink() const 282{ 283 return CFormat(wxT("ed2k://|file|%s|%u|%s|/")) % m_name % m_size % m_hash.Encode(); 284} 285 286 287wxString CED2KFileLink::GetName() const 288{ 289 return m_name; 290} 291 292 293uint64 CED2KFileLink::GetSize() const 294{ 295 return m_size; 296} 297 298 299const CMD4Hash& CED2KFileLink::GetHashKey() const 300{ 301 return m_hash; 302} 303 304 305bool CED2KFileLink::HasValidAICHHash() const 306{ 307 return m_bAICHHashValid; 308} 309 310 311const CAICHHash& CED2KFileLink::GetAICHHash() const 312{ 313 return m_AICHHash; 314} 315// File_checked_for_headers 316