/* * Copyright (C) 2005-2012 MaNGOS * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** \file \ingroup realmd */ #include "PatchHandler.h" #include "AuthCodes.h" #include "Log.h" #include "Common.h" #include #include #include #include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #if defined( __GNUC__ ) #pragma pack(1) #else #pragma pack(push,1) #endif struct Chunk { ACE_UINT8 cmd; ACE_UINT16 data_size; ACE_UINT8 data[4096]; // 4096 - page size on most arch }; #if defined( __GNUC__ ) #pragma pack() #else #pragma pack(pop) #endif PatchHandler::PatchHandler(ACE_HANDLE socket, ACE_HANDLE patch) { reactor(NULL); set_handle(socket); patch_fd_ = patch; } PatchHandler::~PatchHandler() { if(patch_fd_ != ACE_INVALID_HANDLE) ACE_OS::close(patch_fd_); } int PatchHandler::open(void*) { if(get_handle() == ACE_INVALID_HANDLE || patch_fd_ == ACE_INVALID_HANDLE) return -1; int nodelay = 0; if (-1 == peer().set_option(ACE_IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay))) { return -1; } #if defined(TCP_CORK) int cork = 1; if (-1 == peer().set_option(ACE_IPPROTO_TCP, TCP_CORK, &cork, sizeof(cork))) { return -1; } #endif //TCP_CORK (void) peer().disable(ACE_NONBLOCK); return activate(THR_NEW_LWP | THR_DETACHED | THR_INHERIT_SCHED); } int PatchHandler::svc(void) { // Do 1 second sleep, similar to the one in game/WorldSocket.cpp // Seems client have problems with too fast sends. ACE_OS::sleep(1); int flags = MSG_NOSIGNAL; Chunk data; data.cmd = CMD_XFER_DATA; ssize_t r; while((r = ACE_OS::read(patch_fd_, data.data, sizeof(data.data))) > 0) { data.data_size = (ACE_UINT16)r; if(peer().send((const char*)&data, ((size_t) r) + sizeof(data) - sizeof(data.data), flags) == -1) { return -1; } } if(r == -1) { return -1; } return 0; } PatchCache::~PatchCache() { for (Patches::iterator i = patches_.begin (); i != patches_.end (); i++) delete i->second; } PatchCache::PatchCache() { LoadPatchesInfo(); } PatchCache* PatchCache::instance() { return ACE_Singleton::instance(); } void PatchCache::LoadPatchMD5(const char* szFileName) { // Try to open the patch file std::string path = "./patches/"; path += szFileName; FILE * pPatch = fopen(path.c_str (), "rb"); sLog.outDebug("Loading patch info from %s", path.c_str()); if(!pPatch) return; // Calculate the MD5 hash MD5_CTX ctx; MD5_Init(&ctx); const size_t check_chunk_size = 4*1024; ACE_UINT8 buf[check_chunk_size]; while(!feof (pPatch)) { size_t read = fread(buf, 1, check_chunk_size, pPatch); MD5_Update(&ctx, buf, read); } fclose(pPatch); // Store the result in the internal patch hash map patches_[path] = new PATCH_INFO; MD5_Final((ACE_UINT8 *) & patches_[path]->md5, &ctx); } bool PatchCache::GetHash(const char * pat, ACE_UINT8 mymd5[MD5_DIGEST_LENGTH]) { for (Patches::iterator i = patches_.begin (); i != patches_.end (); i++) if (!stricmp(pat, i->first.c_str ())) { memcpy(mymd5, i->second->md5, MD5_DIGEST_LENGTH); return true; } return false; } void PatchCache::LoadPatchesInfo() { ACE_DIR* dirp = ACE_OS::opendir(ACE_TEXT("./patches/")); if(!dirp) return; ACE_DIRENT* dp; while((dp = ACE_OS::readdir(dirp)) != NULL) { int l = strlen(dp->d_name); if (l < 8) continue; if(!memcmp(&dp->d_name[l - 4], ".mpq", 4)) LoadPatchMD5(dp->d_name); } ACE_OS::closedir(dirp); }