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