Files
winamp-mpris/Wawi Source/ClientConnection.cpp
ergosteur 22492dbee9 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
2026-04-08 18:29:40 -04:00

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
}