📄 httpinput.cpp
字号:
/*____________________________________________________________________________
FreeAmp - The Free MP3 Player
Portions Copyright (C) 1998-1999 EMusic.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: httpinput.cpp,v 1.72 2000/11/14 11:07:09 robert Exp $
____________________________________________________________________________*/
/* system headers */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <errno.h>
#include <assert.h>
#ifdef WIN32
#include <winsock.h>
#include <time.h>
#else
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifndef __BEOS__
#include <arpa/inet.h>
#endif
#include <netdb.h>
#include <fcntl.h>
#endif
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#elif HAVE_IO_H
#include <io.h>
#else
#error Must have unistd.h or io.h!
#endif // HAVE_UNISTD_H
/* project headers */
#include "httpinput.h"
#include "facontext.h"
#include "log.h"
#include "tstream.h"
#include "debug.h"
const int iBufferSize = 8192;
const int iOverflowSize = 1536;
const int iTriggerSize = 1024;
const char *szDefaultStreamTitle = "SHOUTcast Stream";
#if !defined(WIN32) && !defined(__BEOS__)
#define closesocket(s) close(s)
#endif
#ifndef F_OK
#define F_OK 0
#endif
const int iHttpPort = 80;
const int iMaxHostNameLen = 64;
const int iGetHostNameBuffer = 1024;
const int iBufferUpInterval = 3;
const int iInitialBufferSize = 1024;
const int iHeaderSize = 1024;
const int iICY_OK = 200;
const int iICY_REDIRECT = 302;
const int iTransmitTimeout = 60;
#ifdef WIN32
const char cDirSepChar = '\\';
#else
const char cDirSepChar = '/';
#endif
#define DB Debug_v("%s:%d\n", __FILE__, __LINE__);
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
extern "C"
{
PhysicalMediaInput *Initialize(FAContext * context)
{
return new HttpInput(context);
}
}
#ifdef WIN32
HINSTANCE g_hinst = NULL;
INT WINAPI
DllMain(HINSTANCE hInstance, ULONG ul_reason_being_called, LPVOID lpReserved)
{
switch (ul_reason_being_called)
{
case DLL_PROCESS_ATTACH:
g_hinst = hInstance;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return 1;
}
#endif
HttpInput::HttpInput(FAContext * context):
PhysicalMediaInput(context)
{
m_path = NULL;
m_hHandle = -1;
m_bLoop = false;
m_bDiscarded = false;
m_bIsStreaming = true;
m_pBufferThread = NULL;
m_fpSave = NULL;
m_szError = new char[iMaxErrorLen];
m_pTitleStream = NULL;
m_bUseBufferReduction = true;
m_iMetaDataInterval = 0;
m_uBytesReceived = 0;
m_bExit = false;
#ifdef WIN32
m_hWnd = NULL;
#endif
m_pContext->prefs->GetPrefBoolean(kUseProxyPref, &m_bUseProxy);
if (m_bUseProxy)
{
uint32 len = iMaxUrlLen;
m_pContext->prefs->GetPrefString(kProxyHostPref, m_szProxyHost, &len);
if (len == 0)
m_pContext->log->Error("useProxy is true but ProxyHost "
"has no value ?!");
}
}
HttpInput::~HttpInput()
{
#ifdef WIN32
if (m_hWnd)
PostMessage(m_hWnd, WM_QUIT, 0, 0);
#endif
m_bExit = true;
m_pSleepSem->Signal();
m_pPauseSem->Signal();
if (m_pTitleStream)
delete m_pTitleStream;
if (m_pBufferThread)
{
m_pBufferThread->Join();
delete m_pBufferThread;
}
if (m_hHandle >= 0)
{
shutdown(m_hHandle, 2);
closesocket(m_hHandle);
}
if (m_fpSave)
fclose(m_fpSave);
delete m_szError;
}
bool
HttpInput::CanHandle(const char *szUrl, char *szTitle)
{
bool bRet;
bRet = strncmp(szUrl, "http://", 7) == 0;
if (szTitle && bRet)
strcpy(szTitle, szDefaultStreamTitle);
return bRet;
}
Error
HttpInput::Prepare(PullBuffer * &pBuffer)
{
int32 iBufferSize = iDefaultBufferSize;
Error result;
if (m_pOutputBuffer)
{
delete m_pOutputBuffer;
m_pOutputBuffer = NULL;
}
if (!IsError
(m_pContext->prefs->GetPrefInt32(kInputBufferSizePref, &iBufferSize)))
iBufferSize *= 1024;
m_pOutputBuffer = new PullBuffer(iBufferSize, iDefaultOverflowSize,
m_pContext);
assert(m_pOutputBuffer);
pBuffer = m_pOutputBuffer;
result = Run();
if (IsError(result))
{
ReportError("Could not initialize http streaming plugin.");
return result;
}
return kError_NoErr;
}
Error
HttpInput::Close(void)
{
#ifdef WIN32
if (m_hWnd)
PostMessage(m_hWnd, WM_QUIT, 0, 0);
#endif
delete m_pOutputBuffer;
m_pOutputBuffer = NULL;
if (m_hHandle >= 0)
{
shutdown(m_hHandle, 2);
closesocket(m_hHandle);
}
if (m_fpSave)
fclose(m_fpSave);
return kError_NoErr;
}
Error
HttpInput::Run(void)
{
if (!m_pBufferThread)
{
m_pBufferThread = Thread::CreateThread();
if (!m_pBufferThread)
{
return (Error) kError_CreateThreadFailed;
}
m_pBufferThread->Create(HttpInput::StartWorkerThread, this);
}
return kError_NoErr;
}
bool
HttpInput::PauseLoop(bool bLoop)
{
bool bRet;
m_bLoop = bLoop;
bRet = m_bDiscarded;
m_bDiscarded = false;
return bRet;
}
#ifdef WIN32
#define WM_WINDOWS_IS_DONE_PICKING_ITS_BUTT (WM_USER + 1)
static LRESULT WINAPI WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam);
Error
HttpInput::Win32GetHostByName(char *szHostName, struct hostent *pHostInfo)
{
WNDCLASS wc;
MSG msg;
HWND hWnd;
HANDLE hHandle;
char szBuffer[MAXGETHOSTSTRUCT];
int result = -1;
memset(&wc, 0x00, sizeof(WNDCLASS));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = g_hinst;
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszClassName = "WindowsSucks";
result = RegisterClass(&wc);
hWnd = CreateWindow(wc.lpszClassName, "Fuss",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, g_hinst, NULL);
if (hWnd == NULL)
return kError_NoDataAvail;
m_hWnd = hWnd;
hHandle =
WSAAsyncGetHostByName(hWnd, WM_WINDOWS_IS_DONE_PICKING_ITS_BUTT,
szHostName, szBuffer, MAXGETHOSTSTRUCT);
if (hHandle == NULL)
{
DestroyWindow(hWnd);
return kError_NoDataAvail;
}
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
if (msg.message == WM_WINDOWS_IS_DONE_PICKING_ITS_BUTT)
result = WSAGETASYNCERROR(msg.lParam);
DispatchMessage(&msg);
}
if (m_bExit)
{
WSACancelAsyncRequest(hHandle);
}
m_hWnd = NULL;
DestroyWindow(hWnd);
if (m_bExit)
{
return kError_Interrupt;
}
if (result == 0)
{
memcpy(pHostInfo, szBuffer, sizeof(struct hostent));
return kError_NoErr;
}
else
{
static unsigned long IP_Adr;
static char *AdrPtrs[2] = { (char *) &IP_Adr, NULL };
// That didn't work. On some stacks a numeric IP address
// will not parse with gethostbyname. Try to convert it as a
// numeric address before giving up.
if ((IP_Adr = inet_addr(szHostName)) == INADDR_NONE)
return kError_NoDataAvail;
pHostInfo->h_length = sizeof(uint32);
pHostInfo->h_addrtype = AF_INET;
pHostInfo->h_addr_list = (char **) &AdrPtrs;
return kError_NoErr;
}
return kError_NoDataAvail;
}
static LRESULT WINAPI
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (msg)
{
case WM_WINDOWS_IS_DONE_PICKING_ITS_BUTT:
{
PostMessage(hwnd, WM_QUIT, 0, 0);
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return result;
}
#endif
Error
HttpInput::GetHostByName(char *szHostName, struct hostent * pResult)
{
struct hostent *pTemp;
struct hostent TempHostent;
static unsigned long IP_Adr;
static char *AdrPtrs[2] = { (char *) &IP_Adr, NULL };
#ifdef WIN32
Error eRet;
eRet = Win32GetHostByName(szHostName, &TempHostent);
if (eRet == kError_Interrupt)
return eRet;
if (IsError(eRet))
pTemp = NULL;
else
pTemp = &TempHostent;
#else
pTemp = gethostbyname(szHostName);
#endif
if (pTemp == NULL)
{
// That didn't work. On some stacks a numeric IP address
// will not parse with gethostbyname. Try to convert it as a
// numeric address before giving up.
if ((int) (IP_Adr = inet_addr(szHostName)) == INADDR_ANY)
return kError_NoDataAvail;
TempHostent.h_length = sizeof(uint32);
TempHostent.h_addrtype = AF_INET;
TempHostent.h_addr_list = (char **) &AdrPtrs;
pTemp = &TempHostent;
}
memcpy(pResult, pTemp, sizeof(struct hostent));
return kError_NoErr;
}
static void
EncodeURI(string & URI)
{
string::size_type convert = 0;
const char *legalCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/?.";
if (strncmp(URI.c_str(), "http://", 7) == 0)
convert = URI.find(string("/"), 7);
while ((convert = URI.find_first_not_of(legalCharacters, convert)) !=
string::npos)
{
string hex = "%";
char num[8];
// Do not replace %## sequences -- they are already encoded and
// ready to roll
if (URI[convert] == '%' && URI.length() - convert > 2 &&
isdigit(URI[convert + 1]) &&
isdigit(URI[convert + 2]))
{
convert++;
continue;
}
sprintf(num, "%02x", URI[convert] & 0xFF);
hex += num;
URI.replace(convert, 1, hex);
convert += hex.length();
}
}
Error
HttpInput::Open(void)
{
char szHostName[iMaxHostNameLen + 1], *szQuery;
char *pEnd;
char *pInitialBuffer, szSourceAddr[100];
char *szStreamName, *szStreamUrl;
unsigned iPort;
int iRet, iRead = 0, iConnect;
struct sockaddr_in sAddr, sSourceAddr;
struct hostent sHost;
Error eRet;
char *pHeaderData = NULL, *pPtr;
fd_set sSet;
struct timeval sTv;
bool bUseTitleStreaming = true, bUseAltNIC = false;
int iHeaderBytes = 0, iCurHeaderSize = 1024;
string file;
szStreamName = NULL;
szStreamUrl = NULL;
if (!m_bUseProxy)
{
const char* ptr;
iRet = sscanf(m_path, " http://%[^:/]:%d", szHostName, &iPort);
if (iRet < 1)
{
ReportError("Bad URL format. URL format: http://<host name>"
":[port][/path]. Please check the URL and try again.");
return (Error) httpError_BadUrl;
}
ptr = strchr(m_path + 7, '/');
file = (ptr ? ptr : "");
}
else
{
iRet = sscanf(m_szProxyHost, " http://%[^:/]:%d", szHostName, &iPort);
if (iRet < 1)
{
ReportError("Bad Proxy URL format. URL format: http:"
"//<host name>:[port]. Please check your proxy settings "
"in the Options.");
m_pContext->log->Error("Debug: m_szProxyHost: '%s'\n", m_szProxyHost);
return (Error) httpError_BadUrl;
}
file = string(m_path);
}
EncodeURI(file);
if (iRet < 2)
iPort = iHttpPort;
memset(&sAddr, 0, sizeof(struct sockaddr_in));
ReportStatus("Looking up host %s...", szHostName);
eRet = GetHostByName(szHostName, &sHost);
if (eRet != kError_NoErr)
{
sprintf(m_szError, "Cannot find host %s\n", szHostName);
ReportError(m_szError);
return (Error) httpError_CustomError;
}
memcpy((char *) &sAddr.sin_addr, sHost.h_addr, sHost.h_length);
sAddr.sin_family = sHost.h_addrtype;
sAddr.sin_port = htons((unsigned short) iPort);
ReportStatus("Contacting host %s...", szHostName);
m_hHandle = socket(sHost.h_addrtype, SOCK_STREAM, 0);
if (m_hHandle < 0)
{
ReportError
("Cannot create socket. Is TCP/IP networking properly installed?");
return (Error) httpError_CannotOpenSocket;
}
m_pContext->prefs->GetPrefBoolean(kUseAlternateNICPref, &bUseAltNIC);
if (bUseAltNIC)
{
uint32 len = 100;
m_pContext->prefs->GetPrefString(kAlternateNICAddressPref, szSourceAddr,
&len);
if (len == 0)
m_pContext->log->Error("UseAlternateNIC is true but AlternateNIC "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -