diff options
author | Lars-Dominik Braun <lars@6xq.net> | 2015-04-06 12:25:13 +0200 |
---|---|---|
committer | Lars-Dominik Braun <lars@6xq.net> | 2015-04-06 12:25:13 +0200 |
commit | 4458cbab76fd98989fa2d4260dd20bbbd66297a4 (patch) | |
tree | 832c7230129b50c278044cb9f4aabe711697ee69 /src | |
parent | b13b61b77b6d58c8b541bc4628b998681e94875f (diff) | |
download | pianobar-4458cbab76fd98989fa2d4260dd20bbbd66297a4.tar.gz pianobar-4458cbab76fd98989fa2d4260dd20bbbd66297a4.tar.bz2 pianobar-4458cbab76fd98989fa2d4260dd20bbbd66297a4.zip |
Switch back to libcurl
Drops libwaitress. Adds the new dependency libcurl and drops gnutls.
I wouldn’t say writing my own HTTP library was a mistake – it was not
and the experience gained was worth it. Instead I have to acknowledge
that libcurl is just better than my own implementation. Sure, it does a
lot more than HTTP – one could call that bloat. Yet if you just want to
get the job done™ reusing code is the way to go.
See #512 and #513.
Diffstat (limited to 'src')
-rw-r--r-- | src/libpiano/request.c | 17 | ||||
-rw-r--r-- | src/libwaitress/config.h | 1 | ||||
-rw-r--r-- | src/libwaitress/waitress-test.c | 143 | ||||
-rw-r--r-- | src/libwaitress/waitress.c | 1240 | ||||
-rw-r--r-- | src/libwaitress/waitress.h | 136 | ||||
-rw-r--r-- | src/main.c | 46 | ||||
-rw-r--r-- | src/main.h | 5 | ||||
-rw-r--r-- | src/player.h | 1 | ||||
-rw-r--r-- | src/settings.h | 1 | ||||
-rw-r--r-- | src/ui.c | 122 | ||||
-rw-r--r-- | src/ui.h | 5 | ||||
-rw-r--r-- | src/ui_act.c | 30 |
12 files changed, 136 insertions, 1611 deletions
diff --git a/src/libpiano/request.c b/src/libpiano/request.c index 6d0c8e5..cad0907 100644 --- a/src/libpiano/request.c +++ b/src/libpiano/request.c @@ -26,12 +26,11 @@ THE SOFTWARE. #define _DARWIN_C_SOURCE /* strdup() on OS X */ #endif +#include <curl/curl.h> #include <json.h> #include <assert.h> #include <stdio.h> #include <string.h> -/* needed for urlencode */ -#include <waitress.h> #include "piano.h" #include "crypt.h" @@ -100,13 +99,16 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req, json_object_object_add (j, "syncTime", json_object_new_int (timestamp)); - urlencAuthToken = WaitressUrlEncode (ph->partner.authToken); + CURL * const curl = curl_easy_init (); + urlencAuthToken = curl_easy_escape (curl, + ph->partner.authToken, 0); assert (urlencAuthToken != NULL); snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH "method=auth.userLogin&" "auth_token=%s&partner_id=%i", urlencAuthToken, ph->partner.id); - free (urlencAuthToken); + curl_free (urlencAuthToken); + curl_easy_cleanup (curl); break; } @@ -432,14 +434,17 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req, assert (ph->user.authToken != NULL); - urlencAuthToken = WaitressUrlEncode (ph->user.authToken); + CURL * const curl = curl_easy_init (); + urlencAuthToken = curl_easy_escape (curl, + ph->user.authToken, 0); assert (urlencAuthToken != NULL); snprintf (req->urlPath, sizeof (req->urlPath), PIANO_RPC_PATH "method=%s&auth_token=%s&partner_id=%i&user_id=%s", method, urlencAuthToken, ph->partner.id, ph->user.listenerId); - free (urlencAuthToken); + curl_free (urlencAuthToken); + curl_easy_cleanup (curl); json_object_object_add (j, "userAuthToken", json_object_new_string (ph->user.authToken)); diff --git a/src/libwaitress/config.h b/src/libwaitress/config.h deleted file mode 100644 index e13fe40..0000000 --- a/src/libwaitress/config.h +++ /dev/null @@ -1 +0,0 @@ -#define PACKAGE "libwaitress" diff --git a/src/libwaitress/waitress-test.c b/src/libwaitress/waitress-test.c deleted file mode 100644 index 8cc3e3a..0000000 --- a/src/libwaitress/waitress-test.c +++ /dev/null @@ -1,143 +0,0 @@ -/* -Copyright (c) 2009-2013 - Lars-Dominik Braun <lars@6xq.net> - -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. -*/ - -/* test cases for libwaitress */ - -/* we are testing static methods and therefore have to include the .c */ -#include "waitress.c" - -#include <stdio.h> -#include <stdbool.h> -#include <string.h> - -#define streq(a,b) (strcmp(a,b) == 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); - } -} - -/* compare two strings - */ -void compareStr (const char *result, const char *expected) { - if (!streq (result, expected)) { - printf ("FAIL for %s, result was %s\n", expected, result); - } else { - printf ("OK for %s\n", expected); - } -} - -/* test entry point - */ -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); - - /* WaitressBase64Encode tests */ - compareStr (WaitressBase64Encode ("M"), "TQ=="); - compareStr (WaitressBase64Encode ("Ma"), "TWE="); - compareStr (WaitressBase64Encode ("Man"), "TWFu"); - compareStr (WaitressBase64Encode ("The quick brown fox jumped over the lazy dog."), - "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2cu"); - compareStr (WaitressBase64Encode ("The quick brown fox jumped over the lazy dog"), - "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2c="); - compareStr (WaitressBase64Encode ("The quick brown fox jumped over the lazy do"), - "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkbw=="); - - return EXIT_SUCCESS; -} - diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c deleted file mode 100644 index 62e06a0..0000000 --- a/src/libwaitress/waitress.c +++ /dev/null @@ -1,1240 +0,0 @@ -/* -Copyright (c) 2009-2013 - Lars-Dominik Braun <lars@6xq.net> - -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 __FreeBSD__ -#define _POSIX_C_SOURCE 1 /* required by getaddrinfo() */ -#define _BSD_SOURCE /* snprintf() */ -#define _DARWIN_C_SOURCE /* snprintf() on OS X */ -#endif - -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <string.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <fcntl.h> -#include <poll.h> -#include <errno.h> -#include <assert.h> -#include <stdint.h> - -#include <gnutls/x509.h> - -#include "config.h" -#include "waitress.h" - -#define strcaseeq(a,b) (strcasecmp(a,b) == 0) -#define WAITRESS_HTTP_VERSION "1.1" - -typedef struct { - char *data; - size_t pos; -} WaitressFetchBufCbBuffer_t; - -static WaitressReturn_t WaitressReceiveHeaders (WaitressHandle_t *, size_t *); - -#define READ_RET(buf, count, size) \ - if ((wRet = waith->request.read (waith, buf, count, size)) != \ - WAITRESS_RET_OK) { \ - return wRet; \ - } - -#define WRITE_RET(buf, count) \ - if ((wRet = waith->request.write (waith, buf, count)) != WAITRESS_RET_OK) { \ - return wRet; \ - } - -void WaitressInit (WaitressHandle_t *waith) { - assert (waith != NULL); - - memset (waith, 0, sizeof (*waith)); - waith->timeout = 30000; -} - -void WaitressFree (WaitressHandle_t *waith) { - assert (waith != NULL); - - free (waith->url.url); - free (waith->proxy.url); - memset (waith, 0, sizeof (*waith)); -} - -/* Proxy set up? - * @param Waitress handle - * @return true|false - */ -static bool WaitressProxyEnabled (const WaitressHandle_t *waith) { - assert (waith != NULL); - - return waith->proxy.host != NULL; -} - -/* urlencode post-data - * @param encode this - * @return malloc'ed encoded string, don't forget to free it - */ -char *WaitressUrlEncode (const char *in) { - assert (in != NULL); - - size_t inLen = strlen (in); - /* worst case: encode all characters */ - char *out = calloc (inLen * 3 + 1, sizeof (*in)); - const char *inPos = in; - char *outPos = out; - - while (inPos - in < inLen) { - if (!isalnum (*inPos) && *inPos != '_' && *inPos != '-' && *inPos != '.') { - *outPos++ = '%'; - snprintf (outPos, 3, "%02x", *inPos & 0xff); - outPos += 2; - } else { - /* copy character */ - *outPos++ = *inPos; - } - ++inPos; - } - - return out; -} - -/* base64 encode data - * @param encode this - * @return malloc'ed string - */ -static char *WaitressBase64Encode (const char *in) { - assert (in != NULL); - - size_t inLen = strlen (in); - char *out, *outPos; - const char *inPos; - static const char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz0123456789+/"; - const size_t alphabetLen = strlen (alphabet); - - /* worst case is 1.333 */ - out = malloc ((inLen * 2 + 1) * sizeof (*out)); - if (out == NULL) { - return NULL; - } - outPos = out; - inPos = in; - - while (inLen >= 3) { - uint8_t idx; - - idx = ((*inPos) >> 2) & 0x3f; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = ((*inPos) & 0x3) << 4; - ++inPos; - idx |= ((*inPos) >> 4) & 0xf; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = ((*inPos) & 0xf) << 2; - ++inPos; - idx |= ((*inPos) >> 6) & 0x3; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = (*inPos) & 0x3f; - ++inPos; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - inLen -= 3; - } - - switch (inLen) { - case 2: { - uint8_t idx; - - idx = ((*inPos) >> 2) & 0x3f; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = ((*inPos) & 0x3) << 4; - ++inPos; - idx |= ((*inPos) >> 4) & 0xf; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = ((*inPos) & 0xf) << 2; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - *outPos = '='; - ++outPos; - break; - } - - case 1: { - uint8_t idx; - - idx = ((*inPos) >> 2) & 0x3f; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - idx = ((*inPos) & 0x3) << 4; - assert (idx < alphabetLen); - *outPos = alphabet[idx]; - ++outPos; - - *outPos = '='; - ++outPos; - - *outPos = '='; - ++outPos; - break; - } - } - *outPos = '\0'; - - return out; -} - -/* Split http url into host, port and path - * @param url - * @param returned url struct - * @return url is a http url? does not say anything about its validity! - */ -static bool WaitressSplitUrl (const char *inurl, WaitressUrl_t *retUrl) { - assert (inurl != NULL); - assert (retUrl != NULL); - - static const char *httpPrefix = "http://"; - - /* 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; - } - - 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; - } - - ++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; - } - return true; - } /* end if strncmp */ - - return false; -} - -/* Parse url and set host, port, path - * @param Waitress handle - * @param url: protocol://host:port/path - */ -bool WaitressSetUrl (WaitressHandle_t *waith, const char *url) { - return WaitressSplitUrl (url, &waith->url); -} - -/* Set http proxy - * @param waitress handle - * @param url, e.g. http://proxy:80/ - */ -bool WaitressSetProxy (WaitressHandle_t *waith, const char *url) { - return WaitressSplitUrl (url, &waith->proxy); -} - -/* Callback for WaitressFetchBuf, appends received data to \0-terminated - * buffer - * @param received data - * @param data size - * @param buffer structure - */ -static WaitressCbReturn_t WaitressFetchBufCb (void *recvData, size_t recvDataSize, - void *extraData) { - char *recvBytes = recvData; - WaitressFetchBufCbBuffer_t *buffer = extraData; - - if (buffer->data == NULL) { - if ((buffer->data = malloc (sizeof (*buffer->data) * - (recvDataSize + 1))) == NULL) { - return WAITRESS_CB_RET_ERR; - } - } else { - char *newbuf; - if ((newbuf = realloc (buffer->data, - sizeof (*buffer->data) * - (buffer->pos + recvDataSize + 1))) == NULL) { - free (buffer->data); - return WAITRESS_CB_RET_ERR; - } - buffer->data = newbuf; - } - memcpy (buffer->data + buffer->pos, recvBytes, recvDataSize); - buffer->pos += recvDataSize; - buffer->data[buffer->pos] = '\0'; - - return WAITRESS_CB_RET_OK; -} - -/* Fetch string. Beware! This overwrites your waith->data pointer - * @param waitress handle - * @param \0-terminated result buffer, malloced (don't forget to free it - * yourself) - */ -WaitressReturn_t WaitressFetchBuf (WaitressHandle_t *waith, char **retBuffer) { - WaitressFetchBufCbBuffer_t buffer; - WaitressReturn_t wRet; - - assert (waith != NULL); - assert (retBuffer != NULL); - - memset (&buffer, 0, sizeof (buffer)); - - waith->data = &buffer; - waith->callback = WaitressFetchBufCb; - - wRet = WaitressFetchCall (waith); - *retBuffer = buffer.data; - return wRet; -} - -/* poll wrapper that retries after signal interrupts, required for socksify - * wrapper - */ -static int WaitressPollLoop (int fd, short events, int timeout) { - int pollres = -1; - struct pollfd sockpoll = {fd, events, 0}; - - assert (fd != -1); - - do { - errno = 0; - pollres = poll (&sockpoll, 1, timeout); - } while (errno == EINTR || errno == EINPROGRESS || errno == EAGAIN); - - return pollres; -} - -/* write () wrapper with poll () timeout - * @param waitress handle - * @param write buffer - * @param write count bytes - * @return number of written bytes or -1 on error - */ -static ssize_t WaitressPollWrite (void *data, const void *buf, size_t count) { - int pollres = -1; - ssize_t retSize; - WaitressHandle_t *waith = data; - - assert (waith != NULL); - assert (buf != NULL); - - /* FIXME: simplify logic */ - pollres = WaitressPollLoop (waith->request.sockfd, POLLOUT, - waith->timeout); - if (pollres == 0) { - waith->request.readWriteRet = WAITRESS_RET_TIMEOUT; - return -1; - } else if (pollres == -1) { - waith->request.readWriteRet = WAITRESS_RET_ERR; - return -1; - } - if ((retSize = write (waith->request.sockfd, buf, count)) == -1) { - waith->request.readWriteRet = WAITRESS_RET_ERR; - return -1; - } - waith->request.readWriteRet = WAITRESS_RET_OK; - return retSize; -} - -static WaitressReturn_t WaitressOrdinaryWrite (void *data, const char *buf, - const size_t size) { - WaitressHandle_t *waith = data; - - WaitressPollWrite (waith, buf, size); - return waith->request.readWriteRet; -} - -static WaitressReturn_t WaitressGnutlsWrite (void *data, const char *buf, - const size_t size) { - WaitressHandle_t *waith = data; - - if (gnutls_record_send (waith->request.tlsSession, buf, size) < 0) { - return WAITRESS_RET_TLS_WRITE_ERR; - } - return waith->request.readWriteRet; -} - -/* read () wrapper with poll () timeout - * @param waitress handle - * @param write to this buf, not NULL terminated - * @param buffer size - * @return number of read bytes or -1 on error - */ -static ssize_t WaitressPollRead (void *data, void *buf, size_t count) { - int pollres = -1; - ssize_t retSize; - WaitressHandle_t *waith = data; - - assert (waith != NULL); - assert (buf != NULL); - - /* FIXME: simplify logic */ - pollres = WaitressPollLoop (waith->request.sockfd, POLLIN, waith->timeout); - if (pollres == 0) { - waith->request.readWriteRet = WAITRESS_RET_TIMEOUT; - return -1; - } else if (pollres == -1) { - waith->request.readWriteRet = WAITRESS_RET_ERR; - return -1; - } - if ((retSize = read (waith->request.sockfd, buf, count)) == -1) { - waith->request.readWriteRet = WAITRESS_RET_READ_ERR; - return -1; - } - waith->request.readWriteRet = WAITRESS_RET_OK; - return retSize; -} - -static WaitressReturn_t WaitressOrdinaryRead (void *data, char *buf, - const size_t size, size_t *retSize) { - WaitressHandle_t *waith = data; - - const ssize_t ret = WaitressPollRead (waith, buf, size); - if (ret != -1) { - assert (ret >= 0); - *retSize = (size_t) ret; - } - return waith->request.readWriteRet; -} - -static WaitressReturn_t WaitressGnutlsRead (void *data, char *buf, - const size_t size, size_t *retSize) { - WaitressHandle_t *waith = data; - - ssize_t ret = gnutls_record_recv (waith->request.tlsSession, buf, size); - if (ret < 0) { - return WAITRESS_RET_TLS_READ_ERR; - } else { - *retSize = ret; - } - return waith->request.readWriteRet; -} - -/* send basic http authorization - * @param waitress handle - * @param url containing user/password - * @param header name prefix - */ -static bool WaitressFormatAuthorization (WaitressHandle_t *waith, - WaitressUrl_t *url, const char *prefix, char *writeBuf, - const size_t writeBufSize) { - assert (waith != NULL); - assert (url != NULL); - assert (prefix != NULL); - assert (writeBuf != NULL); - assert (writeBufSize > 0); - - if (url->user != NULL) { - char userPass[1024], *encodedUserPass; - snprintf (userPass, sizeof (userPass), "%s:%s", url->user, - (url->password != NULL) ? url->password : ""); - encodedUserPass = WaitressBase64Encode (userPass); - assert (encodedUserPass != NULL); - snprintf (writeBuf, writeBufSize, "%sAuthorization: Basic %s\r\n", - prefix, encodedUserPass); - free (encodedUserPass); - return true; - } - return false; -} - -/* get default http port if none was given - */ -static const char *WaitressDefaultPort (const WaitressUrl_t * const url) { - assert (url != NULL); - - if (url->tls) { - return url->tlsPort == NULL ? "443" : url->tlsPort; - } else { - return url->port == NULL ? "80" : url->port; - } -} - -/* get line from string - * @param string beginning/return value of last call - * @return start of _next_ line or NULL if there is no next line - */ -static char *WaitressGetline (char * const str) { - char *eol; - - assert (str != NULL); - - eol = strchr (str, '\n'); - if (eol == NULL) { - return NULL; - } - - /* make lines parseable by string routines */ - *eol = '\0'; - if (eol-1 >= str && *(eol-1) == '\r') { - *(eol-1) = '\0'; - } - /* skip \0 */ - ++eol; - - assert (eol >= str); - - return eol; -} - -/* identity encoding handler - */ -static WaitressHandlerReturn_t WaitressHandleIdentity (void *data, char *buf, - const size_t size) { - assert (data != NULL); - assert (buf != NULL); - - WaitressHandle_t *waith = data; - - waith->request.contentReceived += size; - if (waith->callback (buf, size, waith->data) == WAITRESS_CB_RET_ERR) { - return WAITRESS_HANDLER_ABORTED; - } else { - return WAITRESS_HANDLER_CONTINUE; - } -} - -/* chunked encoding handler - */ -static WaitressHandlerReturn_t WaitressHandleChunked (void *data, char *buf, - const size_t size) { - assert (data != NULL); - assert (buf != NULL); - - WaitressHandle_t * const waith = data; - size_t pos = 0; - - while (pos < size) { - switch (waith->request.chunkedState) { - case CHUNKSIZE: - /* Poor man’s hex to integer. This avoids another buffer that - * fills until the terminating \r\n is received. */ - if (buf[pos] >= '0' && buf[pos] <= '9') { - waith->request.chunkSize <<= 4; - waith->request.chunkSize |= buf[pos] & 0xf; - } else if (buf[pos] >= 'a' && buf[pos] <= 'f') { - waith->request.chunkSize <<= 4; - waith->request.chunkSize |= (buf[pos]+9) & 0xf; - } else if (buf[pos] == '\r') { - /* ignore */ - } else if (buf[pos] == '\n') { - waith->request.chunkedState = DATA; - /* last chunk has size 0 */ - if (waith->request.chunkSize == 0) { - return WAITRESS_HANDLER_DONE; - } - } else { - /* everything else is a protocol violation */ - return WAITRESS_HANDLER_ERR; - } - ++pos; - break; - - case DATA: - if (waith->request.chunkSize > 0) { - assert (size >= pos); - size_t payloadSize = size - pos; - - if (payloadSize > waith->request.chunkSize) { - payloadSize = waith->request.chunkSize; - } - if (WaitressHandleIdentity (waith, &buf[pos], - payloadSize) == WAITRESS_HANDLER_ABORTED) { - return WAITRESS_HANDLER_ABORTED; - } - pos += payloadSize; - assert (waith->request.chunkSize >= payloadSize); - waith->request.chunkSize -= payloadSize; - } else { - /* next chunk size starts in the next line */ - if (buf[pos] == '\n') { - waith->request.chunkedState = CHUNKSIZE; - } - ++pos; - } - break; - } - } - - return WAITRESS_HANDLER_CONTINUE; -} - -/* handle http header - */ -static void WaitressHandleHeader (WaitressHandle_t *waith, const char * const key, - const char * const value) { - assert (waith != NULL); - assert (key != NULL); - assert (value != NULL); - - if (strcaseeq (key, "Content-Length")) { - waith->request.contentLength = atol (value); - waith->request.contentLengthKnown = true; - } else if (strcaseeq (key, "Transfer-Encoding")) { - if (strcaseeq (value, "chunked")) { - waith->request.dataHandler = WaitressHandleChunked; - } - } -} - -/* parse http status line and return status code - */ -static int WaitressParseStatusline (const char * const line) { - char status[4] = "000"; - - assert (line != NULL); - - if (sscanf (line, "HTTP/1.%*1[0-9] %3[0-9] ", status) == 1) { - return atoi (status); - } - return -1; -} - -/* verify server certificate - */ -static WaitressReturn_t WaitressTlsVerify (const WaitressHandle_t *waith) { - gnutls_session_t session = waith->request.tlsSession; - unsigned int certListSize; - const gnutls_datum_t *certList; - gnutls_x509_crt_t cert; - - if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - if ((certList = gnutls_certificate_get_peers (session, - &certListSize)) == NULL) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - if (gnutls_x509_crt_import (cert, &certList[0], - GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - char fingerprint[20]; - size_t fingerprintSize = sizeof (fingerprint); - if (gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, fingerprint, - &fingerprintSize) != 0) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - assert (waith->tlsFingerprint != NULL); - if (memcmp (fingerprint, waith->tlsFingerprint, sizeof (fingerprint)) != 0) { - return WAITRESS_RET_TLS_FINGERPRINT_MISMATCH; - } - - gnutls_x509_crt_deinit (cert); - - return WAITRESS_RET_OK; -} - -/* Connect to server - */ -static WaitressReturn_t WaitressConnect (WaitressHandle_t *waith) { - WaitressReturn_t ret; - struct addrinfo hints, *gares; - - memset (&hints, 0, sizeof hints); - - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - /* Use proxy? */ - if (WaitressProxyEnabled (waith)) { - if (getaddrinfo (waith->proxy.host, - WaitressDefaultPort (&waith->proxy), &hints, &gares) != 0) { - return WAITRESS_RET_GETADDR_ERR; - } - } else { - if (getaddrinfo (waith->url.host, - WaitressDefaultPort (&waith->url), &hints, &gares) != 0) { - return WAITRESS_RET_GETADDR_ERR; - } - } - - /* try all addresses */ - for (struct addrinfo *gacurr = gares; gacurr != NULL; - gacurr = gacurr->ai_next) { - int sock = -1; - - ret = WAITRESS_RET_OK; - - if ((sock = socket (gacurr->ai_family, gacurr->ai_socktype, - gacurr->ai_protocol)) == -1) { - ret = WAITRESS_RET_SOCK_ERR; - } else { - int pollres; - - /* we need shorter timeouts for connect() */ - fcntl (sock, F_SETFL, O_NONBLOCK); - - /* non-blocking connect will return immediately */ - connect (sock, gacurr->ai_addr, gacurr->ai_addrlen); - - pollres = WaitressPollLoop (sock, POLLOUT, waith->timeout); - if (pollres == 0) { - ret = WAITRESS_RET_TIMEOUT; - } else if (pollres == -1) { - ret = WAITRESS_RET_ERR; - } else { - /* check connect () return value */ - socklen_t pollresSize = sizeof (pollres); - getsockopt (sock, SOL_SOCKET, SO_ERROR, &pollres, - &pollresSize); - if (pollres != 0) { - ret = WAITRESS_RET_CONNECT_REFUSED; - } else { - /* this one is working */ - waith->request.sockfd = sock; - break; - } - } - close (sock); - } - } - - freeaddrinfo (gares); - /* could not connect to any of the addresses */ - if (ret != WAITRESS_RET_OK) { - return ret; - } - - if (waith->url.tls) { - WaitressReturn_t wRet; - - /* set up proxy tunnel */ - if (WaitressProxyEnabled (waith)) { - char buf[256]; - size_t size; - - snprintf (buf, sizeof (buf), "CONNECT %s:%s HTTP/" - WAITRESS_HTTP_VERSION "\r\n" - "Host: %s:%s\r\n" - "Proxy-Connection: close\r\n", - waith->url.host, WaitressDefaultPort (&waith->url), - waith->url.host, WaitressDefaultPort (&waith->url)); - WRITE_RET (buf, strlen (buf)); - - /* write authorization headers */ - if (WaitressFormatAuthorization (waith, &waith->proxy, "Proxy-", - buf, WAITRESS_BUFFER_SIZE)) { - WRITE_RET (buf, strlen (buf)); - } - - WRITE_RET ("\r\n", 2); - - if ((wRet = WaitressReceiveHeaders (waith, &size)) != - WAITRESS_RET_OK) { - return wRet; - } - } - - /* Ignore return code as connection will likely still succeed */ - gnutls_server_name_set (waith->request.tlsSession, GNUTLS_NAME_DNS, - waith->url.host, strlen (waith->url.host)); - - if (gnutls_handshake (waith->request.tlsSession) != GNUTLS_E_SUCCESS) { - return WAITRESS_RET_TLS_HANDSHAKE_ERR; - } - - if ((wRet = WaitressTlsVerify (waith)) != WAITRESS_RET_OK) { - return wRet; - } - - /* now we can talk encrypted */ - waith->request.read = WaitressGnutlsRead; - waith->request.write = WaitressGnutlsWrite; - } - - return WAITRESS_RET_OK; -} - -/* Write http header/post data to socket - */ -static WaitressReturn_t WaitressSendRequest (WaitressHandle_t *waith) { - assert (waith != NULL); - assert (waith->request.buf != NULL); - - const char *path = waith->url.path; - char * const buf = waith->request.buf; - WaitressReturn_t wRet = WAITRESS_RET_OK; - - 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) && !waith->url.tls) { - snprintf (buf, WAITRESS_BUFFER_SIZE, - "%s http://%s:%s/%s HTTP/" WAITRESS_HTTP_VERSION "\r\n" - "Host: %s\r\nUser-Agent: " PACKAGE "\r\nConnection: Close\r\n", - (waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"), - waith->url.host, - WaitressDefaultPort (&waith->url), path, waith->url.host); - } else { - snprintf (buf, WAITRESS_BUFFER_SIZE, - "%s /%s HTTP/" WAITRESS_HTTP_VERSION "\r\n" - "Host: %s\r\nUser-Agent: " PACKAGE "\r\nConnection: Close\r\n", - (waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"), - path, waith->url.host); - } - WRITE_RET (buf, strlen (buf)); - - if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) { - snprintf (buf, WAITRESS_BUFFER_SIZE, "Content-Length: %zu\r\n", - strlen (waith->postData)); - WRITE_RET (buf, strlen (buf)); - } - - /* write authorization headers */ - if (WaitressFormatAuthorization (waith, &waith->url, "", buf, - WAITRESS_BUFFER_SIZE)) { - WRITE_RET (buf, strlen (buf)); - } - /* don't leak proxy credentials to destination server if tls is used */ - if (!waith->url.tls && - WaitressFormatAuthorization (waith, &waith->proxy, "Proxy-", - buf, WAITRESS_BUFFER_SIZE)) { - WRITE_RET (buf, strlen (buf)); - } - - if (waith->extraHeaders != NULL) { - WRITE_RET (waith->extraHeaders, strlen (waith->extraHeaders)); - } - - WRITE_RET ("\r\n", 2); - - if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) { - WRITE_RET (waith->postData, strlen (waith->postData)); - } - - return WAITRESS_RET_OK; -} - -/* receive response headers - * @param Waitress handle - * @param return unhandled bytes count in buf - */ -static WaitressReturn_t WaitressReceiveHeaders (WaitressHandle_t *waith, - size_t *retRemaining) { - char * const buf = waith->request.buf; - size_t bufFilled = 0, recvSize = 0; - char *nextLine = NULL, *thisLine = NULL; - enum {HDRM_HEAD, HDRM_LINES, HDRM_FINISHED} hdrParseMode = HDRM_HEAD; - WaitressReturn_t wRet = WAITRESS_RET_OK; - - /* receive answer */ - nextLine = buf; - while (hdrParseMode != HDRM_FINISHED) { - READ_RET (buf+bufFilled, WAITRESS_BUFFER_SIZE-1 - bufFilled, &recvSize); - if (recvSize == 0) { - /* connection closed too early */ - return WAITRESS_RET_CONNECTION_CLOSED; - } - bufFilled += recvSize; - buf[bufFilled] = '\0'; - thisLine = buf; - - /* split */ - while (hdrParseMode != HDRM_FINISHED && - (nextLine = WaitressGetline (thisLine)) != NULL) { - switch (hdrParseMode) { - /* Status code */ - case HDRM_HEAD: - switch (WaitressParseStatusline (thisLine)) { - case 200: - case 206: - hdrParseMode = HDRM_LINES; - break; - - case 400: - return WAITRESS_RET_BAD_REQUEST; - break; - - case 403: - return WAITRESS_RET_FORBIDDEN; - break; - - case 404: - return WAITRESS_RET_NOTFOUND; - break; - - case -1: - /* ignore invalid line */ - break; - - default: - return WAITRESS_RET_STATUS_UNKNOWN; - break; - } - break; - - /* Everything else, except status code */ - case HDRM_LINES: - /* empty line => content starts here */ - if (*thisLine == '\0') { - hdrParseMode = HDRM_FINISHED; - } else { - /* parse header: "key: value", ignore invalid lines */ - char *key = thisLine, *val; - - val = strchr (thisLine, ':'); - if (val != NULL) { - *val++ = '\0'; - while (*val != '\0' && isspace ((unsigned char) *val)) { - ++val; - } - WaitressHandleHeader (waith, key, val); - } - } - break; - - default: - break; - } /* end switch */ - thisLine = nextLine; - } /* end while strchr */ - memmove (buf, thisLine, bufFilled-(thisLine-buf)); - bufFilled -= (thisLine-buf); - } /* end while hdrParseMode */ - - *retRemaining = bufFilled; - - return wRet; -} - -/* read response header and data - */ -static WaitressReturn_t WaitressReceiveResponse (WaitressHandle_t *waith) { - assert (waith != NULL); - assert (waith->request.buf != NULL); - - char * const buf = waith->request.buf; - size_t recvSize = 0; - WaitressReturn_t wRet = WAITRESS_RET_OK; - - if ((wRet = WaitressReceiveHeaders (waith, &recvSize)) != WAITRESS_RET_OK) { - return wRet; - } - - do { - /* data must be \0-terminated for chunked handler */ - buf[recvSize] = '\0'; - switch (waith->request.dataHandler (waith, buf, recvSize)) { - case WAITRESS_HANDLER_DONE: - return WAITRESS_RET_OK; - break; - - case WAITRESS_HANDLER_ERR: - return WAITRESS_RET_DECODING_ERR; - break; - - case WAITRESS_HANDLER_ABORTED: - return WAITRESS_RET_CB_ABORT; - break; - - case WAITRESS_HANDLER_CONTINUE: - /* go on */ - break; - } - if (waith->request.contentLengthKnown && - waith->request.contentReceived >= waith->request.contentLength) { - /* don’t call read() again if we know the body’s size and have all - * of it already */ - break; - } - READ_RET (buf, WAITRESS_BUFFER_SIZE-1, &recvSize); - } while (recvSize > 0); - - return WAITRESS_RET_OK; -} - -/* Receive data from host and call *callback () - * @param waitress handle - * @return WaitressReturn_t - */ -WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) { - WaitressReturn_t wRet = WAITRESS_RET_OK; - - /* initialize */ - memset (&waith->request, 0, sizeof (waith->request)); - waith->request.sockfd = -1; - waith->request.dataHandler = WaitressHandleIdentity; - waith->request.read = WaitressOrdinaryRead; - waith->request.write = WaitressOrdinaryWrite; - waith->request.contentLengthKnown = false; - - if (waith->url.tls) { - gnutls_init (&waith->request.tlsSession, GNUTLS_CLIENT); - gnutls_set_default_priority (waith->request.tlsSession); - - gnutls_certificate_allocate_credentials (&waith->tlsCred); - if (gnutls_credentials_set (waith->request.tlsSession, - GNUTLS_CRD_CERTIFICATE, - waith->tlsCred) != GNUTLS_E_SUCCESS) { - return WAITRESS_RET_ERR; - } - - /* set up custom read/write functions */ - gnutls_transport_set_ptr (waith->request.tlsSession, - (gnutls_transport_ptr_t) waith); - gnutls_transport_set_pull_function (waith->request.tlsSession, - WaitressPollRead); - gnutls_transport_set_push_function (waith->request.tlsSession, - WaitressPollWrite); - } - - /* buffer is required for connect already */ - waith->request.buf = malloc (WAITRESS_BUFFER_SIZE * - sizeof (*waith->request.buf)); - - /* request */ - if ((wRet = WaitressConnect (waith)) == WAITRESS_RET_OK) { - if ((wRet = WaitressSendRequest (waith)) == WAITRESS_RET_OK) { - wRet = WaitressReceiveResponse (waith); - } - if (waith->url.tls) { - gnutls_bye (waith->request.tlsSession, GNUTLS_SHUT_RDWR); - } - } - - /* cleanup */ - if (waith->url.tls) { - gnutls_deinit (waith->request.tlsSession); - gnutls_certificate_free_credentials (waith->tlsCred); - } - if (waith->request.sockfd != -1) { - close (waith->request.sockfd); - } - free (waith->request.buf); - - if (wRet == WAITRESS_RET_OK && - waith->request.contentReceived < waith->request.contentLength) { - return WAITRESS_RET_PARTIAL_FILE; - } - return wRet; -} - -const char *WaitressErrorToStr (WaitressReturn_t wRet) { - switch (wRet) { - case WAITRESS_RET_OK: - return "Everything's fine :)"; - break; - - case WAITRESS_RET_ERR: - return "Unknown."; - break; - - case WAITRESS_RET_STATUS_UNKNOWN: - return "Unknown HTTP status code."; - break; - - case WAITRESS_RET_NOTFOUND: - return "File not found."; - break; - - case WAITRESS_RET_FORBIDDEN: - return "Forbidden."; - break; - - case WAITRESS_RET_CONNECT_REFUSED: - return "Connection refused."; - break; - - case WAITRESS_RET_SOCK_ERR: - return "Socket error."; - break; - - case WAITRESS_RET_GETADDR_ERR: - return "DNS lookup failed."; - break; - - case WAITRESS_RET_CB_ABORT: - return "Callback aborted request."; - break; - - case WAITRESS_RET_PARTIAL_FILE: - return "Partial file."; - break; - - case WAITRESS_RET_TIMEOUT: - return "Timeout."; - break; - - case WAITRESS_RET_READ_ERR: - return "Read error."; - break; - - case WAITRESS_RET_CONNECTION_CLOSED: - return "Connection closed by remote host."; - break; - - case WAITRESS_RET_DECODING_ERR: - return "Invalid encoded data."; - break; - - case WAITRESS_RET_TLS_WRITE_ERR: - return "TLS write failed."; - break; - - case WAITRESS_RET_TLS_READ_ERR: - return "TLS read failed."; - break; - - case WAITRESS_RET_TLS_HANDSHAKE_ERR: - return "TLS handshake failed."; - break; - - case WAITRESS_RET_TLS_FINGERPRINT_MISMATCH: - return "TLS fingerprint mismatch."; - break; - - default: - return "No error message available."; - break; - } -} - diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h deleted file mode 100644 index b17d3c1..0000000 --- a/src/libwaitress/waitress.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright (c) 2009-2013 - Lars-Dominik Braun <lars@6xq.net> - -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_LIBWAITRESS_WAITRESS_H_ZE5NT8JI -#define SRC_LIBWAITRESS_WAITRESS_H_ZE5NT8JI - -#include <stdlib.h> -#include <unistd.h> -#include <stdbool.h> -#include <gnutls/gnutls.h> - -#define WAITRESS_BUFFER_SIZE 10*1024 - -typedef enum { - WAITRESS_METHOD_GET = 0, - WAITRESS_METHOD_POST, -} WaitressMethod_t; - -typedef enum { - WAITRESS_CB_RET_ERR, - WAITRESS_CB_RET_OK, -} WaitressCbReturn_t; - -typedef enum { - WAITRESS_HANDLER_CONTINUE, - WAITRESS_HANDLER_DONE, - WAITRESS_HANDLER_ERR, - WAITRESS_HANDLER_ABORTED, -} WaitressHandlerReturn_t; - -typedef struct { - char *url; /* splitted url, unusable */ - bool tls; - const char *user; - const char *password; - const char *host; - const char *port; - const char *tlsPort; - const char *path; /* without leading '/' */ -} WaitressUrl_t; - -typedef enum { - WAITRESS_RET_ERR = 0, - WAITRESS_RET_OK, - WAITRESS_RET_CB_ABORT, - /* http error codes */ - WAITRESS_RET_STATUS_UNKNOWN, - WAITRESS_RET_NOTFOUND, - WAITRESS_RET_FORBIDDEN, - WAITRESS_RET_BAD_REQUEST, - /* socket errors */ - WAITRESS_RET_CONNECT_REFUSED, - WAITRESS_RET_SOCK_ERR, - WAITRESS_RET_GETADDR_ERR, - WAITRESS_RET_TIMEOUT, - WAITRESS_RET_READ_ERR, - WAITRESS_RET_CONNECTION_CLOSED, - WAITRESS_RET_TLS_WRITE_ERR, - WAITRESS_RET_TLS_READ_ERR, - /* protocol errors */ - WAITRESS_RET_PARTIAL_FILE, - WAITRESS_RET_DECODING_ERR, - WAITRESS_RET_TLS_HANDSHAKE_ERR, - WAITRESS_RET_TLS_FINGERPRINT_MISMATCH, -} WaitressReturn_t; - -/* reusable handle - */ -typedef struct { - int timeout; - WaitressMethod_t method; - - const char *extraHeaders; - const char *postData; - /* extra data handed over to callback function */ - void *data; - WaitressCbReturn_t (*callback) (void *, size_t, void *); - const char *tlsFingerprint; - - WaitressUrl_t url; - WaitressUrl_t proxy; - - gnutls_certificate_credentials_t tlsCred; - - /* per-request data */ - struct { - int sockfd; - - /* temporary return value storage */ - WaitressReturn_t readWriteRet; - - size_t contentLength, contentReceived, chunkSize; - bool contentLengthKnown; - enum {CHUNKSIZE = 0, DATA = 1} chunkedState; - - char *buf; - /* first argument is WaitressHandle_t, but that's not defined yet */ - WaitressHandlerReturn_t (*dataHandler) (void *, char *, const size_t); - WaitressReturn_t (*read) (void *, char *, const size_t, size_t *); - WaitressReturn_t (*write) (void *, const char *, const size_t); - - gnutls_session_t tlsSession; - } request; -} WaitressHandle_t; - -void WaitressInit (WaitressHandle_t *); -void WaitressFree (WaitressHandle_t *); -bool WaitressSetProxy (WaitressHandle_t *, const char *); -char *WaitressUrlEncode (const char *); -bool WaitressSetUrl (WaitressHandle_t *, const char *); -WaitressReturn_t WaitressFetchBuf (WaitressHandle_t *, char **); -WaitressReturn_t WaitressFetchCall (WaitressHandle_t *); -const char *WaitressErrorToStr (WaitressReturn_t); - -#endif /* SRC_LIBWAITRESS_WAITRESS_H_ZE5NT8JI */ - @@ -61,33 +61,11 @@ THE SOFTWARE. #include "ui_dispatch.h" #include "ui_readline.h" -/* copy proxy settings to waitress handle - */ -static void BarMainLoadProxy (const BarSettings_t *settings, - WaitressHandle_t *waith) { - /* 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 (!WaitressSetProxy (waith, settings->controlProxy)) { - /* 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 (!WaitressSetProxy (waith, settings->proxy)) { - /* if setting proxy fails, url is invalid */ - BarUiMsg (settings, MSG_ERR, "Proxy (%s) is invalid!\n", - settings->proxy); - } - } -} - /* authenticate user */ static bool BarMainLoginUser (BarApp_t *app) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataLogin_t reqData; bool ret; @@ -188,7 +166,7 @@ static bool BarMainGetLoginCredentials (BarSettings_t *settings, */ static bool BarMainGetStations (BarApp_t *app) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; bool ret; BarUiMsg (&app->settings, MSG_INFO, "Get stations... "); @@ -235,7 +213,7 @@ static void BarMainHandleUserInput (BarApp_t *app) { */ static void BarMainGetPlaylist (BarApp_t *app) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataGetPlaylist_t reqData; reqData.station = app->curStation; reqData.quality = app->settings.audioQuality; @@ -288,7 +266,7 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) { /* throw event */ BarUiStartEventCmd (&app->settings, "songstart", app->curStation, curSong, &app->player, app->ph.stations, - PIANO_RET_OK, WAITRESS_RET_OK); + PIANO_RET_OK, CURLE_OK); /* prevent race condition, mode must _not_ be DEAD if * thread has been started */ @@ -306,7 +284,7 @@ static void BarMainPlayerCleanup (BarApp_t *app, pthread_t *playerThread) { BarUiStartEventCmd (&app->settings, "songfinish", app->curStation, app->playlist, &app->player, app->ph.stations, PIANO_RET_OK, - WAITRESS_RET_OK); + CURLE_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ @@ -358,8 +336,6 @@ static void BarMainLoop (BarApp_t *app) { return; } - BarMainLoadProxy (&app->settings, &app->waith); - if (!BarMainLoginUser (app)) { return; } @@ -427,7 +403,6 @@ int main (int argc, char **argv) { gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); - gnutls_global_init (); BarPlayerInit (); BarSettingsInit (&app.settings); @@ -452,10 +427,9 @@ int main (int argc, char **argv) { app.settings.keys[BAR_KS_HELP]); } - WaitressInit (&app.waith); - app.waith.url.host = app.settings.rpcHost; - app.waith.url.tlsPort = app.settings.rpcTlsPort; - app.waith.tlsFingerprint = app.settings.tlsFingerprint; + curl_global_init (CURL_GLOBAL_DEFAULT); + app.http = curl_easy_init (); + assert (app.http != NULL); /* init fds */ FD_ZERO(&app.input.set); @@ -496,9 +470,9 @@ int main (int argc, char **argv) { PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); - WaitressFree (&app.waith); + curl_easy_cleanup (app.http); + curl_global_cleanup (); BarPlayerDestroy (); - gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ @@ -24,8 +24,9 @@ THE SOFTWARE. #ifndef SRC_MAIN_H_4ZGSCG6X #define SRC_MAIN_H_4ZGSCG6X +#include <curl/curl.h> + #include <piano.h> -#include <waitress.h> #include "player.h" #include "settings.h" @@ -33,7 +34,7 @@ THE SOFTWARE. typedef struct { PianoHandle_t ph; - WaitressHandle_t waith; + CURL *http; player_t player; BarSettings_t settings; /* first item is current song */ diff --git a/src/player.h b/src/player.h index 5c60e59..b97f5b2 100644 --- a/src/player.h +++ b/src/player.h @@ -36,7 +36,6 @@ THE SOFTWARE. #include <libavfilter/avfilter.h> #include <libavfilter/avfiltergraph.h> #include <piano.h> -#include <waitress.h> #include "settings.h" #include "config.h" diff --git a/src/settings.h b/src/settings.h index b56351e..ae22111 100644 --- a/src/settings.h +++ b/src/settings.h @@ -27,7 +27,6 @@ THE SOFTWARE. #include <stdbool.h> #include <piano.h> -#include <waitress.h> /* update structure in ui_dispatch.h if you add shortcuts here */ typedef enum { @@ -131,32 +131,100 @@ void BarUiMsg (const BarSettings_t *settings, const BarUiMsg_t type, fflush (stdout); } -/* fetch http resource (post request) - * @param waitress handle - * @param piano request (initialized by PianoRequest()) - */ -static WaitressReturn_t BarPianoHttpRequest (WaitressHandle_t *waith, - PianoRequest_t *req) { - waith->extraHeaders = "Content-Type: text/plain\r\n"; - waith->postData = req->postData; - waith->method = WAITRESS_METHOD_POST; - waith->url.path = req->urlPath; - waith->url.tls = req->secure; - - return WaitressFetchBuf (waith, &req->responseData); +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); + + /* 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) - * @param app handle - * @param request type - * @param request data - * @param stores piano return code - * @param stores waitress return code * @return 1 on success, 0 otherwise */ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, - void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { + void *data, PianoReturn_t *pRet, CURLcode *wRet) { PianoRequest_t req; memset (&req, 0, sizeof (req)); @@ -172,9 +240,10 @@ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, return 0; } - *wRet = BarPianoHttpRequest (&app->waith, &req); - if (*wRet != WAITRESS_RET_OK) { - BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", WaitressErrorToStr (*wRet)); + *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 (req.responseData != NULL) { free (req.responseData); } @@ -189,7 +258,7 @@ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoReturn_t authpRet; - WaitressReturn_t authwRet; + CURLcode authwRet; PianoRequestDataLogin_t reqData; reqData.user = app->settings.username; reqData.password = app->settings.password; @@ -481,7 +550,7 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station, if (BarReadlineStr (lineBuf, sizeof (lineBuf), &app->input, BAR_RL_DEFAULT) > 0) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataSearch_t reqData; reqData.searchStr = lineBuf; @@ -680,12 +749,11 @@ size_t BarUiListSongs (const BarSettings_t *settings, * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) - * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const player_t * const player, PianoStation_t *stations, - PianoReturn_t pRet, WaitressReturn_t wRet) { + PianoReturn_t pRet, CURLcode wRet) { pid_t chld; int pipeFd[2]; @@ -748,7 +816,7 @@ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, pRet, PianoErrorToStr (pRet), wRet, - WaitressErrorToStr (wRet), + curl_easy_strerror (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, @@ -27,7 +27,6 @@ THE SOFTWARE. #include <stdbool.h> #include <piano.h> -#include <waitress.h> #include "settings.h" #include "player.h" @@ -50,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 player_t *, - PianoStation_t *, PianoReturn_t, WaitressReturn_t); + PianoStation_t *, PianoReturn_t, CURLcode); int BarUiPianoCall (BarApp_t * const, PianoRequestType_t, - void *, PianoReturn_t *, WaitressReturn_t *); + void *, PianoReturn_t *, CURLcode *); 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 c39f98b..adb2f87 100644 --- a/src/ui_act.c +++ b/src/ui_act.c @@ -63,7 +63,7 @@ static inline void BarUiDoSkipSong (player_t * const player) { */ static int BarTransformIfShared (BarApp_t *app, PianoStation_t *station) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; assert (station != NULL); @@ -96,7 +96,7 @@ BarUiActCallback(BarUiActHelp) { */ BarUiActCallback(BarUiActAddMusic) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataAddSeed_t reqData; assert (selStation != NULL); @@ -122,7 +122,7 @@ BarUiActCallback(BarUiActAddMusic) { */ BarUiActCallback(BarUiActBanSong) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoStation_t *realStation; assert (selStation != NULL); @@ -154,7 +154,7 @@ BarUiActCallback(BarUiActBanSong) { */ BarUiActCallback(BarUiActCreateStation) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataCreateStation_t reqData; reqData.type = PIANO_MUSICTYPE_INVALID; @@ -172,7 +172,7 @@ BarUiActCallback(BarUiActCreateStation) { */ BarUiActCallback(BarUiActCreateStationFromSong) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataCreateStation_t reqData; char selectBuf[2]; @@ -202,7 +202,7 @@ BarUiActCallback(BarUiActCreateStationFromSong) { */ BarUiActCallback(BarUiActAddSharedStation) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; char stationId[50]; PianoRequestDataCreateStation_t reqData; @@ -222,7 +222,7 @@ BarUiActCallback(BarUiActAddSharedStation) { */ BarUiActCallback(BarUiActDeleteStation) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; assert (selStation != NULL); @@ -247,7 +247,7 @@ BarUiActCallback(BarUiActDeleteStation) { */ BarUiActCallback(BarUiActExplain) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataExplain_t reqData; assert (selSong != NULL); @@ -266,7 +266,7 @@ BarUiActCallback(BarUiActExplain) { */ BarUiActCallback(BarUiActStationFromGenre) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; const PianoGenreCategory_t *curCat; const PianoGenre_t *curGenre; int i; @@ -376,7 +376,7 @@ BarUiActCallback(BarUiActDebug) { */ BarUiActCallback(BarUiActLoveSong) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoStation_t *realStation; assert (selStation != NULL); @@ -438,7 +438,7 @@ BarUiActCallback(BarUiActTogglePause) { */ BarUiActCallback(BarUiActRenameStation) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; char lineBuf[100]; assert (selStation != NULL); @@ -481,7 +481,7 @@ BarUiActCallback(BarUiActSelectStation) { */ BarUiActCallback(BarUiActTempBanSong) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; assert (selSong != NULL); @@ -547,7 +547,7 @@ static void BarUiActQuickmixCallback (BarApp_t *app, char *buf) { */ BarUiActCallback(BarUiActSelectQuickMix) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; assert (selStation != NULL); @@ -616,7 +616,7 @@ BarUiActCallback(BarUiActHistory) { */ BarUiActCallback(BarUiActBookmark) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; char selectBuf[2]; assert (selSong != NULL); @@ -660,7 +660,7 @@ BarUiActCallback(BarUiActVolReset) { */ BarUiActCallback(BarUiActManageStation) { PianoReturn_t pRet; - WaitressReturn_t wRet; + CURLcode wRet; PianoRequestDataGetStationInfo_t reqData; char selectBuf[2], allowedActions[6], *allowedPos = allowedActions; char question[64]; |