OregonCore  revision fb2a440-git
Your Favourite TBC server
PatchHandler.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "PatchHandler.h"
19 #include "AuthCodes.h"
20 #include "Log.h"
21 #include "Common.h"
22 
23 #include <ace/OS_NS_sys_socket.h>
24 #include <ace/OS_NS_dirent.h>
25 #include <ace/OS_NS_errno.h>
26 #include <ace/OS_NS_unistd.h>
27 
28 #include <ace/os_include/netinet/os_tcp.h>
29 
30 #ifndef MSG_NOSIGNAL
31 #define MSG_NOSIGNAL 0
32 #endif
33 
34 #if defined( __GNUC__ )
35 #pragma pack(1)
36 #else
37 #pragma pack(push,1)
38 #endif
39 
40 struct Chunk
41 {
42  ACE_UINT8 cmd;
43  ACE_UINT16 data_size;
44  ACE_UINT8 data[4096]; // 4096 - page size on most arch
45 };
46 
47 #if defined( __GNUC__ )
48 #pragma pack()
49 #else
50 #pragma pack(pop)
51 #endif
52 
53 PatchHandler::PatchHandler(ACE_HANDLE socket, ACE_HANDLE patch)
54 {
55  reactor(NULL);
56  set_handle(socket);
57  patch_fd_ = patch;
58 }
59 
61 {
62  if (patch_fd_ != ACE_INVALID_HANDLE)
63  ACE_OS::close(patch_fd_);
64 }
65 
67 {
68  if (get_handle() == ACE_INVALID_HANDLE || patch_fd_ == ACE_INVALID_HANDLE)
69  return -1;
70 
71  int nodelay = 0;
72  if (-1 == peer().set_option(ACE_IPPROTO_TCP,
73  TCP_NODELAY,
74  &nodelay,
75  sizeof(nodelay)))
76  return -1;
77 
78  #if defined(TCP_CORK)
79  int cork = 1;
80  if (-1 == peer().set_option(ACE_IPPROTO_TCP,
81  TCP_CORK,
82  &cork,
83  sizeof(cork)))
84  return -1;
85  #endif // TCP_CORK
86 
87  (void) peer().disable(ACE_NONBLOCK);
88 
89  return activate(THR_NEW_LWP | THR_DETACHED | THR_INHERIT_SCHED);
90 }
91 
93 {
94  // Do 1 second sleep, similar to the one in game/WorldSocket.cpp
95  // Seems client have problems with too fast sends.
96  ACE_OS::sleep(1);
97 
98  int flags = MSG_NOSIGNAL;
99 
100  Chunk data;
101  data.cmd = CMD_XFER_DATA;
102 
103  ssize_t r;
104 
105  while ((r = ACE_OS::read(patch_fd_, data.data, sizeof(data.data))) > 0)
106  {
107  data.data_size = (ACE_UINT16)r;
108 
109  if (peer().send((const char*)&data,
110  ((size_t) r) + sizeof(data) - sizeof(data.data),
111  flags) == -1)
112  return -1;
113  }
114 
115  if (r == -1)
116  return -1;
117 
118  return 0;
119 }
120 
122 {
123  for (Patches::iterator i = patches_.begin (); i != patches_.end (); i++)
124  delete i->second;
125 }
126 
128 {
129  LoadPatchesInfo();
130 }
131 
133 {
134  return ACE_Singleton<PatchCache, ACE_Thread_Mutex>::instance();
135 }
136 
137 void PatchCache::LoadPatchMD5(const char* szFileName)
138 {
139  // Try to open the patch file
140  std::string path = "./patches/";
141  path += szFileName;
142  FILE* pPatch = fopen(path.c_str (), "rb");
143  sLog.outDebug("Loading patch info from %s", path.c_str());
144 
145  if (!pPatch)
146  return;
147 
148  // Calculate the MD5 hash
149  MD5_CTX ctx;
150  MD5_Init(&ctx);
151 
152  const size_t check_chunk_size = 4 * 1024;
153 
154  ACE_UINT8 buf[check_chunk_size];
155 
156  while (!feof (pPatch))
157  {
158  size_t read = fread(buf, 1, check_chunk_size, pPatch);
159  MD5_Update(&ctx, buf, read);
160  }
161 
162  fclose(pPatch);
163 
164  // Store the result in the internal patch hash map
165  patches_[path] = new PATCH_INFO;
166  MD5_Final((ACE_UINT8*) & patches_[path]->md5, &ctx);
167 }
168 
169 bool PatchCache::GetHash(const char* pat, ACE_UINT8 mymd5[MD5_DIGEST_LENGTH])
170 {
171  for (Patches::iterator i = patches_.begin (); i != patches_.end (); i++)
172  if (!stricmp(pat, i->first.c_str ()))
173  {
174  memcpy(mymd5, i->second->md5, MD5_DIGEST_LENGTH);
175  return true;
176  }
177 
178  return false;
179 }
180 
182 {
183  ACE_DIR* dirp = ACE_OS::opendir(ACE_TEXT("./patches/"));
184 
185  if (!dirp)
186  return;
187 
188  ACE_DIRENT* dp;
189 
190  while ((dp = ACE_OS::readdir(dirp)) != NULL)
191  {
192  int l = strlen(dp->d_name);
193  if (l < 8)
194  continue;
195 
196  if (!memcmp(&dp->d_name[l - 4], ".mpq", 4))
197  LoadPatchMD5(dp->d_name);
198  }
199 
200  ACE_OS::closedir(dirp);
201 }
202 
void LoadPatchMD5(const char *)
#define MSG_NOSIGNAL
virtual int svc(void)
int open(void *=0)
void LoadPatchesInfo()
ACE_UINT16 data_size
#define sLog
Log class singleton.
Definition: Log.h:187
virtual ~PatchHandler()
static PatchCache * instance()
PatchHandler(ACE_HANDLE socket, ACE_HANDLE patch)
ACE_UINT8 data[4096]
ACE_UINT8 cmd
bool GetHash(const char *pat, ACE_UINT8 mymd5[MD5_DIGEST_LENGTH])