From 09355b9299ddfa80465aff01b94e9d4e3f5c1961 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Wed, 8 Jun 2011 17:55:40 +0200 Subject: waitress: Rewrite split url Accepts user and password in urls now. Tests added. --- src/libwaitress/waitress.c | 332 ++++++++++++++++++++++++++++++++++----------- src/libwaitress/waitress.h | 31 ++--- src/main.c | 22 +-- src/ui.c | 2 +- 4 files changed, 272 insertions(+), 115 deletions(-) (limited to 'src') 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 Permission is hereby granted, free of charge, to any person obtaining a copy @@ -35,6 +35,7 @@ THE SOFTWARE. #include #include #include +#include #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 +#include +#include +#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 Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,11 +25,8 @@ THE SOFTWARE. #define _WAITRESS_H #include +#include -#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); diff --git a/src/main.c b/src/main.c index 3fcb2da..ae661c8 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/ui.c b/src/ui.c index 69f7bec..a025b9c 100644 --- a/src/ui.c +++ b/src/ui.c @@ -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); } -- cgit v1.2.3