/* * $Id$ * * (C) 2003-2006 Gabest * (C) 2006-2010 see AUTHORS * * This file is part of mplayerc. * * Mplayerc 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 3 of the License, or * (at your option) any later version. * * Mplayerc 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, see . * */ #include "stdafx.h" #include #include "mplayerc.h" #include "resource.h" #include "MainFrm.h" #include "../../Subtitles/TextFile.h" #include "WebServer.h" #include "WebClientSocket.h" CWebClientSocket::CWebClientSocket(CWebServer* pWebServer, CMainFrame* pMainFrame) : m_pWebServer(pWebServer) , m_pMainFrame(pMainFrame) { } CWebClientSocket::~CWebClientSocket() { } bool CWebClientSocket::SetCookie(CString name, CString value, __time64_t expire, CString path, CString domain) { if(name.IsEmpty()) return(false); if(value.IsEmpty()) { m_cookie.RemoveKey(name); return true; } m_cookie[name] = value; m_cookieattribs[name].path = path; m_cookieattribs[name].domain = domain; if(expire >= 0) { CTime t(expire); SYSTEMTIME st; t.GetAsSystemTime(st); CStringA str; SystemTimeToHttpDate(st, str); m_cookieattribs[name].expire = str; } return true; } void CWebClientSocket::Clear() { m_hdr.Empty(); m_hdrlines.RemoveAll(); m_data.Empty(); m_cmd.Empty(); m_path.Empty(); m_ver.Empty(); m_get.RemoveAll(); m_post.RemoveAll(); m_cookie.RemoveAll(); m_request.RemoveAll(); } void CWebClientSocket::Header() { if(m_cmd.IsEmpty()) { if(m_hdr.IsEmpty()) return; CAtlList lines; Explode(m_hdr, lines, '\n'); CString str = lines.RemoveHead(); CAtlList sl; ExplodeMin(str, sl, ' ', 3); m_cmd = sl.RemoveHead().MakeUpper(); m_path = sl.RemoveHead(); m_ver = sl.RemoveHead().MakeUpper(); ASSERT(sl.GetCount() == 0); POSITION pos = lines.GetHeadPosition(); while(pos) { Explode(lines.GetNext(pos), sl, ':', 2); if(sl.GetCount() == 2) m_hdrlines[sl.GetHead().MakeLower()] = sl.GetTail(); } } // remember new cookies POSITION pos = m_hdrlines.GetStartPosition(); while(pos) { CString key, value; m_hdrlines.GetNextAssoc(pos, key, value); if(key == _T("cookie")) { CAtlList sl; Explode(value, sl, ';'); POSITION pos2 = sl.GetHeadPosition(); while(pos2) { CAtlList sl2; Explode(sl.GetNext(pos2), sl2, '=', 2); m_cookie[sl2.GetHead()] = sl2.GetCount() == 2 ? sl2.GetTail() : _T(""); } } } // start new session if(!m_cookie.Lookup(_T("MPCSESSIONID"), m_sessid)) { srand((unsigned int)time(NULL)); m_sessid.Format(_T("%08x"), rand()*0x12345678); SetCookie(_T("MPCSESSIONID"), m_sessid); } else { // TODO: load session } CStringA reshdr, resbody; if(m_cmd == _T("POST")) { CString str; if(m_hdrlines.Lookup(_T("content-length"), str)) { int len = _tcstol(str, NULL, 10); str.Empty(); int err = 0; char c; int timeout = 1000; do { for(; len > 0 && (err = Receive(&c, 1)) > 0; len--) { m_data += c; if(c == '\r') continue; str += c; if(c == '\n' || len == 1) { CAtlList sl; Explode(AToT(UrlDecode(TToA(str))), sl, '&'); // FIXME POSITION pos = sl.GetHeadPosition(); while(pos) { CAtlList sl2; Explode(sl.GetNext(pos), sl2, '=', 2); m_post[sl2.GetHead().MakeLower()] = sl2.GetCount() == 2 ? sl2.GetTail() : _T(""); } str.Empty(); } } if(err == SOCKET_ERROR) Sleep(1); } while(err == SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK && timeout-- > 0); // FIXME: this is just a dirty fix now // FIXME: with IE it will only work if I read +2 bytes (?), btw Receive will just return -1 Receive(&c, 1); Receive(&c, 1); } } if(m_cmd == _T("GET") || m_cmd == _T("HEAD") || m_cmd == _T("POST")) { CAtlList sl; Explode(m_path, sl, '?', 2); m_path = sl.RemoveHead(); m_query.Empty(); if(!sl.IsEmpty()) { m_query = sl.GetTail(); Explode(Explode(m_query, sl, '#', 2), sl, '&'); // oh yeah // Explode(AToT(UrlDecode(TToA(Explode(m_query, sl, '#', 2)))), sl, '&'); // oh yeah POSITION pos = sl.GetHeadPosition(); while(pos) { CAtlList sl2; Explode(AToT(UrlDecode(TToA(sl.GetNext(pos)))), sl2, '=', 2); // Explode(sl.GetNext(pos), sl2, '=', 2); m_get[sl2.GetHead()] = sl2.GetCount() == 2 ? sl2.GetTail() : _T(""); } } // m_request <-- m_get+m_post+m_cookie { CString key, value; POSITION pos; pos = m_get.GetStartPosition(); while(pos) { m_get.GetNextAssoc(pos, key, value); m_request[key] = value; } pos = m_post.GetStartPosition(); while(pos) { m_post.GetNextAssoc(pos, key, value); m_request[key] = value; } pos = m_cookie.GetStartPosition(); while(pos) { m_cookie.GetNextAssoc(pos, key, value); m_request[key] = value; } } m_pWebServer->OnRequest(this, reshdr, resbody); } else { reshdr = "HTTP/1.0 400 Bad Request\r\n"; } if(!reshdr.IsEmpty()) { // cookies { POSITION pos = m_cookie.GetStartPosition(); while(pos) { CString key, value; m_cookie.GetNextAssoc(pos, key, value); reshdr += "Set-Cookie: " + key + "=" + value; POSITION pos2 = m_cookieattribs.GetStartPosition(); while(pos2) { CString key; cookie_attribs value; m_cookieattribs.GetNextAssoc(pos2, key, value); if(!value.path.IsEmpty()) reshdr += " path=" + value.path; if(!value.expire.IsEmpty()) reshdr += " expire=" + value.expire; if(!value.domain.IsEmpty()) reshdr += " domain=" + value.domain; } reshdr += "\r\n"; } } reshdr += "Server: MPC WebServer\r\n" "Connection: close\r\n" "\r\n"; Send(reshdr, reshdr.GetLength()); if(m_cmd != _T("HEAD") && reshdr.Find("HTTP/1.0 200 OK") == 0 && !resbody.IsEmpty()) { Send(resbody, resbody.GetLength()); } CString connection = _T("close"); m_hdrlines.Lookup(_T("connection"), connection); Clear(); // TODO // if(connection == _T("close")) OnClose(0); } } // void CWebClientSocket::OnReceive(int nErrorCode) { if(nErrorCode == 0) { char c; while(Receive(&c, 1) > 0) { if(c == '\r') continue; else m_hdr += c; int len = m_hdr.GetLength(); if(len >= 2 && m_hdr[len-2] == '\n' && m_hdr[len-1] == '\n') { Header(); return; } } } __super::OnReceive(nErrorCode); } void CWebClientSocket::OnClose(int nErrorCode) { // TODO: save session m_pWebServer->OnClose(this); __super::OnClose(nErrorCode); } //////////////////// bool CWebClientSocket::OnCommand(CStringA& hdr, CStringA& body, CStringA& mime) { CString arg; if(m_request.Lookup(_T("wm_command"), arg)) { int id = _ttol(arg); if(id > 0) { if(id == ID_FILE_EXIT) m_pMainFrame->PostMessage(WM_COMMAND, id); else m_pMainFrame->SendMessage(WM_COMMAND, id); } else { if(arg == CMD_SETPOS && m_request.Lookup(_T("position"), arg)) { int h, m, s, ms = 0; TCHAR c; if(_stscanf(arg, _T("%d%c%d%c%d%c%d"), &h, &c, &m, &c, &s, &c, &ms) >= 5) { REFERENCE_TIME rtPos = 10000i64*(((h*60+m)*60+s)*1000+ms); m_pMainFrame->SeekTo(rtPos); for(int retries = 20; retries-- > 0; Sleep(50)) { if(abs((int)((rtPos - m_pMainFrame->GetPos())/10000)) < 100) break; } } } else if(arg == CMD_SETPOS && m_request.Lookup(_T("percent"), arg)) { float percent = 0; if(_stscanf_s(arg, _T("%f"), &percent) == 1) m_pMainFrame->SeekTo((REFERENCE_TIME)(percent / 100 * m_pMainFrame->GetDur())); } else if(arg == CMD_SETVOLUME && m_request.Lookup(_T("volume"), arg)) { int volume = _tcstol(arg, NULL, 10); m_pMainFrame->m_wndToolBar.Volume = min(max(volume, 1), 100); m_pMainFrame->OnPlayVolume(0); } } } CString ref; if(!m_hdrlines.Lookup(_T("referer"), ref)) return true; hdr = "HTTP/1.0 302 Found\r\n" "Location: " + CStringA(ref) + "\r\n"; return true; } bool CWebClientSocket::OnIndex(CStringA& hdr, CStringA& body, CStringA& mime) { CStringA wmcoptions; // generate page AppSettings& s = AfxGetAppSettings(); POSITION pos = s.wmcmds.GetHeadPosition(); while(pos) { wmcmd& wc = s.wmcmds.GetNext(pos); CStringA str; str.Format("%d", wc.cmd); wmcoptions += "