aboutsummaryrefslogtreecommitdiffstats
path: root/lib/http_client.h
blob: 899b3ccd6e1d0688bea6f8d8c0ee5c168238625f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2012 Wilmer van der Gaast and others                *
  \********************************************************************/

/* HTTP(S) module                                                       */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  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 General Public License for more details.

  You should have received a copy of the GNU General Public License with
  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
  Fifth Floor, Boston, MA  02110-1301  USA
*/

/* http_client allows you to talk (asynchronously, again) to HTTP servers.
   In the "background" it will send the whole query and wait for a complete
   response to come back. Initially written for MS Passport authentication,
   but used for many other things now like OAuth and Twitter.

   It's very useful for doing quick requests without blocking the whole
   program. Unfortunately it doesn't support fancy stuff like HTTP keep-
   alives. */

#include <glib.h>
#include "ssl_client.h"

struct http_request;

typedef enum http_client_flags {
	HTTPC_STREAMING = 1,
	HTTPC_EOF = 2,
	HTTPC_CHUNKED = 4,

	/* Let's reserve 0x1000000+ for lib users. */
} http_client_flags_t;

/* Your callback function should look like this: */
typedef void (*http_input_function)(struct http_request *);

/* This structure will be filled in by the http_dorequest* functions, and
   it will be passed to the callback function. Use the data field to add
   your own data. */
struct http_request {
	char *request;          /* The request to send to the server. */
	int request_length;     /* Its size. */
	short status_code;      /* The numeric HTTP status code. (Or -1
	                           if something really went wrong) */
	char *status_string;    /* The error text. */
	char *reply_headers;
	char *reply_body;
	int body_size;          /* The number of bytes in reply_body. */
	short redir_ttl;        /* You can set it to 0 if you don't want
	                           http_client to follow them. */

	http_client_flags_t flags;

	http_input_function func;
	gpointer data;

	/* Please don't touch the things down here, you shouldn't need them. */
	void *ssl;
	int fd;

	int inpa;
	int bytes_written;
	int bytes_read;
	int content_length;     /* "Content-Length:" header or -1 */

	/* Used in streaming mode. Caller should read from reply_body. */
	char *sbuf;
	size_t sblen;

	/* Chunked encoding only. Raw chunked stream is decoded from here. */
	char *cbuf;
	size_t cblen;
};

/* The _url variant is probably more useful than the raw version. The raw
   version is probably only useful if you want to do POST requests or if
   you want to add some extra headers. As you can see, HTTPS connections
   are also supported (using ssl_client). */
struct http_request *http_dorequest(char *host, int port, int ssl, char *request, http_input_function func,
                                    gpointer data);
struct http_request *http_dorequest_url(char *url_string, http_input_function func, gpointer data);

/* For streaming connections only; flushes len bytes at the start of the buffer. */
void http_flush_bytes(struct http_request *req, size_t len);
void http_close(struct http_request *req);
>/* FIXME */ } void *ssl_connect(char *host, int port, ssl_input_function func, gpointer data) { struct scd *conn = g_new0(struct scd, 1); conn->fd = proxy_connect(host, port, ssl_connected, conn); sock_make_nonblocking(conn->fd); conn->func = func; conn->data = data; conn->host = g_strdup(host); if (conn->fd < 0) { g_free(conn); return NULL; } if (!initialized) { sspi_global_init(); initialized = TRUE; atexit(sspi_global_deinit); } return conn; } static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond) { struct scd *conn = _conn; SCHANNEL_CRED ssl_cred; TimeStamp timestamp; SecBuffer ibuf[2],obuf[1]; SecBufferDesc ibufs,obufs; ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | ISC_REQ_MANUAL_CRED_VALIDATION; ULONG a; gsize size = 0; gchar *data = NULL; memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED)); ssl_cred.dwVersion = SCHANNEL_CRED_VERSION; ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, &timestamp); if (st != SEC_E_OK) { conn->func(conn->data, NULL, cond); return; } do { /* initialize buffers */ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data; ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL; obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL; ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; ibuf[1].BufferType = SECBUFFER_EMPTY; /* initialize buffer descriptors */ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; ibufs.cBuffers = 2; obufs.cBuffers = 1; ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, &timestamp); if (obuf[0].pvBuffer && obuf[0].cbBuffer) { /* FIXME: Check return value */ send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0); } switch (st) { case SEC_I_INCOMPLETE_CREDENTIALS: break; case SEC_I_CONTINUE_NEEDED: break; case SEC_E_INCOMPLETE_MESSAGE: break; case SEC_E_OK: break; } QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes); } while (1); conn->func(conn->data, conn, cond); } int ssl_read(void *conn, char *retdata, int len) { struct scd *scd = conn; SecBufferDesc msg; SecBuffer buf[4]; int ret = -1, i; char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); /* FIXME: Try to read some data */ msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; msg.pBuffers = buf; buf[0].BufferType = SECBUFFER_DATA; buf[0].cbBuffer = len; buf[0].pvBuffer = data; buf[1].BufferType = SECBUFFER_EMPTY; buf[2].BufferType = SECBUFFER_EMPTY; buf[3].BufferType = SECBUFFER_EMPTY; SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL); if (st != SEC_E_OK) { /* FIXME */ return -1; } for (i = 0; i < 4; i++) { if (buf[i].BufferType == SECBUFFER_DATA) { memcpy(retdata, buf[i].pvBuffer, len); ret = len; } } g_free(data); return -1; } int ssl_write(void *conn, const char *userdata, int len) { struct scd *scd = conn; SecBuffer buf[4]; SecBufferDesc msg; char *data; int ret; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; msg.pBuffers = buf; data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); memcpy(data + scd->sizes.cbHeader, userdata, len); buf[0].BufferType = SECBUFFER_STREAM_HEADER; buf[0].cbBuffer = scd->sizes.cbHeader; buf[0].pvBuffer = data; buf[1].BufferType = SECBUFFER_DATA; buf[1].cbBuffer = len; buf[1].pvBuffer = data + scd->sizes.cbHeader; buf[2].BufferType = SECBUFFER_STREAM_TRAILER; buf[2].cbBuffer = scd->sizes.cbTrailer; buf[2].pvBuffer = data + scd->sizes.cbHeader + len; buf[3].BufferType = SECBUFFER_EMPTY; SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0); ret = send(scd->fd, data, buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0); g_free(data); return ret; } void ssl_disconnect(void *conn) { struct scd *scd = conn; SecBufferDesc msg; SecBuffer buf; DWORD dw; dw = SCHANNEL_SHUTDOWN; buf.cbBuffer = sizeof(dw); buf.BufferType = SECBUFFER_TOKEN; buf.pvBuffer = &dw; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 1; msg.pBuffers = &buf; SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg); if (st != SEC_E_OK) { /* FIXME */ } /* FIXME: call InitializeSecurityContext(Schannel), passing * in empty buffers*/ DeleteSecurityContext(&scd->context); FreeCredentialsHandle(&scd->cred); closesocket(scd->fd); g_free(scd->host); g_free(scd); } int ssl_getfd(void *conn) { return ((struct scd*)conn)->fd; } GaimInputCondition ssl_getdirection( void *conn ) { return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */ }