- 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
341 lines
8.7 KiB
C++
341 lines
8.7 KiB
C++
#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;
|
|
}
|