/* Copyright (C) 2011 Birunthan Mohanathas (www.poiru.net) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "StdAfx.h" #include "Internet.h" HINTERNET CInternet::c_NetHandle = NULL; /* ** Initialize ** ** Initialize internet handle and crtical section. ** */ void CInternet::Initialize() { c_NetHandle = InternetOpen(L"Rainmeter NowPlaying.dll", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (!c_NetHandle) { LSLog(LOG_ERROR, L"Rainmeter", L"NowPlayingPlugin: Unable to open net handle."); } } /* ** Finalize ** ** Close handles and delete critical section. ** */ void CInternet::Finalize() { if (c_NetHandle) InternetCloseHandle(c_NetHandle); } /* ** DownloadUrl ** ** Downloads given url and returns it as a string. ** */ std::wstring CInternet::DownloadUrl(const std::wstring& url, int codepage) { std::wstring result; DWORD flags = INTERNET_FLAG_RESYNCHRONIZE; HINTERNET hUrlDump = InternetOpenUrl(c_NetHandle, url.c_str(), NULL, NULL, flags, 0); if (!hUrlDump) { LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlguin: Unable to open internet file."); return result; } // Allocate the buffer. const int CHUNK_SIZE = 8192; BYTE* lpData = new BYTE[CHUNK_SIZE]; BYTE* lpOutPut; BYTE* lpHolding = NULL; int nCounter = 1; int nBufferSize; DWORD dwDataSize = 0; DWORD dwSize = 0; do { // Read the data. if (!InternetReadFile(hUrlDump, (LPVOID)lpData, CHUNK_SIZE, &dwSize)) { LSLog(LOG_DEBUG, L"Rainmeter", L"NowPlayingPlguin: Unable to read internet file."); break; } else { // Check if all of the data has been read. This should // never get called on the first time through the loop. if (dwSize == 0) { break; } // Determine the buffer size to hold the new data and the data // already written (if any). nBufferSize = dwDataSize + dwSize; // Allocate the output buffer. lpOutPut = new BYTE[nBufferSize + 2]; // Make sure the buffer is not the initial buffer. if (lpHolding != NULL) { // Copy the data in the holding buffer. memcpy(lpOutPut, lpHolding, dwDataSize); // Delete the old buffer delete [] lpHolding; lpHolding = lpOutPut; lpOutPut = lpOutPut + dwDataSize; } else { lpHolding = lpOutPut; } // Copy the data buffer. memcpy(lpOutPut, lpData, dwSize); dwDataSize += dwSize; // End with double null lpOutPut[dwSize] = 0; lpOutPut[dwSize + 1] = 0; // Increment the number of buffers read. ++nCounter; // Clear the buffer memset(lpData, 0, CHUNK_SIZE); } } while (true); InternetCloseHandle(hUrlDump); delete [] lpData; if (lpHolding) { result = ConvertToWide((LPCSTR)lpHolding, codepage); delete [] lpHolding; } return result; } /* ** EncodeUrl ** ** Encode reserved characters. ** */ std::wstring CInternet::EncodeUrl(const std::wstring& url) { // Based on http://www.zedwood.com/article/111/cpp-urlencode-function static const std::wstring url_chars = L" !*'();:@&=+$,/?#[]"; std::wstring ret; for (size_t i = 0, max = url.length(); i < max; ++i) { if (url_chars.find_first_of(url[i]) != std::string::npos) { // If reserved character ret.append(L"%"); WCHAR buffer[3]; _snwprintf_s(buffer, 3, L"%.2X", url[i]); ret.append(buffer); } else { ret.push_back(url[i]); } } return ret; } /* ** DecodeReferences ** ** Decodes numeric references. ** */ void CInternet::DecodeReferences(std::wstring& str) { // From WebParser.cpp std::wstring::size_type start = 0; while ((start = str.find(L'&', start)) != std::wstring::npos) { std::wstring::size_type end, pos; if ((end = str.find(L';', start)) == std::wstring::npos) break; pos = start + 1; if (pos == end) // &; - skip { start = end + 1; continue; } else if ((end - pos) > 10) // name (or number) is too long { ++start; continue; } if (str[pos] == L'#') // Numeric character reference { if (++pos == end) // &#; - skip { start = end + 1; continue; } int base; if (str[pos] == L'x' || str[pos] == L'X') { if (++pos == end) // &#x; or &#X; - skip { start = end + 1; continue; } base = 16; } else { base = 10; } std::wstring num(str, pos, end - pos); WCHAR* pch = NULL; errno = 0; long ch = wcstol(num.c_str(), &pch, base); if (pch == NULL || *pch != L'\0' || errno == ERANGE || ch <= 0 || ch >= 0xFFFE) // invalid character { start = pos; continue; } str.replace(start, end - start + 1, 1, (WCHAR)ch); ++start; } else // Character entity reference { start = end + 1; continue; } } } /* ** ConvertToWide ** ** Convert from multibyte ti wide string. ** */ std::wstring CInternet::ConvertToWide(LPCSTR str, int codepage) { std::wstring szWide; if (str && *str) { int strLen = (int)strlen(str) + 1; int bufLen = MultiByteToWideChar(codepage, 0, str, strLen, NULL, 0); if (bufLen > 0) { WCHAR* wideSz = new WCHAR[bufLen]; wideSz[0] = 0; MultiByteToWideChar(codepage, 0, str, strLen, wideSz, bufLen); szWide = wideSz; delete [] wideSz; } } return szWide; }