+2003-09-17 Dan Winship <danw@ximian.com>
+
+ * libsoup/soup-connection.c (soup_connection_new): Replaces the
+ three previous soup_connection_new* functions and uses gobject
+ properties to set the destination and proxy uris.
+ (class_init): set up two more signals, authenticate and
+ reauthenticate.
+ (soup_connection_send_request): virtualize
+ (send_request): Default implementation
+
+ * libsoup/soup-connection-ntlm.c: New SoupConnection subclass that
+ also handles NTLM authentication. Includes all of the NTLM code
+ formerly in soup-auth-ntlm.c.
+
+ * libsoup/soup-auth-ntlm.[ch]: Gone.
+
+ * libsoup/soup-auth.c: Remove NTLM refs
+
+ * libsoup/soup-session.c (class_init): Add gobject properties for
+ proxy, max_conns, use_ntlm. Change the "authenticate" and
+ "reauthenticate" signal prototypes to not pass a SoupAuth (so they
+ can be used for authenticating SoupConnectionNTLM as well, which
+ doesn't use a SoupAuth).
+ (soup_session_new): Renamed from soup_session_new_default.
+ (soup_session_new_with_options): Replaces
+ soup_session_new_with_proxy and soup_session_new_full. Takes
+ gobject properties.
+ (run_queue): Create a new connection of type SoupConnection or
+ SoupConnectionNTLM depending on our "use_ntlm" property. Connect
+ to its authenticate and reauthenticate signals.
+ (connection_authenticate, connection_reauthenticate): proxy these
+ signals.
+
+ * libsoup/soup-address.c (update_address_from_entry): Fix a
+ crasher when failing to resolve the address.
+
+ * libsoup/soup-dns.c (check_hostent): Fix some "how was this
+ working before" bugs.
+
+ * libsoup/soup-message-client-io.c (soup_message_send_request):
+ call soup_message_prepare() to clean up the existing response
+ state.
+
+ * libsoup/soup-message-io.c (io_error): Set the read_state to DONE
+ when processing an OK EOF.
+
+ * libsoup/soup-status.h (SoupStatusClass): fix the numbering of
+ these so that SOUP_STATUS_CLASS_SUCCESS is 2, etc.
+
+ * tests/auth-test.c (authenticate, reauthenticate): Update for new
+ prototypes.
+ (main): Use soup_session_new.
+ * tests/get.c (main): Likewise.
+ * tests/simple-proxy.c (main): Likewise.
+
2003-09-10 Dan Winship <danw@ximian.com>
* libsoup/soup-session.c: Add "authenticate" and "reauthenticate"
soup-auth-basic.c \
soup-auth-digest.h \
soup-auth-digest.c \
- soup-auth-ntlm.h \
- soup-auth-ntlm.c \
soup-connection.c \
+ soup-connection-ntlm.h \
+ soup-connection-ntlm.c \
soup-dns.h \
soup-dns.c \
soup-gnutls.h \
struct hostent *h;
h = soup_dns_entry_get_hostent (addr->priv->lookup);
+ if (!h)
+ return SOUP_STATUS_CANT_RESOLVE;
if (!addr->priv->name)
addr->priv->name = g_strdup (h->h_name);
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-ntlm-offset: 8 -*- */
-/*
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifndef SOUP_AUTH_NTLM_H
-#define SOUP_AUTH_NTLM_H 1
-
-#include "soup-auth.h"
-
-#define SOUP_TYPE_AUTH_NTLM (soup_auth_ntlm_get_type ())
-#define SOUP_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
-#define SOUP_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
-#define SOUP_IS_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NTLM))
-#define SOUP_IS_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_NTLM))
-#define SOUP_AUTH_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
-
-typedef struct SoupAuthNTLMPrivate SoupAuthNTLMPrivate;
-
-typedef struct {
- SoupAuth parent;
-
- SoupAuthNTLMPrivate *priv;
-} SoupAuthNTLM;
-
-typedef struct {
- SoupAuthClass parent_class;
-
-} SoupAuthNTLMClass;
-
-GType soup_auth_ntlm_get_type (void);
-
-SoupAuth *soup_auth_ntlm_new (void);
-
-#endif /*SOUP_AUTH_NTLM_H*/
#include "soup-auth.h"
#include "soup-auth-basic.h"
#include "soup-auth-digest.h"
-#include "soup-auth-ntlm.h"
#include "soup-private.h"
#define PARENT_TYPE G_TYPE_OBJECT
static AuthScheme known_auth_schemes [] = {
{ "Basic", soup_auth_basic_get_type, 0 },
- { "NTLM", soup_auth_ntlm_get_type, 2 },
{ "Digest", soup_auth_digest_get_type, 3 },
{ NULL }
};
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-ntlm-offset: 8 -*- */
/*
- * soup-auth-ntlm.c: HTTP NTLM Authentication
+ * soup-connection-ntlm.c: NTLM-using Connection
*
* Copyright (C) 2001-2003, Ximian, Inc.
*/
#include <ctype.h>
#include <string.h>
-#include "soup-auth-ntlm.h"
+#include "soup-connection-ntlm.h"
#include "soup-private.h"
#include "soup-uri.h"
-static void construct (SoupAuth *auth, const char *header);
-static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
-static const char *get_realm (SoupAuth *auth);
-static void authenticate (SoupAuth *auth, const char *username, const char *password);
-static gboolean is_authenticated (SoupAuth *auth);
-static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
-
-char *soup_ntlm_request (void);
-gboolean soup_ntlm_parse_challenge (const char *challenge,
- char **nonce,
- char **default_domain);
-char *soup_ntlm_response (const char *nonce,
- const char *user,
- const char *password,
- const char *host,
- const char *domain);
-
-struct SoupAuthNTLMPrivate {
+void send_request (SoupConnection *conn, SoupMessage *req);
+
+struct SoupConnectionNTLMPrivate {
+ char *user;
+ guchar nt_hash[21], lm_hash[21];
gboolean authenticated;
- char *header;
- char *response;
};
-#define PARENT_TYPE SOUP_TYPE_AUTH
-static SoupAuthClass *parent_class;
+#define PARENT_TYPE SOUP_TYPE_CONNECTION
+static SoupConnectionClass *parent_class;
+
+static char *soup_ntlm_request (void);
+static gboolean soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain);
+static char *soup_ntlm_response (const char *nonce,
+ const char *user,
+ const char *password,
+ const char *host,
+ const char *domain);
static void
init (GObject *object)
{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (object);
+ SoupConnectionNTLM *ntlm = SOUP_CONNECTION_NTLM (object);
- ntlm->priv = g_new0 (SoupAuthNTLMPrivate, 1);
+ ntlm->priv = g_new0 (SoupConnectionNTLMPrivate, 1);
}
static void
finalize (GObject *object)
{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (object);
+ SoupConnectionNTLM *ntlm = SOUP_CONNECTION_NTLM (object);
+
+ g_free (ntlm->priv->user);
+ memset (ntlm->priv->nt_hash, 0, sizeof (ntlm->priv->nt_hash));
+ memset (ntlm->priv->lm_hash, 0, sizeof (ntlm->priv->lm_hash));
- g_free (ntlm->priv->header);
- g_free (ntlm->priv->response);
g_free (ntlm->priv);
G_OBJECT_CLASS (parent_class)->finalize (object);
static void
class_init (GObjectClass *object_class)
{
- SoupAuthClass *auth_class = SOUP_AUTH_CLASS (object_class);
+ SoupConnectionClass *connection_class =
+ SOUP_CONNECTION_CLASS (object_class);
parent_class = g_type_class_ref (PARENT_TYPE);
- auth_class->scheme_name = "NTLM";
-
- auth_class->construct = construct;
- auth_class->get_protection_space = get_protection_space;
- auth_class->get_realm = get_realm;
- auth_class->authenticate = authenticate;
- auth_class->is_authenticated = is_authenticated;
- auth_class->get_authorization = get_authorization;
-
+ connection_class->send_request = send_request;
object_class->finalize = finalize;
}
-SOUP_MAKE_TYPE (soup_auth_ntlm, SoupAuthNTLM, class_init, init, PARENT_TYPE)
-
+SOUP_MAKE_TYPE (soup_connection_ntlm, SoupConnectionNTLM, class_init, init, PARENT_TYPE)
-SoupAuth *
-soup_auth_ntlm_new (void)
-{
- SoupAuth *auth;
-
- auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
- construct (auth, "");
- return auth;
-}
static void
-construct (SoupAuth *auth, const char *header)
+ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
-
- ntlm->priv->header = g_strdup (header);
-}
-
-static GSList *
-get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
-{
- /* The protection space is the whole server. */
- return g_slist_prepend (NULL, g_strdup (""));
-}
-
-static const char *
-get_realm (SoupAuth *auth)
-{
- return "";
-}
-
-static void
-authenticate (SoupAuth *auth, const char *username, const char *password)
-{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
- char *domain, *nonce;
+ SoupConnectionNTLM *ntlm = user_data;
+ const GSList *headers;
+ const char *val;
+ char *nonce, *header;
+ char *username, *domain_username = NULL, *password = NULL;
+ char *slash, *domain;
+
+ if (ntlm->priv->authenticated) {
+ /* We already authenticated, but then got another 401.
+ * That means "permission denied", so don't try to
+ * authenticate again.
+ */
+ return;
+ }
- if (!ntlm->priv->header || strlen (ntlm->priv->header) < sizeof ("NTLM"))
+ headers = soup_message_get_header_list (msg->response_headers,
+ "WWW-Authenticate");
+ while (headers) {
+ val = headers->data;
+ if (!strncmp (val, "NTLM ", 5))
+ break;
+ }
+ if (!headers)
return;
- if (ntlm->priv->response)
- g_free (ntlm->priv->response);
+ if (!soup_ntlm_parse_challenge (val, &nonce, &domain))
+ return;
- if (soup_ntlm_parse_challenge (ntlm->priv->header, &nonce, &domain)) {
- ntlm->priv->response =
- soup_ntlm_response (nonce, username, password,
- NULL, domain);
+ soup_connection_authenticate (SOUP_CONNECTION (ntlm), msg,
+ "NTLM", domain,
+ &domain_username, &password);
+ if (!domain_username) {
g_free (nonce);
g_free (domain);
+ return;
+ }
+
+ slash = strpbrk (domain_username, "\\/");
+ if (slash) {
+ g_free (domain);
+ domain = g_strdup (domain_username);
+ username = slash + 1;
} else
- ntlm->priv->response = NULL;
+ username = domain_username;
- g_free (ntlm->priv->header);
- ntlm->priv->header = NULL;
+ header = soup_ntlm_response (nonce, username, password, NULL, domain);
+ g_free (domain_username);
+ g_free (password);
+ g_free (nonce);
+ soup_message_remove_header (msg->request_headers, "Authorization");
+ soup_message_add_header (msg->request_headers,
+ "Authorization", header);
+ g_free (header);
ntlm->priv->authenticated = TRUE;
+
+ /* Remove the WWW-Authenticate headers so the session won't try
+ * to do Basic auth too.
+ */
+ soup_message_remove_header (msg->response_headers, "WWW-Authenticate");
}
-static gboolean
-is_authenticated (SoupAuth *auth)
+static void
+ntlm_authorize_post (SoupMessage *msg, gpointer conn)
{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+ SoupConnectionNTLM *ntlm = conn;
- return ntlm->priv->authenticated;
+ if (ntlm->priv->authenticated &&
+ soup_message_get_header (msg->request_headers, "Authorization")) {
+ /* We just added the last Auth header, so restart it. */
+ soup_connection_send_request (conn, msg);
+ }
}
-static char *
-get_authorization (SoupAuth *auth, SoupMessage *msg)
+void
+send_request (SoupConnection *conn, SoupMessage *req)
{
- SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
- char *ret;
+ SoupConnectionNTLM *ntlm = SOUP_CONNECTION_NTLM (conn);
- if (!ntlm->priv->authenticated)
- return soup_ntlm_request ();
+ if (!ntlm->priv->authenticated) {
+ char *header = soup_ntlm_request ();
- /* Otherwise, return the response; but only once */
- ret = ntlm->priv->response;
- ntlm->priv->response = NULL;
- return ret;
+ soup_message_add_header (req->request_headers,
+ "Authorization", header);
+ g_free (header);
+ }
+
+ soup_message_remove_handler (req, SOUP_HANDLER_PRE_BODY,
+ ntlm_authorize_pre, conn);
+ soup_message_add_status_code_handler (req, SOUP_STATUS_UNAUTHORIZED,
+ SOUP_HANDLER_PRE_BODY,
+ ntlm_authorize_pre, conn);
+
+ soup_message_remove_handler (req, SOUP_HANDLER_POST_BODY,
+ ntlm_authorize_post, conn);
+ soup_message_add_status_code_handler (req, SOUP_STATUS_UNAUTHORIZED,
+ SOUP_HANDLER_POST_BODY,
+ ntlm_authorize_post, conn);
+
+ SOUP_CONNECTION_CLASS (parent_class)->send_request (conn, req);
}
*offset += len;
}
-char *
+static char *
soup_ntlm_request (void)
{
return g_strdup ("NTLM TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
}
-gboolean
+static gboolean
soup_ntlm_parse_challenge (const char *challenge,
char **nonce,
char **default_domain)
return TRUE;
}
-char *
+static char *
soup_ntlm_response (const char *nonce,
const char *user,
- const char *password,
+ const char *password,
const char *host,
const char *domain)
{
unsigned char *out, *p;
int state, save;
- lanmanager_hash (password, hash);
- calc_response (hash, nonce, lm_resp);
nt_hash (password, hash);
calc_response (hash, nonce, nt_resp);
+ lanmanager_hash (password, hash);
+ calc_response (hash, nonce, lm_resp);
memset (&resp, 0, sizeof (resp));
memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-ntlm-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_CONNECTION_NTLM_H
+#define SOUP_CONNECTION_NTLM_H 1
+
+#include "soup-connection.h"
+
+#define SOUP_TYPE_CONNECTION_NTLM (soup_connection_ntlm_get_type ())
+#define SOUP_CONNECTION_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CONNECTION_NTLM, SoupConnectionNTLM))
+#define SOUP_CONNECTION_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONNECTION_NTLM, SoupConnectionNTLMClass))
+#define SOUP_IS_CONNECTION_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CONNECTION_NTLM))
+#define SOUP_IS_CONNECTION_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CONNECTION_NTLM))
+#define SOUP_CONNECTION_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION_NTLM, SoupConnectionNTLMClass))
+
+typedef struct SoupConnectionNTLMPrivate SoupConnectionNTLMPrivate;
+
+typedef struct {
+ SoupConnection parent;
+
+ SoupConnectionNTLMPrivate *priv;
+} SoupConnectionNTLM;
+
+typedef struct {
+ SoupConnectionClass parent_class;
+
+} SoupConnectionNTLMClass;
+
+GType soup_connection_ntlm_get_type (void);
+
+#endif /*SOUP_CONNECTION_NTLM_H*/
enum {
CONNECT_RESULT,
DISCONNECTED,
+ AUTHENTICATE,
+ REAUTHENTICATE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
+enum {
+ PROP_0,
+
+ PROP_DEST_URI,
+ PROP_PROXY_URI,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
static void request_done (SoupMessage *req, gpointer user_data);
+static void send_request (SoupConnection *conn, SoupMessage *req);
static void
init (GObject *object)
static void
class_init (GObjectClass *object_class)
{
+ SoupConnectionClass *connection_class =
+ SOUP_CONNECTION_CLASS (object_class);
+
parent_class = g_type_class_ref (PARENT_TYPE);
+ /* virtual method definition */
+ connection_class->send_request = send_request;
+
/* virtual method override */
object_class->dispose = dispose;
object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
/* signals */
signals[CONNECT_RESULT] =
NULL, NULL,
soup_marshal_NONE__NONE,
G_TYPE_NONE, 0);
+ signals[AUTHENTICATE] =
+ g_signal_new ("authenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupConnectionClass, authenticate),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
+ G_TYPE_NONE, 5,
+ SOUP_TYPE_MESSAGE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
+ signals[REAUTHENTICATE] =
+ g_signal_new ("reauthenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupConnectionClass, reauthenticate),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
+ G_TYPE_NONE, 5,
+ SOUP_TYPE_MESSAGE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
+
+ /* properties */
+ g_object_class_install_property (
+ object_class, PROP_DEST_URI,
+ g_param_spec_pointer (SOUP_CONNECTION_DEST_URI,
+ "Destination URI",
+ "The HTTP destination server to use for this connection",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_pointer (SOUP_CONNECTION_PROXY_URI,
+ "Proxy URI",
+ "The HTTP Proxy to use for this connection",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
/**
* soup_connection_new:
- * @uri: remote machine to connect to
+ * @propname1: name of first property to set
+ * @...:
+ *
+ * Creates an HTTP connection. You must set at least one of
+ * %SOUP_CONNECTION_DEST_URI or %SOUP_CONNECTION_PROXY_URI. If you set a
+ * destination URI but no proxy URI, this will be a direct connection. If
+ * you set a proxy URI and an https destination URI, this will be a tunnel.
+ * Otherwise it will be an http proxy connection.
*
- * Creates a connection to @uri. You must call
- * soup_connection_connect_async() or soup_connection_connect_sync()
- * to connect it after creating it.
+ * You must call soup_connection_connect_async() or
+ * soup_connection_connect_sync() to connect it after creating it.
*
* Return value: the new connection (not yet ready for use).
**/
SoupConnection *
-soup_connection_new (const SoupUri *uri)
+soup_connection_new (const char *propname1, ...)
{
SoupConnection *conn;
+ va_list ap;
- conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
- conn->priv->dest_uri = soup_uri_copy_root (uri);
+ va_start (ap, propname1);
+ conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
+ propname1, ap);
+ va_end (ap);
return conn;
}
-/**
- * soup_connection_new_proxy:
- * @proxy_uri: proxy to connect to
- *
- * Creates a connection to @proxy_uri. As with soup_connection_new(),
- * the returned object is not yet connected.
- *
- * Return value: the new connection (not yet ready for use).
- **/
-SoupConnection *
-soup_connection_new_proxy (const SoupUri *proxy_uri)
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
{
- SoupConnection *conn;
-
- conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
- conn->priv->proxy_uri = soup_uri_copy_root (proxy_uri);
-
- return conn;
+ SoupConnection *conn = SOUP_CONNECTION (object);
+ gpointer pval;
+
+ switch (prop_id) {
+ case PROP_DEST_URI:
+ pval = g_value_get_pointer (value);
+ conn->priv->dest_uri = pval ? soup_uri_copy (pval) : NULL;
+ break;
+ case PROP_PROXY_URI:
+ pval = g_value_get_pointer (value);
+ conn->priv->proxy_uri = pval ? soup_uri_copy (pval) : NULL;
+ break;
+ default:
+ break;
+ }
}
-/**
- * soup_connection_new_tunnel:
- * @proxy_uri: proxy to connect to
- * @dest_uri: remote machine to ask the proxy to connect to
- *
- * Creates a connection to @uri via @proxy_uri. As with
- * soup_connection_new(), the returned object is not yet connected.
- *
- * Return value: the new connection (not yet ready for use).
- **/
-SoupConnection *
-soup_connection_new_tunnel (const SoupUri *proxy_uri, const SoupUri *dest_uri)
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
{
- SoupConnection *conn;
-
- conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
- conn->priv->dest_uri = soup_uri_copy_root (dest_uri);
- conn->priv->proxy_uri = soup_uri_copy_root (proxy_uri);
+ SoupConnection *conn = SOUP_CONNECTION (object);
- return conn;
+ switch (prop_id) {
+ case PROP_DEST_URI:
+ g_value_set_pointer (value, conn->priv->dest_uri ?
+ soup_uri_copy (conn->priv->dest_uri) :
+ NULL);
+ break;
+ case PROP_PROXY_URI:
+ g_value_set_pointer (value, conn->priv->proxy_uri ?
+ soup_uri_copy (conn->priv->proxy_uri) :
+ NULL);
+ default:
+ break;
+ }
}
-
static void
socket_disconnected (SoupSocket *sock, gpointer conn)
{
soup_connection_disconnect (conn);
}
+static void
+send_request (SoupConnection *conn, SoupMessage *req)
+{
+ if (req != conn->priv->cur_req) {
+ g_return_if_fail (conn->priv->cur_req == NULL);
+ conn->priv->cur_req = req;
+ g_object_add_weak_pointer (G_OBJECT (req),
+ (gpointer *)conn->priv->cur_req);
+
+ g_signal_connect (req, "finished",
+ G_CALLBACK (request_done), conn);
+ }
+
+ soup_message_io_cancel (req);
+ soup_message_send_request (req, conn->priv->socket,
+ conn->priv->proxy_uri != NULL);
+}
+
void
soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
{
g_return_if_fail (SOUP_IS_CONNECTION (conn));
g_return_if_fail (SOUP_IS_MESSAGE (req));
g_return_if_fail (conn->priv->socket != NULL);
- g_return_if_fail (conn->priv->cur_req == NULL);
- conn->priv->cur_req = req;
- g_object_add_weak_pointer (G_OBJECT (req),
- (gpointer *)conn->priv->cur_req);
+ SOUP_CONNECTION_GET_CLASS (conn)->send_request (conn, req);
+}
- g_signal_connect (req, "finished", G_CALLBACK (request_done), conn);
+void
+soup_connection_authenticate (SoupConnection *conn, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password)
+{
+ g_signal_emit (conn, signals[AUTHENTICATE], 0,
+ msg, auth_type, auth_realm, username, password);
+}
- soup_message_send_request (req, conn->priv->socket,
- conn->priv->proxy_uri != NULL);
+void
+soup_connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password)
+{
+ g_signal_emit (conn, signals[REAUTHENTICATE], 0,
+ msg, auth_type, auth_realm, username, password);
}
/* signals */
void (*connect_result) (SoupConnection *, guint);
- void (*disconnected) (SoupConnection *);
+ void (*disconnected) (SoupConnection *);
+
+ void (*authenticate) (SoupConnection *, SoupMessage *,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password);
+ void (*reauthenticate) (SoupConnection *, SoupMessage *,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password);
+
+ /* methods */
+ void (*send_request) (SoupConnection *, SoupMessage *);
} SoupConnectionClass;
GType soup_connection_get_type (void);
+#define SOUP_CONNECTION_DEST_URI "dest-uri"
+#define SOUP_CONNECTION_PROXY_URI "proxy-uri"
+
+SoupConnection *soup_connection_new (const char *propname1,
+ ...);
+
typedef void (*SoupConnectionCallback) (SoupConnection *sock,
guint status,
gpointer data);
-SoupConnection *soup_connection_new (const SoupUri *uri);
-SoupConnection *soup_connection_new_proxy (const SoupUri *proxy_uri);
-SoupConnection *soup_connection_new_tunnel (const SoupUri *proxy_uri,
- const SoupUri *dest_uri);
-
void soup_connection_connect_async (SoupConnection *conn,
SoupConnectionCallback,
gpointer user_data);
void soup_connection_send_request (SoupConnection *conn,
SoupMessage *req);
+/* protected */
+void soup_connection_authenticate (SoupConnection *conn,
+ SoupMessage *msg,
+ const char *auth_type,
+ const char *auth_realm,
+ char **username,
+ char **password);
+void soup_connection_reauthenticate (SoupConnection *conn,
+ SoupMessage *msg,
+ const char *auth_type,
+ const char *auth_realm,
+ char **username,
+ char **password);
+
+
#endif /* SOUP_CONNECTION_H */
check_hostent (SoupDNSEntry *entry, gboolean block)
{
char buf[256], *namelenp, *name, *typep, *addrlenp, *addr;
- int nread;
+ int nread, status;
fd_set readfds;
- struct timeval tv = { 0, 0 };
+ struct timeval tv = { 0, 0 }, *tvp;
soup_dns_lock ();
return;
}
- FD_ZERO (&readfds);
- FD_SET (entry->fd, &readfds);
- if (select (entry->fd + 1, &readfds, NULL, NULL, &tv) != 0) {
+ if (block)
+ tvp = &tv;
+ else
+ tvp = NULL;
+
+ do {
+ FD_ZERO (&readfds);
+ FD_SET (entry->fd, &readfds);
+ status = select (entry->fd + 1, &readfds, NULL, NULL, tvp);
+ } while (status == -1 && errno == EINTR);
+
+ if (status == 0) {
soup_dns_unlock ();
return;
}
struct hostent *h;
check_hostent (entry, TRUE);
- h = copy_hostent (entry->h);
+ h = entry->h ? copy_hostent (entry->h) : NULL;
soup_dns_entry_unref (entry);
return h;
NONE:NONE
NONE:INT
NONE:OBJECT
-NONE:POINTER
-NONE:POINTER,OBJECT
+NONE:OBJECT,STRING,STRING,POINTER,POINTER
soup_message_send_request (SoupMessage *req, SoupSocket *sock,
gboolean is_via_proxy)
{
+ soup_message_prepare (req);
soup_message_io_client (req, sock,
get_request_headers,
parse_response_headers,
/* Closing the connection to signify EOF is sometimes ok */
if (io->read_state == SOUP_MESSAGE_IO_STATE_BODY &&
io->read_encoding == SOUP_TRANSFER_UNKNOWN) {
+ io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
soup_message_io_finished (msg);
return;
}
SoupMessageParseHeadersFn parse_headers_cb,
gpointer user_data);
-void soup_message_io_cancel (SoupMessage *msg);
-
#endif /* SOUP_MESSAGE_PRIVATE_H */
gboolean via_proxy);
void soup_message_read_request (SoupMessage *req,
SoupSocket *sock);
+void soup_message_io_cancel (SoupMessage *msg);;
void soup_message_io_pause (SoupMessage *msg);
void soup_message_io_unpause (SoupMessage *msg);
#include "soup-session.h"
#include "soup-connection.h"
+#include "soup-connection-ntlm.h"
#include "soup-marshal.h"
#include "soup-message-queue.h"
#include "soup-private.h"
GHashTable *auth_realms; /* path -> scheme:realm */
GHashTable *auths; /* scheme:realm -> SoupAuth */
-
- GHashTable *ntlm_auths; /* SoupConnection -> SoupAuth */
} SoupSessionHost;
struct SoupSessionPrivate {
SoupUri *proxy_uri;
guint max_conns, max_conns_per_host;
+ gboolean use_ntlm;
SoupMessageQueue *queue;
static guint signals[LAST_SIGNAL] = { 0 };
+enum {
+ PROP_0,
+
+ PROP_PROXY_URI,
+ PROP_MAX_CONNS,
+ PROP_MAX_CONNS_PER_HOST,
+ PROP_USE_NTLM,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
static void
init (GObject *object)
{
/* virtual method override */
object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
/* signals */
signals[AUTHENTICATE] =
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSessionClass, authenticate),
NULL, NULL,
- soup_marshal_NONE__POINTER_OBJECT,
- G_TYPE_NONE, 2,
+ soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
+ G_TYPE_NONE, 5,
+ SOUP_TYPE_MESSAGE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
G_TYPE_POINTER,
- SOUP_TYPE_MESSAGE);
+ G_TYPE_POINTER);
signals[REAUTHENTICATE] =
g_signal_new ("reauthenticate",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSessionClass, reauthenticate),
NULL, NULL,
- soup_marshal_NONE__POINTER_OBJECT,
- G_TYPE_NONE, 2,
+ soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
+ G_TYPE_NONE, 5,
+ SOUP_TYPE_MESSAGE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
G_TYPE_POINTER,
- SOUP_TYPE_MESSAGE);
+ G_TYPE_POINTER);
+
+ /* properties */
+ g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_pointer (SOUP_SESSION_PROXY_URI,
+ "Proxy URI",
+ "The HTTP Proxy to use for this session",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_MAX_CONNS,
+ g_param_spec_int (SOUP_SESSION_MAX_CONNS,
+ "Max Connection Count",
+ "The maximum number of connections that the session can open at once",
+ 1,
+ G_MAXINT,
+ 10,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class, PROP_MAX_CONNS_PER_HOST,
+ g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
+ "Max Per-Host Connection Count",
+ "The maximum number of connections that the session can open at once to a given host",
+ 1,
+ G_MAXINT,
+ 4,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class, PROP_USE_NTLM,
+ g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
+ "Use NTLM",
+ "Whether or not to use NTLM authentication",
+ FALSE,
+ G_PARAM_READWRITE));
}
SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
SoupSession *
-soup_session_new_default (void)
+soup_session_new (void)
{
return g_object_new (SOUP_TYPE_SESSION, NULL);
}
SoupSession *
-soup_session_new_with_proxy (const SoupUri *proxy_uri)
+soup_session_new_with_options (const char *optname1, ...)
{
SoupSession *session;
+ va_list ap;
- session = soup_session_new_default ();
- if (proxy_uri)
- session->priv->proxy_uri = soup_uri_copy (proxy_uri);
+ va_start (ap, optname1);
+ session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION, optname1, ap);
+ va_end (ap);
return session;
}
-SoupSession *
-soup_session_new_full (const SoupUri *proxy_uri,
- guint max_conns, guint max_conns_per_host)
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
{
- SoupSession *session;
+ SoupSession *session = SOUP_SESSION (object);
+ gpointer pval;
+
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ pval = g_value_get_pointer (value);
+ session->priv->proxy_uri = pval ? soup_uri_copy (pval) : NULL;
+ break;
+ case PROP_MAX_CONNS:
+ session->priv->max_conns = g_value_get_int (value);
+ break;
+ case PROP_MAX_CONNS_PER_HOST:
+ session->priv->max_conns_per_host = g_value_get_int (value);
+ break;
+ case PROP_USE_NTLM:
+ session->priv->use_ntlm = g_value_get_boolean (value);
+ break;
+ default:
+ break;
+ }
+}
- session = soup_session_new_with_proxy (proxy_uri);
- session->priv->max_conns = max_conns;
- session->priv->max_conns_per_host = max_conns_per_host;
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupSession *session = SOUP_SESSION (object);
- return session;
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ g_value_set_pointer (value, session->priv->proxy_uri ?
+ soup_uri_copy (session->priv->proxy_uri) :
+ NULL);
+ break;
+ case PROP_MAX_CONNS:
+ g_value_set_int (value, session->priv->max_conns);
+ break;
+ case PROP_MAX_CONNS_PER_HOST:
+ g_value_set_int (value, session->priv->max_conns_per_host);
+ break;
+ case PROP_USE_NTLM:
+ g_value_set_boolean (value, session->priv->use_ntlm);
+ break;
+ default:
+ break;
+ }
}
SoupMessage *msg, gboolean prior_auth_failed)
{
const SoupUri *uri = soup_message_get_uri (msg);
+ char *username = NULL, *password = NULL;
if (uri->passwd && !prior_auth_failed) {
soup_auth_authenticate (auth, uri->user, uri->passwd);
return TRUE;
}
- g_signal_emit (session, signals[prior_auth_failed ? REAUTHENTICATE : AUTHENTICATE], 0, auth, msg);
+ g_signal_emit (session, signals[prior_auth_failed ? REAUTHENTICATE : AUTHENTICATE], 0,
+ msg, soup_auth_get_scheme_name (auth),
+ soup_auth_get_realm (auth),
+ &username, &password);
+ if (username || password)
+ soup_auth_authenticate (auth, username, password);
+ if (username)
+ g_free (username);
+ if (password) {
+ memset (password, 0, strlen (password));
+ g_free (password);
+ }
+
return soup_auth_is_authenticated (auth);
}
}
static void
+connection_authenticate (SoupConnection *conn, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password, gpointer session)
+{
+ g_signal_emit (session, signals[AUTHENTICATE], 0,
+ msg, auth_type, auth_realm, username, password);
+}
+
+static void
+connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password,
+ gpointer user_data)
+{
+ g_signal_emit (conn, signals[REAUTHENTICATE], 0,
+ msg, auth_type, auth_realm, username, password);
+}
+
+
+static void
authorize_handler (SoupMessage *msg, gpointer user_data)
{
SoupSession *session = user_data;
"WWW-Authenticate");
proxy = FALSE;
}
+ if (!headers)
+ return;
if (update_auth_internal (session, msg, headers, proxy, TRUE))
soup_session_requeue_message (session, msg);
static void
request_finished (SoupMessage *req, gpointer user_data)
{
- SoupSession *session = user_data;
-
- soup_message_queue_remove_message (session->priv->queue, req);
req->status = SOUP_MESSAGE_STATUS_FINISHED;
}
static void
-final_finished (SoupMessage *req, gpointer session)
+final_finished (SoupMessage *req, gpointer user_data)
{
+ SoupSession *session = user_data;
+
if (!SOUP_MESSAGE_IS_STARTING (req)) {
+ soup_message_queue_remove_message (session->priv->queue, req);
+
g_signal_handlers_disconnect_by_func (req, request_finished, session);
g_signal_handlers_disconnect_by_func (req, final_finished, session);
g_object_unref (req);
}
/* Otherwise, open a new connection */
- if (session->priv->proxy_uri &&
- host->root_uri->protocol == SOUP_PROTOCOL_HTTPS) {
- conn = soup_connection_new_tunnel (
- session->priv->proxy_uri, host->root_uri);
- } else if (session->priv->proxy_uri) {
- conn = soup_connection_new_proxy (
- session->priv->proxy_uri);
- } else {
- conn = soup_connection_new (host->root_uri);
- }
+ conn = g_object_new (
+ (session->priv->use_ntlm ?
+ SOUP_TYPE_CONNECTION_NTLM : SOUP_TYPE_CONNECTION),
+ SOUP_CONNECTION_DEST_URI, host->root_uri,
+ SOUP_CONNECTION_PROXY_URI, session->priv->proxy_uri,
+ NULL);
+ g_signal_connect (conn, "authenticate",
+ G_CALLBACK (connection_authenticate),
+ session);
+ g_signal_connect (conn, "reauthenticate",
+ G_CALLBACK (connection_reauthenticate),
+ session);
soup_connection_connect_async (conn, got_connection, session);
g_signal_connect (conn, "disconnected",
static void
queue_message (SoupSession *session, SoupMessage *req, gboolean requeue)
{
- soup_message_prepare (req);
-
req->status = SOUP_MESSAGE_STATUS_QUEUED;
if (!requeue)
soup_message_queue_append (session->priv->queue, req);
#define SOUP_SESSION_H 1
#include <libsoup/soup-types.h>
-#include <libsoup/soup-auth.h>
#include <libsoup/soup-message.h>
#define SOUP_TYPE_SESSION (soup_session_get_type ())
GObjectClass parent_class;
/* signals */
- void (*authenticate) (SoupSession *, SoupAuth *, SoupMessage *);
- void (*reauthenticate) (SoupSession *, SoupAuth *, SoupMessage *);
+ void (*authenticate) (SoupSession *, SoupMessage *,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password);
+ void (*reauthenticate) (SoupSession *, SoupMessage *,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password);
} SoupSessionClass;
GType soup_session_get_type (void);
-SoupSession *soup_session_new_default (void);
-SoupSession *soup_session_new_with_proxy (const SoupUri *proxy_uri);
-SoupSession *soup_session_new_full (const SoupUri *proxy_uri,
- guint max_conns,
- guint max_per_host);
-
-void soup_session_queue_message (SoupSession *session,
- SoupMessage *req,
- SoupMessageCallbackFn callback,
- gpointer user_data);
-void soup_session_requeue_message (SoupSession *session,
- SoupMessage *req);
-
-guint soup_session_send_message (SoupSession *session,
- SoupMessage *req);
+#define SOUP_SESSION_PROXY_URI "proxy-uri"
+#define SOUP_SESSION_MAX_CONNS "max-conns"
+#define SOUP_SESSION_MAX_CONNS_PER_HOST "max-conns-per-host"
+#define SOUP_SESSION_USE_NTLM "use-ntlm"
+
+SoupSession *soup_session_new (void);
+SoupSession *soup_session_new_with_options (const char *optname1,
+ ...);
+
+void soup_session_queue_message (SoupSession *session,
+ SoupMessage *req,
+ SoupMessageCallbackFn callback,
+ gpointer user_data);
+void soup_session_requeue_message (SoupSession *session,
+ SoupMessage *req);
+
+guint soup_session_send_message (SoupSession *session,
+ SoupMessage *req);
#endif /* SOUP_SESSION_H */
#define SOUP_STATUS_H 1
typedef enum {
- SOUP_STATUS_CLASS_TRANSPORT = 1,
+ SOUP_STATUS_CLASS_TRANSPORT = 0,
SOUP_STATUS_CLASS_INFORMATIONAL,
SOUP_STATUS_CLASS_SUCCESS,
SOUP_STATUS_CLASS_REDIRECT,
}
static void
-authenticate (SoupSession *session, SoupAuth *auth, SoupMessage *msg, gpointer data)
+authenticate (SoupSession *session, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password, gpointer data)
{
- char user[6], password[7];
int *i = data;
if (tests[*i].provided[0]) {
- sprintf (user, "user%c", tests[*i].provided[0]);
- sprintf (password, "realm%c", tests[*i].provided[0]);
- soup_auth_authenticate (auth, user, password);
+ *username = g_strdup_printf ("user%c", tests[*i].provided[0]);
+ *password = g_strdup_printf ("realm%c", tests[*i].provided[0]);
}
}
static void
-reauthenticate (SoupSession *session, SoupAuth *auth, SoupMessage *msg, gpointer data)
+reauthenticate (SoupSession *session, SoupMessage *msg,
+ const char *auth_type, const char *auth_realm,
+ char **username, char **password, gpointer data)
{
- char user[6], password[7];
int *i = data;
if (tests[*i].provided[0] && tests[*i].provided[1]) {
- sprintf (user, "user%c", tests[*i].provided[1]);
- sprintf (password, "realm%c", tests[*i].provided[1]);
- soup_auth_authenticate (auth, user, password);
+ *username = g_strdup_printf ("user%c", tests[*i].provided[1]);
+ *password = g_strdup_printf ("realm%c", tests[*i].provided[1]);
}
}
g_type_init ();
- session = soup_session_new_default ();
+ session = soup_session_new ();
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), &i);
g_signal_connect (session, "reauthenticate",
int opt;
g_type_init ();
- session = soup_session_new_default ();
+ session = soup_session_new ();
while ((opt = getopt (argc, argv, "r")) != -1) {
switch (opt) {
soup_server_get_port (server));
soup_server_run_async (server);
- session = soup_session_new_default ();
+ session = soup_session_new ();
printf ("\nWaiting for requests...\n");