6 #include "..\..\subtitles\TextFile.h"
7 #include ".\webserver.h"
8 #include ".\webclientsocket.h"
10 CWebClientSocket::CWebClientSocket(CWebServer
* pWebServer
, CMainFrame
* pMainFrame
)
11 : m_pWebServer(pWebServer
)
12 , m_pMainFrame(pMainFrame
)
16 CWebClientSocket::~CWebClientSocket()
20 bool CWebClientSocket::SetCookie(CString name
, CString value
, __time64_t expire
, CString path
, CString domain
)
22 if(name
.IsEmpty()) return(false);
23 if(value
.IsEmpty()) {m_cookie
.RemoveKey(name
); return true;}
25 m_cookie
[name
] = value
;
27 m_cookieattribs
[name
].path
= path
;
28 m_cookieattribs
[name
].domain
= domain
;
34 t
.GetAsSystemTime(st
);
36 SystemTimeToHttpDate(st
, str
);
37 m_cookieattribs
[name
].expire
= str
;
43 void CWebClientSocket::Clear()
46 m_hdrlines
.RemoveAll();
55 m_request
.RemoveAll();
58 void CWebClientSocket::Header()
62 if(m_hdr
.IsEmpty()) return;
64 CAtlList
<CString
> lines
;
65 Explode(m_hdr
, lines
, '\n');
66 CString str
= lines
.RemoveHead();
69 ExplodeMin(str
, sl
, ' ', 3);
70 m_cmd
= sl
.RemoveHead().MakeUpper();
71 m_path
= sl
.RemoveHead();
72 m_ver
= sl
.RemoveHead().MakeUpper();
73 ASSERT(sl
.GetCount() == 0);
75 POSITION pos
= lines
.GetHeadPosition();
78 Explode(lines
.GetNext(pos
), sl
, ':', 2);
79 if(sl
.GetCount() == 2)
80 m_hdrlines
[sl
.GetHead().MakeLower()] = sl
.GetTail();
84 // remember new cookies
86 POSITION pos
= m_hdrlines
.GetStartPosition();
90 m_hdrlines
.GetNextAssoc(pos
, key
, value
);
92 if(key
== _T("cookie"))
95 Explode(value
, sl
, ';');
96 POSITION pos2
= sl
.GetHeadPosition();
99 CAtlList
<CString
> sl2
;
100 Explode(sl
.GetNext(pos2
), sl2
, '=', 2);
101 m_cookie
[sl2
.GetHead()] = sl2
.GetCount() == 2 ? sl2
.GetTail() : _T("");
108 if(!m_cookie
.Lookup(_T("MPCSESSIONID"), m_sessid
))
110 srand((unsigned int)time(NULL
));
111 m_sessid
.Format(_T("%08x"), rand()*0x12345678);
112 SetCookie(_T("MPCSESSIONID"), m_sessid
);
116 // TODO: load session
119 CStringA reshdr
, resbody
;
121 if(m_cmd
== _T("POST"))
124 if(m_hdrlines
.Lookup(_T("content-length"), str
))
126 int len
= _tcstol(str
, NULL
, 10);
136 for(; len
> 0 && (err
= Receive(&c
, 1)) > 0; len
--)
139 if(c
== '\r') continue;
141 if(c
== '\n' || len
== 1)
143 CAtlList
<CString
> sl
;
144 Explode(AToT(UrlDecode(TToA(str
))), sl
, '&'); // FIXME
145 POSITION pos
= sl
.GetHeadPosition();
148 CAtlList
<CString
> sl2
;
149 Explode(sl
.GetNext(pos
), sl2
, '=', 2);
150 m_post
[sl2
.GetHead().MakeLower()] = sl2
.GetCount() == 2 ? sl2
.GetTail() : _T("");
156 if(err
== SOCKET_ERROR
)
159 while(err
== SOCKET_ERROR
&& GetLastError() == WSAEWOULDBLOCK
160 && timeout
-- > 0); // FIXME: this is just a dirty fix now
162 // FIXME: with IE it will only work if I read +2 bytes (?), btw Receive will just return -1
168 if(m_cmd
== _T("GET") || m_cmd
== _T("HEAD") || m_cmd
== _T("POST"))
170 CAtlList
<CString
> sl
;
172 Explode(m_path
, sl
, '?', 2);
173 m_path
= sl
.RemoveHead();
178 m_query
= sl
.GetTail();
180 Explode(Explode(m_query
, sl
, '#', 2), sl
, '&'); // oh yeah
181 // Explode(AToT(UrlDecode(TToA(Explode(m_query, sl, '#', 2)))), sl, '&'); // oh yeah
182 POSITION pos
= sl
.GetHeadPosition();
185 CAtlList
<CString
> sl2
;
186 Explode(AToT(UrlDecode(TToA(sl
.GetNext(pos
)))), sl2
, '=', 2);
187 // Explode(sl.GetNext(pos), sl2, '=', 2);
188 m_get
[sl2
.GetHead()] = sl2
.GetCount() == 2 ? sl2
.GetTail() : _T("");
192 // m_request <-- m_get+m_post+m_cookie
196 pos
= m_get
.GetStartPosition();
197 while(pos
) {m_get
.GetNextAssoc(pos
, key
, value
); m_request
[key
] = value
;}
198 pos
= m_post
.GetStartPosition();
199 while(pos
) {m_post
.GetNextAssoc(pos
, key
, value
); m_request
[key
] = value
;}
200 pos
= m_cookie
.GetStartPosition();
201 while(pos
) {m_cookie
.GetNextAssoc(pos
, key
, value
); m_request
[key
] = value
;}
204 m_pWebServer
->OnRequest(this, reshdr
, resbody
);
208 reshdr
= "HTTP/1.0 400 Bad Request\r\n";
211 if(!reshdr
.IsEmpty())
215 POSITION pos
= m_cookie
.GetStartPosition();
219 m_cookie
.GetNextAssoc(pos
, key
, value
);
220 reshdr
+= "Set-Cookie: " + key
+ "=" + value
;
221 POSITION pos2
= m_cookieattribs
.GetStartPosition();
225 cookie_attribs value
;
226 m_cookieattribs
.GetNextAssoc(pos2
, key
, value
);
227 if(!value
.path
.IsEmpty()) reshdr
+= " path=" + value
.path
;
228 if(!value
.expire
.IsEmpty()) reshdr
+= " expire=" + value
.expire
;
229 if(!value
.domain
.IsEmpty()) reshdr
+= " domain=" + value
.domain
;
236 "Server: MPC WebServer\r\n"
237 "Connection: close\r\n"
240 Send(reshdr
, reshdr
.GetLength());
242 if(m_cmd
!= _T("HEAD") && reshdr
.Find("HTTP/1.0 200 OK") == 0 && !resbody
.IsEmpty())
244 Send(resbody
, resbody
.GetLength());
247 CString connection
= _T("close");
248 m_hdrlines
.Lookup(_T("connection"), connection
);
253 // if(connection == _T("close"))
260 void CWebClientSocket::OnReceive(int nErrorCode
)
265 while(Receive(&c
, 1) > 0)
267 if(c
== '\r') continue;
270 int len
= m_hdr
.GetLength();
271 if(len
>= 2 && m_hdr
[len
-2] == '\n' && m_hdr
[len
-1] == '\n')
279 __super::OnReceive(nErrorCode
);
282 void CWebClientSocket::OnClose(int nErrorCode
)
284 // TODO: save session
285 m_pWebServer
->OnClose(this);
286 __super::OnClose(nErrorCode
);
291 bool CWebClientSocket::OnCommand(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
294 if(m_request
.Lookup(_T("wm_command"), arg
))
300 if(id
== ID_FILE_EXIT
) m_pMainFrame
->PostMessage(WM_COMMAND
, id
);
301 else m_pMainFrame
->SendMessage(WM_COMMAND
, id
);
305 if(arg
== CMD_SETPOS
&& m_request
.Lookup(_T("position"), arg
))
309 if(_stscanf(arg
, _T("%d%c%d%c%d%c%d"), &h
, &c
, &m
, &c
, &s
, &c
, &ms
) >= 5)
311 REFERENCE_TIME rtPos
= 10000i64
*(((h
*60+m
)*60+s
)*1000+ms
);
312 m_pMainFrame
->SeekTo(rtPos
);
313 for(int retries
= 20; retries
-- > 0; Sleep(50))
315 if(abs((int)((rtPos
- m_pMainFrame
->GetPos())/10000)) < 100)
320 else if(arg
== CMD_SETPOS
&& m_request
.Lookup(_T("percent"), arg
))
323 if(_stscanf(arg
, _T("%f"), &percent
) == 1)
324 m_pMainFrame
->SeekTo((REFERENCE_TIME
)(percent
/ 100 * m_pMainFrame
->GetDur()));
326 else if(arg
== CMD_SETVOLUME
&& m_request
.Lookup(_T("volume"), arg
))
328 int volume
= _tcstol(arg
, NULL
, 10);
329 m_pMainFrame
->m_wndToolBar
.Volume
= min(max(volume
, 1), 100);
330 m_pMainFrame
->OnPlayVolume(0);
336 if(!m_hdrlines
.Lookup(_T("referer"), ref
))
340 "HTTP/1.0 302 Found\r\n"
341 "Location: " + CStringA(ref
) + "\r\n";
346 bool CWebClientSocket::OnIndex(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
352 AppSettings
& s
= AfxGetAppSettings();
353 POSITION pos
= s
.wmcmds
.GetHeadPosition();
356 wmcmd
& wc
= s
.wmcmds
.GetNext(pos
);
358 str
.Format("%d", wc
.cmd
);
359 wmcoptions
+= "<option value=\"" + str
+ "\">"
360 + CStringA(wc
.name
) + "\r\n";
363 m_pWebServer
->LoadPage(IDR_HTML_INDEX
, body
, m_path
);
364 body
.Replace("[wmcoptions]", wmcoptions
);
369 bool CWebClientSocket::OnBrowser(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
371 CAtlList
<CStringA
> rootdrives
;
372 for(TCHAR drive
[] = _T("A:"); drive
[0] <= 'Z'; drive
[0]++)
373 if(GetDriveType(drive
) != DRIVE_NO_ROOT_DIR
)
374 rootdrives
.AddTail(CStringA(drive
) + '\\');
380 if(m_get
.Lookup(_T("path"), path
))
382 path
= WToT(UTF8To16(TToA(path
)));
384 if(CFileGetStatus(path
, fs
) && !(fs
.m_attribute
&CFile::directory
))
386 // TODO: make a new message for just opening files, this is a bit overkill now...
388 CAtlList
<CString
> cmdln
;
393 if(m_get
.Lookup(_T("focus"), focus
) && !focus
.CompareNoCase(_T("no")))
394 cmdln
.AddTail(_T("/nofocus"));
398 POSITION pos
= cmdln
.GetHeadPosition();
401 CString
& str
= cmdln
.GetNext(pos
);
402 len
+= (str
.GetLength()+1)*sizeof(TCHAR
);
405 CAutoVectorPtr
<BYTE
> buff
;
406 if(buff
.Allocate(4+len
))
409 *(DWORD
*)p
= cmdln
.GetCount();
412 POSITION pos
= cmdln
.GetHeadPosition();
415 CString
& str
= cmdln
.GetNext(pos
);
416 len
= (str
.GetLength()+1)*sizeof(TCHAR
);
417 memcpy(p
, (LPCTSTR
)str
, len
);
422 cds
.dwData
= 0x6ABE51;
423 cds
.cbData
= p
- buff
;
424 cds
.lpData
= (void*)(BYTE
*)buff
;
425 m_pMainFrame
->SendMessage(WM_COPYDATA
, (WPARAM
)NULL
, (LPARAM
)&cds
);
435 path
= m_pMainFrame
->m_wndPlaylistBar
.GetCur();
437 if(CFileGetStatus(path
, fs
) && !(fs
.m_attribute
&CFile::directory
))
445 if(path
.Find(_T("://")) >= 0)
448 if(CFileGetStatus(path
, fs
) && (fs
.m_attribute
&CFile::directory
)
449 || path
.Find(_T("\\")) == 0) // FIXME
462 POSITION pos
= rootdrives
.GetHeadPosition();
465 CStringA
& drive
= rootdrives
.GetNext(pos
);
467 files
+= "<tr class=\"dir\">\r\n";
469 "<td class=\"dirname\"><a href=\"[path]?path=" + UrlEncode(drive
) + "\">" + drive
+ "</a></td>"
470 "<td class=\"dirtype\">Directory</td>"
471 "<td class=\"dirsize\"> </td>\r\n"
472 "<td class=\"dirdate\"> </td>";
473 files
+= "</tr>\r\n";
482 if(path
.GetLength() > 3)
484 CPath
p(path
+ "..");
490 files
+= "<tr class=\"dir\">\r\n";
492 "<td class=\"dirname\"><a href=\"[path]?path=" + parent
+ "\">..</a></td>"
493 "<td class=\"dirtype\">Directory</td>"
494 "<td class=\"dirsize\"> </td>\r\n"
495 "<td class=\"dirdate\"> </td>";
496 files
+= "</tr>\r\n";
498 WIN32_FIND_DATA fd
= {0};
500 HANDLE hFind
= FindFirstFile(path
+ "*.*", &fd
);
501 if(hFind
!= INVALID_HANDLE_VALUE
)
505 if(!(fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) || fd
.cFileName
[0] == '.')
508 CString fullpath
= path
+ fd
.cFileName
;
510 files
+= "<tr class=\"dir\">\r\n";
512 "<td class=\"dirname\"><a href=\"[path]?path=" + UTF8Arg(fullpath
) + "\">" + UTF8(fd
.cFileName
) + "</a></td>"
513 "<td class=\"dirtype\">Directory</td>"
514 "<td class=\"dirsize\"> </td>\r\n"
515 "<td class=\"dirdate\"><nobr>" + CStringA(CTime(fd
.ftLastWriteTime
).Format(_T("%Y.%m.%d %H:%M"))) + "</nobr></td>";
516 files
+= "</tr>\r\n";
518 while(FindNextFile(hFind
, &fd
));
523 hFind
= FindFirstFile(path
+ "*.*", &fd
);
524 if(hFind
!= INVALID_HANDLE_VALUE
)
528 if(fd
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
)
531 CString fullpath
= path
+ fd
.cFileName
;
532 TCHAR
*ext
= _tcsrchr(fd
.cFileName
, '.');
533 if (ext
!= NULL
) ext
++;
536 size
.Format("%I64dK", ((UINT64
)fd
.nFileSizeHigh
<<22)|(fd
.nFileSizeLow
>>10));
538 CString
type(_T(" "));
539 LoadType(fullpath
, type
);
542 files
+= "<tr class=\"" + UTF8(ext
) + "\">\r\n";
544 files
+= "<tr class=\"noext\">\r\n";
546 "<td class=\"filename\"><a href=\"[path]?path=" + UTF8Arg(fullpath
) + "\">" + UTF8(fd
.cFileName
) + "</a></td>"
547 "<td class=\"filetype\"><nobr>" + UTF8(type
) + "</nobr></td>"
548 "<td class=\"filesize\" align=\"right\"><nobr>" + size
+ "</nobr></td>\r\n"
549 "<td class=\"filedate\"><nobr>" + CStringA(CTime(fd
.ftLastWriteTime
).Format(_T("%Y.%m.%d %H:%M"))) + "</nobr></td>";
550 files
+= "</tr>\r\n";
552 while(FindNextFile(hFind
, &fd
));
558 m_pWebServer
->LoadPage(IDR_HTML_BROWSER
, body
, m_path
);
559 body
.Replace("[charset]", "UTF-8"); // FIXME: win9x build...
560 body
.Replace("[currentdir]", UTF8(path
));
561 body
.Replace("[currentfiles]", files
);
566 bool CWebClientSocket::OnControls(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
568 CString path
= m_pMainFrame
->m_wndPlaylistBar
.GetCur();
578 OAFilterState fs
= m_pMainFrame
->GetMediaState();
580 state
.Format(_T("%d"), fs
);
584 case State_Stopped
: statestring
= ResStr(IDS_CONTROLS_STOPPED
); break;
585 case State_Paused
: statestring
= ResStr(IDS_CONTROLS_PAUSED
); break;
586 case State_Running
: statestring
= ResStr(IDS_CONTROLS_PLAYING
); break;
587 default: statestring
= _T("n/a"); break;
590 int pos
= (int)(m_pMainFrame
->GetPos()/10000);
591 int dur
= (int)(m_pMainFrame
->GetDur()/10000);
593 CString position
, duration
;
594 position
.Format(_T("%d"), pos
);
595 duration
.Format(_T("%d"), dur
);
597 CString positionstring
, durationstring
, playbackrate
;
598 // positionstring.Format(_T("%02d:%02d:%02d.%03d"), (pos/3600000), (pos/60000)%60, (pos/1000)%60, pos%1000);
599 // durationstring.Format(_T("%02d:%02d:%02d.%03d"), (dur/3600000), (dur/60000)%60, (dur/1000)%60, dur%1000);
600 positionstring
.Format(_T("%02d:%02d:%02d"), (pos
/3600000), (pos
/60000)%60, (pos
/1000)%60);
601 durationstring
.Format(_T("%02d:%02d:%02d"), (dur
/3600000), (dur
/60000)%60, (dur
/1000)%60);
602 playbackrate
= _T("1"); // TODO
604 CString volumelevel
, muted
;
605 volumelevel
.Format(_T("%d"), m_pMainFrame
->m_wndToolBar
.m_volctrl
.GetPos());
606 muted
.Format(_T("%d"), m_pMainFrame
->m_wndToolBar
.Volume
== -10000 ? 1 : 0);
608 CString
reloadtime(_T("0")); // TODO
610 m_pWebServer
->LoadPage(IDR_HTML_CONTROLS
, body
, m_path
);
611 body
.Replace("[charset]", "UTF-8"); // FIXME: win9x build...
612 body
.Replace("[filepatharg]", UTF8Arg(path
));
613 body
.Replace("[filepath]", UTF8(path
));
614 body
.Replace("[filedirarg]", UTF8Arg(dir
));
615 body
.Replace("[filedir]", UTF8(dir
));
616 body
.Replace("[state]", UTF8(state
));
617 body
.Replace("[statestring]", UTF8(statestring
));
618 body
.Replace("[position]", UTF8(position
));
619 body
.Replace("[positionstring]", UTF8(positionstring
));
620 body
.Replace("[duration]", UTF8(duration
));
621 body
.Replace("[durationstring]", UTF8(durationstring
));
622 body
.Replace("[volumelevel]", UTF8(volumelevel
));
623 body
.Replace("[muted]", UTF8(muted
));
624 body
.Replace("[playbackrate]", UTF8(playbackrate
));
625 body
.Replace("[reloadtime]", UTF8(reloadtime
));
630 bool CWebClientSocket::OnStatus(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
633 CString path = m_pMainFrame->m_wndPlaylistBar.GetCur(), dir;
634 if(!path.IsEmpty()) {CPath p(path); p.RemoveFileSpec(); dir = (LPCTSTR)p;}
635 path.Replace(_T("'"), _T("\\'"));
636 dir.Replace(_T("'"), _T("\\'"));
638 CString volumelevel, muted;
639 volumelevel.Format(_T("%d"), m_pMainFrame->m_wndToolBar.m_volctrl.GetPos());
640 muted.Format(_T("%d"), m_pMainFrame->m_wndToolBar.Volume == -10000 ? 1 : 0);
641 body.Replace("[volumelevel]", UTF8(volumelevel));
642 body.Replace("[muted]", UTF8(muted));
645 m_pMainFrame
->GetWindowText(title
);
647 CString status
= m_pMainFrame
->GetStatusMessage();
649 int pos
= (int)(m_pMainFrame
->GetPos()/10000);
650 int dur
= (int)(m_pMainFrame
->GetDur()/10000);
652 CString posstr
, durstr
;
653 posstr
.Format(_T("%02d:%02d:%02d"), (pos
/3600000), (pos
/60000)%60, (pos
/1000)%60);
654 durstr
.Format(_T("%02d:%02d:%02d"), (dur
/3600000), (dur
/60000)%60, (dur
/1000)%60);
656 title
.Replace(_T("'"), _T("\\'"));
657 status
.Replace(_T("'"), _T("\\'"));
659 body
.Format("OnStatus('%s', '%s', %d, '%s', %d, '%s', %d, %d)", // , '%s', '%s'
660 UTF8(title
), UTF8(status
),
661 pos
, UTF8(posstr
), dur
, UTF8(durstr
),
662 m_pMainFrame
->IsMuted(), m_pMainFrame
->GetVolume()
663 /*, UTF8(path), UTF8(dir)*/);
668 bool CWebClientSocket::OnError404(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
670 m_pWebServer
->LoadPage(IDR_HTML_404
, body
, m_path
);
674 bool CWebClientSocket::OnPlayer(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
676 m_pWebServer
->LoadPage(IDR_HTML_PLAYER
, body
, m_path
);
682 bool CWebClientSocket::OnSnapShotJpeg(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
684 // TODO: add quality control and return logo when nothing is loaded
690 CAtlArray
<BYTE
> jpeg
;
691 if(m_pMainFrame
->GetDIB(&pData
, size
, true))
693 if(CJpegEncoderMem().Encode(pData
, jpeg
))
696 "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n"
697 "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n"
698 "Pragma: no-cache\r\n";
699 body
= CStringA((char*)jpeg
.GetData(), jpeg
.GetCount());
710 #include "ConvertDlg.h"
712 bool CWebClientSocket::OnConvRes(CStringA
& hdr
, CStringA
& body
, CStringA
& mime
)
715 if(!m_get
.Lookup(_T("id"), id
))
719 if(1 != _stscanf(id
, _T("%x"), &key
) || key
== 0)
722 CAutoLock
cAutoLock(&CDSMResource::m_csResources
);
724 CDSMResource
* res
= NULL
;
725 if(!CDSMResource::m_resources
.Lookup(key
, res
) || !res
)
728 body
= CStringA((const char*)res
->data
.GetData(), res
->data
.GetCount());
729 mime
= CString(res
->mime
);