diff options
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | src/libwaitress/waitress.c | 332 | ||||
| -rw-r--r-- | src/libwaitress/waitress.h | 31 | ||||
| -rw-r--r-- | src/main.c | 22 | ||||
| -rw-r--r-- | src/ui.c | 2 | 
5 files changed, 282 insertions, 118 deletions
| @@ -115,15 +115,22 @@ libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \  			-I ${LIBEZXML_INCLUDE} -c -fPIC -o $@ $<  clean: -	${RM} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} ${LIBEZXML_OBJ} \ -			${LIBPIANO_RELOBJ} ${LIBWAITRESS_RELOBJ} ${LIBEZXML_RELOBJ} pianobar \ -			libpiano.so* libpiano.a +	${RM} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} ${LIBWAITRESS_OBJ}/test.o \ +			${LIBEZXML_OBJ} ${LIBPIANO_RELOBJ} ${LIBWAITRESS_RELOBJ} \ +			${LIBEZXML_RELOBJ} pianobar libpiano.so* libpiano.a waitress-test  all: pianobar  debug: pianobar  debug: CFLAGS=-Wall -pedantic -ggdb +waitress-test: CFLAGS+= -DTEST +waitress-test: ${LIBWAITRESS_OBJ} +	${CC} ${LDFLAGS} ${LIBWAITRESS_OBJ} -o waitress-test + +test: waitress-test +	./waitress-test +  ifeq (${DYNLINK},1)  install: pianobar install-libpiano  else diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c index dad5d47..ec711b8 100644 --- a/src/libwaitress/waitress.c +++ b/src/libwaitress/waitress.c @@ -1,5 +1,5 @@  /* -Copyright (c) 2009-2010 +Copyright (c) 2009-2011  	Lars-Dominik Braun <lars@6xq.net>  Permission is hereby granted, free of charge, to any person obtaining a copy @@ -35,6 +35,7 @@ THE SOFTWARE.  #include <fcntl.h>  #include <poll.h>  #include <errno.h> +#include <assert.h>  #include "config.h"  #include "waitress.h" @@ -44,12 +45,14 @@ typedef struct {  	size_t pos;  } WaitressFetchBufCbBuffer_t; -inline void WaitressInit (WaitressHandle_t *waith) { +void WaitressInit (WaitressHandle_t *waith) {  	memset (waith, 0, sizeof (*waith));  	waith->socktimeout = 30000;  } -inline void WaitressFree (WaitressHandle_t *waith) { +void WaitressFree (WaitressHandle_t *waith) { +	free (waith->url.url); +	free (waith->proxy.url);  	memset (waith, 0, sizeof (*waith));  } @@ -57,19 +60,8 @@ inline void WaitressFree (WaitressHandle_t *waith) {   *	@param Waitress handle   *	@return true|false   */ -static inline char WaitressProxyEnabled (const WaitressHandle_t *waith) { -	return *waith->proxyHost != '\0' && *waith->proxyPort != '\0'; -} - -/*	Set http proxy - *	@param waitress handle - *	@param host - *	@param port - */ -inline void WaitressSetProxy (WaitressHandle_t *waith, const char *host, -		const char *port) { -	strncpy (waith->proxyHost, host, sizeof (waith->proxyHost)-1); -	strncpy (waith->proxyPort, port, sizeof (waith->proxyPort)-1); +bool WaitressProxyEnabled (const WaitressHandle_t *waith) { +	return waith->proxy.host != NULL;  }  /*	urlencode post-data @@ -100,80 +92,143 @@ char *WaitressUrlEncode (const char *in) {  /*	Split http url into host, port and path   *	@param url - *	@param return buffer: host - *	@param host buffer size - *	@param return buffer: port, defaults to 80 - *	@param port buffer size - *	@param return buffer: path - *	@param path buffer size - *	@param 1 = ok, 0 = not a http url; if your buffers are too small horrible - *			things will happen... But 1 is returned anyway. + *	@param returned url struct + *	@return url is a http url? does not say anything about its validity!   */ -char WaitressSplitUrl (const char *url, char *retHost, size_t retHostSize, -		char *retPort, size_t retPortSize, char *retPath, size_t retPathSize) { -	size_t urlSize = strlen (url); -	const char *urlPos = url, *lastPos; +static bool WaitressSplitUrl (const char *inurl, WaitressUrl_t *retUrl) { +	assert (inurl != NULL); +	assert (retUrl != NULL); + +	static const char *httpPrefix = "http://"; -	if (urlSize > sizeof ("http://")-1 && -			memcmp (url, "http://", sizeof ("http://")-1) == 0) { -		memset (retHost, 0, retHostSize); -		memset (retPort, 0, retPortSize); -		strncpy (retPort, "80", retPortSize-1); -		memset (retPath, 0, retPathSize); - -		urlPos += sizeof ("http://")-1; -		lastPos = urlPos; - -		/* find host */ -		while (*urlPos != '\0' && *urlPos != ':' && *urlPos != '/' && -				urlPos - lastPos < retHostSize-1) { -			*retHost++ = *urlPos++; +	/* is http url? */ +	if (strncmp (httpPrefix, inurl, strlen (httpPrefix)) == 0) { +		enum {FIND_USER, FIND_PASS, FIND_HOST, FIND_PORT, FIND_PATH, DONE} +				state = FIND_USER, newState = FIND_USER; +		char *url, *urlPos, *assignStart; +		const char **assign = NULL; + +		url = strdup (inurl); +		retUrl->url = url; + +		urlPos = url + strlen (httpPrefix); +		assignStart = urlPos; + +		if (*urlPos == '\0') { +			state = DONE;  		} -		lastPos = urlPos; -		/* port, if available */ -		if (*urlPos == ':') { -			/* skip : */ -			++urlPos; -			++lastPos; -			while (*urlPos != '\0' && *urlPos != '/' && -					urlPos - lastPos < retPortSize-1) { -				*retPort++ = *urlPos++; +		while (state != DONE) { +			const char c = *urlPos; + +			switch (state) { +				case FIND_USER: { +					if (c == ':') { +						assign = &retUrl->user; +						newState = FIND_PASS; +					} else if (c == '@') { +						assign = &retUrl->user; +						newState = FIND_HOST; +					} else if (c == '/') { +						/* not a user */ +						assign = &retUrl->host; +						newState = FIND_PATH; +					} else if (c == '\0') { +						assign = &retUrl->host; +						newState = DONE; +					} +					break; +				} + +				case FIND_PASS: { +					if (c == '@') { +						assign = &retUrl->password; +						newState = FIND_HOST; +					} else if (c == '/') { +						/* not a password */ +						assign = &retUrl->port; +						newState = FIND_PATH; +					} else if (c == '\0') { +						assign = &retUrl->port; +						newState = DONE; +					} +					break; +				} + +				case FIND_HOST: { +					if (c == ':') { +						assign = &retUrl->host; +						newState = FIND_PORT; +					} else if (c == '/') { +						assign = &retUrl->host; +						newState = FIND_PATH; +					} else if (c == '\0') { +						assign = &retUrl->host; +						newState = DONE; +					} +					break; +				} + +				case FIND_PORT: { +					if (c == '/') { +						assign = &retUrl->port; +						newState = FIND_PATH; +					} else if (c == '\0') { +						assign = &retUrl->port; +						newState = DONE; +					} +					break; +				} + +				case FIND_PATH: { +					if (c == '\0') { +						assign = &retUrl->path; +						newState = DONE; +					} +					break; +				} + +				case DONE: +					break; +			} /* end switch */ + +			if (assign != NULL) { +				*assign = assignStart; +				*urlPos = '\0'; +				assignStart = urlPos+1; + +				state = newState; +				assign = NULL;  			} -		} -		lastPos = urlPos; -		/* path */ -		while (*urlPos != '\0' && *urlPos != '#' && -				urlPos - lastPos < retPathSize-1) { -			*retPath++ = *urlPos++; +			++urlPos; +		} /* end while */ + +		/* fixes for our state machine logic */ +		if (retUrl->user != NULL && retUrl->host == NULL && retUrl->port != NULL) { +			retUrl->host = retUrl->user; +			retUrl->user = NULL;  		} -	} else { -		return 0; -	} -	return 1; +		return true; +	} /* end if strncmp */ + +	return false;  }  /*	Parse url and set host, port, path   *	@param Waitress handle   *	@param url: protocol://host:port/path   */ -inline char WaitressSetUrl (WaitressHandle_t *waith, const char *url) { -	return WaitressSplitUrl (url, waith->host, sizeof (waith->host), -		waith->port, sizeof (waith->port), waith->path, sizeof (waith->path)); +bool WaitressSetUrl (WaitressHandle_t *waith, const char *url) { +	return WaitressSplitUrl (url, &waith->url);  } -/*	Set host, port, path - *	@param Waitress handle - *	@param host - *	@param port (getaddrinfo () needs a string...) - *	@param path, including leading / +/*	Set http proxy + *	@param waitress handle + *  @param url, e.g. http://proxy:80/   */ -inline void WaitressSetHPP (WaitressHandle_t *waith, const char *host, -		const char *port, const char *path) { -	strncpy (waith->host, host, sizeof (waith->host)-1); -	strncpy (waith->port, port, sizeof (waith->port)-1); -	strncpy (waith->path, path, sizeof (waith->path)-1); +bool WaitressSetProxy (WaitressHandle_t *waith, const char *url) { +	return WaitressSplitUrl (url, &waith->proxy);  }  /*	Callback for WaitressFetchBuf, appends received data to NULL-terminated buffer @@ -306,6 +361,12 @@ static WaitressReturn_t WaitressPollRead (int sockfd, char *buf, size_t count,  			CLOSE_RET (wRet); \  		} +/*	get default http port if none was given + */ +static const char *WaitressDefaultPort (WaitressUrl_t *url) { +	return url->port == NULL ? "80" : url->port; +} +  /*	Receive data from host and call *callback ()   *	@param waitress handle   *	@return WaitressReturn_t @@ -335,11 +396,13 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {  	/* Use proxy? */  	if (WaitressProxyEnabled (waith)) { -		if (getaddrinfo (waith->proxyHost, waith->proxyPort, &hints, &res) != 0) { +		if (getaddrinfo (waith->proxy.host, +				WaitressDefaultPort (&waith->proxy), &hints, &res) != 0) {  			return WAITRESS_RET_GETADDR_ERR;  		}  	} else { -		if (getaddrinfo (waith->host, waith->port, &hints, &res) != 0) { +		if (getaddrinfo (waith->url.host, +				WaitressDefaultPort (&waith->url), &hints, &res) != 0) {  			return WAITRESS_RET_GETADDR_ERR;  		}  	} @@ -375,22 +438,32 @@ WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {  		return WAITRESS_RET_CONNECT_REFUSED;  	} +	const char *path = waith->url.path; +	if (waith->url.path == NULL) { +		/* avoid NULL pointer deref */ +		path = ""; +	} else if (waith->url.path[0] == '/') { +		/* most servers don't like "//" */ +		++path; +	} +  	/* send request */  	if (WaitressProxyEnabled (waith)) {  		snprintf (writeBuf, sizeof (writeBuf), -			"%s http://%s:%s%s HTTP/1.0\r\n", +			"%s http://%s:%s/%s HTTP/1.0\r\n",  			(waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"), -			waith->host, waith->port, waith->path); +			waith->url.host, +			WaitressDefaultPort (&waith->url), path);  	} else {  		snprintf (writeBuf, sizeof (writeBuf), -			"%s %s HTTP/1.0\r\n", +			"%s /%s HTTP/1.0\r\n",  			(waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"), -			waith->path); +			path);  	}  	WRITE_RET (writeBuf, strlen (writeBuf));  	snprintf (writeBuf, sizeof (writeBuf), -			"Host: %s\r\nUser-Agent: " PACKAGE "\r\n", waith->host); +			"Host: %s\r\nUser-Agent: " PACKAGE "\r\n", waith->url.host);  	WRITE_RET (writeBuf, strlen (writeBuf));  	if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) { @@ -571,3 +644,100 @@ const char *WaitressErrorToStr (WaitressReturn_t wRet) {  	}  } +#ifdef TEST +/* test cases for libwaitress */ + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include "waitress.h" + +#define streq(x,y) (strcmp(x,y) == 0) + +/*	string equality test (memory location or content) + */ +static bool streqtest (const char *x, const char *y) { +	return (x == y) || (x != NULL && y != NULL && streq (x, y)); +} + +/*	test WaitressSplitUrl + *	@param tested url + *	@param expected user + *	@param expected password + *	@param expected host + *	@param expected port + *	@param expected path + */ +static void compareUrl (const char *url, const char *user, +		const char *password, const char *host, const char *port, +		const char *path) { +	WaitressUrl_t splitUrl; + +	memset (&splitUrl, 0, sizeof (splitUrl)); + +	WaitressSplitUrl (url, &splitUrl); + +	bool userTest, passwordTest, hostTest, portTest, pathTest, overallTest; + +	userTest = streqtest (splitUrl.user, user); +	passwordTest = streqtest (splitUrl.password, password); +	hostTest = streqtest (splitUrl.host, host); +	portTest = streqtest (splitUrl.port, port); +	pathTest = streqtest (splitUrl.path, path); + +	overallTest = userTest && passwordTest && hostTest && portTest && pathTest; + +	if (!overallTest) { +		printf ("FAILED test(s) for %s\n", url); +		if (!userTest) { +			printf ("user: %s vs %s\n", splitUrl.user, user); +		} +		if (!passwordTest) { +			printf ("password: %s vs %s\n", splitUrl.password, password); +		} +		if (!hostTest) { +			printf ("host: %s vs %s\n", splitUrl.host, host); +		} +		if (!portTest) { +			printf ("port: %s vs %s\n", splitUrl.port, port); +		} +		if (!pathTest) { +			printf ("path: %s vs %s\n", splitUrl.path, path); +		} +	} else { +		printf ("OK for %s\n", url); +	} +} + +int main () { +	/* WaitressSplitUrl tests */ +	compareUrl ("http://www.example.com/", NULL, NULL, "www.example.com", NULL, +			""); +	compareUrl ("http://www.example.com", NULL, NULL, "www.example.com", NULL, +			NULL); +	compareUrl ("http://www.example.com:80/", NULL, NULL, "www.example.com", +			"80", ""); +	compareUrl ("http://www.example.com:/", NULL, NULL, "www.example.com", "", +			""); +	compareUrl ("http://:80/", NULL, NULL, "", "80", ""); +	compareUrl ("http://www.example.com/foobar/barbaz", NULL, NULL, +			"www.example.com", NULL, "foobar/barbaz"); +	compareUrl ("http://www.example.com:80/foobar/barbaz", NULL, NULL, +			"www.example.com", "80", "foobar/barbaz"); +	compareUrl ("http://foo:bar@www.example.com:80/foobar/barbaz", "foo", "bar", +			"www.example.com", "80", "foobar/barbaz"); +	compareUrl ("http://foo:@www.example.com:80/foobar/barbaz", "foo", "", +			"www.example.com", "80", "foobar/barbaz"); +	compareUrl ("http://foo@www.example.com:80/foobar/barbaz", "foo", NULL, +			"www.example.com", "80", "foobar/barbaz"); +	compareUrl ("http://:foo@www.example.com:80/foobar/barbaz", "", "foo", +			"www.example.com", "80", "foobar/barbaz"); +	compareUrl ("http://:@:80", "", "", "", "80", NULL); +	compareUrl ("http://", NULL, NULL, NULL, NULL, NULL); +	compareUrl ("http:///", NULL, NULL, "", NULL, ""); +	compareUrl ("http://foo:bar@", "foo", "bar", "", NULL, NULL); + +	return EXIT_SUCCESS; +} +#endif /* TEST */ + diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h index 66c5bfa..90f0f9d 100644 --- a/src/libwaitress/waitress.h +++ b/src/libwaitress/waitress.h @@ -1,5 +1,5 @@  /* -Copyright (c) 2009-2010 +Copyright (c) 2009-2011  	Lars-Dominik Braun <lars@6xq.net>  Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,11 +25,8 @@ THE SOFTWARE.  #define _WAITRESS_H  #include <stdlib.h> +#include <stdbool.h> -#define WAITRESS_HOST_SIZE 100 -/* max: 65,535 */ -#define WAITRESS_PORT_SIZE 6 -#define WAITRESS_PATH_SIZE 1000  #define WAITRESS_RECV_BUFFER 10*1024  typedef enum {WAITRESS_METHOD_GET = 0, WAITRESS_METHOD_POST} WaitressMethod_t; @@ -37,16 +34,22 @@ typedef enum {WAITRESS_METHOD_GET = 0, WAITRESS_METHOD_POST} WaitressMethod_t;  typedef enum {WAITRESS_CB_RET_ERR, WAITRESS_CB_RET_OK} WaitressCbReturn_t;  typedef struct { -	char host[WAITRESS_HOST_SIZE]; -	char port[WAITRESS_PORT_SIZE]; -	char path[WAITRESS_PATH_SIZE]; +	char *url; /* splitted url, unusable */ +	const char *user; +	const char *password; +	const char *host; +	const char *port; +	const char *path; /* without leading '/' */ +} WaitressUrl_t; + +typedef struct { +	WaitressUrl_t url;  	WaitressMethod_t method;  	const char *extraHeaders;  	const char *postData;  	size_t contentLength;  	size_t contentReceived; -	char proxyHost[WAITRESS_HOST_SIZE]; -	char proxyPort[WAITRESS_PORT_SIZE]; +	WaitressUrl_t proxy;  	/* extra data handed over to callback function */  	void *data;  	WaitressCbReturn_t (*callback) (void *, size_t, void *); @@ -63,13 +66,9 @@ typedef enum {WAITRESS_RET_ERR = 0, WAITRESS_RET_OK, WAITRESS_RET_STATUS_UNKNOWN  void WaitressInit (WaitressHandle_t *);  void WaitressFree (WaitressHandle_t *); -void WaitressSetProxy (WaitressHandle_t *, const char *, const char *); +bool WaitressSetProxy (WaitressHandle_t *, const char *);  char *WaitressUrlEncode (const char *); -char WaitressSplitUrl (const char *, char *, size_t, char *, size_t, char *, -		size_t); -char WaitressSetUrl (WaitressHandle_t *, const char *); -void WaitressSetHPP (WaitressHandle_t *, const char *, const char *, -		const char *); +bool WaitressSetUrl (WaitressHandle_t *, const char *);  WaitressReturn_t WaitressFetchBuf (WaitressHandle_t *, char **);  WaitressReturn_t WaitressFetchCall (WaitressHandle_t *);  const char *WaitressErrorToStr (WaitressReturn_t); @@ -57,19 +57,13 @@ THE SOFTWARE.   */  static void BarMainLoadProxy (const BarSettings_t *settings,  		WaitressHandle_t *waith) { -	char tmpPath[2]; -  	/* 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 */ -		WaitressSplitUrl (settings->controlProxy, waith->proxyHost, -				sizeof (waith->proxyHost), waith->proxyPort, -				sizeof (waith->proxyPort), tmpPath, sizeof (tmpPath)); +		WaitressSetProxy (waith, settings->controlProxy);  	} else if (settings->proxy != NULL && strlen (settings->proxy) > 0) { -		WaitressSplitUrl (settings->proxy, waith->proxyHost, -				sizeof (waith->proxyHost), waith->proxyPort, -				sizeof (waith->proxyPort), tmpPath, sizeof (tmpPath)); +		WaitressSetProxy (waith, settings->proxy);  	}  } @@ -200,13 +194,7 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) {  		/* set up global proxy, player is NULLed on songfinish */  		if (app->settings.proxy != NULL) { -			char tmpPath[2]; -			WaitressSplitUrl (app->settings.proxy, -					app->player.waith.proxyHost, -					sizeof (app->player.waith.proxyHost), -					app->player.waith.proxyPort, -					sizeof (app->player.waith.proxyPort), tmpPath, -					sizeof (tmpPath)); +			WaitressSetProxy (&app->player.waith, app->settings.proxy);  		}  		app->player.gain = app->playlist->fileGain; @@ -351,8 +339,8 @@ int main (int argc, char **argv) {  	PianoInit (&app.ph);  	WaitressInit (&app.waith); -	strncpy (app.waith.host, PIANO_RPC_HOST, sizeof (app.waith.host)-1); -	strncpy (app.waith.port, PIANO_RPC_PORT, sizeof (app.waith.port)-1); +	app.waith.url.host = strdup (PIANO_RPC_HOST); +	app.waith.url.port = strdup (PIANO_RPC_PORT);  	BarSettingsInit (&app.settings);  	BarSettingsRead (&app.settings); @@ -137,7 +137,7 @@ static WaitressReturn_t BarPianoHttpRequest (WaitressHandle_t *waith,  	waith->extraHeaders = "Content-Type: text/xml\r\n";  	waith->postData = req->postData;  	waith->method = WAITRESS_METHOD_POST; -	strncpy (waith->path, req->urlPath, sizeof (waith->path)-1); +	waith->url.path = req->urlPath;  	return WaitressFetchBuf (waith, &req->responseData);  } | 
