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

View File

@@ -0,0 +1,340 @@
#include <windows.h>
#include <winsock.h>
#include "resource.h"
#include "main.h"
extern char szAppName[];
extern char username[20], password[20], title[255];
extern char host[255];
extern int port;
extern bool use_auth;
extern HWND hwnd;
// Base64 character array
// 0 1 2 3 4 5 6
// 01234567890123456789012345678901234567890123456789012345678901234
const char bchars[] = "=BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+*";
// --------------------------------------------------------------------------------
// Start winsock
bool StartWinSock()
{
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData;
if (WSAStartup(wVersionRequested, &wsaData ) != 0 )
{
MessageBox(NULL, "ERROR: Winsock not available", szAppName, MB_OK);
return false;
}
return true;
}
// --------------------------------------------------------------------------------
// Low level socket writing function - strings
void prints(SOCKET socket, char * string)
{
int i=0;
while (string[i] != 0)
i++;
if (send(socket,string,i,0) == SOCKET_ERROR)
{
//MessageBox(NULL,"Socket send() error!",szAppName,MB_OK);
}
}
// Low level socket writing function - integers
void printsi(SOCKET socket,int number)
{
char buffer[256];
wsprintf(buffer,"%d",number); // Stick it in a string and call prints()
prints(socket,(char*)&buffer);
}
// --------------------------------------------------------------------------------
// Opens socket
bool MakeRequest(char * host, int port, char * url, bool verbose, bool auth, bool gettitle)
{
//MessageBox(NULL,"Entering MakeRequest","wawitray",MB_OK | MB_TOPMOST);
SOCKET ssocket = socket(AF_INET,SOCK_STREAM,0);
if (ssocket == INVALID_SOCKET)
{
if (verbose)
MessageBox(NULL, "ERROR: Couldn't create socket", szAppName, MB_OK);
return false;
}
struct sockaddr_in server_address;
ZeroMemory(&server_address,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr(host);
server_address.sin_port = htons(port);
if (SOCKET_ERROR == connect(ssocket,(sockaddr*)&server_address,sizeof(sockaddr_in)))
{
if (verbose)
MessageBox(hwnd,"Couldn't connect to remote Winamp!\n\nCheck you have the address and port right.",szAppName,MB_OK|MB_ICONERROR);
return false;
}
char method[6];
if (gettitle)
wsprintf(method,"GET");
else
wsprintf(method,"HEAD");
char request[100];
if (auth)
{
char b64userpass[100], userpass[100];
wsprintf(userpass,"%s:%s",username,password);
ToBase64(userpass,b64userpass);
wsprintf(request,"%s %s HTTP/1.0\nAuthorisation: Basic %s\n\n",method,url,b64userpass);
}
else
{
wsprintf(request,"%s %s HTTP/1.0\nConnection: Close\n\n",method,url);
}
prints(ssocket,request);
int res = GetResponse(ssocket,gettitle);
closesocket(ssocket);
switch(res)
{
case RES_OK:
break;
case RES_AUTH:
if (verbose)
MessageBox(hwnd,"Incorrect username or password!\n\nCheck the details are right in the Config window.",szAppName,MB_OK|MB_ICONERROR | MB_TOPMOST);
break;
case RES_NOTFOUND:
if (verbose)
MessageBox(hwnd,"Received a 404 Page Not Found response, which the Winamp HTTP Server does not produce!\n\nAre you sure your host and port settings are correct?",szAppName,MB_OK | MB_ICONERROR | MB_TOPMOST);
break;
case RES_BADREQUEST:
if (verbose)
MessageBox(hwnd,"Received a 400 Bad Request message, which the Winamp HTTP Server does not produce!\n\nCheck your Host and Port settings are correct.",szAppName, MB_OK | MB_ICONERROR | MB_TOPMOST);
case RES_UNKNOWN:
case RES_FAIL:
if (verbose)
MessageBox(hwnd,"Unknown HTTP Response",szAppName,MB_OK | MB_ICONWARNING | MB_TOPMOST);
break;
}
//MessageBox(hwnd,"Leaving MakeRequest","wawitray",MB_OK | MB_TOPMOST);
return true;
}
int WASend(char * command)
{
MakeRequest(host,port,command,true,use_auth,false);
return 1;
}
// --------------------------------------------------------------------------------
// Converts a string to Base64
void ToBase64(char * instr, char * outstr)
{
//MessageBox(NULL,instr,"instr",MB_OK);
int i=0;
int o=0;
int j = lstrlen(instr);
bool done = false;
while(instr[i] != 0 && done == false)
{
outstr[o] = bchars[(instr[i] & 0xFC) / 4];
outstr[o+1] = bchars[((instr[i] & 0x03) * 16) + ((instr[i+1] & 0xF0) / 16)];
if (instr[i+1] == 0)
{
outstr[o+2] = '=';
outstr[o+3] = '=';
done = true;
}
else
{
outstr[o+2] = bchars[((instr[i+1] & 0x0F) * 4) + ((instr[i+2] & 0xC0) /64)];
if (instr[i+2] == 0)
{
outstr[o+3] = '=';
done = true;
}
else
outstr[o+3] = bchars[(instr[i+2] & 0x3F)];
}
o += 4;
i += 3;
}
outstr[o] = 0;
}
// --------------------------------------------------------------------------------
// Receives return HTTP header
int GetResponse(SOCKET socket, bool gettitle)
{
char buffer[2048],http_version[20],response[5],text[100];
int buffer_len;
int buffer_filled = 0;
int buffer_left;
bool complete = false;
int breaks;
if (gettitle)
breaks = 1;
else
breaks = 0;
while(true)
{
// receive data
buffer_len = sizeof(buffer);
buffer_left = buffer_len-buffer_filled;
if (buffer_left <= 0)
buffer_len = 0;
else
buffer_len = recv(socket,buffer + buffer_filled,buffer_len-buffer_filled,0);
if (buffer_len != SOCKET_ERROR && buffer_len != 0)
{
buffer_filled += buffer_len;
// scan for end of header
for (int i=0;i<=buffer_filled;i++)
if (buffer[i]=='\n' && (buffer[i+1]=='\n' || buffer[i+2]=='\n'))
{
if (breaks == 0)
{
buffer[i+1] = 0;
//MessageBox(NULL,buffer,"gen_httpsrv debug", MB_OK);
char * chptr = (char*)&buffer;
chptr = GetAString(chptr,http_version);
chptr = GetAString(chptr,response);
chptr = GetAString(chptr,text);
chptr = SkipHeader(chptr);
if (StrComp(response,"200"))
{
if (gettitle)
GetALine(chptr,title);
return RES_OK;
}
if (StrComp(response,"401"))
return RES_AUTH;
if (StrComp(response,"404"))
return RES_NOTFOUND;
if (StrComp(response,"400"))
return RES_BADREQUEST;
return RES_UNKNOWN;
}
else
breaks--;
}
}
else
{
return RES_FAIL;
}
}
}
// --------------------------------------------------------------------------------
// Retrieves next token from buffer
char * GetAString(char* chptr, char* text) // Old GetAString - only accepts single words
{
int bptr = 0;
int tptr = 0;
while (chptr[bptr] == ' ' || chptr[bptr] == '\n' || chptr[bptr] == '\r') // skip leading spaces and stuff
bptr++;
while (chptr[bptr] != 0 && chptr[bptr] != ' ' && chptr[bptr] != '\n' && chptr[bptr] != '\r' && bptr < 255) // while current char is a letter
{
text[tptr] = chptr[bptr]; // add letter to text[]
bptr++;
tptr++;
}
text[tptr] = 0;
chptr = chptr + bptr;
return chptr; // return pointer to the end of this string
}
// --------------------------------------------------------------------------------
// Skips the rest of the HTTP header
char * SkipHeader(char * chptr)
{
while (chptr[0] != '\n' || (chptr[1] != '\n' && chptr[2] != '\r'))
chptr++;
return chptr;
}
// --------------------------------------------------------------------------------
// Gets a whole line
char * GetALine(char * chptr, char * text)
{
int bptr = 0;
int tptr = 0;
while (chptr[bptr] == ' ' || chptr[bptr] == '\n' || chptr[bptr] == '\r') // skip leading spaces and stuff
bptr++;
while (chptr[bptr] != 0 && chptr[bptr] != '\n' && chptr[bptr] != '\r' && bptr < 255) // while current char is a letter
{
text[tptr] = chptr[bptr]; // add letter to text[]
bptr++;
tptr++;
}
text[tptr] = 0;
chptr = chptr + bptr;
return chptr; // return pointer to the end of this string
}
// --------------------------------------------------------------------------------
// Strcmp() without using Clib
bool StrComp(char * string1, char * string2)
{
int ptr=0;
while (ToLowerCase(string1[ptr]) == ToLowerCase(string2[ptr])) // while the strings are the same, keep going
{
if (string1[ptr] == 0) // if they're the same and 0, they're identical null terminated strings
return true;
ptr++;
}
return false; // if not the same, they're... not the same
}
// --------------------------------------------------------------------------------
// Convert a char to lower case
char ToLowerCase(char c)
{
if (c >= 'A' && c <= 'Z')
c += 32;
return c;
}