#include #include "main.h" #include #ifdef NO_WASABI #include "../../jnetlib/httpget.h" api_httpreceiver *CreateGet() { return new JNL_HTTPGet; } void ReleaseGet(api_httpreceiver *&get) { delete (JNL_HTTPGet *)get; get=0; } #else #include "../..\Components\wac_network\wac_network_http_receiver_api.h" #include #include #include "../../Winamp/in2.h" extern In_Module mod; waServiceFactory *httpFactory = 0; api_httpreceiver *CreateGet() { api_httpreceiver *get = 0; if (!httpFactory && mod.service) httpFactory = mod.service->service_getServiceByGuid(httpreceiverGUID); if (httpFactory) get = (api_httpreceiver *)httpFactory->getInterface(); return get; } void ReleaseGet(api_httpreceiver *&get) { if (!get) return ; if (!httpFactory && mod.service) waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); if (httpFactory) httpFactory->releaseInterface(get); get = 0; } #endif #define MAX_MULTICONNECTS 8 class HTTPReader : public IDataReader { public: HTTPReader(const char *url); ~HTTPReader(); size_t read(char *buf, size_t len); bool iseof() { return !!m_eof; } char *gettitle() { return m_title; } char *geterror() { return m_err; } bool canseek() { return m_content_length != 0xFFFFFFFF && m_accept_ranges && !m_meta_interval; } int seek(unsigned __int64 newpos) { if (!canseek()) return 1; doConnect((int)newpos); return 0; } unsigned __int64 getsize() { return m_content_length; } char *getheader(char *header_name) { return m_get ? (char *)m_get->getheader(header_name) : NULL; } private: int serialconnect( int seekto , int timeout); void doConnect(int seekto); int getProxyInfo(const char *url, char *out); char *m_url; char *m_err; char *m_title; int m_eof; int m_meta_init, m_meta_interval, m_meta_pos, m_meta_size, m_meta_buf_pos; char m_meta_buf[4096]; int m_read_headers; unsigned int m_content_length; int m_accept_ranges; int m_is_uvox; int m_uvox_readpos; int m_uvox_enough_bytes; char proxybuf[8400], *proxy; api_httpreceiver *m_get; // this structure is allocated once, and freed once struct { char *url; //pointers into m_url // these two are only active temporarily. api_httpreceiver *get; char *error; } m_cons[MAX_MULTICONNECTS]; int m_numcons; int m_newcons; int m_serialized; int m_mstimeout; int m_contryptr; int m_serialfailed; int m_useaproxy; }; HTTPReader::HTTPReader(const char *url) { m_title = 0; m_is_uvox = m_uvox_readpos = 0; m_meta_init = m_meta_interval = m_meta_pos = m_meta_size = m_meta_buf_pos = 0; m_meta_buf[0] = 0; m_err = NULL; m_eof = 0; m_read_headers = 0; m_content_length = 0xFFFFFFFF; m_accept_ranges = 0; m_get = NULL; m_serialized = 0; m_mstimeout = 0; m_contryptr = 0; m_newcons = 0; m_serialfailed = 0; m_useaproxy = 0; // TCP multiconnect // JF> using ; as a delimiter is vomit inducing and breaks a lot of other // code. I petition we use <> to delimit, and I'm making it do that. m_numcons = 0; m_url = _strdup(url); int allowproxy = 1; char *tmpurl = m_url; while (m_numcons < MAX_MULTICONNECTS) { char *next = strstr( tmpurl, "<>" ); if ( next ) *next = '\0'; if (tmpurl[0]) { m_cons[m_numcons].error = NULL; m_cons[m_numcons].get = NULL; m_cons[m_numcons++].url = tmpurl; if (!_strnicmp(tmpurl, "uvox:", 5)) allowproxy = 0; if (!_strnicmp(tmpurl, "order://", 8)) { char *p = tmpurl + 8; // serialized mctp m_serialized = 1; m_numcons--; m_mstimeout = atoi(p); if ( m_mstimeout < 1 ) { m_serialized = 0; m_mstimeout = 0; } } } if (!next) break; tmpurl = next + 2; } memset(proxybuf, 0, sizeof(proxybuf)); proxy = NULL; if (allowproxy && getProxyInfo(url, proxybuf)) { proxy = strstr(proxybuf, "http="); if (!proxy) proxy = proxybuf; else { proxy += 5; char *tp = strstr(proxy, ";"); if (tp) *tp = 0; } } m_is_uvox = 0; if ( m_serialized && m_numcons > 1 ) // sanity check { int rval = 0, i; m_newcons = 1; // walk the list, set the url such that m_cons[0].url points to each item. try to connect // serialconnect returns error codes -1 on error, 0 on timeout, 1 on successfull connect for ( i = 0; i < m_numcons; i++ ) { if ( i ) { m_cons[0].url = m_cons[i].url; } rval = serialconnect(0, m_mstimeout); if ( rval == 1 ) break; } if ( rval < 1 ) { // we didnt get a connection so... m_serialfailed = 1; } } else doConnect(0); } void HTTPReader::doConnect(int seekto) { ReleaseGet(m_get); m_uvox_readpos = 0; m_eof = 0; int i; for (i = 0; i < m_numcons; i++ ) { free(m_cons[i].error); m_cons[i].error = NULL; ReleaseGet(m_cons[i].get); m_cons[i].get = CreateGet(); if (!m_cons[i].get) break; m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL); #ifdef WINAMP_PLUGIN m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)"); #else # ifdef WINAMPX m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)"); # else m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)"); # endif #endif m_cons[i].get->addheader("Accept:*/*"); m_cons[i].get->addheader("Connection:close"); m_cons[i].get->addheader("Ultravox-transport-type: TCP"); if (seekto) { char buf[64] = {0}; wsprintfA(buf, "Range:bytes=%d-", seekto); m_cons[i].get->addheader(buf); } else m_cons[i].get->addheader("icy-metadata:1"); m_cons[i].get->connect(m_cons[i].url, !!seekto); } m_uvox_enough_bytes = 1; } HTTPReader::~HTTPReader() { ReleaseGet(m_get); free(m_title); free(m_err); free(m_url); int i; for (i = 0; i < m_numcons; i++) { ReleaseGet(m_cons[i].get); free(m_cons[i].error); } } int HTTPReader::serialconnect(int seekto , int timeout) { ReleaseGet(m_get); m_uvox_readpos = 0; m_eof = 0; int64_t mythen, mynow , myref; LARGE_INTEGER then, now, ref; QueryPerformanceFrequency( &ref); myref = ref.QuadPart; QueryPerformanceCounter( &then ); mythen = then.QuadPart; int timer = 0; int i = 0; { ReleaseGet(m_cons[i].get); m_cons[i].get = CreateGet(); if (m_cons[i].get == NULL) return 0; m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL); #ifdef WINAMP_PLUGIN m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)"); #else # ifdef WINAMPX m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)"); # else m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)"); # endif #endif m_cons[i].get->addheader("Accept:*/*"); m_cons[i].get->addheader("Connection:close"); m_cons[i].get->addheader("Ultravox-transport-type: TCP"); if (seekto) { char buf[64] = {0}; wsprintfA(buf, "Range:bytes=%d-", seekto); m_cons[i].get->addheader(buf); } else m_cons[i].get->addheader("icy-metadata:1"); m_cons[i].get->connect(m_cons[i].url, !!seekto); } m_uvox_enough_bytes = 1; int ret, status; if (!m_get) { if (m_err) return 0; int i; int found = 0; i = 0; while ( timer < timeout ) { if (!m_cons[i].get) return 0; found = 1; QueryPerformanceCounter( &now ); mynow = now.QuadPart; float profiletime = (float)(mynow - mythen); profiletime /= myref; profiletime *= 1000.0; timer = (int) profiletime; ret = m_cons[i].get->run(); status = m_cons[i].get->get_status(); if (ret < 0 || status < 0) { const char *t = m_cons[i].get->geterrorstr(); if (t) {} ReleaseGet(m_cons[i].get); break; } if ( status > 0 ) { int code = m_cons[i].get->getreplycode(); if ( code < 200 || code > 299 ) { ReleaseGet(m_cons[i].get); //wsprintf( m_cons[i].error, "Error: Server returned %d", code ); break; } else { // we're in good shape, make our getter current, and delete all the gay ones ReleaseGet(m_get); // just in case, probably zero anyway m_get = m_cons[i].get; m_cons[i].get = NULL; // trash i here, but we are breaking anyway :) /* for (i = 0; i < m_numcons; i++) { delete m_cons[i].get; m_cons[i].get = NULL; free( m_cons[i].error ); m_cons[i].error = NULL; }*/ break; } } #ifdef _WIN32 Sleep(1); #else usleep(1000); #endif } // while if ( timer > timeout ) { ReleaseGet(m_cons[i].get); ReleaseGet(m_get); return 0; } if (!m_get) { return 0; } } if ( m_get ) return 1; else return 0; } size_t HTTPReader::read(char *buffer, size_t len) { int ret, status; if (!m_get) { if (m_err) return 0; int i; int found = 0; for (i = 0; !m_get && i < m_numcons; i++) { if (!m_cons[i].get) continue; found = 1; ret = m_cons[i].get->run(); status = m_cons[i].get->get_status(); if (ret < 0 || status < 0) { const char *t = m_cons[i].get->geterrorstr(); if (t) { free(m_cons[i].error); m_cons[i].error = _strdup( t ); } ReleaseGet(m_cons[i].get); } if ( status > 0 ) { int code = m_cons[i].get->getreplycode(); if ( code < 200 || code > 299 ) { ReleaseGet(m_cons[i].get); m_cons[i].get = NULL; free(m_cons[i].error); m_cons[i].error = (char *)malloc( 100 ); wsprintfA( m_cons[i].error, "Error: Server returned %d", code ); } else { // we're in good shape, make our getter current, and delete all the gay ones ReleaseGet(m_get); // just in case, probably zero anyway m_get = m_cons[i].get; m_cons[i].get = NULL; // trash i here, but we are breaking anyway :) for (i = 0; i < m_numcons; i++) { ReleaseGet(m_cons[i].get); free( m_cons[i].error ); m_cons[i].error = NULL; } break; // exit loop of connections } } } // loop of connections if (!found) // out of attempted connections heh { free( m_err ); if (m_numcons > 1) { size_t size = 0; for (i = 0; i < m_numcons; i++) if ( m_cons[i].error ) size += strlen( m_cons[i].error ) + 1; m_err = (char *)malloc(size + 100); wsprintfA( m_err, "No Valid Multiconnect URLs (%d);", m_numcons ); for (i = 0; i < m_numcons; i++) { strcat( m_err, m_cons[i].error ); strcat( m_err, ";" ); free(m_cons[i].error); m_cons[i].error = NULL; } } else { m_err = m_cons[0].error; m_cons[0].error = NULL; if (!m_err) m_err = _strdup("Connection error (Invalid URL?)"); } } if (!m_get) return 0; } ret = m_get->run(); status = m_get->get_status(); if (ret > 0 && (!m_get->bytes_available() || !m_uvox_enough_bytes) && status > 1) { m_eof = 1; } if (ret < 0 || status < 0) { const char *t = m_get->geterrorstr(); if (t) { free( m_err ); m_err = (char *)malloc( strlen( t) + 16 ); wsprintfA( m_err, "Error: %s", t ); return 0; } } if (status > 0) { if (!m_read_headers) { if (status > 1) { const char *v = m_get->getheader("Content-Length"); if (v) m_content_length = atoi(v); v = m_get->getheader("Accept-Ranges"); if (v) while (v && *v) { if (!_strnicmp(v, "bytes", 5)) { m_accept_ranges = 1; break; } v++; } v = m_get->getheader("icy-metaint"); if (v) { m_meta_interval = atoi(v); } if (!m_title) { v = m_get->getheader("icy-name"); if (v) m_title = _strdup(v); } #ifdef WINAMP_PLUGIN extern void process_url(char *url); v = m_get->getheader("icy-url"); if (v && !strstr(v, "shoutcast.com")) { char *p = (char *)v; while (p && *p && *p == ' ') p++; process_url(p); } #endif v = m_get->getheader("content-type"); if (v && !_stricmp(v, "misc/ultravox")) { v = m_get->getheader("ultravox-max-msg"); if (v) m_is_uvox = atoi(v); if (!m_is_uvox) m_is_uvox = 16000; } if (!m_title) { v = m_get->getheader("content-disposition"); if (v) v = strstr(v, "filename="); if (v) { m_title = _strdup(v + 9); } } m_read_headers = 1; } } size_t l = m_get->bytes_available(); if (m_is_uvox) { again: if (l >= 6) { unsigned char buf[32768*2] = {0}; m_get->peek_bytes((char *)buf, 6); if (buf[0] != 0x5A) { l--; m_get->get_bytes((char *)buf, 1); goto again; } int resqos = buf[1]; int classtype = (buf[2] << 8) | buf[3]; int msglen = (buf[4] << 8) | buf[5]; if (msglen > m_is_uvox) // length is too long { m_get->get_bytes((char *)buf, 1); l--; goto again; } if (msglen + 7 <= (int)l) { m_uvox_enough_bytes = 1; m_get->peek_bytes((char *)buf, msglen + 7); if (buf[msglen + 6]) { m_get->get_bytes((char *)buf, 1); l--; goto again; } if (classtype == 0x7777) // take any data for now, ignore all other frames { l = msglen - m_uvox_readpos; if (l > len) l = len; memcpy(buffer, buf + 6 + m_uvox_readpos, l); m_uvox_readpos += (int)l; if (m_uvox_readpos >= msglen) { m_uvox_readpos = 0; m_get->get_bytes((char *)buf, msglen + 7); } return l; #ifdef WINAMP_PLUGIN } else if ( classtype == 0x3001 ) { extern void process_metadata(char *buf, int size); m_get->get_bytes((char *)buf, msglen + 7); process_metadata((char*)buf + 6, msglen + 1); #endif } else { m_get->get_bytes((char *)buf, msglen + 7); } } else { m_uvox_enough_bytes = 0; } } return 0; } else { if (l > len) l = len; m_get->get_bytes(buffer, (int)l); if (m_meta_interval) { int x = (int)l; unsigned char *buf = (unsigned char *)buffer; if (m_meta_size) // already in meta block { int len = min(x, m_meta_size - m_meta_buf_pos); memcpy(m_meta_buf + m_meta_buf_pos, buf, len); m_meta_buf_pos += len; if (m_meta_buf_pos == m_meta_size) { // if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size); m_meta_buf_pos = 0; m_meta_size = 0; m_meta_pos = 0; } x -= len; if (x) memcpy(buf, buf + len, x); } else if (m_meta_pos + x > m_meta_interval) // block contains meta data somewhere in it, and we're not alreayd reading a block { int start_offs = m_meta_interval - m_meta_pos; int len; m_meta_size = ((unsigned char *)buf)[start_offs] * 16; len = min(x - start_offs - 1, m_meta_size); if (len) memcpy(m_meta_buf, buf + start_offs + 1, len); m_meta_buf_pos = len; if (m_meta_buf_pos == m_meta_size) // full read of metadata successful { x -= m_meta_size + 1; if (x > start_offs) memcpy(buf + start_offs, buf + start_offs + 1 + m_meta_size, x - start_offs); #ifdef WINAMP_PLUGIN extern void process_metadata(char *buf, int size); process_metadata(m_meta_buf, m_meta_size); #endif //if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size); m_meta_buf_pos = 0; m_meta_pos = -start_offs; m_meta_size = 0; } else { x = start_offs; // otherwise, there's only the first block of data } } if (x > 0) { m_meta_pos += x; } l = x; } // end of poopie metadata } // !uvox #if 0 { FILE *fh = fopen("c:\\dump.nsv", "ab"); fwrite(buffer, 1, l, fh); fclose(fh); } #endif return l; } return 0; } static void parseURL(char *url, char *lp, char *host, int *port, char *req) { char *p, *np; /* if (_strnicmp(url,"http://",4) && _strnicmp(url,"icy://",6) && _strnicmp(url,"sc://",6) && _strnicmp(url,"shoutcast://",12)) return; */ np = p = strstr(url, "://"); if (!np) np = (char*)url; else np += 3; if (!p) p = (char*)url; else p += 3; while (np && *np != '/' && *np) *np++; if (np && *np) { lstrcpynA(req, np, 2048); *np++ = 0; } else strcpy(req, "/"); np = p; while (np && *np != '@' && *np) np++; if (np && *np) { *np++ = 0; lstrcpynA(lp, p, 256); p = np; } else lp[0] = 0; np = p; while (np && *np != ':' && *np) np++; if (*np) { *np++ = 0; *port = atoi(np); } else *port = 80; lstrcpynA(host, p, 256); } int HTTPReader::getProxyInfo(const char *url, char *out) { #ifndef WINAMPX char INI_FILE[MAX_PATH] = {0}; char *p; GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE)); p = INI_FILE + strlen(INI_FILE); while (p >= INI_FILE && *p != '.') p--; strcpy(++p, "ini"); GetPrivateProfileStringA("Winamp", "proxy", "", out, 8192, INI_FILE); return !!out[0]; #else DWORD v = 0; HKEY hKey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\AOL\\Unagi", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { DWORD l = 4; DWORD t; if (RegQueryValueEx(hKey, "ProxyEnable", NULL, &t, (unsigned char *)&v, &l) == ERROR_SUCCESS && t == REG_DWORD) { if ( v != 2 ) { l = 8192; if (RegQueryValueEx(hKey, "ProxyServer", NULL, &t, (unsigned char *)out, &l ) != ERROR_SUCCESS || t != REG_SZ) { v = 0; *out = 0; } } else return 0; } else v = 0; out[512 - 1] = 0; RegCloseKey(hKey); } if ( !v && m_useaproxy ) { char blah[8192] = ""; lstrcpyn(blah, url, 8192); char plp[512] = {0}; char phost[512] = {0}; int pport = 80; char pthereq[1024] = {0}; parseURL(blah, plp, phost, &pport, pthereq); v = ResolvProxyFromURL(url, phost, out); if ( v < 0 ) v = 0; // error getting proxy } if ( v > 0) { char prox[1024] = {0}; wsprintf(prox, "PROXY: %s", out); SendMetadata(prox, 1); } return v; #endif } class Win32FileReader : public IDataReader { public: Win32FileReader(HANDLE file) { m_hFile = file; m_eof = 0; m_err = NULL; } ~Win32FileReader() { CloseHandle(m_hFile); } size_t read(char *buf, size_t len) { DWORD ob = 0; if (!len) return 0; if (!ReadFile(m_hFile, buf, (DWORD)len, &ob, NULL)) { m_err = "Error calling ReadFile()!"; return 0; } else if (!ob) m_eof = true; return ob; } bool iseof() { return m_eof; } bool canseek() { return 1; } int seek(uint64_t newpos) { LARGE_INTEGER li; li.QuadPart = newpos; li.LowPart = SetFilePointer (m_hFile, li.LowPart, &li.HighPart, SEEK_SET); if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { li.QuadPart = -1; } return li.QuadPart== ~0; } uint64_t getsize() { LARGE_INTEGER position; position.QuadPart=0; position.LowPart = GetFileSize(m_hFile, (LPDWORD)&position.HighPart); if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) return INVALID_FILE_SIZE; else return position.QuadPart; } char *geterror() { return m_err; } private: HANDLE m_hFile; bool m_eof; char *m_err; }; #define VAR_TO_FPOS(fpos, var) (fpos) = (var) #define FPOS_TO_VAR(fpos, typed, var) (var) = (typed)(fpos) class FileReader : public IDataReader { public: FileReader(FILE *file) { fp = file; m_err = NULL; } ~FileReader() { fclose(fp); } size_t read(char *buf, size_t len) { size_t ob; if (!len) return 0; ob = fread(buf, 1, len, fp); if (!ob && ferror(fp)) { m_err = "Error calling fread()!"; return 0; } return ob; } bool iseof() { return !!feof(fp); } bool canseek() { return 1; } int seek(uint64_t newpos) { fpos_t pos= newpos; VAR_TO_FPOS(pos, newpos); return fsetpos(fp, &pos); } unsigned __int64 getsize() { struct stat s; if (fstat(fileno(fp), &s) < 0) { m_err = "Error calling fread()!"; return 0; } return s.st_size; } char *geterror() { return m_err; } private: FILE *fp; char *m_err; }; IDataReader *CreateReader(const char *url) { if (strstr(url, "://")) return new HTTPReader(url); #ifdef _WIN32 HANDLE hFile = CreateFileA(url, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile != INVALID_HANDLE_VALUE) return new Win32FileReader(hFile); #else FILE *fp = fopen(url, "r"); if (fp) return new FileReader(fp); #endif return NULL; }