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:
2026-04-08 18:29:40 -04:00
parent 1e9257a27f
commit 22492dbee9
120 changed files with 9092 additions and 10 deletions

530
Wawi Source/WebAdmin.cpp Normal file
View File

@@ -0,0 +1,530 @@
/* --- WEBADMIN.CPP ---
/
/ Written by Phil Himsworth, 2001-2002.
/
/*/
#include <windows.h>
#include "types.h"
#include "main.h"
#include "html.h"
#include "plugin.h"
#include "resource.h"
#include "op_winamp.h"
extern char mp3root[MAX_PATH], playlistdir[MAX_PATH], logfiledir[MAX_PATH], filetypes[255];
extern int browse_enable, dl_wa_files, dl_other_files, show_other_files, frames, title_refresh, securepassword;
extern char szAppVer[];
extern char pagetitle[255];
extern winampGeneralPurposePlugin plugin;
// --------------------------------------------------------------------------------
// Returns Web Admin page
int CControl::WebAdmin(connection * conn)
{
OpenHTTPHeader(conn,"text/html",0,NULL);
if (conn->http.reqID == RID_HEAD)
return ST_CLOSE;
OpenHtmlHeader(conn);
Style(conn);
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<h3>\n<i>\n... Wawi Remote Admin ...\n</i></h3>\n");
prints(conn,"<h4>");
Link(conn,"/user","<font color=red>[</font>User Management<font color=red>]</font>",T_MAIN);
prints(conn,"</h4>\n");
prints(conn,"<form action=\"/apply\" method=\"get\">\n");
prints(conn,"<table width=\"100%\" cellspacing=\"5\" cellpadding=\"0\" border=\"0\">\n<tr>\n");
prints(conn,"<td align=\"right\">Main Page Title</td>\n<td><input type=\"text\" name=\"title\" size=\"30\" value=\"");
prints(conn,pagetitle);
prints(conn,"\"></td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Loadable Filetypes</td>\n<td><input type=\"text\" name=\"filetypes\" size=\"30\" value=\"");
prints(conn,filetypes);
prints(conn,"\"></td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Music Root Directory</td>\n<td><input type=\"text\" name=\"root\" size=\"30\" value=\"");
prints(conn,mp3root);
prints(conn,"\"></td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Playlists Directory</td>\n<td><input type=\"text\" name=\"playlists\" size=\"30\" value=\"");
prints(conn,playlistdir);
prints(conn,"\"></td>\n</tr>\n<tr>\n<td>&nbsp;</td>\n</tr>\n<tr>\n");
// Frames mode radio buttons
prints(conn,"<td align=\"right\">Frames:</td>\n<td><input type=\"radio\" name=\"frames\" value=\"1\"");
if (frames == 1)
prints(conn," checked=\"on\"");
prints(conn,">No Frames</td>\n</tr>\n<tr>\n<td align=\"right\">&nbsp;</td>\n<td><input type=\"radio\" name=\"frames\" value=\"2\"");
if (frames == 2)
prints(conn," checked=\"on\"");
prints(conn,">2</td>\n</tr>\n<tr>\n<td align=\"right\">&nbsp;</td>\n<td><input type=\"radio\" name=\"frames\" value=\"3\"");
if (frames == 3)
prints(conn," checked=\"on\"");
prints(conn,">3</td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Secure Passwords</td>\n<td><input type=\"checkbox\" name=\"securepassword\"");
if (securepassword == 1)
prints(conn," checked=\"on\"");
prints(conn,"><b>Warning!</b> Changing this will invalidate all user passwords.</td>\n</tr>\n<tr>");
prints(conn,"<td>&nbsp;</td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Winamp Files</td>\n<td><input type=\"checkbox\" name=\"dl_wa_files\"");
if (dl_wa_files == 1)
prints(conn," checked=\"on\"");
prints(conn,">Download</td>\n</tr>\n<tr>\n");
prints(conn,"<td align=\"right\">Other Files</td>\n<td><input type=\"checkbox\" name=\"dl_other_files\"");
if (dl_other_files == 1)
prints(conn," checked=\"on\"");
prints(conn,">Download <input type=\"checkbox\" name=\"show_other_files\"");
if (show_other_files == 1)
prints(conn," checked=\"on\"");
prints(conn,">Show</td>\n</tr>\n");
prints(conn,"<td colspan=\"2\" align=\"center\"><input type=\"image\" alt=\"Apply Changes\" src=\"img?image=");
printsi(conn,IDR_IMG_OK);
prints(conn,"\"></td>\n</tr>\n</table>");
prints(conn,"</form>\n");
if (frames == 1)
LinkBar(conn);
prints(conn,"<p>&nbsp;</p>");
return ST_CLOSEBODY;
}
// Returns admin apply page and actually does the stuff
int CControl::ApplyAdmin(connection * conn)
{
bool changed = false;
OpenHTTPHeader(conn,"text/html",0,NULL);
if (conn->http.reqID == RID_HEAD)
return ST_CLOSE;
OpenHtmlHeader(conn);
Style(conn);
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<h3>\n<i>\n... Wawi Remote Admin ...\n</i></h3>\n");
char instring[255]; // used for everything
if (GetArgValue(conn,"title",instring,255))
{
Unescape_url(instring);
if (!StrComp(pagetitle,instring))
{
lstrcpy(pagetitle,instring);
prints(conn,"<p>Page title changed to <b>");
prints(conn,pagetitle);
prints(conn,"</b></p>\n");
changed = true;
}
}
if (GetArgValue(conn,"filetypes",instring,255))
{
Unescape_url(instring);
if (!StrComp(filetypes,instring))
{
lstrcpy(filetypes,instring);
prints(conn,"<p>Filetypes changed to <b>");
prints(conn,filetypes);
prints(conn,"</b></p>\n");
changed = true;
}
}
if (GetArgValue(conn,"root",instring,255))
{
Unescape_url(instring);
if (!StrComp(mp3root,instring))
{
lstrcpy(mp3root,instring);
prints(conn,"<p>Root music directory changed to <b>");
prints(conn,mp3root);
prints(conn,"</b></p>\n");
changed = true;
}
}
if (GetArgValue(conn,"playlists",instring,255))
{
Unescape_url(instring);
if (!StrComp(playlistdir,instring))
{
lstrcpy(playlistdir,instring);
prints(conn,"<p>Playlist directory changed to <b>");
prints(conn,playlistdir);
prints(conn,"</b></p>\n");
changed = true;
}
}
wsprintf(instring,"2");
GetArgValue(conn,"frames",instring,255);
if (StrComp(instring,"1"))
{
if (frames != 1)
{
frames = 1;
prints(conn,"<p>No Frames mode turned <b>On</b></p>");
changed = true;
}
}
else
{
if (StrComp(instring,"3"))
{
if (frames != 3)
{
frames = 3;
prints(conn,"<p>Three Frames mode turned <b>On</b></p>");
changed = true;
}
}
else
{
if (frames != 2)
{
frames = 2;
prints(conn,"<p>Two Frames mode turned <b>On</b></p>");
changed = true;
}
}
}
wsprintf(instring,"off");
GetArgValue(conn,"securepassword",instring,255);
if (StrComp(instring,"on"))
{
if (securepassword == 0)
{
securepassword = 1;
prints(conn,"<p>Secure passwords turned <b>On</b></p>");
changed = true;
}
}
else
{
if (securepassword == 1)
{
securepassword = 0;
prints(conn,"<p>Secure passwords turned <b>Off</b></p>");
changed = true;
}
}
wsprintf(instring,"off");
GetArgValue(conn,"dl_wa_files",instring,255);
if (StrComp(instring,"on"))
{
if (dl_wa_files == 0)
{
dl_wa_files = 1;
prints(conn,"<p>Downloading of Winamp files is <b>Enabled</b></p>");
changed = true;
}
}
else
{
if (dl_wa_files == 1)
{
dl_wa_files = 0;
prints(conn,"<p>Downloading of Winamp files is <b>Disabled</b></p>");
changed = true;
}
}
wsprintf(instring,"off");
GetArgValue(conn,"dl_other_files",instring,255);
if (StrComp(instring,"on"))
{
if (dl_other_files == 0)
{
dl_other_files = 1;
prints(conn,"<p>Downloading other files is <b>Enabled</b></p>");
changed = true;
}
}
else
{
if (dl_other_files == 1)
{
dl_other_files = 0;
prints(conn,"<p>Downloading other files is <b>Disabled</b></p>");
changed = true;
}
}
wsprintf(instring,"off");
GetArgValue(conn,"show_other_files",instring,255);
if (StrComp(instring,"on"))
{
if (show_other_files == 0)
{
show_other_files = 1;
prints(conn,"<p>Listing other files is <b>Enabled</b></p>");
changed = true;
}
}
else
{
if (show_other_files == 1)
{
show_other_files = 0;
prints(conn,"<p>Listing other files is <b>Disabled</b></p>");
changed = true;
}
}
if (changed == false)
prints(conn,"<p>No changes made.</p>\n");
config_write();
prints(conn,"<p>");
ImgLink(conn,"/",IDR_IMG_OK,"Return to Main Page",0,0,T_TOP);
prints(conn,"</p>\n");
return ST_CLOSEBODY;
}
// --------------------------------------------------------------------------------
// User admin page
int CControl::User(connection * conn)
{
char username[20];
char password[40];
int access;
if (!GetArgValue(conn,"user",username,20))
wsprintf(username,"anon");
OpenHTTPHeader(conn,"text/html",0,NULL);
if (conn->http.reqID == RID_HEAD)
return ST_CLOSE;
OpenHtmlHeader(conn);
Style(conn);
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<h3>\n<i>\n... Wawi User Admin ...\n</i></h3>\n");
prints(conn,"<form action=\"/user\" method=\"get\">\n<p>\n");
prints(conn,"<table border=\"0\" cellpadding=\"0\" cellspacing=\"5\">\n<tr>\n<td align=\"right\">");
prints(conn,"Show User:</td>\n<td colspan=\"2\"><select onchange=\"form.submit()\" size=\"1\" name=\"user\">\n");
user * users = NULL;
char ini_file[MAX_PATH], *p;
GetModuleFileName(plugin.hDllInstance,ini_file,sizeof(ini_file));
p=ini_file+lstrlen(ini_file);
while (p >= ini_file && *p != '\\') p--;
if (++p >= ini_file) *p = 0;
lstrcat(ini_file,"gen_httpsrv.ini");
users = RetrieveUsers(users, ini_file); // fill list 'users' with usernames
user * current = users; // pointer to start of list
bool goodname = false;
while(current != NULL)
{
prints(conn,"<option value=\"");
prints(conn,current->name);
if (StrComp(current->name,username))
{
prints(conn,"\" selected>");
goodname = true;
}
else
prints(conn,"\">");
prints(conn,current->name);
prints(conn,"</option>\n");
current = current->next;
delete users;
users = current;
}
prints(conn,"</select>\n");
prints(conn,"<input type=\"image\" alt=\"Show information\" src=\"img?image=");
printsi(conn,IDR_IMG_OK);
prints(conn,"\"></td>\n</tr>\n</table>\n</p>\n</form>\n");
if (!goodname)
{
prints(conn,"<p>User <b>");
prints(conn,username);
prints(conn,"</b> is not a user of this server.</p>");
return ST_CLOSEBODY;
}
else
{
char userkey[30];
wsprintf(userkey,"#%s_access",username);
access = GetPrivateProfileInt("Users",userkey,AUTH_ANON,ini_file);
GetPrivateProfileString("Users",username,"password",password,40,ini_file);
prints(conn,"<form action=\"/setuser\" method=\"get\">\n<p>\n");
prints(conn,"<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"5\">\n");
prints(conn,"<tr>\n<td align=\"right\">User</td>\n");
prints(conn,"<td colspan=\"2\"><input type=\"text\" name=\"user\" size=\"40\" value=\"");
prints(conn,username);
prints(conn,"\"></td>\n</tr>\n");
prints(conn,"<tr>\n<td align=\"right\">Password</td>\n");
prints(conn,"<td colspan=\"2\"><input type=\"password\" name=\"p\" size=\"40\" value=\"");
if (!StrComp(username,"anon"))
prints(conn,password);
prints(conn,"\"></td>\n</tr>\n");
prints(conn,"<tr>\n<td colspan=\"2\">&nbsp;</td>\n</tr>\n");
prints(conn,"<tr>\n<td align=\"right\">Server<input type=\"checkbox\" name=\"a_server\" ");
if (access & AUTH_SERVER)
prints(conn,"checked=\"on\"");
prints(conn,"></td>\n");
prints(conn,"<td align=\"left\"><input type=\"checkbox\" name=\"a_playlist\" ");
if (access & AUTH_PLAYLIST)
prints(conn,"checked=\"on\"");
prints(conn,">Add files</td>\n</tr>\n");
prints(conn,"<tr>\n<td align=\"right\">Play<input type=\"checkbox\" name=\"a_control\" ");
if (access & AUTH_CONTROL)
prints(conn,"checked=\"on\"");
prints(conn,"></td>\n");
prints(conn,"<td align=\"left\"><input type=\"checkbox\" name=\"a_download\" ");
if (access & AUTH_DOWNLOAD)
prints(conn,"checked=\"on\"");
prints(conn,">Download</td>\n</tr>\n");
prints(conn,"<tr>\n<td align=\"right\">Remove files<input type=\"checkbox\" name=\"a_clear\" ");
if (access & AUTH_CLEAR)
prints(conn,"checked=\"on\"");
prints(conn,"></td>\n");
prints(conn,"<td align=\"left\"><input type=\"checkbox\" name=\"a_browse\" ");
if (access & AUTH_BROWSE)
prints(conn,"checked=\"on\"");
prints(conn,">Browse</td>\n</tr>\n<tr>\n<td colspan=\"2\" align=\"center\">");
prints(conn,"<input type=\"submit\" name=\"addupd\" value=\"Add/Update\">");
prints(conn,"<input type=\"submit\" name=\"delete\" value=\"Delete\">\n</td>\n</tr>\n</table>\n</p>\n</form>\n");
}
if (frames == 1)
LinkBar(conn);
prints(conn,"<p>&nbsp;</p>\n");
return ST_CLOSEBODY;
}
// --------------------------------------------------------------------------------
// Sets stuff from the user admin page
int CControl::SetUser(connection * conn)
{
OpenHTTPHeader(conn,"text/html",0,NULL);
if (conn->http.reqID == RID_HEAD)
return ST_CLOSE;
OpenHtmlHeader(conn);
Style(conn);
prints(conn,"<h3>\n<i>\n... Wawi User Admin ...\n</i></h3>\n");
char username[20];
char password[40];
if (!GetArgValue(conn,"user",username,20) || StrComp(username,""))
{
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<p>No user selected.</p>\n");
ImgLink(conn,"/main",IDR_IMG_OK,"Return to main page",0,0,T_MAIN);
return ST_CLOSEBODY;
}
char ini_file[MAX_PATH], *p;
GetModuleFileName(plugin.hDllInstance,ini_file,sizeof(ini_file));
p=ini_file+lstrlen(ini_file);
while (p >= ini_file && *p != '\\') p--;
if (++p >= ini_file) *p = 0;
lstrcat(ini_file,"gen_httpsrv.ini");
char whichbutton[20];
if (GetArgValue(conn,"delete",whichbutton,20))
{
DeleteUser(ini_file,username);
RefreshHeader(conn,"/user");
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<p>Deleting user <b>");
prints(conn,username);
prints(conn,"</b></p>\n");
return ST_CLOSEBODY;
}
char refreshto[40];
wsprintf(refreshto,"/user?user=%s",username);
RefreshHeader(conn,refreshto);
CloseHeader(conn);
OpenPageBody(conn);
prints(conn,"<p>Updating user information for <b>");
prints(conn,username);
prints(conn,"</b></p>\n");
GetArgValue(conn,"p",password,40);
//MessageBox(NULL,password,"password",MB_OK | MB_TOPMOST);
int access = 0;
char authstring[10];
if (GetArgValue(conn,"a_server",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_SERVER;
if (GetArgValue(conn,"a_control",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_CONTROL;
if (GetArgValue(conn,"a_clear",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_CLEAR;
if (GetArgValue(conn,"a_playlist",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_PLAYLIST;
if (GetArgValue(conn,"a_download",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_DOWNLOAD;
if (GetArgValue(conn,"a_browse",authstring,10))
if (StrComp(authstring,"on"))
access += AUTH_BROWSE;
UpdateUser(ini_file,username,password,access);
return ST_CLOSEBODY;
}