diff options
| -rw-r--r-- | src/http.c | 481 | ||||
| -rw-r--r-- | src/http.h | 46 | ||||
| -rw-r--r-- | src/main.c | 52 | ||||
| -rw-r--r-- | src/main.h | 6 | ||||
| -rw-r--r-- | src/settings.c | 2 | ||||
| -rw-r--r-- | src/ui.c | 116 | ||||
| -rw-r--r-- | src/ui.h | 4 | ||||
| -rw-r--r-- | src/ui_act.c | 22 | 
8 files changed, 570 insertions, 159 deletions
| diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..2c86b44 --- /dev/null +++ b/src/http.c @@ -0,0 +1,481 @@ +/*
 +Copyright (c) 2015
 +	Michał Cichoń <thedmd@interia.pl>
 +
 +Permission is hereby granted, free of charge, to any person obtaining a copy
 +of this software and associated documentation files (the "Software"), to deal
 +in the Software without restriction, including without limitation the rights
 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 +copies of the Software, and to permit persons to whom the Software is
 +furnished to do so, subject to the following conditions:
 +
 +The above copyright notice and this permission notice shall be included in
 +all copies or substantial portions of the Software.
 +
 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 +THE SOFTWARE.
 +*/
 +
 +#include "config.h"
 +#include "http.h"
 +#include <Windows.h>
 +#include <winhttp.h>
 +#pragma comment(lib, "winhttp.lib")
 +
 +struct _http_t {
 +	HINTERNET		session;
 +	HINTERNET		connection;
 +	wchar_t*		endpoint;
 +	wchar_t*		securePort;
 +	wchar_t*		autoProxy;
 +	wchar_t*		proxy;
 +	wchar_t*		proxyUsername;
 +	wchar_t*		proxyPassword;
 +	char*			error;
 +};
 +
 +static char* HttpToString(const wchar_t* wideString, int size);
 +static wchar_t* HttpToWideString(const char* string, int size);
 +static bool HttpCreateConnection (http_t http);
 +static void HttpCloseConnection (http_t http);
 +static void HttpSetLastError (http_t http, const char* message);
 +static void HttpSetLastErrorFromWinHttp (http_t http);
 +static char* HttpFormatWinApiError (DWORD errorCode, HINSTANCE module);
 +static char* HttpFormatWinHttpError (DWORD errorCode);
 +static void HttpClearProxy (http_t http);
 +
 +# define WINHTTP_SAFE(condition) do { if (condition) break; HttpSetLastErrorFromWinHttp (http); return false; } while (false)
 +# define WINHTTP_SAFE_DONE(condition) do { if (condition) break; HttpSetLastErrorFromWinHttp (http); goto done; } while (false)
 +
 +static char* HttpToString(const wchar_t* wideString, int size) {
 +	int utfSize = WideCharToMultiByte(CP_UTF8, 0, wideString, size, NULL, 0, NULL, NULL);
 +	char* utfMessage = malloc(utfSize + 1);
 +	if (utfMessage)	{
 +		utfMessage[utfSize] = 0;
 +		WideCharToMultiByte(CP_UTF8, 0, wideString, size, utfMessage, utfSize, NULL, NULL);
 +	}
 +	return utfMessage;
 +}
 +
 +static wchar_t* HttpToWideString(const char* string, int size) {
 +	int wideSize = MultiByteToWideChar(CP_UTF8, 0, string, size, NULL, 0);
 +	int wideBytes = (wideSize + 1) * sizeof(wchar_t);
 +	wchar_t* wideMessage = malloc(wideBytes);
 +	if (wideMessage) {
 +		wideMessage[wideSize] = 0;
 +		MultiByteToWideChar(CP_UTF8, 0, string, size, wideMessage, wideSize);
 +	}
 +	return wideMessage;
 +}
 +
 +
 +static bool HttpCreateConnection (http_t http) {
 +	INTERNET_PORT defaultPort = INTERNET_DEFAULT_PORT;
 +
 +	HttpCloseConnection (http);
 +
 +	http->session = WinHttpOpen(
 +		L"WinHTTP/1.0",
 +		WINHTTP_ACCESS_TYPE_NO_PROXY,
 +		WINHTTP_NO_PROXY_NAME,
 +		WINHTTP_NO_PROXY_BYPASS,
 +		0);
 +	WINHTTP_SAFE(http->session != NULL);
 +
 +	WinHttpSetTimeouts(http->session,
 +		60 * 1000,  // DNS time-out
 +		60 * 1000,  // connect time-out
 +		30 * 1000,  // send time-out
 +		30 * 1000); // receive time-out
 +
 +	http->connection = WinHttpConnect(
 +		http->session,
 +		http->endpoint,
 +		defaultPort,
 +		0);
 +	WINHTTP_SAFE(http->connection != NULL);
 +
 +	return true;
 +}
 +
 +static void HttpCloseConnection (http_t http) {
 +	if (http->connection) {
 +		WinHttpCloseHandle(http->connection);
 +		http->connection = NULL;
 +	}
 +
 +	if (http->session) {
 +		WinHttpCloseHandle(http->session);
 +		http->session = NULL;
 +	}
 +}
 +
 +static void HttpSetLastError (http_t http, const char* message) {
 +	free(http->error);
 +	http->error = NULL;
 +	
 +	if (message)
 +		http->error = strdup(message);
 +}
 +
 +static void HttpSetLastErrorFromWinHttp (http_t http) {
 +	free(http->error);
 +	http->error = NULL;
 +
 +	DWORD error = GetLastError();
 +	if (error)
 +		http->error = HttpFormatWinHttpError(error);
 +}
 +
 +static char* HttpFormatWinApiError (DWORD errorCode, HINSTANCE module) {
 +	const int source_flag = module ? FORMAT_MESSAGE_FROM_HMODULE : FORMAT_MESSAGE_FROM_SYSTEM;
 +
 +	HLOCAL buffer = NULL;
 +	int bufferLength = FormatMessageW(
 +		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | source_flag,
 +		(void*)module,
 +		errorCode,
 +		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 +		(LPWSTR)&buffer,
 +		0,
 +		NULL);
 +
 +	if (bufferLength > 0) {
 +		char* message;
 +
 +		wchar_t* wideMessage = (wchar_t*)buffer;
 +
 +		/* Drop new line from the end. */
 +		wchar_t* wideMessageBack = wideMessage + bufferLength - 1;
 +		while (wideMessageBack > wideMessage && (*wideMessageBack == '\r' || *wideMessageBack == '\n'))
 +			--wideMessageBack;
 +
 +		message = HttpToString (wideMessage, wideMessageBack - wideMessage + 1);
 +
 +		LocalFree(buffer);
 +
 +		return message;
 +	}
 +	else
 +		return NULL;
 +}
 +
 +static char* HttpFormatWinHttpError (DWORD errorCode) {
 +	if (errorCode >= WINHTTP_ERROR_BASE && errorCode <= WINHTTP_ERROR_LAST) {
 +		HMODULE module = GetModuleHandleW(L"WinHTTP.dll");
 +		if (module) {
 +			char* message = HttpFormatWinApiError(errorCode, module);
 +			if (message)
 +				return message;
 +		}
 +	}
 +
 +	return HttpFormatWinApiError(errorCode, NULL);
 +}
 +
 +bool HttpInit(http_t* http, const char* endpoint, const char* securePort) {
 +	http_t out = malloc(sizeof(struct _http_t));
 +	if (!out)
 +		return false;
 +	memset(out, 0, sizeof(struct _http_t));
 +
 +	out->endpoint   = HttpToWideString(endpoint, -1);
 +	out->securePort = HttpToWideString(securePort, -1);
 +
 +	if (!HttpCreateConnection (out)) {
 +		HttpDestroy (out);
 +		return false;
 +	}
 +
 +	*http = out;
 +	return true;
 +}
 +
 +void HttpDestroy(http_t http) {
 +	if (http) {
 +		free(http->endpoint);
 +		free(http->securePort);
 +		http->endpoint = NULL;
 +		http->securePort = NULL;
 +		HttpCloseConnection (http);
 +		HttpClearProxy (http);
 +	}
 +	free(http);
 +}
 +
 +static void HttpClearProxy (http_t http) {
 +	if (http->autoProxy) {
 +		free(http->autoProxy);
 +		http->autoProxy = NULL;
 +	}
 +
 +	if (http->proxy) {
 +		free(http->proxy);
 +		http->proxy = NULL;
 +	}
 +
 +	if (http->proxyUsername) {
 +		free(http->proxyUsername);
 +		http->proxyUsername = NULL;
 +	}
 +
 +	if (http->proxyPassword) {
 +		free(http->proxyPassword);
 +		http->proxyPassword = NULL;
 +	}
 +}
 +
 +bool HttpSetAutoProxy (http_t http, const char* url) {
 +	HttpClearProxy (http);
 +	if (HttpSetProxy (http, url)) {
 +		http->autoProxy = http->proxy;
 +		http->proxy     = NULL;
 +		return true;
 +	}
 +	else
 +		return false;
 +}
 +
 +bool HttpSetProxy (http_t http, const char* url) {
 +	URL_COMPONENTS urlComponents;
 +	wchar_t* wideUrl = NULL;
 +	wchar_t* wideUrl2 = NULL;
 +	wchar_t* wideUsername = NULL;
 +	wchar_t* widePassword = NULL;
 +
 +	ZeroMemory(&urlComponents, sizeof(urlComponents));
 +	urlComponents.dwStructSize      = sizeof(urlComponents);
 +	urlComponents.dwUserNameLength = -1;
 +	urlComponents.dwPasswordLength = -1;
 +
 +	wideUrl = HttpToWideString(url, -1);
 +	if (WinHttpCrackUrl(wideUrl, wcslen(wideUrl), 0, &urlComponents)) {
 +		if (urlComponents.lpszUserName && urlComponents.dwUserNameLength > 0) {
 +			wideUsername = wcsdup(urlComponents.lpszUserName);
 +			wideUsername[urlComponents.dwUserNameLength] = 0;
 +		}
 +		if (urlComponents.lpszPassword && urlComponents.dwPasswordLength > 0) {
 +			widePassword = wcsdup(urlComponents.lpszPassword);
 +			widePassword[urlComponents.dwPasswordLength] = 0;
 +		}
 +	}
 +
 +	ZeroMemory(&urlComponents, sizeof(urlComponents));
 +	urlComponents.dwStructSize = sizeof(urlComponents);
 +	urlComponents.dwHostNameLength  = -1;
 +	urlComponents.dwUrlPathLength   = -1;
 +
 +	if (!WinHttpCrackUrl(wideUrl, wcslen(wideUrl), 0, &urlComponents)) {
 +		free(wideUsername);
 +		free(widePassword);
 +		return false;
 +	}
 +
 +	if (urlComponents.lpszHostName && urlComponents.dwHostNameLength > 0) {
 +		wideUrl2 = wcsdup(urlComponents.lpszHostName);
 +		wideUrl2[urlComponents.lpszUrlPath - urlComponents.lpszHostName] = 0;
 +	}
 +
 +	free(wideUrl);
 +
 +	HttpClearProxy(http);
 +	http->proxy         = wideUrl2;
 +	http->proxyUsername = wideUsername;
 +	http->proxyPassword = widePassword;
 +	return true;
 +}
 +
 +bool HttpRequest(http_t http, PianoRequest_t * const request) {
 +	HINTERNET handle = NULL;
 +	wchar_t* wideQuery = NULL;
 +	bool requestSent = false;
 +	bool complete = false;
 +	int retryLimit = 3;
 +	size_t responseDataSize;
 +
 +	wideQuery = HttpToWideString(request->urlPath, -1);
 +	WINHTTP_SAFE_DONE(wideQuery != NULL);
 +
 +	handle = WinHttpOpenRequest(
 +		http->connection,
 +		L"POST",
 +		wideQuery,
 +		L"HTTP/1.1",
 +		WINHTTP_NO_REFERER,
 +		WINHTTP_DEFAULT_ACCEPT_TYPES,
 +		request->secure ? WINHTTP_FLAG_SECURE : 0);
 +	WINHTTP_SAFE_DONE(handle != NULL);
 +
 +	if (http->proxy || http->autoProxy) {
 +		wchar_t* fullUrl;
 +		DWORD fullUrlSize = 0;
 +		WINHTTP_PROXY_INFO proxyInfo;
 +		bool success;
 +
 +		if (http->autoProxy) {
 +			WINHTTP_AUTOPROXY_OPTIONS proxyOptions = { 0 };
 +
 +			success = WinHttpQueryOption(request, WINHTTP_OPTION_URL, NULL, &fullUrlSize);
 +			WINHTTP_SAFE(!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
 +			fullUrl = calloc(1, fullUrlSize + 1);
 +			success = WinHttpQueryOption(request, WINHTTP_OPTION_URL, fullUrl, &fullUrlSize);
 +			if (!success) {
 +				free(fullUrl);
 +				WINHTTP_SAFE(success);
 +			}
 +
 +			proxyOptions.lpszAutoConfigUrl = http->autoProxy;
 +			proxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
 +
 +			if (!(success = WinHttpGetProxyForUrl(http->session, fullUrl, &proxyOptions, &proxyInfo))) {
 +				proxyOptions.fAutoLogonIfChallenged = true;
 +				success = WinHttpGetProxyForUrl(http->session, fullUrl, &proxyOptions, &proxyInfo);
 +			}
 +
 +			if (!success) {
 +				free(fullUrl);
 +				WINHTTP_SAFE(success);
 +			}
 +		}
 +		else {
 +			proxyInfo.dwAccessType    = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
 +			proxyInfo.lpszProxy       = http->proxy;
 +			proxyInfo.lpszProxyBypass = NULL;
 +		}
 +
 +		WINHTTP_SAFE(WinHttpSetOption(handle,
 +			WINHTTP_OPTION_PROXY,
 +			&proxyInfo, sizeof(proxyInfo)));
 +
 +		if (http->proxyUsername && http->proxyPassword) {
 +			WINHTTP_SAFE(WinHttpSetCredentials(handle,
 +				WINHTTP_AUTH_TARGET_PROXY,
 +				WINHTTP_AUTH_SCHEME_BASIC,
 +				http->proxyUsername,
 +				http->proxyPassword,
 +				NULL));
 +		}
 +	}
 +
 +	while (retryLimit > 0) {
 +		DWORD errorCode, statusCode, statusCodeSize;
 +		bool succeeded = false;
 +		bool retry = false;
 +
 +		if (!requestSent) {
 +			size_t postDataSize = strlen(request->postData);
 +			succeeded = WinHttpSendRequest(handle,
 +				WINHTTP_NO_ADDITIONAL_HEADERS,
 +				0,
 +				request->postData,
 +				postDataSize,
 +				postDataSize,
 +				0);
 +
 +			if (succeeded)
 +				requestSent = true;
 +		}
 +
 +		if (requestSent)
 +			succeeded = WinHttpReceiveResponse(handle, NULL);
 +
 +		errorCode = GetLastError();
 +
 +		statusCode = 0;
 +		statusCodeSize = sizeof(statusCode);
 +		if (!WinHttpQueryHeaders(handle,
 +				WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
 +				WINHTTP_HEADER_NAME_BY_INDEX,
 +				&statusCode, &statusCodeSize, WINHTTP_NO_HEADER_INDEX)) {
 +			statusCode = 0;
 +		}
 +
 +		if (succeeded && statusCode == 407) {
 +			requestSent = false;
 +			retry       = true;
 +		}
 +		else {
 +			if (errorCode == ERROR_SUCCESS)
 +				break;
 +
 +			switch (errorCode) {
 +				case ERROR_WINHTTP_RESEND_REQUEST:
 +					requestSent = false;
 +					/* pass trough */
 +
 +				case ERROR_WINHTTP_NAME_NOT_RESOLVED:
 +				case ERROR_WINHTTP_CANNOT_CONNECT:
 +				case ERROR_WINHTTP_TIMEOUT:
 +					retry = true;
 +					break;
 +
 +				default:
 +					HttpSetLastErrorFromWinHttp (http);
 +					goto done;
 +			}
 +		}
 +
 +		if (retry)
 +			--retryLimit;
 +	}
 +
 +	responseDataSize = 0;
 +	while (retryLimit > 0)
 +	{
 +		DWORD bytesLeft;
 +		char* writePtr;
 +
 +		DWORD bytesAvailable = 0;
 +		if (!WinHttpQueryDataAvailable(handle, &bytesAvailable)) {
 +			WINHTTP_SAFE(GetLastError() == ERROR_WINHTTP_TIMEOUT);
 +			--retryLimit;
 +			continue;
 +		}
 +
 +		if (0 == bytesAvailable)
 +			break;
 +
 +		responseDataSize += bytesAvailable;
 +		request->responseData = realloc(request->responseData, responseDataSize + 1);
 +
 +		writePtr = request->responseData + responseDataSize - bytesAvailable;
 +		writePtr[bytesAvailable] = 0;
 +
 +		bytesLeft = bytesAvailable;
 +		while (bytesLeft > 0)
 +		{
 +			DWORD bytesRead = 0;
 +			if (!WinHttpReadData(handle, writePtr, bytesLeft, &bytesRead))
 +			{
 +				WINHTTP_SAFE(GetLastError() == ERROR_WINHTTP_TIMEOUT);
 +				if (--retryLimit == 0)
 +					break;
 +
 +				continue;
 +			}
 +
 +			bytesLeft -= bytesRead;
 +			writePtr  += bytesRead;
 +		}
 +
 +		if (bytesLeft > 0)
 +			HttpSetLastError (http, "Maximum retries count exceeded");
 +	}
 +
 +	complete = true;
 +
 +	HttpSetLastError (http, NULL);
 +
 +done:
 +	free(wideQuery);
 +	return complete;
 +}
 +
 +const char* HttpGetError(http_t http) {
 +	return http->error;
 +}
 diff --git a/src/http.h b/src/http.h new file mode 100644 index 0000000..a321cd3 --- /dev/null +++ b/src/http.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2015 +	Michał Cichoń <thedmd@interia.pl> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef SRC_HTTP_H_CN979RE9 +#define SRC_HTTP_H_CN979RE9 + +#include "config.h" + +#include <stdbool.h> + +#include "piano.h" +#include "settings.h" + +typedef struct _http_t *http_t; + +bool HttpInit (http_t*, const char*, const char*); +void HttpDestroy (http_t); + +bool HttpSetAutoProxy (http_t, const char*); +bool HttpSetProxy(http_t, const char*); + +bool HttpRequest (http_t, PianoRequest_t * const); +const char* HttpGetError (http_t); + +#endif /* SRC_HTTP_H_CN979RE9 */ + @@ -36,7 +36,6 @@ THE SOFTWARE.   */  static bool BarMainLoginUser (BarApp_t *app) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataLogin_t reqData;  	bool ret; @@ -45,9 +44,9 @@ static bool BarMainLoginUser (BarApp_t *app) {  	reqData.step = 0;  	BarUiMsg (&app->settings, MSG_INFO, "Login... "); -	ret = BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRet, &wRet); +	ret = BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRet);  	BarUiStartEventCmd (&app->settings, "userlogin", NULL, NULL, &app->player, -			NULL, pRet, wRet); +			NULL, pRet);  	return ret;  } @@ -139,13 +138,12 @@ static bool BarMainGetLoginCredentials (BarSettings_t *settings,   */  static bool BarMainGetStations (BarApp_t *app) {  	PianoReturn_t pRet; -	CURLcode wRet;  	bool ret;  	BarUiMsg (&app->settings, MSG_INFO, "Get stations... "); -	ret = BarUiPianoCall (app, PIANO_REQUEST_GET_STATIONS, NULL, &pRet, &wRet); +	ret = BarUiPianoCall (app, PIANO_REQUEST_GET_STATIONS, NULL, &pRet);  	BarUiStartEventCmd (&app->settings, "usergetstations", NULL, NULL, &app->player, -			app->ph.stations, pRet, wRet); +			app->ph.stations, pRet);  	return ret;  } @@ -186,14 +184,13 @@ static void BarMainHandleUserInput (BarApp_t *app) {   */  static void BarMainGetPlaylist (BarApp_t *app) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataGetPlaylist_t reqData;  	reqData.station = app->curStation;  	reqData.quality = app->settings.audioQuality;  	BarUiMsg (&app->settings, MSG_INFO, "Receiving new playlist... ");  	if (!BarUiPianoCall (app, PIANO_REQUEST_GET_PLAYLIST, -			&reqData, &pRet, &wRet)) { +			&reqData, &pRet)) {  		app->curStation = NULL;  	} else {  		app->playlist = reqData.retPlaylist; @@ -204,7 +201,7 @@ static void BarMainGetPlaylist (BarApp_t *app) {  	}  	BarUiStartEventCmd (&app->settings, "stationfetchplaylist",  			app->curStation, app->playlist, &app->player, app->ph.stations, -			pRet, wRet); +			pRet);  }  /*	start new player thread @@ -225,16 +222,18 @@ static void BarMainStartPlayback (BarApp_t *app) {  			strncmp (curSong->audioUrl, httpPrefix, strlen (httpPrefix)) != 0) {  		BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n");  	} else { -        BarPlayer2Finish(app->player);  		BarPlayer2SetGain(app->player, curSong->fileGain);  		BarPlayer2Open(app->player, curSong->audioUrl);  		/* throw event */  		BarUiStartEventCmd (&app->settings, "songstart",  				app->curStation, curSong, &app->player, app->ph.stations, -				PIANO_RET_OK, CURLE_OK); +				PIANO_RET_OK); -		BarPlayer2Play(app->player); +		if (!BarPlayer2Play(app->player)) +			++app->playerErrors; +		else +			app->playerErrors = 0;  	}  } @@ -242,24 +241,17 @@ static void BarMainStartPlayback (BarApp_t *app) {   */  static void BarMainPlayerCleanup (BarApp_t *app) {  	BarUiStartEventCmd (&app->settings, "songfinish", app->curStation, -			app->playlist, &app->player, app->ph.stations, PIANO_RET_OK, -			CURLE_OK); +			app->playlist, &app->player, app->ph.stations, PIANO_RET_OK);  	BarPlayer2Finish(app->player);  	BarConsoleSetTitle (TITLE); -	//if (threadRet == (void *) PLAYER_RET_OK) { -	//	app->playerErrors = 0; -	//} else if (threadRet == (void *) PLAYER_RET_SOFTFAIL) { -	//	++app->playerErrors; -	//	if (app->playerErrors >= app->settings.maxPlayerErrors) { -	//		/* don't continue playback if thread reports too many error */ -	//		app->curStation = NULL; -	//	} -	//} else { -	//	app->curStation = NULL; -	//} +	if (app->playerErrors >= app->settings.maxPlayerErrors) { +		/* don't continue playback if thread reports too many error */ +		app->curStation = NULL; +		app->playerErrors = 0; +	}  }  /*	print song duration @@ -369,9 +361,10 @@ int main (int argc, char **argv) {  				app.settings.keys[BAR_KS_HELP]);  	} -	curl_global_init (CURL_GLOBAL_DEFAULT); -	app.http = curl_easy_init (); -	assert (app.http != NULL); +	HttpInit(&app.http2, app.settings.rpcHost, app.settings.rpcTlsPort); +	if (app.settings.controlProxy) +		HttpSetProxy(app.http2, app.settings.controlProxy); +  	BarReadlineInit (&app.rl); @@ -385,8 +378,7 @@ int main (int argc, char **argv) {  	PianoDestroy (&app.ph);  	PianoDestroyPlaylist (app.songHistory);  	PianoDestroyPlaylist (app.playlist); -	curl_easy_cleanup (app.http); -	curl_global_cleanup (); +	HttpDestroy (app.http2);  	BarPlayer2Destroy (app.player);  	BarSettingsDestroy (&app.settings);  	BarConsoleDestroy (); @@ -24,17 +24,19 @@ THE SOFTWARE.  #ifndef SRC_MAIN_H_4ZGSCG6X  #define SRC_MAIN_H_4ZGSCG6X -#include <curl/curl.h> +//#include <curl/curl.h>  #include <piano.h>  #include "player2.h" +#include "http.h"  #include "settings.h"  #include "ui_readline.h"  typedef struct {  	PianoHandle_t ph; -	CURL *http; +	//CURL *http; +	http_t http2;  	player2_t player;  	BarSettings_t settings;  	/* first item is current song */ diff --git a/src/settings.c b/src/settings.c index 6ce55c9..d58eb5c 100644 --- a/src/settings.c +++ b/src/settings.c @@ -30,6 +30,8 @@ THE SOFTWARE.  #include "ui_dispatch.h"  #include <stdlib.h>  #include <assert.h> +#include <memory.h> +#include <string.h>  #define PACKAGE_CONFIG	PACKAGE ".cfg"  #define PACKAGE_STATE	PACKAGE ".state" @@ -29,6 +29,7 @@ THE SOFTWARE.  #include "ui_readline.h"  #include "console.h"  #include <assert.h> +#include <stdio.h>  typedef int (*BarSortFunc_t) (const void *, const void *); @@ -147,103 +148,12 @@ void BarUiMsg (const BarSettings_t *settings, const BarUiMsg_t type,  	fflush (stdout);  } -typedef struct { -	char *data; -	size_t pos; -} buffer; - -static size_t httpFetchCb (char *ptr, size_t size, size_t nmemb, -		void *userdata) { -	buffer * const buffer = userdata; -	size_t recvSize = size * nmemb; - -	if (buffer->data == NULL) { -		if ((buffer->data = malloc (sizeof (*buffer->data) * -				(recvSize + 1))) == NULL) { -			return 0; -		} -	} else { -		char *newbuf; -		if ((newbuf = realloc (buffer->data, sizeof (*buffer->data) * -				(buffer->pos + recvSize + 1))) == NULL) { -			free (buffer->data); -			return 0; -		} -		buffer->data = newbuf; -	} -	memcpy (buffer->data + buffer->pos, ptr, recvSize); -	buffer->pos += recvSize; -	buffer->data[buffer->pos] = '\0'; - -	return recvSize; -} - -#define setAndCheck(k,v) \ -	httpret = curl_easy_setopt (http, k, v); \ -	assert (httpret == CURLE_OK); - -static CURLcode BarPianoHttpRequest (CURL * const http, -		const BarSettings_t * const settings, PianoRequest_t * const req) { -	buffer buffer = {NULL, 0}; -	char url[2048]; -	int ret = snprintf (url, sizeof (url), "%s://%s:%s%s", -		req->secure ? "https" : "http", -		settings->rpcHost, -		req->secure ? settings->rpcTlsPort : "80", -		req->urlPath); -	assert (ret >= 0 && ret <= (int) sizeof (url)); - -	curl_easy_reset (http); -	CURLcode httpret; -	setAndCheck (CURLOPT_URL, url); -	setAndCheck (CURLOPT_USERAGENT, PACKAGE "-" VERSION); -	setAndCheck (CURLOPT_POSTFIELDS, req->postData); -	setAndCheck (CURLOPT_WRITEFUNCTION, httpFetchCb); -	setAndCheck (CURLOPT_WRITEDATA, &buffer); -	setAndCheck (CURLOPT_POST, 1); -	setAndCheck (CURLOPT_TIMEOUT, 30); -	if (settings->caBundle != NULL) { -		setAndCheck (CURLOPT_CAINFO, settings->caBundle); -	} - -	/* set up proxy (control proxy for non-us citizen or global proxy for poor -	 * firewalled fellows) */ -	if (settings->controlProxy != NULL) { -		/* control proxy overrides global proxy */ -		if (curl_easy_setopt (http, CURLOPT_PROXY, -				settings->controlProxy) != CURLE_OK) { -			/* if setting proxy fails, url is invalid */ -			BarUiMsg (settings, MSG_ERR, "Control proxy (%s) is invalid!\n", -					 settings->controlProxy); -		} -	} else if (settings->proxy != NULL && strlen (settings->proxy) > 0) { -		if (curl_easy_setopt (http, CURLOPT_PROXY, -				settings->proxy) != CURLE_OK) { -			/* if setting proxy fails, url is invalid */ -			BarUiMsg (settings, MSG_ERR, "Proxy (%s) is invalid!\n", -					 settings->proxy); -		} -	} - -	struct curl_slist *list = NULL; -	list = curl_slist_append (list, "Content-Type: text/plain"); -	setAndCheck (CURLOPT_HTTPHEADER, list); - -	httpret = curl_easy_perform (http); - -	curl_slist_free_all (list); - -	req->responseData = buffer.data; - -	return httpret; -} -  /*	piano wrapper: prepare/execute http request and pass result back to   *	libpiano (updates data structures)   *	@return 1 on success, 0 otherwise   */  int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, -		void *data, PianoReturn_t *pRet, CURLcode *wRet) { +		void *data, PianoReturn_t *pRet) {  	PianoRequest_t req;  	memset (&req, 0, sizeof (req)); @@ -259,14 +169,13 @@ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type,  			return 0;  		} -		*wRet = BarPianoHttpRequest (app->http, &app->settings, &req); -		if (*wRet != CURLE_OK) { -			BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", -					curl_easy_strerror (*wRet)); +		if (!HttpRequest(app->http2, &req)) { +			BarUiMsg(&app->settings, MSG_NONE, "Network error: %s\n", +				HttpGetError(app->http2));  			if (req.responseData != NULL) { -				free (req.responseData); +				free(req.responseData);  			} -			PianoDestroyRequest (&req); +			PianoDestroyRequest(&req);  			return 0;  		} @@ -277,17 +186,14 @@ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type,  					type != PIANO_REQUEST_LOGIN) {  				/* reauthenticate */  				PianoReturn_t authpRet; -				CURLcode authwRet;  				PianoRequestDataLogin_t reqData;  				reqData.user = app->settings.username;  				reqData.password = app->settings.password;  				reqData.step = 0;  				BarUiMsg (&app->settings, MSG_NONE, "Reauthentication required... "); -				if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &authpRet, -						&authwRet)) { +				if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &authpRet)) {  					*pRet = authpRet; -					*wRet = authwRet;  					if (req.responseData != NULL) {  						free (req.responseData);  					} @@ -569,14 +475,12 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station,  	if (BarReadlineStr (lineBuf, sizeof (lineBuf), app->rl,  			BAR_RL_DEFAULT) > 0) {  		PianoReturn_t pRet; -		CURLcode wRet;  		PianoRequestDataSearch_t reqData;  		reqData.searchStr = lineBuf;  		BarUiMsg (&app->settings, MSG_INFO, "Searching... "); -		if (!BarUiPianoCall (app, PIANO_REQUEST_SEARCH, &reqData, &pRet, -				&wRet)) { +		if (!BarUiPianoCall (app, PIANO_REQUEST_SEARCH, &reqData, &pRet)) {  			return NULL;  		}  		memcpy (&searchResult, &reqData.searchResult, sizeof (searchResult)); @@ -776,7 +680,7 @@ size_t BarUiListSongs (const BarSettings_t *settings,  void BarUiStartEventCmd (const BarSettings_t *settings, const char *type,  		const PianoStation_t *curStation, const PianoSong_t *curSong,  		const player2_t * const player, PianoStation_t *stations, -		PianoReturn_t pRet, CURLcode wRet) { +		PianoReturn_t pRet) {  	//pid_t chld;  	//int pipeFd[2]; @@ -49,9 +49,9 @@ void BarUiPrintSong (const BarSettings_t *, const PianoSong_t *,  size_t BarUiListSongs (const BarSettings_t *, const PianoSong_t *, const char *);  void BarUiStartEventCmd (const BarSettings_t *, const char *,  		const PianoStation_t *, const PianoSong_t *, const player2_t * const, -		PianoStation_t *, PianoReturn_t, CURLcode); +		PianoStation_t *, PianoReturn_t);  int BarUiPianoCall (BarApp_t * const, PianoRequestType_t, -		void *, PianoReturn_t *, CURLcode *); +		void *, PianoReturn_t *);  void BarUiHistoryPrepend (BarApp_t *app, PianoSong_t *song);  #endif /* SRC_UI_H_46P20TS0 */ diff --git a/src/ui_act.c b/src/ui_act.c index 64c756b..c8a808f 100644 --- a/src/ui_act.c +++ b/src/ui_act.c @@ -37,12 +37,12 @@ THE SOFTWARE.   */  #define BarUiActDefaultEventcmd(name) BarUiStartEventCmd (&app->settings, \  		name, selStation, selSong, &app->player, app->ph.stations, \ -		pRet, wRet) +		pRet)  /*	standard piano call   */  #define BarUiActDefaultPianoCall(call, arg) BarUiPianoCall (app, \ -		call, arg, &pRet, &wRet) +		call, arg, &pRet)  /*	helper to _really_ skip a song (unlock mutex, quit player)   *	@param player handle @@ -60,7 +60,6 @@ static inline void BarUiDoSkipSong (player2_t player) {   */  static int BarTransformIfShared (BarApp_t *app, PianoStation_t *station) {  	PianoReturn_t pRet; -	CURLcode wRet;  	assert (station != NULL); @@ -68,7 +67,7 @@ static int BarTransformIfShared (BarApp_t *app, PianoStation_t *station) {  	if (!station->isCreator) {  		BarUiMsg (&app->settings, MSG_INFO, "Transforming station... ");  		if (!BarUiPianoCall (app, PIANO_REQUEST_TRANSFORM_STATION, station, -				&pRet, &wRet)) { +				&pRet)) {  			return 0;  		}  	} @@ -93,7 +92,6 @@ BarUiActCallback(BarUiActHelp) {   */  BarUiActCallback(BarUiActAddMusic) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataAddSeed_t reqData;  	assert (selStation != NULL); @@ -119,7 +117,6 @@ BarUiActCallback(BarUiActAddMusic) {   */  BarUiActCallback(BarUiActBanSong) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoStation_t *realStation;  	assert (selStation != NULL); @@ -151,7 +148,6 @@ BarUiActCallback(BarUiActBanSong) {   */  BarUiActCallback(BarUiActCreateStation) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataCreateStation_t reqData;  	reqData.type = PIANO_MUSICTYPE_INVALID; @@ -169,7 +165,6 @@ BarUiActCallback(BarUiActCreateStation) {   */  BarUiActCallback(BarUiActCreateStationFromSong) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataCreateStation_t reqData;  	char selectBuf[2]; @@ -199,7 +194,6 @@ BarUiActCallback(BarUiActCreateStationFromSong) {   */  BarUiActCallback(BarUiActAddSharedStation) {  	PianoReturn_t pRet; -	CURLcode wRet;  	char stationId[50];  	PianoRequestDataCreateStation_t reqData; @@ -219,7 +213,6 @@ BarUiActCallback(BarUiActAddSharedStation) {   */  BarUiActCallback(BarUiActDeleteStation) {  	PianoReturn_t pRet; -	CURLcode wRet;  	assert (selStation != NULL); @@ -244,7 +237,6 @@ BarUiActCallback(BarUiActDeleteStation) {   */  BarUiActCallback(BarUiActExplain) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataExplain_t reqData;  	assert (selSong != NULL); @@ -263,7 +255,6 @@ BarUiActCallback(BarUiActExplain) {   */  BarUiActCallback(BarUiActStationFromGenre) {  	PianoReturn_t pRet; -	CURLcode wRet;  	const PianoGenreCategory_t *curCat;  	const PianoGenre_t *curGenre;  	int i; @@ -373,7 +364,6 @@ BarUiActCallback(BarUiActDebug) {   */  BarUiActCallback(BarUiActLoveSong) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoStation_t *realStation;  	assert (selStation != NULL); @@ -429,7 +419,6 @@ BarUiActCallback(BarUiActTogglePause) {   */  BarUiActCallback(BarUiActRenameStation) {  	PianoReturn_t pRet; -	CURLcode wRet;  	char lineBuf[100];  	assert (selStation != NULL); @@ -472,7 +461,6 @@ BarUiActCallback(BarUiActSelectStation) {   */  BarUiActCallback(BarUiActTempBanSong) {  	PianoReturn_t pRet; -	CURLcode wRet;  	assert (selSong != NULL); @@ -538,7 +526,6 @@ static void BarUiActQuickmixCallback (BarApp_t *app, char *buf) {   */  BarUiActCallback(BarUiActSelectQuickMix) {  	PianoReturn_t pRet; -	CURLcode wRet;  	assert (selStation != NULL); @@ -607,7 +594,6 @@ BarUiActCallback(BarUiActHistory) {   */  BarUiActCallback(BarUiActBookmark) {  	PianoReturn_t pRet; -	CURLcode wRet;  	char selectBuf[2];  	assert (selSong != NULL); @@ -655,7 +641,6 @@ static const char *boolToYesNo (const bool value) {   */  BarUiActCallback(BarUiActSettings) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoSettings_t settings;  	PianoRequestDataChangeSettings_t reqData;  	bool modified = false; @@ -751,7 +736,6 @@ BarUiActCallback(BarUiActSettings) {   */  BarUiActCallback(BarUiActManageStation) {  	PianoReturn_t pRet; -	CURLcode wRet;  	PianoRequestDataGetStationInfo_t reqData;  	char selectBuf[2], allowedActions[6], *allowedPos = allowedActions;  	char question[64]; | 
