Implement XDG compliance, logging, and improved 401 error handling
- 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
This commit is contained in:
541
Wawi Source/ClientConnection.cpp
Normal file
541
Wawi Source/ClientConnection.cpp
Normal file
@@ -0,0 +1,541 @@
|
||||
/* --- 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
|
||||
}
|
||||
Reference in New Issue
Block a user