- Install to ~/.local/bin/winamp-mpris - Use ~/.local/state/winamp-mpris/bridge.log for logging - Use $XDG_RUNTIME_DIR/winamp-mpris.pid for PID management - Add detailed user notification for 401 Unauthorized errors - Add install.sh for automated, standard-compliant setup - Include Winamp Web Interface source code and installer in repository
541 lines
14 KiB
C++
541 lines
14 KiB
C++
/* --- CLIENTCONNECTION.CPP ---
|
|
/
|
|
/ Written by Phil Himsworth, 2001-2002.
|
|
/
|
|
/*/
|
|
|
|
#include <windows.h>
|
|
#include "types.h"
|
|
#include "wamessage.h"
|
|
#include "main.h"
|
|
#include "op_winamp.h"
|
|
#include "html.h"
|
|
#include "plugin.h"
|
|
#include "resource.h"
|
|
|
|
|
|
extern char mp3root[MAX_PATH], logfiledir[MAX_PATH];
|
|
extern int dl_wa_files, dl_other_files, frames, title_refresh;
|
|
extern char szAppVer[];
|
|
extern char pagetitle[255];
|
|
|
|
extern winampGeneralPurposePlugin plugin;
|
|
|
|
bool loglocked = false;
|
|
|
|
char realm_server[] = "Wawi Server Control";
|
|
char realm_control[] = "Wawi Playback Control";
|
|
char realm_clear[] = "Wawi Playlist Clear Control";
|
|
char realm_playlist[] = "Wawi Playlist Control";
|
|
char realm_download[] = "Wawi Downloads";
|
|
char realm_browse[] = "Wawi Music Collection";
|
|
|
|
CControl Control;
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// Thread called for each connection
|
|
DWORD WINAPI ClientConnection(LPVOID socket_param) // new thread for a new client.
|
|
{
|
|
|
|
|
|
bool matched = false;
|
|
connection conn; // structure to hold request details
|
|
ZeroMemory(&conn,sizeof(conn));
|
|
conn.http.auth = AUTH_ANON;
|
|
wsprintf(conn.http.user,"anon");
|
|
conn.log = true;
|
|
conn.state = ST_NONE;
|
|
conn.http.responsecode = 200;
|
|
|
|
conn.socket = (SOCKET)socket_param; // convert param into socket
|
|
|
|
|
|
int rhost_len = sizeof(conn.remote_host);
|
|
getpeername(conn.socket,(sockaddr*)&conn.remote_host,&rhost_len);
|
|
|
|
if (!GetRequest(&conn))
|
|
{
|
|
//MessageBox(NULL,conn.http.file,"400",MB_TOPMOST | MB_OK);
|
|
conn.http.responsecode = 400;
|
|
Send400(&conn);
|
|
shutdown(conn.socket,0);
|
|
closesocket(conn.socket);
|
|
MakeLogEntry(&conn);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// ------------- Images ------------------
|
|
// Of the form /img?image=n where 'n' is the id of the image, eg. IDR_DIRECTORY.
|
|
if (StrComp(conn.http.page,"/img"))
|
|
{
|
|
//MessageBox(NULL,conn.http.file,"File",MB_OK | MB_TOPMOST);
|
|
//MessageBox(NULL,conn.http.page,"Page",MB_OK | MB_TOPMOST);
|
|
conn.log = false;
|
|
conn.state = Control.Image(&conn,IMG_GIF);
|
|
}
|
|
|
|
// ------------- Favicon.ico!!!-----------
|
|
// Sent the same as a img resource but with a different header
|
|
if (StrComp(conn.http.page,"/favicon.ico"))
|
|
{
|
|
//conn.log = false;
|
|
wsprintf(conn.http.file,"/img?%d",IDR_ICO_WINAMP);
|
|
conn.state = Control.Image(&conn,IMG_ICO);
|
|
}
|
|
|
|
|
|
// ------------ Top -----------------
|
|
if (StrComp(conn.http.page,"/top"))
|
|
{
|
|
conn.log = false;
|
|
conn.state = Control.TopFrame(&conn);
|
|
}
|
|
|
|
|
|
// ------------- Small Title ------------
|
|
// No page formatting at all; just the song info.
|
|
|
|
if (StrComp(conn.http.page,"/smalltitle"))
|
|
conn.state = Control.SmallTitle(&conn);
|
|
|
|
|
|
// -------------- Title ----------------
|
|
if (StrComp(conn.http.page,"/title"))
|
|
{
|
|
conn.log = false;
|
|
conn.state = Control.Title(&conn);
|
|
}
|
|
|
|
|
|
// ---------- Shutdown Computer -------------
|
|
if (StrComp(conn.http.page,"/shutdown"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_SERVER))
|
|
conn.state = SendBasic401(&conn,realm_server);
|
|
else
|
|
conn.state = Control.Shutdown(&conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------- Clear Playlist -----
|
|
if (StrComp(conn.http.page,"/clear"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CLEAR))
|
|
conn.state = SendBasic401(&conn,realm_playlist);
|
|
else
|
|
conn.state = Control.Clear(&conn);
|
|
}
|
|
|
|
|
|
// ---------- Sort playlist ----------
|
|
if (StrComp(conn.http.page,"/sort"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_PLAYLIST))
|
|
conn.state = SendBasic401(&conn,realm_playlist);
|
|
else
|
|
conn.state = Control.Sort(&conn);
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------ Browse -----------
|
|
if (StrComp(conn.http.page,"/browse"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_BROWSE))
|
|
conn.state = SendBasic401(&conn,realm_browse);
|
|
else
|
|
{
|
|
char mp3path[MAX_PATH];
|
|
if (GetArgValue(&conn,"path",(char*)&mp3path,MAX_PATH) == false)
|
|
wsprintf(mp3path,"%%5c");
|
|
ReplaceSlashes(mp3path);
|
|
conn.state = Browse(&conn, mp3path);
|
|
if (frames == 1)
|
|
LinkBar(&conn);
|
|
}
|
|
}
|
|
|
|
// ------------ Download ---------
|
|
if (StrComp(conn.http.page,"/dl"))
|
|
if (!(conn.http.auth & AUTH_DOWNLOAD))
|
|
conn.state = SendBasic401(&conn,realm_download);
|
|
else
|
|
conn.state = Control.Download(&conn);
|
|
|
|
|
|
|
|
// ------------ Load -------------
|
|
if (StrComp(conn.http.page,"/ld")) // ld = load
|
|
{
|
|
if (!(conn.http.auth & AUTH_PLAYLIST))
|
|
conn.state = SendBasic401(&conn,realm_playlist);
|
|
else
|
|
conn.state = Control.Load(&conn);
|
|
}
|
|
|
|
// ------------ List -------------
|
|
if (StrComp(conn.http.page,"/list"))
|
|
conn.state = Control.List(&conn);
|
|
|
|
|
|
// ------------ Next -------------
|
|
if (StrComp(conn.http.page,"/next")) // Next track
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Next(&conn);
|
|
}
|
|
|
|
|
|
// ------------ Prev -------------
|
|
if (StrComp(conn.http.page,"/prev")) // Previous track
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Prev(&conn);
|
|
}
|
|
|
|
|
|
// ------------ Play -------------
|
|
if (StrComp(conn.http.page,"/play"))
|
|
// Play (or play?xxxx)
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Play(&conn);
|
|
}
|
|
|
|
// ------------ Stop -------------
|
|
if (StrComp(conn.http.page,"/stop"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Stop(&conn);
|
|
}
|
|
|
|
// ----------- Stop slowly -----------
|
|
|
|
if (StrComp(conn.http.page,"/stopslow"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.StopSlow(&conn);
|
|
}
|
|
|
|
|
|
// ------------ Pause ------------
|
|
if (StrComp(conn.http.page,"/pause"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Pause(&conn);
|
|
}
|
|
|
|
// ------------ Volume -----------
|
|
if (StrComp(conn.http.page,"/vol"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Volume(&conn);
|
|
}
|
|
|
|
// ----------- Random / Repeat --------------
|
|
if (StrComp(conn.http.page,"/playmode"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CONTROL))
|
|
conn.state = SendBasic401(&conn,realm_control);
|
|
else
|
|
conn.state = Control.Playmode(&conn);
|
|
}
|
|
|
|
// ------------- Login/out---------------
|
|
if (StrComp(conn.http.page,"/login"))
|
|
conn.state = SendBasic401(&conn,"Winamp Web Interface");
|
|
|
|
// ------------ About ----------------
|
|
|
|
if (StrComp(conn.http.page,"/about"))
|
|
{
|
|
conn.state = Control.About(&conn);
|
|
}
|
|
|
|
|
|
// ---------- Delete playlist entry ----
|
|
if (StrComp(conn.http.page,"/delete"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_CLEAR))
|
|
conn.state = SendBasic401(&conn,realm_clear);
|
|
else
|
|
conn.state = Control.Delete(&conn);
|
|
}
|
|
|
|
|
|
// ---------- Stylesheet -------------
|
|
if (StrComp(conn.http.page,"/wawi.css"))
|
|
{
|
|
conn.log = false;
|
|
conn.state = Control.UserStyle(&conn);
|
|
}
|
|
|
|
// ------------ Main page ----------------
|
|
if (StrComp(conn.http.page,"/main"))
|
|
{
|
|
conn.state = Control.Main(&conn);
|
|
}
|
|
|
|
|
|
// ------------ PDA Page ----------------
|
|
if (StrComp(conn.http.page,"/pda"))
|
|
{
|
|
conn.state = Control.pda(&conn);
|
|
}
|
|
|
|
// ------------ Admin ------------------
|
|
if (StrComp(conn.http.page,"/admin"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_SERVER))
|
|
conn.state = SendBasic401(&conn,realm_server);
|
|
else
|
|
conn.state = Control.WebAdmin(&conn);
|
|
}
|
|
|
|
if (StrComp(conn.http.page,"/apply"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_SERVER))
|
|
conn.state = SendBasic401(&conn,realm_server);
|
|
else
|
|
conn.state = Control.ApplyAdmin(&conn);
|
|
}
|
|
|
|
|
|
// -------------- Users -----------------
|
|
if (StrComp(conn.http.page,"/user"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_SERVER))
|
|
conn.state = SendBasic401(&conn,realm_server);
|
|
else
|
|
conn.state = Control.User(&conn);
|
|
}
|
|
|
|
// ------ Set user permissions ------------
|
|
if (StrComp(conn.http.page,"/setuser"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_SERVER))
|
|
conn.state = SendBasic401(&conn,realm_server);
|
|
else
|
|
conn.state = Control.SetUser(&conn);
|
|
}
|
|
|
|
// ------ Save playlist ------------
|
|
if (StrComp(conn.http.page,"/save"))
|
|
{
|
|
if (!(conn.http.auth & AUTH_PLAYLIST))
|
|
conn.state = SendBasic401(&conn,realm_playlist);
|
|
else
|
|
conn.state = Control.SavePlaylist(&conn);
|
|
}
|
|
|
|
|
|
// ------ Page redirect - page?page=(main|list|browse)
|
|
if (StrComp(conn.http.page,"/page"))
|
|
{
|
|
char page[10];
|
|
GetArgValue(&conn,"page",page,10);
|
|
|
|
if (!(StrComp(page,"main") || StrComp(page,"list") || StrComp(page,"browse")))
|
|
wsprintf(page,"main");
|
|
|
|
prints(&conn,"HTTP/1.0 302 Temporarily Moved\nLocation: /");
|
|
prints(&conn,page);
|
|
prints(&conn,"\nConnection: close\nContent-type: text/html\n\n");
|
|
prints(&conn,"<html>\n<head>\n<title>Winamp Web Interface</title>\n</head>\n");
|
|
prints(&conn,"<body>\n<p><a href=\"/");
|
|
prints(&conn,page);
|
|
prints(&conn,"\">Winamp Web Interface</a></p>\n</body>\n</html>\n\n");
|
|
|
|
conn.state = ST_CLOSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (StrComp(conn.http.page,"/winamp"))
|
|
{
|
|
OpenHTTPHeader(&conn,"text/html",0,NULL);
|
|
if (conn.http.reqID == RID_HEAD)
|
|
conn.state = ST_CLOSE;
|
|
else
|
|
{
|
|
char reqpage[10];
|
|
GetArgValue(&conn,"page",reqpage,10);
|
|
|
|
if (!(StrComp(reqpage,"main") || StrComp(reqpage,"list") || StrComp(reqpage,"browse")))
|
|
wsprintf(reqpage,"main");
|
|
|
|
OpenHtmlHeader(&conn);
|
|
Style(&conn);
|
|
CloseHeader(&conn);
|
|
if (frames == 3)
|
|
{
|
|
prints(&conn,"<frameset border=\"0\" frameborder=\"0\" rows=\"35,1*,27\">\n<frame name=\"top\" src=\"/top\" noresize marginheight=\"0\" marginwidth=\"0\">\n<frame name=\"main\" src=\"/page?page=");
|
|
prints(&conn,reqpage);
|
|
prints(&conn,"\" noresize>\n<frame name=\"bottom\" src=\"title\" noresize marginheight=\"1\" marginwidth=\"5\">\n");
|
|
prints(&conn,"<noframes><p><b>Frames are required for this site!<b></p><p>If no-frames operation is required, check the <b>No Frames Mode</b> box in the Options screen.</p>\n");
|
|
prints(&conn,"</frameset>\n");
|
|
}
|
|
else
|
|
{
|
|
prints(&conn,"<frameset border=\"0\" frameborder=\"0\" rows=\"35,1*\">\n<frame name=\"top\" src=\"/top\" noresize marginheight=\"0\" marginwidth=\"0\">\n<frame name=\"main\" src=\"/page?page=");
|
|
prints(&conn,reqpage);
|
|
prints(&conn,"\" noresize>\n");
|
|
prints(&conn,"<noframes><p><b>Frames are required for this site!<b></p><p>If no-frames operation is required, check the <b>No Frames Mode</b> box in the Options screen.</p>\n");
|
|
prints(&conn,"</frameset>\n");
|
|
}
|
|
|
|
conn.state = ST_CLOSEBODY;
|
|
}
|
|
}
|
|
|
|
if (conn.state == ST_NONE) // something else, including '/'
|
|
{ // 302 to /winamp?page=frames
|
|
if (frames == 1)
|
|
{
|
|
prints(&conn,"HTTP/1.0 302 Temporarily Moved\nLocation: /page?page=main\nConnection: close\nContent-type: text/html\n\n");
|
|
prints(&conn,"<html>\n<head>\n<title>Winamp Web Interface</title>\n</head>\n");
|
|
prints(&conn,"<body>\n<p><a href=\"/page?page=main\">Winamp Web Interface</a></p>\n</body>\n</html>\n\n");
|
|
}
|
|
else
|
|
{
|
|
prints(&conn,"HTTP/1.0 302 Temporarily Moved\nLocation: /winamp?page=main\nConnection: close\nContent-type: text/html\n\n");
|
|
prints(&conn,"<html>\n<head>\n<title>Winamp Web Interface</title>\n</head>\n");
|
|
prints(&conn,"<body>\n<p><a href=\"/winamp?page=main\">Winamp Web Interface</a></p>\n</body>\n</html>\n\n");
|
|
}
|
|
//conn.log = false;
|
|
conn.state = ST_CLOSE;
|
|
}
|
|
|
|
if (conn.state == ST_CLOSEBODY)
|
|
CloseBody(&conn); // end html
|
|
|
|
|
|
flush(&conn);
|
|
closesocket(conn.socket); // close connection
|
|
|
|
if (conn.log)
|
|
MakeLogEntry(&conn);
|
|
|
|
|
|
return true;
|
|
};
|
|
|
|
/*
|
|
struct HOSTENT FAR * gethostbyaddr (
|
|
const char FAR * addr,
|
|
int len,
|
|
int type
|
|
);
|
|
*/
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// Adds entry in log
|
|
void MakeLogEntry(connection * conn)
|
|
{
|
|
int logbuffer_len;
|
|
|
|
SYSTEMTIME time;
|
|
GetSystemTime(&time);
|
|
char timestr[100]; // "[DD/MM/YYYY HH:MM:SS]"
|
|
wsprintf(timestr,"[%02d.%02d.%04d %02d:%02d:%02d]",time.wDay, time.wMonth, time.wYear, time.wHour, time.wMinute, time.wSecond);
|
|
|
|
char logbuffer[2048];
|
|
ZeroMemory(&logbuffer,sizeof(logbuffer));
|
|
|
|
logbuffer_len = wsprintf(logbuffer,"%s %s %s %d %s %s\r\n",timestr, (char*)inet_ntoa(conn->remote_host.sin_addr), conn->http.user, conn->http.responsecode, conn->http.request, conn->http.file);
|
|
|
|
// Find log file, in winamp plugin directory (same dir as gen_httpsrv.dll)
|
|
char logfilepath[MAX_PATH], *p;
|
|
char logfile[MAX_PATH];
|
|
|
|
if (lstrlen(logfiledir) == 0)
|
|
{
|
|
GetModuleFileName(plugin.hDllInstance,logfilepath,sizeof(logfilepath));
|
|
p=logfilepath+lstrlen(logfilepath);
|
|
while (p >= logfilepath && *p != '\\') p--;
|
|
if (++p >= logfilepath) *p = 0;
|
|
wsprintf(logfile,"%shttpsrv.log",logfilepath);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(logfilepath,logfiledir);
|
|
wsprintf(logfile,"%s\\httpsrv.log",logfilepath);
|
|
}
|
|
|
|
//lstrcat(logfile,"gen_httpsrv_log.log);
|
|
|
|
//MessageBox(NULL,logfile,"Logfile",MB_OK| MB_TOPMOST);
|
|
|
|
while(loglocked == true) // wait for log file to be free
|
|
Sleep(50);
|
|
|
|
loglocked = true; // lock it
|
|
|
|
HANDLE hLog = CreateFile( logfile,
|
|
GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_ALWAYS,
|
|
0,
|
|
0);
|
|
|
|
if (hLog == NULL)
|
|
{
|
|
//MessageBox(NULL,"Couldn't open log file","gen_httpSrv debug", MB_OK);
|
|
return;
|
|
}
|
|
|
|
DWORD dwSize = GetFileSize(hLog, NULL);
|
|
|
|
//MessageBox(NULL,logfilename,"logfile size",MB_OK);
|
|
SetFilePointer(hLog,(LONG)dwSize,NULL,FILE_BEGIN);
|
|
|
|
unsigned long written;
|
|
WriteFile(hLog,&logbuffer,logbuffer_len,&written,NULL);
|
|
|
|
CloseHandle(hLog);
|
|
|
|
if (dwSize >= 50000) // rename log file to something else to start a new one
|
|
{
|
|
char logfilename[MAX_PATH];
|
|
bool name_used = true;
|
|
int letter = 1;
|
|
while(name_used)
|
|
{ // loop through acceptable names, find first unused one of form httpsrv_dd-dd-yyyy_n.log
|
|
wsprintf(logfilename,"%shttpsrv_%d-%d-%d_%d.log",logfilepath,time.wDay,time.wMonth,time.wYear,letter);
|
|
//MessageBox(NULL,logfilename,"Logfilename",MB_OK | MB_TOPMOST);
|
|
if (GetFileAttributes(logfilename) == 0xFFFFFFFF)
|
|
{
|
|
name_used = false;
|
|
MoveFile(logfile,logfilename);
|
|
}
|
|
else
|
|
{
|
|
letter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
loglocked = false; // release log file
|
|
} |