libpurple/protocols/gg/lib/http.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
--- a/libpurple/protocols/gg/lib/http.c	Wed Oct 26 10:17:10 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,592 +0,0 @@
-/* $Id$ */
-
-/*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License Version
- *  2.1 as published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
- *  USA.
- */
-
-/**
- * \file http.c
- *
- * \brief Obsługa połączeń HTTP
- */
-
-#include "strman.h"
-#include "network.h"
-#include "libgadu.h"
-#include "resolver.h"
-#include "internal.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define GG_HTTP_MAX_LENGTH 1000000000
-
-/**
- * Rozpoczyna połączenie HTTP.
- *
- * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
- * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
- * nie powiedzie.
- *
- * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
- * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
- * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
- *
- * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
- * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
- * chwili za pomocą \c gg_http_stop().
- *
- * \param hostname Adres serwera
- * \param port Port serwera
- * \param async Flaga asynchronicznego połączenia
- * \param method Metoda HTTP
- * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
- * \param header Nagłówek zapytania plus ewentualne dane dla POST
- *
- * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
- *
- * \ingroup http
- */
-struct gg_http *gg_http_connect(const char *hostname, int port, int async,
-	const char *method, const char *path, const char *header)
-{
-	struct gg_http *h;
-
-	if (!hostname || !port || !method || !path || !header) {
-		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
-		errno = EFAULT;
-		return NULL;
-	}
-
-	if (!(h = malloc(sizeof(*h))))
-		return NULL;
-	memset(h, 0, sizeof(*h));
-
-	h->async = async;
-	h->port = port;
-	h->fd = -1;
-	h->type = GG_SESSION_HTTP;
-
-	gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
-
-	if (gg_proxy_enabled) {
-		char *auth = gg_proxy_auth();
-
-		h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
-				method, hostname, port, path, (auth) ? auth :
-				"", header);
-		hostname = gg_proxy_host;
-		h->port = port = gg_proxy_port;
-		free(auth);
-
-	} else {
-		h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
-				method, path, header);
-	}
-
-	if (h->query == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
-		free(h);
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
-
-	if (async) {
-		if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
-			gg_http_free(h);
-			errno = ENOENT;
-			return NULL;
-		}
-
-		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
-
-		h->state = GG_STATE_RESOLVING;
-		h->check = GG_CHECK_READ;
-		h->timeout = GG_DEFAULT_TIMEOUT;
-	} else {
-		struct in_addr *addr_list = NULL;
-		unsigned int addr_count;
-
-		if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
-			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
-			gg_http_free(h);
-			free(addr_list);
-			errno = ENOENT;
-			return NULL;
-		}
-
-		h->fd = gg_connect(&addr_list[0], port, 0);
-
-		if (h->fd == -1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() "
-				"connection failed (errno=%d, %s)\n",
-				errno, strerror(errno));
-			gg_http_free(h);
-			free(addr_list);
-			return NULL;
-		}
-
-		free(addr_list);
-
-		h->state = GG_STATE_CONNECTING;
-
-		while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
-			if (gg_http_watch_fd(h) == -1)
-				break;
-		}
-
-		if (h->state != GG_STATE_PARSING) {
-			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
-			gg_http_free(h);
-			return NULL;
-		}
-	}
-
-	h->callback = gg_http_watch_fd;
-	h->destroy = gg_http_free;
-
-	return h;
-}
-
-#ifndef DOXYGEN
-
-#define gg_http_error(x) \
-	if (h->fd > -1) \
-		close(h->fd); \
-	h->fd = -1; \
-	h->state = GG_STATE_ERROR; \
-	h->error = x; \
-	return 0;
-
-#endif /* DOXYGEN */
-
-/**
- * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
- *
- * Operacja będzie zakończona, gdy pole \c state będzie równe
- * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
- * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
- * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
- * w polu \c error.
- *
- * \param h Struktura połączenia
- *
- * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup http
- */
-int gg_http_watch_fd(struct gg_http *h)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
-
-	if (h == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (h->state == GG_STATE_RESOLVING) {
-		struct in_addr addr;
-		int res;
-
-		gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
-
-		do {
-			res = gg_resolver_recv(h->fd, &addr, sizeof(addr));
-		} while (res == -1 && errno == EINTR);
-
-		h->resolver_cleanup(&h->resolver, 0);
-
-		if (res != sizeof(addr) || addr.s_addr == INADDR_NONE) {
-			gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
-			gg_http_error(GG_ERROR_RESOLVING);
-		}
-
-		close(h->fd);
-		h->fd = -1;
-
-		gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(addr), h->port);
-
-		h->fd = gg_connect(&addr, h->port, h->async);
-
-		if (h->fd == -1) {
-			gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
-			gg_http_error(GG_ERROR_CONNECTING);
-		}
-
-		h->state = GG_STATE_CONNECTING;
-		h->check = GG_CHECK_WRITE;
-		h->timeout = GG_DEFAULT_TIMEOUT;
-
-		return 0;
-	}
-
-	if (h->state == GG_STATE_CONNECTING) {
-		int res = 0;
-		socklen_t res_size = sizeof(res);
-
-		if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-			gg_debug(GG_DEBUG_MISC, "=> http, async connection "
-				"failed (errno=%d, %s)\n", (res) ? res : errno,
-				strerror((res) ? res : errno));
-			close(h->fd);
-			h->fd = -1;
-			h->state = GG_STATE_ERROR;
-			h->error = GG_ERROR_CONNECTING;
-			if (res)
-				errno = res;
-			return 0;
-		}
-
-		gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
-
-		h->state = GG_STATE_SENDING_QUERY;
-	}
-
-	if (h->state == GG_STATE_SENDING_QUERY) {
-		int res;
-
-		res = send(h->fd, h->query, strlen(h->query), 0);
-
-		if (res == -1 && errno != EINTR && errno != EAGAIN) {
-			gg_debug(GG_DEBUG_MISC, "=> http, send() failed "
-				"(len=%" GG_SIZE_FMT ", res=%d, errno=%d)\n",
-				strlen(h->query), res, errno);
-			gg_http_error(GG_ERROR_WRITING);
-		}
-
-		if (res == -1) {
-			gg_debug(GG_DEBUG_MISC, "=> http, non-critical send "
-				"error (errno=%d, %s)\n",
-				errno, strerror(errno));
-			return 0;
-		}
-
-		if ((size_t) res < strlen(h->query)) {
-			gg_debug(GG_DEBUG_MISC, "=> http, partial header sent "
-				"(led=%" GG_SIZE_FMT ", sent=%d)\n",
-				strlen(h->query), res);
-
-			memmove(h->query, h->query + res, strlen(h->query) - res + 1);
-			h->state = GG_STATE_SENDING_QUERY;
-			h->check = GG_CHECK_WRITE;
-			h->timeout = GG_DEFAULT_TIMEOUT;
-		} else {
-			gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%"
-				GG_SIZE_FMT ")\n", strlen(h->query));
-			free(h->query);
-			h->query = NULL;
-
-			h->state = GG_STATE_READING_HEADER;
-			h->check = GG_CHECK_READ;
-			h->timeout = GG_DEFAULT_TIMEOUT;
-		}
-
-		return 0;
-	}
-
-	if (h->state == GG_STATE_READING_HEADER) {
-		char buf[1024], *tmp;
-		int res;
-
-		res = recv(h->fd, buf, sizeof(buf), 0);
-
-		if (res == -1 && errno != EINTR && errno != EAGAIN) {
-			gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
-			free(h->header);
-			h->header = NULL;
-			gg_http_error(GG_ERROR_READING);
-		}
-
-		if (res == -1) {
-			gg_debug(GG_DEBUG_MISC, "=> http, non-critical recv "
-				"error (errno=%d, %s)\n",
-				errno, strerror(errno));
-			return 0;
-		}
-
-		if (res == 0) {
-			gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
-			free(h->header);
-			h->header = NULL;
-			gg_http_error(GG_ERROR_READING);
-		}
-
-		gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
-
-		tmp = realloc(h->header, h->header_size + res + 1);
-
-		if (tmp == NULL) {
-			gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
-			free(h->header);
-			h->header = NULL;
-			gg_http_error(GG_ERROR_READING);
-		}
-
-		h->header = tmp;
-
-		memcpy(h->header + h->header_size, buf, res);
-		h->header_size += res;
-
-		gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
-
-		h->header[h->header_size] = 0;
-
-		if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
-			int sep_len = (*tmp == '\r') ? 4 : 2;
-			unsigned int left;
-			char *line;
-
-			left = h->header_size - ((size_t)(tmp) - (size_t)(h->header) + sep_len);
-
-			gg_debug(GG_DEBUG_MISC, "=> http, got all header "
-				"(%d bytes, %d left)\n",
-				h->header_size - left, left);
-
-			/* HTTP/1.1 200 OK */
-			if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
-				gg_debug(GG_DEBUG_MISC,
-					"=> -----BEGIN-HTTP-HEADER-----\n%s\n"
-					"=> -----END-HTTP-HEADER-----\n",
-					h->header);
-
-				gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
-				free(h->header);
-				h->header = NULL;
-				gg_http_error(GG_ERROR_CONNECTING);
-			}
-
-			h->body_size = 0;
-			line = h->header;
-			*tmp = 0;
-
-			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----"
-				"\n%s\n=> -----END-HTTP-HEADER-----\n",
-				h->header);
-
-			while (line) {
-				if (!strncasecmp(line, "Content-length: ", 16)) {
-					h->body_size = atoi(line + 16);
-				}
-				line = strchr(line, '\n');
-				if (line)
-					line++;
-			}
-
-			if (h->body_size <= 0) {
-				gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
-				h->body_size = left;
-			}
-
-			if (h->body_size > GG_HTTP_MAX_LENGTH) {
-				gg_debug(GG_DEBUG_MISC, "=> http, content-length too big\n");
-				h->body_size = GG_HTTP_MAX_LENGTH;
-			}
-
-			if (left > h->body_size) {
-				gg_debug(GG_DEBUG_MISC, "=> http, oversized "
-					"reply (%d bytes needed, "
-					"%d bytes left)\n", h->body_size, left);
-				h->body_size = left;
-			}
-
-			gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
-
-			if (!(h->body = malloc(h->body_size + 1))) {
-				gg_debug(GG_DEBUG_MISC, "=> http, not enough "
-					"memory (%d bytes for body_buf)\n",
-					h->body_size + 1);
-				free(h->header);
-				h->header = NULL;
-				gg_http_error(GG_ERROR_READING);
-			}
-
-			if (left) {
-				memcpy(h->body, tmp + sep_len, left);
-				h->body_done = left;
-			}
-
-			h->body[left] = 0;
-
-			h->state = GG_STATE_READING_DATA;
-			h->check = GG_CHECK_READ;
-			h->timeout = GG_DEFAULT_TIMEOUT;
-		}
-
-		return 0;
-	}
-
-	if (h->state == GG_STATE_READING_DATA) {
-		char buf[1024];
-		int res;
-
-		res = recv(h->fd, buf, sizeof(buf), 0);
-
-		if (res == -1 && errno != EINTR && errno != EAGAIN) {
-			gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
-			free(h->body);
-			h->body = NULL;
-			gg_http_error(GG_ERROR_READING);
-		}
-
-		if (res == -1) {
-			gg_debug(GG_DEBUG_MISC, "=> http, non-critical "
-				"recv error (errno=%d, %s)\n",
-				errno, strerror(errno));
-			return 0;
-		}
-
-		if (res == 0) {
-			if (h->body_done >= h->body_size) {
-				gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
-				h->state = GG_STATE_PARSING;
-				close(h->fd);
-				h->fd = -1;
-			} else {
-				gg_debug(GG_DEBUG_MISC, "=> http, "
-					"connection closed while reading "
-					"(have %d, need %d)\n",
-					h->body_done, h->body_size);
-				free(h->body);
-				h->body = NULL;
-				gg_http_error(GG_ERROR_READING);
-			}
-
-			return 0;
-		}
-
-		gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
-
-		if (h->body_done + res > h->body_size) {
-			char *tmp;
-
-			gg_debug(GG_DEBUG_MISC, "=> http, too much data "
-				"(%d bytes, %d needed), enlarging buffer\n",
-				h->body_done + res, h->body_size);
-
-			if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
-				gg_debug(GG_DEBUG_MISC, "=> http, not enough "
-					"memory for data (%d needed)\n",
-					h->body_done + res + 1);
-				free(h->body);
-				h->body = NULL;
-				gg_http_error(GG_ERROR_READING);
-			}
-
-			h->body = tmp;
-			h->body_size = h->body_done + res;
-		}
-
-		h->body[h->body_done + res] = 0;
-		memcpy(h->body + h->body_done, buf, res);
-		h->body_done += res;
-
-		gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
-
-		return 0;
-	}
-
-	if (h->fd != -1)
-		close(h->fd);
-
-	h->fd = -1;
-	h->state = GG_STATE_ERROR;
-	h->error = 0;
-
-	return -1;
-}
-
-/**
- * Kończy asynchroniczne połączenie HTTP.
- *
- * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
- *
- * \param h Struktura połączenia
- *
- * \ingroup http
- */
-void gg_http_stop(struct gg_http *h)
-{
-	if (!h)
-		return;
-
-	if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
-		return;
-
-	h->resolver_cleanup(&h->resolver, 1);
-
-	if (h->fd != -1) {
-		close(h->fd);
-		h->fd = -1;
-	}
-}
-
-/**
- * \internal Zwalnia pola struktury \c gg_http.
- *
- * Funkcja zwalnia same pola, nie zwalnia struktury.
- *
- * \param h Struktura połączenia
- */
-void gg_http_free_fields(struct gg_http *h)
-{
-	if (h == NULL)
-		return;
-
-	free(h->body);
-	h->body = NULL;
-
-	free(h->query);
-	h->query = NULL;
-
-	free(h->header);
-	h->header = NULL;
-}
-
-/**
- * Zwalnia zasoby po połączeniu HTTP.
- *
- * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
- *
- * \param h Struktura połączenia
- *
- * \ingroup http
- */
-void gg_http_free(struct gg_http *h)
-{
-	if (h == NULL)
-		return;
-
-	gg_http_stop(h);
-	gg_http_free_fields(h);
-	free(h);
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */

mercurial