1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2011 shakraw ( shakraw@users.sourceforge.net )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 //
26
27
28 #include "WebSocket.h"
29
30
31 #ifdef ENABLE_UPNP
32 # include "UPnPBase.h"
33 #endif
34
CWebSocket(CWebServerBase * parent)35 CWebSocket::CWebSocket(CWebServerBase *parent)
36 {
37 m_pHead = 0;
38 m_pTail = 0;
39 m_pBuf = new char [4096];
40 m_dwBufSize = 4096;
41 m_dwRecv = 0;
42 m_dwHttpHeaderLen = 0;
43 m_dwHttpContentLen = 0;
44 m_Cookie = 0;
45 m_IsGet = false;
46 m_IsPost = false;
47
48 m_pParent = parent;
49
50 #ifndef ASIO_SOCKETS
51 SetEventHandler(*parent, ID_WEBCLIENTSOCKET_EVENT);
52 SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_LOST_FLAG);
53 #endif
54 Notify(true);
55
56 }
57
OnLost()58 void CWebSocket::OnLost()
59 {
60 Close();
61 Destroy();
62 }
63
OnReceive(int)64 void CWebSocket::OnReceive(int)
65 {
66 uint32 read = Read(m_pBuf + m_dwRecv, m_dwBufSize - m_dwRecv);
67 m_dwRecv += read;
68 while ((m_dwRecv == m_dwBufSize) && (read != 0) && (!LastError())) {
69 // Buffer is too small. Make it bigger.
70 uint32 newsize = m_dwBufSize + (m_dwBufSize >> 1);
71 char* newbuffer = new char[newsize];
72 char* oldbuffer = m_pBuf;
73 memcpy(newbuffer, oldbuffer, m_dwBufSize);
74 delete[] oldbuffer;
75 m_pBuf = newbuffer;
76 m_dwBufSize = newsize;
77 // And read again
78 read = Read(m_pBuf + m_dwRecv, m_dwBufSize - m_dwRecv);
79 m_dwRecv += read;
80 }
81
82 if (read == 0) {
83 if (LastError()) {
84 Close();
85 return ;
86 }
87 }
88
89 m_pBuf[m_dwRecv] = '\0';
90
91
92 //
93 // Check what kind of request is that
94 if ( !m_IsGet && !m_IsPost && m_dwRecv >= 4) {
95 if ( !strncasecmp(m_pBuf, "GET", 3) ) {
96 m_IsGet = true;
97 } else if ( !strncasecmp(m_pBuf, "POST", 4) ) {
98 m_IsPost = true;
99 } else {
100 // unknown request - close the socket
101 Close();
102 return ;
103 }
104 }
105 //
106 // RFC1945:
107 //
108
109 //
110 // "GET" must have last line empty
111 if ( m_IsGet ) {
112 if ( !strncasecmp(m_pBuf + m_dwRecv - 4, "\r\n\r\n", 4) ) {
113 //
114 // Process request
115 OnRequestReceived(m_pBuf, 0, 0);
116 }
117 }
118 //
119 // "POST" have "Content-Length"
120 if ( m_IsPost ) {
121 char *cont_len = strstr(m_pBuf, "Content-Length");
122 // do we have received all the line ?
123 if ( cont_len && strstr(cont_len, "\r\n\r\n") ) {
124 cont_len += strlen("Content-Length:");
125 // can be white space following
126 while ( isspace(*cont_len) ) cont_len++;
127 int len = atoi(cont_len);
128 if ( !len ) {
129 Close();
130 return ;
131 }
132 // do we have all of data ?
133 char *cont = strstr(m_pBuf, "\r\n\r\n");
134 cont += 4;
135 if ( cont - m_pBuf + len <= (int)m_dwRecv ) {
136 OnRequestReceived(m_pBuf, cont, len);
137 }
138 }
139 }
140 }
141
OnSend(int)142 void CWebSocket::OnSend(int)
143 {
144 while (m_pHead && m_pHead->m_pToSend) {
145 uint32 nRes = Write(m_pHead->m_pToSend, m_pHead->m_dwSize);
146 if (nRes >= m_pHead->m_dwSize) {
147 // erase this chunk
148 CChunk* pNext = m_pHead->m_pNext;
149 delete m_pHead;
150 if (!(m_pHead = pNext)) {
151 m_pTail = NULL;
152 }
153 } else {
154 if (LastError()) {
155 Close();
156 break;
157 } else if (nRes > 0) {
158 m_pHead->m_pToSend += nRes;
159 m_pHead->m_dwSize -= nRes;
160 } else {
161 // blocks
162 break;
163 }
164 }
165 }
166 }
167
OnRequestReceived(char * pHeader,char * pData,uint32 dwDataLen)168 void CWebSocket::OnRequestReceived(char* pHeader, char* pData, uint32 dwDataLen)
169 {
170 bool is_post = false;
171 if ( strncmp(pHeader, "GET", 3) == 0 ) {
172 } else if ( strncmp(pHeader, "POST", 4) == 0 ) {
173 is_post = true;
174 } else {
175 // invalid request
176 return ;
177 }
178 char *path = strchr(pHeader, ' ');
179 if ( !path ) {
180 return;
181 }
182 *path++ = 0;
183 pHeader = strchr(path, ' ');
184 if ( !pHeader ) {
185 return;
186 }
187 *pHeader++ = 0;
188
189 wxString sURL(char2unicode(path));
190 if ( is_post ) {
191 wxString sData(char2unicode(pData));
192 sURL += wxT("?") + sData.Left(dwDataLen);
193 }
194
195 //
196 // Find session cookie.
197 //
198 int sessid = 0;
199 char *current_cookie = strstr(pHeader, "Cookie: ");
200 if ( current_cookie == NULL ) {
201 current_cookie = strstr(pHeader, "cookie: ");
202 }
203 if ( current_cookie ) {
204 current_cookie = strstr(current_cookie, "amuleweb_session_id");
205 if ( current_cookie ) {
206 char *value = strchr(current_cookie, '=');
207 if ( value ) {
208 sessid = atoi(++value);
209 }
210 }
211 }
212 ThreadData Data = { CParsedUrl(sURL), sURL, sessid, this };
213
214 wxString sFile = Data.parsedURL.File();
215 if (sFile.Length() > 4 ) {
216 wxString url_ext = sFile.Right( sFile.Length() - sFile.Find('.', true) ).MakeLower();
217 if ( (url_ext==wxT(".gif")) || (url_ext==wxT(".jpg")) || (url_ext==wxT(".ico")) ||
218 (url_ext==wxT(".png")) || (url_ext==wxT(".bmp")) || (url_ext==wxT(".jpeg")) ) {
219 m_pParent->ProcessImgFileReq(Data);
220 } else {
221 m_pParent->ProcessURL(Data);
222 }
223 } else {
224 m_pParent->ProcessURL(Data);
225 }
226
227 //
228 // Done processing, reset state
229 //
230 m_dwRecv = 0;
231 m_IsGet = 0;
232 m_IsPost = 0;
233 }
234
SendContent(const char * szStdResponse,const void * pContent,uint32 dwContentSize)235 void CWebSocket::SendContent(const char* szStdResponse, const void* pContent, uint32 dwContentSize) {
236 char szBuf[0x1000]; // 0x1000 is safe because it's just used for the header
237 int nLen = snprintf(szBuf, sizeof(szBuf), "HTTP/1.1 200 OK\r\n%sContent-Length: %d\r\n\r\n", szStdResponse, dwContentSize);
238 SendData(szBuf, nLen);
239 SendData(pContent, dwContentSize);
240 }
241
SendHttpHeaders(const char * szType,bool use_gzip,uint32 content_len,int session_id)242 void CWebSocket::SendHttpHeaders(const char* szType, bool use_gzip, uint32 content_len, int session_id)
243 {
244 char szBuf[0x1000];
245
246 char cookie[256];
247 if ( session_id ) {
248 snprintf(cookie, sizeof(cookie), "Set-Cookie: amuleweb_session_id=%d\r\n", session_id);
249 } else {
250 cookie[0] = 0;
251 }
252
253 snprintf(szBuf, sizeof(szBuf), "HTTP/1.1 200 OK\r\nServer: aMule\r\nPragma: no-cache\r\nExpires: 0\r\n"
254 "Cache-Control: no-cache, no-store, must-revalidate\r\n"
255 "%s"
256 "Connection: close\r\nContent-Type: %s\r\n"
257 "Content-Length: %d\r\n%s\r\n",
258 cookie, szType, content_len, (use_gzip ? "Content-Encoding: gzip\r\n" : ""));
259
260 SendData(szBuf, strlen(szBuf));
261 }
262
SendData(const void * pData,uint32 dwDataSize)263 void CWebSocket::SendData(const void* pData, uint32 dwDataSize)
264 {
265 if (!dwDataSize) { // sanity
266 return;
267 }
268 const char * data = (const char*) pData;
269 bool outputRequired = !m_pHead;
270
271 // push it to our tails
272 CChunk* pChunk = new CChunk;
273 pChunk->m_pNext = NULL;
274 pChunk->m_dwSize = dwDataSize;
275 pChunk->m_pData = new char[dwDataSize];
276 memcpy(pChunk->m_pData, data, dwDataSize);
277 // push it to the end of our queue
278 pChunk->m_pToSend = pChunk->m_pData;
279 if (m_pTail) {
280 m_pTail->m_pNext = pChunk;
281 } else {
282 m_pHead = pChunk;
283 }
284 m_pTail = pChunk;
285
286 if (outputRequired) {
287 OnSend(0);
288 }
289 }
290 // File_checked_for_headers
291