Merge libsoup-2.4 branch to trunk
authorDan Winship <danw@src.gnome.org>
Tue, 15 Jan 2008 17:40:47 +0000 (17:40 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 15 Jan 2008 17:40:47 +0000 (17:40 +0000)
* Merge libsoup-2.4 branch to trunk

svn path=/trunk/; revision=1041

155 files changed:
ChangeLog
README
configure.in
docs/reference/Makefile.am
docs/reference/client-howto.xml
docs/reference/libsoup-docs.sgml
docs/reference/libsoup-overrides.txt
docs/reference/libsoup-sections.txt
docs/reference/libsoup.types
docs/reference/porting-2.2-2.4.xml [new file with mode: 0644]
docs/reference/server-howto.xml
docs/reference/tmpl/libsoup-unused.sgml [deleted file]
docs/reference/tmpl/soup-address.sgml [deleted file]
docs/reference/tmpl/soup-auth.sgml [deleted file]
docs/reference/tmpl/soup-connection-ntlm.sgml [deleted file]
docs/reference/tmpl/soup-connection.sgml [deleted file]
docs/reference/tmpl/soup-dns.sgml [deleted file]
docs/reference/tmpl/soup-md5-utils.sgml [deleted file]
docs/reference/tmpl/soup-message-filter.sgml [deleted file]
docs/reference/tmpl/soup-message-private.sgml [deleted file]
docs/reference/tmpl/soup-message-queue.sgml [deleted file]
docs/reference/tmpl/soup-message.sgml [deleted file]
docs/reference/tmpl/soup-misc.sgml [deleted file]
docs/reference/tmpl/soup-server-auth.sgml [deleted file]
docs/reference/tmpl/soup-server-message.sgml [deleted file]
docs/reference/tmpl/soup-server.sgml [deleted file]
docs/reference/tmpl/soup-session-async.sgml [deleted file]
docs/reference/tmpl/soup-session-sync.sgml [deleted file]
docs/reference/tmpl/soup-session.sgml [deleted file]
docs/reference/tmpl/soup-soap-message.sgml [deleted file]
docs/reference/tmpl/soup-soap-response.sgml [deleted file]
docs/reference/tmpl/soup-socket.sgml [deleted file]
docs/reference/tmpl/soup-ssl.sgml [deleted file]
docs/reference/tmpl/soup-status.sgml [deleted file]
docs/reference/tmpl/soup-uri.sgml [deleted file]
docs/reference/tmpl/soup-xmlrpc-message.sgml [deleted file]
docs/reference/tmpl/soup-xmlrpc-response.sgml [deleted file]
libsoup/Makefile.am
libsoup/soup-address.c
libsoup/soup-address.h
libsoup/soup-auth-basic.c
libsoup/soup-auth-digest.c
libsoup/soup-auth-digest.h
libsoup/soup-auth-domain-basic.c [new file with mode: 0644]
libsoup/soup-auth-domain-basic.h [new file with mode: 0644]
libsoup/soup-auth-domain-digest.c [new file with mode: 0644]
libsoup/soup-auth-domain-digest.h [new file with mode: 0644]
libsoup/soup-auth-domain.c [new file with mode: 0644]
libsoup/soup-auth-domain.h [new file with mode: 0644]
libsoup/soup-auth-manager.c [new file with mode: 0644]
libsoup/soup-auth-manager.h [new file with mode: 0644]
libsoup/soup-auth-ntlm.c [new file with mode: 0644]
libsoup/soup-auth-ntlm.h [new file with mode: 0644]
libsoup/soup-auth.c
libsoup/soup-auth.h
libsoup/soup-connection-ntlm.c
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-date.c
libsoup/soup-date.h
libsoup/soup-dns.c
libsoup/soup-dns.h
libsoup/soup-enum-types.c.tmpl [new file with mode: 0644]
libsoup/soup-enum-types.h.tmpl [new file with mode: 0644]
libsoup/soup-form.c [new file with mode: 0644]
libsoup/soup-form.h [new file with mode: 0644]
libsoup/soup-gnutls.c
libsoup/soup-headers.c
libsoup/soup-headers.h
libsoup/soup-logger.c [new file with mode: 0644]
libsoup/soup-logger.h [new file with mode: 0644]
libsoup/soup-marshal.list
libsoup/soup-md5-utils.c [deleted file]
libsoup/soup-md5-utils.h [deleted file]
libsoup/soup-message-body.c [new file with mode: 0644]
libsoup/soup-message-body.h [new file with mode: 0644]
libsoup/soup-message-client-io.c
libsoup/soup-message-filter.c [deleted file]
libsoup/soup-message-filter.h [deleted file]
libsoup/soup-message-handlers.c [deleted file]
libsoup/soup-message-headers.c [new file with mode: 0644]
libsoup/soup-message-headers.h [new file with mode: 0644]
libsoup/soup-message-io.c
libsoup/soup-message-private.h
libsoup/soup-message-queue.c
libsoup/soup-message-queue.h
libsoup/soup-message-server-io.c
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-method.c [deleted file]
libsoup/soup-method.h
libsoup/soup-misc.c
libsoup/soup-misc.h
libsoup/soup-path-map.c [new file with mode: 0644]
libsoup/soup-path-map.h [new file with mode: 0644]
libsoup/soup-server-auth.c [deleted file]
libsoup/soup-server-auth.h [deleted file]
libsoup/soup-server-message.c [deleted file]
libsoup/soup-server-message.h [deleted file]
libsoup/soup-server.c
libsoup/soup-server.h
libsoup/soup-session-async.c
libsoup/soup-session-async.h
libsoup/soup-session-private.h [new file with mode: 0644]
libsoup/soup-session-sync.c
libsoup/soup-session-sync.h
libsoup/soup-session.c
libsoup/soup-session.h
libsoup/soup-soap-message.c [deleted file]
libsoup/soup-soap-message.h [deleted file]
libsoup/soup-soap-response.c [deleted file]
libsoup/soup-soap-response.h [deleted file]
libsoup/soup-socket.c
libsoup/soup-socket.h
libsoup/soup-ssl.h
libsoup/soup-status.c
libsoup/soup-status.h
libsoup/soup-types.h
libsoup/soup-uri.c
libsoup/soup-uri.h
libsoup/soup-value-utils.c [new file with mode: 0644]
libsoup/soup-value-utils.h [new file with mode: 0644]
libsoup/soup-xmlrpc-message.c [deleted file]
libsoup/soup-xmlrpc-message.h [deleted file]
libsoup/soup-xmlrpc-response.c [deleted file]
libsoup/soup-xmlrpc-response.h [deleted file]
libsoup/soup-xmlrpc.c [new file with mode: 0644]
libsoup/soup-xmlrpc.h [new file with mode: 0644]
libsoup/soup.h
tests/Makefile.am
tests/apache-wrapper.c [deleted file]
tests/apache-wrapper.h [deleted file]
tests/auth-test.c
tests/context-test.c
tests/continue-test.c [new file with mode: 0644]
tests/date.c
tests/dict.c [deleted file]
tests/dns.c
tests/get.c
tests/getbug.c
tests/header-parsing.c
tests/httpd.conf.in
tests/ntlm-test.c
tests/proxy-test.c
tests/pull-api.c
tests/query-test.c [new file with mode: 0644]
tests/revserver.c [deleted file]
tests/server-auth-test.c [new file with mode: 0644]
tests/simple-httpd.c
tests/simple-proxy.c
tests/ssl-test.c
tests/test-utils.c [new file with mode: 0644]
tests/test-utils.h [new file with mode: 0644]
tests/uri-parsing.c
tests/xmlrpc-test.c

index b97a83b..3f625e1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,808 @@
-2008-01-09  Benjamin Otte  <otte@gnome.org>
+2008-01-15  Dan Winship  <danw@gnome.org>
+
+       * Merge libsoup-2.4 branch to trunk
+       
+2008-01-15  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-dns.c (resolve_status): Fix the logic here
+
+2008-01-15  Dan Winship  <danw@gnome.org>
+
+       * docs/reference/porting-2.2-2.4.xml: add a few more updates
+
+2008-01-15  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-digest.c: Use GChecksum for MD5
+
+       * libsoup/soup-md5-utils.[ch]: gone
+
+2008-01-15  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-server.c (soup_server_run_async):
+       (soup_server_quit): Don't ref/unref the server here. It doesn't
+       match the way other things work. #494128, Mathias Hasselmann.
+
+2008-01-14  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-address.h:
+       * libsoup/soup-auth-domain-basic.h:
+       * libsoup/soup-auth-domain-digest.h:
+       * libsoup/soup-auth-domain.h:
+       * libsoup/soup-auth.h:
+       * libsoup/soup-logger.h:
+       * libsoup/soup-message.h:
+       * libsoup/soup-server.h:
+       * libsoup/soup-session-async.h:
+       * libsoup/soup-session-sync.h:
+       * libsoup/soup-session.h:
+       * libsoup/soup-socket.h: Add padding for future expansion to class
+       structs
+
+2008-01-14  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-uri.c: Add more documentation.
+       (soup_uri_is_https): gone, replaced by SOUP_URI_SCHEME_HTTP /
+       SOUP_URI_SCHEME_HTTPS
+       (soup_uri_new): allow passing NULL to get back an "empty" SoupURI.
+       (soup_uri_to_string): rename just_path to just_path_and_query, to
+       avoid fooling people.
+       (soup_uri_decode, soup_uri_normalize): Change these to return the
+       decoded/normalized string rather than modifying it in place.
+       (soup_uri_set_scheme, etc): provide setters for SoupURI parts.
+       (soup_uri_set_query_from_form): set uri->query via
+       soup_form_encode_urlencoded().
+
+2008-01-14  Dan Winship  <danw@gnome.org>
+
+       * configure.in: require glib 2.15.0, and gio
+
+       * libsoup/soup-dns.c (soup_dns_lookup_resolve)
+       (soup_dns_lookup_resolve_async): Add GCancellables, and support
+       cancellation of DNS lookups.
+       (resolve_address, resolve_name): If we get a DNS failure (eg,
+       because we're disconnected from the network), don't cache that
+       result, just try again next time someone asks. [#508593]
+
+       * libsoup/soup-address.c (soup_address_resolve_async)
+       (soup_address_resolve_sync): Add GCancellables, pass them to
+       soup-dns.
+
+       * libsoup/soup-socket.c (soup_socket_connect_async)
+       (soup_socket_connect_sync): Add GCancellables and implement
+       cancellation.
+       (soup_socket_start_ssl, soup_socket_start_proxy_ssl)
+       (soup_socket_read, soup_socket_read_until, soup_socket_write): add
+       GCancellables, though these routines don't actually implement
+       cancellation yet.
+       (soup_socket_disconnect): Don't close() the socket if someone is
+       doing I/O on it, as that creates a race condition. (The fd number
+       might be quickly recycled.) Instead, keep the socket open but
+       dead, via shutdown().
+
+2008-01-14  Benjamin Otte  <otte@gnome.org>
 
        * libsoup/soup-socket.c: (soup_socket_class_init): clarify docs for
-       new-connetion signal.
+       new-connection signal.
+
+2008-01-14  Dan Winship  <danw@gnome.org>
+
+       * tests/test-utils.c: renamed from apache-wrappers and expanded.
+       (test_init): do option parsing and general setup
+       (test_cleanup): print error count and do cleanup
+       (debug_printf): define here rather than in each test, and rename
+       from dprintf [#501631]
+       (soup_test_server_new): create a SoupServer, optionally in its own
+       thread, and clean it up when exiting.
+       (soup_test_session_new): create a SoupSession, optionally with
+       an attached SoupLogger (if requested via command line)
+       
+       * tests/*.c: use test-utils
+
+2008-01-13  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-logger.c: New HTTP debug logging object. (Based on
+       E2K_DEBUG and its clones.)
+
+       * libsoup/soup-message.c (soup_message_class_init)
+       (soup_message_add_header_handler)
+       (soup_message_add_status_code_handler): Change things around a
+       little; remove the "requeuing or cancelling the message stops
+       signal emission" rule, and instead make that be a feature of just
+       the header and status code handlers. (Makes the basic signal
+       handlers behave more predictably.)
+
+2008-01-11  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-domain.c (soup_auth_domain_set_filter):
+       * libsoup/soup-auth-domain-basic.c
+       (soup_auth_domain_basic_set_auth_callback):
+       * libsoup/soup-auth-domain-digest.c
+       (soup_auth_domain_digest_set_auth_callback):
+       * libsoup/soup-message.c (soup_message_cleanup_response)
+       (soup_message_set_flags, soup_message_set_http_version)
+       (soup_message_set_uri, soup_message_set_status)
+       (soup_message_set_status_full): 
+       * libsoup/soup-message-client-io.c (parse_response_headers): 
+       * libsoup/soup-message-server-io.c (parse_request_headers):
+       Call g_object_notify() when changing properties.        
+
+       * libsoup/soup-session.c (soup_session_class_init): bump the
+       default value of SOUP_SESSION_MAX_CONNS_PER_HOST down to 2, per
+       RFC 2616.
+
+       * libsoup/soup-message-body.c (soup_buffer_copy): When copying a
+       TEMPORARY buffer, keep a reference to the copy, so that a second
+       copy will get that same buffer, rather than actually copying it
+       again.
+
+       * libsoup/soup-types.h: remove SoupMessageFilter, which doesn't
+       exist any more
+
+2008-01-07  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-session.c (soup_session_class_init): Change
+       request_started signal to have a SoupSocket as its last parameter.
+
+       * libsoup/soup-server.c: Fix request_* signals to all be (server,
+       msg, client) rather than (server, client, msg).
+
+2008-01-07  Dan Winship  <danw@gnome.org>
+
+       * docs/reference/porting-2.2-2.4.xml: Notes on porting from 2.2 to
+       2.4
+
+2008-01-07  Dan Winship  <danw@gnome.org>
+
+       * libsoup/*.c: Move gtk-doc stuff from docs/reference/tmpl/ to the
+       C files themselves. Some updates.
+
+       * docs/reference/Makefile.am: fix (kludge?) this up to not require
+       tmpl/ to exist
+
+       * docs/reference/client-howto.xml: 
+       * docs/reference/server-howto.xml: update
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-soap-message.c:
+       * libsoup/soup-soap-response.c: For the second time, remove SOAP
+       support from libsoup... These APIs are not really all that helpful
+       in the grand scheme of SOAPiness, and are only used by the
+       Evolution GroupWise backend, which can just import this code and
+       integrate it better there.
+
+       * libsoup/soup-misc.c (soup_xml_real_node): 
+       * libsoup/soup-xmlrpc.c: Move soup_xml_real_node out of soup-misc
+       to soup-xmlrpc, and make it private. libxml is no longer exposed
+       in the public API.
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-date.c (soup_date_new_from_now): new method to
+       generate a date relative to now.
+       (soup_date_new, etc): document SoupDate methods
+
+       * libsoup/soup-server.c (got_headers): set Date header, as
+       required by RFC 2616
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-server.c (got_headers): if raw_paths isn't set,
+       decode the request's uri->path before doing anything else
+       (soup_server_class_init): add "raw-paths" property, to tell
+       SoupServer to NOT decode the Request-URI path.
+
+       * libsoup/soup-auth-domain.c (soup_auth_domain_covers): Revert
+       earlier path-decoding change; that happens at the SoupServer level
+       now.
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-body.c (soup_buffer_get_type): Register
+       SoupBuffer as a boxed type.
+
+       * libsoup/soup-message.c (soup_message_class_init): Use
+       SOUP_TYPE_BUFFER in got_chunk signal definition
+
+       * libsoup/soup-server.c (soup_client_context_get_type): Register
+       SoupClientContext as a pointer type
+       (soup_server_class_init): use SOUP_TYPE_CLIENT_CONTEXT in signal
+       definitions.
+
+       * libsoup/soup-marshal.list: clean this up
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-server.c (SoupClientContext): Make this opaque.
+       (soup_client_context_get_socket)
+       (soup_client_context_get_auth_domain)
+       (soup_client_context_get_auth_user): New accessors
+       (soup_server_class_init): Make the signals take a
+       SoupClientContext rather than a SoupSocket.
+       (start_request, check_auth, call_handler, request_finished): Clean
+       these up by using a SoupClientContext to communicate between them.
+       (soup_server_add_handler): tweak the argument order to match the
+       gtk standard (callback, user_data, destroynotify).
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-address.c: remove the "dns_result" signal, which
+       was just an implementation detail of soup_address_resolve_async().
+
+2008-01-06  Dan Winship  <danw@gnome.org>
+
+       * libsoup/*.c: misc documentation updates/gtk-doc fixes
+
+       * libsoup/soup-server.c: finally start documenting this properly.
+
+       * libsoup/soup-status.h (SoupStatusClass): kill this, since
+       soup_message_add_status_class_handler() is gone now.
+
+       * libsoup/soup-status.c (soup_status_get_phrase): Update docs to
+       explain that you probably don't want to use this.
+
+       * libsoup/soup-misc.h (SOUP_SSL_ERROR, SoupSSLError): Move these
+       here, since soup-ssl.h isn't installed.
+
+       * docs/references: start updating this...
+
+2008-01-04  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-body.c (soup_buffer_new)
+       (soup_message_body_append): Reorder the arguments to match
+       soup_message_set_request/response, so it's not confusing.
+
+       * libsoup/soup-message.c (wrote_chunk): remove the "chunk" arg
+       from the signal, as it turns out to be *in*convenient, since most
+       callers use this signal to mean "need another chunk", so they want
+       it to have the same prototype as "wrote_headers", which means
+       "need first chunk".
+
+2008-01-04  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-domain.c: add documentation
+       (soup_auth_domain_set_filter): take a GDestroyNotify, for better
+       bindability
+
+       * libsoup/soup-auth-domain-basic.c:
+       * libsoup/soup-auth-domain-digest.c: Add documentation. Replace
+       authentication signals with more-easily-bindable authentication
+       callbacks (with GDestroyNotifys).
+       (soup_auth_domain_digest_evil_check_password): Add this for the
+       benefit of code that depends on being able to do the equivalent
+       of the old soup_server_auth_check_passwd().
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-body.h (SoupMessageBody): add data and
+       length parameters like SoupBuffer, to make this easier for callers
+       to use.
+
+       * libsoup/soup-message-body.c (soup_message_body_append)
+       (soup_message_body_append_buffer)
+       (soup_message_body_truncate): Update body->length
+       (soup_message_body_flatten): Fill in body->data (and NUL-terminate
+       it as an added bonus).
+
+       * libsoup/soup-message.c (got_body): flatten the newly-gotten
+       body.
+       (soup_message_get_request, soup_message_get_response): gone
+       
+       * libsoup/soup-message-client-io.c (get_request_headers): 
+       * libsoup/soup-message-server-io.c (get_response_headers): 
+       * libsoup/soup-soap-message.c (soup_soap_message_parse_response):
+       * tests/*.c: simplify
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/Makefile.am (soup_headers): oops, move soup-auth.h here
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-form.c: new HTML-form-related methods (just URI
+       decoding/encoding at the moment).
+
+       * libsoup/soup-server.h (SoupServerCallback): change the prototype
+       to include the decoded path and query rather than the undecoded
+       URI.
+
+       * libsoup/soup-server.c (call_handler): %-decode the URI path
+       before looking up a handler. Decode query if available. Pass path
+       and query to the callback.
+
+       * libsoup/soup-auth-domain.c (soup_auth_domain_covers): fix this
+       to %-decode the URI path before testing it
+
+       * libsoup/soup-message-body.c (soup_message_body_append): allow
+       0-length appends
+
+       * tests/query-test.c: URI query parsing test
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-uri.c:
+       * libsoup/soup-uri.h: Change all the "const SoupURI *" to just
+       "SoupURI *", since the const is just there to be annoying.
+
+       * */*.c: update
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-body.c (soup_message_body_get_length)
+       (soup_message_body_get_chunk): Use goffset rather than gsize for
+       references to the entire size of the message body. (SoupBuffer
+       still uses gsize, so individual chunks can only be G_MAXSIZE
+       long.)
+       
+       * libsoup/soup-message-headers.c
+       (soup_message_headers_get_content_length):
+       (soup_message_headers_set_content_length): Likewise, use goffset.
+
+2008-01-02  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-headers.c (soup_message_headers_get):
+       Renamed from soup_message_headers_find, and with new behavior; now
+       multiple headers with the same name are automatically merged
+       together into a single comma-separated value, to ensure that apps
+       treat multivalued headers the same regardless of how upstream
+       servers generate them.
+       (soup_message_headers_find_nth): no longer needed/wanted
+
+       * libsoup/soup-auth-manager.c: Update to deal with
+       SoupMessageHeaders change. (Ugh.)
+
+       * tests/header-parsing.c: Update multiple-values test, and undo a
+       change that mistakenly got committed while debugging something
+       earlier.
+
+2008-01-01  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-manager.c:
+       * libsoup/soup-dns.c: 
+       * libsoup/soup-gnutls.c: 
+       * libsoup/soup-message.c: 
+       * libsoup/soup-message-io.c: 
+       * libsoup/soup-message-queue.c: 
+       * libsoup/soup-misc.c: 
+       * libsoup/soup-path-map.c: 
+       * libsoup/soup-server.c: 
+       * libsoup/soup-session.c: 
+       * libsoup/soup-session-sync.c: 
+       * libsoup/soup-socket.c: Use g_slice.
+
+2008-01-01  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-session.c (soup_session_cancel_message): add a
+       "status_code" argument rather than having the caller set the
+       status code separately, to prevent a race condition.
+
+2008-01-01  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-session.c (soup_session_queue_message): change the
+       callback type to include the SoupSession as a parameter as well.
+
+       * *.c: update
+
+2007-12-31  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-session.c (soup_session_class_init): change
+       the "authenticate" signal to include a SoupAuth rather than its
+       components, and to have a "retrying" parameter rather than
+       separating "authenticate" and "reauthenticate".
+
+       * libsoup/soup-connection.c (soup_connection_class_init): Likewise
+
+       * libsoup/soup-auth-manager.c (authenticate_auth): update
+
+       * libsoup/soup-auth.c: make various attributes into gobject
+       properties.
+       (soup_auth_is_for_proxy): check whether an auth is plain or proxy
+       (soup_auth_get_host): get the hostname associated with an auth
+
+       * libsoup/soup-auth-ntlm.c: dummy class used by SoupConnectionNTLM
+       in the authenticate signal
+
+       * libsoup/soup-connection-ntlm.c (ntlm_authorize_pre): update for
+       authenticate signals changes; use a fake SoupAuthNTLM to assist.
+
+2007-12-20  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message.c (soup_message_add_header_handler)
+       (soup_message_add_status_code_handler): Make these be wrappers
+       around g_signal_connect() rather than having a completely separate
+       system.
+       (soup_message_class_init): improve signal docs. Use
+       "got_foo_signal_wrapper" to wrap the got-foo signals.
+       (got_foo_signal_wrapper): Wraps the marshaller for the got-foo
+       signals and cancels the signal emission if the message gets
+       cancelled or requeued.
+       (got_informational, got_headers, got_chunk, got_body): remove
+       no-longer-needed default implementations.
+
+       * libsoup/soup-message-handlers.c: gone
+
+       * tests/ntlm-test.c (do_message): Simplify now that callback
+       processing doesn't happen in two separate phases.
+
+2007-12-20  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-domain.c:
+       * libsoup/soup-auth-domain-basic.c:
+       * libsoup/soup-auth-domain-digest.c: New server-side auth system.
+
+       * libsoup/soup-server.c: remove SoupServerAuth / SoupAuthContext
+       stuff, add SoupAuthDomain support.
+       (SoupServerCallbackFn): improve the args here
+       (SoupClientContext): renamed from SoupServerContext and made less
+       redundant
+
+       * libsoup/soup-server-auth.c: gone!
+
+       * libsoup/soup-auth-digest.c (soup_auth_digest_parse_algorithm)
+       (soup_auth_digest_get_algorithm, soup_auth_digest_parse_qop)
+       (soup_auth_digest_get_qop, soup_auth_digest_compute_hex_urp)
+       (soup_auth_digest_compute_hex_a1)
+       (soup_auth_digest_compute_response): New routines shared between
+       client-side and server-side digest auth.
+
+       * tests/server-auth-test.c: test server-side auth, using curl for
+       the client side
+
+       * configure.in: check for curl, for server-auth-test
+
+2007-12-20  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-headers.c (soup_header_parse_list)
+       (soup_header_parse_quality_list): New methods to parse list-type
+       headers (with optional qvalues) correctly.
+       (soup_header_parse_param_list): Rename to match the other methods,
+       and update the semantics a bit.
+       (soup_header_contains): Correctly check for a token in a list
+
+       * libsoup/soup-message.c (soup_message_is_keepalive):
+       * libsoup/soup-message-client-io.c (get_request_headers):
+       * libsoup/soup-message-server-io.c (parse_request_headers): Use
+       soup_header_contains() with Connection headers.
+
+       * tests/header-parsing.c (do_qvalue_tests): add
+       soup_header_parse_quality_list() test
+
+2007-12-20  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-auth-manager.c: Move auth-related code from
+       SoupSession and SoupAuth here, and make various cleanups and
+       beginnings of cleanups.
+
+       * libsoup/soup-session.c: lots of stuff moved to
+       soup-auth-manager.c
+
+       * libsoup/soup-auth.c (soup_auth_new_from_headers): partly moved
+       to soup-auth-manager.c, partly renamed to soup_auth_new().
+       (soup_auth_update): new method to update an existing auth based on
+       a new WWW-Authenticate/Proxy-Authenticate header. Also replaces
+       the old "construct" method.
+
+       * libsoup/soup-auth-digest.c (update): Implement. If the new auth
+       has stale=true, don't invalidate the auth, just update the nonce.
+       (get_authorization): add a header handler to the message to catch
+       Authentication-Info/Proxy-Authentication-Info headers so that if
+       there's a nextnonce, we can start using it. #471380.
+
+       * libsoup/soup-auth-basic.c (update): Implement. (Updating an
+       existing Basic auth always invalidates it.)
+
+       * tests/http.conf.in:
+       * tests/auth-test.c: add a test for digest nonce handling
+
+2007-12-20  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-path-map.c: New type representing a sparse
+       path->something mapping
+
+       * libsoup/soup-server.c: Use SoupPathMap to record handlers. Make
+       SoupServerHandler a private type.
+       (soup_server_new): Rewrite this to just be a thin wrapper, and put
+       all of the code into a constructor override. #491653
+       (soup_server_add_handler): Turn the "unregister" arg into a
+       GDestroyNotify, for better bindability.
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-server.c: define new request_started, request_read,
+       request_finished, and request_aborted signals, for finer-grained
+       tracking than normal handlers allow.
+       (check_auth): split this out of call_handler, and run it
+       immediately after "got_headers", not "got_body", so that we can
+       preemptively reject "Expect: 100-continue" messages that will
+       require auth.
+
+       * libsoup/soup-message-io.c (io_write, io_read): Fix up
+       100-continue processing
+
+       * tests/continue-test.c: new test of client/server 100-continue
+       processing
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-socket.c: Cleanup. Remove the "connect_result"
+       signal. Make local_address and remote_address
+       into (construct-only) properties.
+       (soup_socket_connect_async, soup_socket_connect_sync): Replace
+       soup_socket_connect. _async takes a callback+user_data (like the
+       old soup_socket_client_new_async), but doesn't implement the
+       callback in terms of a connect_result signal.
+       (soup_socket_client_new_async, soup_socket_client_new_sync): Gone.
+       (Unused since the async_context addition anyway). Replaced by the
+       new construct properties and connect methods.
+       (soup_socket_read, soup_socket_read_until, soup_socket_write):
+       Make these actually take a GError rather than doing an ugly hack
+       to preserve the old API.
+       (SOUP_SOCKET_FLAG_NODELAY, SOUP_SOCKET_FLAG_REUSEADDR)
+       (SOUP_SOCKET_FLAG_CLOEXEC): kill these off (all three are always
+       TRUE now); SoupSocket is libsoup's socket API; it's not
+       necessarily intended to be generically useful for everyone.
+
+       * *.c: Update for SoupSocket changes
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-server-message.c: Kill!
+
+       * libsoup/soup-message-server-io.c (parse_request_headers):
+       Generate the full request URL from the socket's data, since we no
+       longer have soup_server_message_get_server().
+
+       * libsoup/soup-server.c (request_finished, call_handler)
+       (start_request, new_connection): update
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-headers.c: Add some more fields to
+       SoupMessageHeaders, and start caching the parsed values of certain
+       important headers.
+       (soup_message_headers_get/set_encoding): replaces old SoupMessage
+       methods, and only deals with the declared transfer encoding, not
+       the wire encoding.
+       (soup_message_headers_get/set_content_length): Handle
+       Content-Length.
+       (soup_message_headers_get_expectations): Handle Expect. (Replaces
+       the SOUP_MESSAGE_EXPECT_CONTINUE flag).
+
+       * libsoup/soup-message.c (soup_message_get_request_encoding):
+       (soup_message_get_response_encoding):
+       (soup_message_set_response_encoding): replaced by
+       SoupMessageHeaders methods.
+
+       * libsoup/soup-message-client-io.c:
+       * libsoup/soup-message-server-io.c:
+       * libsoup/soup-message-io.c: Update for SoupMessageHeaders changes
+       with encoding/content-length stuff.
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message-body.c (SoupMessageBody): new opaque type
+       for request/response bodies allowing less hacky handling of
+       chunked encoding.
+       (SoupBuffer): refcounted buffer type
+
+       * libsoup/soup-message.h (SoupMessage): turn request and response
+       members into SoupMessageBody.
+       (SoupOwnership, SoupDataBuffer): gone, replaced by
+       SoupMessageBody/SoupBuffer.
+
+       * libsoup/soup-message.c (soup_message_wrote_chunk)
+       (soup_message_got_chunk): add the chunk as a signal param rather
+       than having it be visible in msg->request/response.
+       (soup_message_add_chunk, soup_message_add_final_chunk)
+       (soup_message_pop_chunk): replaced by SoupMessageBody methods now.
+
+2007-12-19  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-xmlrpc.c:
+       * libsoup/soup-value-utils.c: Oops. Change the API a bunch so this
+       works on x86; apparently I was doing illegal things with va_lists
+       before that only work on x86_64.
+
+2007-12-14  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message.c: use GObject properties for SoupMessage
+       fields.
+
+       * libsoup/soup-message-server-io.c: 
+       * libsoup/soup-soap-message.c: update for that
+
+2007-12-14  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-uri.c: Rename from SoupUri to SoupURI. Use the
+       slice allocator and register as a boxed type.
+       (SoupURI): Rename "protocol" field to "scheme" and "passwd" to
+       "password". Make scheme an interned string. Replace
+       SOUP_PROTOCOL_HTTPS with soup_uri_is_https().
+
+       * *.c: update
+       
+2007-12-14  Dan Winship  <danw@gnome.org>
+
+       * libsoup/Makefile.am: Use glib-mkenums to build soup-enum-types.c
+       and soup-enum-types.h
+
+       * libsoup/soup-address.h (SoupAddressFamily): redo this definition
+       again, to make glib-mkenums happy.
+
+2007-12-13  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-xmlrpc.c: New easier-to-use and
+       easier-to-do-language-bindings-of XML-RPC code.
+
+       * libsoup/soup-xmlrpc-message.c: 
+       * libsoup/soup-xmlrpc-response.c: gone
+
+       * libsoup/soup-value-utils.c: Utilites for working with
+       GValueArray, and GHashTables of GValues, used by soup-xmlrpc.
+
+       * tests/getbug.c: 
+       * tests/xmlrpc-test.c: Update to use new XML-RPC stuff
+
+2007-12-13  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-date.c: Make a SoupDate type, and redo in terms of
+       that rather than struct tm and time_t. Also be much more liberal
+       when parsing.
+
+       * libsoup/soup-xmlrpc-message.c (soup_xmlrpc_message_write_datetime): 
+       * libsoup/soup-xmlrpc-response.c (soup_xmlrpc_value_get_datetime):
+       Use SoupDate.
+
+       * tests/date.c: Use SoupDate, test parsing lots more formats
+
+       * tests/xmlrpc-test.c: update for SoupDate
+
+2007-12-12  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message.c: 
+       * libsoup/soup-message-private.h: Remove SoupMessageStatus,
+       msg->status, and soup_message_io_* from the public API, as they
+       all really belong to the session, not the message. (For now
+       they've just been moved to soup-message-private.h, but some day
+       they'll be fully refactored away from SoupMessage.)
+
+       * libsoup/soup-server.c (soup_server_pause_message)
+       (soup_server_unpause_message): 
+       * libsoup/soup-session.c (soup_session_pause_message)
+       (soup_session_unpause_message): session/server-level methods to
+       replace soup_message_io_pause() and soup_message_io_unpause().
+
+       * libsoup/soup-server-message.c: Remove some unused methods
+
+       * */*.c: Update
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-connection.c:
+       * libsoup/soup-session.c: replace message filters with a
+       "request_started" signal
+
+       * libsoup/soup-message-filter.c: gone
+
+       * libsoup/soup-types.h (SOUP_MAKE_INTERFACE): no longer needed
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-uri.c: Update for RFC 3986 changes, bgo 266516, and
+       general conformance
+       (soup_uri_get_protocol): match protocols case-insensitively
+       (soup_uri_new_with_base): Don't fully %-decode the fragment,
+       query, and path, but do %-decode anything which isn't supposed to
+       be encoded. Recognize IPv6 address literals. Use stricter
+       "../"-stripping rules on the path. Reject URIs with junk between
+       the port number and the path.
+       (soup_uri_to_string): Update for the fact that the host might be
+       an IPv6 literal, and for the fact that path, query, and fragment
+       are now pre-escaped.
+       (soup_uri_equal): compare hostnames case-insensitively
+       (uri_encoded_char): update to match RFC 3986
+       (append_uri_encoded): use uppercase hex letters as recommended by
+       RFC 3986.
+       (soup_uri_normalize): decode only %-escapes that don't belong
+       there.
+
+       * docs/reference/tmpl/soup-uri.sgml: add some more SoupUri docs
+
+       * tests/uri-parsing.c: Add new tests from RFC 3986, RFC 2732, RFC
+       2616, bgo 266516, and elsewhere. Update some tests to match new
+       parsing/unparsing rules.
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message.c (soup_message_new)
+       (soup_message_new_from_uri): g_intern_string() the method name
+       rather than assuming it's static. Also remove the NULL==GET
+       assumption.
+
+       * libsoup/soup-method.c:
+       * libsoup/soup-method.h: remove the SOUP_METHOD_ID_* macros, and
+       have the SOUP_METHOD_* macros return interned strings
+
+       * libsoup/soup-server.h (SoupServerContext): remove method_id
+       field.
+
+       * libsoup/soup-server-message.c (finalize): no longer needed,
+       since smsg->method is now an interned string just like with a
+       normal SoupMessage.
+
+       * libsoup/soup-soap-message.c (soup_soap_message_new_from_uri):
+       remove NULL==GET assumption
+
+       * *.c: update
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-message.h (SoupHTTPVersion): rename (from
+       SoupHttpVersion)
+
+       * libsoup/soup-message-headers.c: New opaque type representing
+       message headers, and new methods that work on it. Uses an array
+       rather than a hash table, to preserve header ordering as required
+       by RFC 2616. (Also fixes the API wart that
+       "soup_message_get_header", etc, did not actually take a
+       SoupMessage.)
+
+       * libsoup/soup-message.c: Kill off old header-manipulating
+       methods.
+
+       * libsoup/soup-headers.c (soup_headers_parse_request): return a
+       guint rather than gboolean, so we can properly return
+       SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED where appropriate. Also fix
+       up HTTP-Version parsing to conform with the RFC.
+       (soup_headers_parse_status_line): Likewise update HTTP-Version
+       parsing.
+       
+       * libsoup/soup-message-server-io.c (parse_request_headers): set
+       return status appropriately on parse errors
+
+       * tests/header-parsing.c: update / add more tests
+
+       * *.c: update
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-misc.c: remove deprecated base64 methods
+
+       * tests/auth-test.c (identify_auth): oops, update to use
+       g_base64_decode.
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * libsoup/Makefile.am (libsoupinclude_HEADERS): remove
+       soup-connection.h and soup-message-queue.h
+
+       * libsoup/soup-types.h: remove SoupConnection and SoupMessageQueue
+       which are no longer public
+
+       * libsoup/soup.h: sync this to reality for the first time in years
+
+       * libsoup/soup-session.c (soup_session_get_queue): Add this, for
+       subclasses, as the queue is no longer a public part of the session
+       struct.
+
+       * libsoup/soup-message.h:
+       * libsoup/soup-message-private.h: Move soup_message_send_request()
+       and soup_message_receive_request() to soup-message-private.h,
+       remove soup_message_send_request_internal().
+
+       * libsoup/soup-session-private.h: Move "protected" SoupSession
+       methods (soup_session_get_connection,
+       soup_session_try_prune_connection) here from soup-session.h
+       Add soup_session_get_queue.
+
+2007-12-05  Dan Winship  <danw@gnome.org>
+
+       * configure.in: bump version to 2.3.0 and SOUP_API_VERSION to 2.4,
+       and drop AGE/CURRENT/REVISION all to 0.
+
+       * libsoup/Makefile.am: Rename library to libsoup-2.4.la
+
+       (start of libsoup-2.4 branch)
 
 2007-11-26  Dan Winship  <danw@gnome.org>
 
diff --git a/README b/README
index d2f656a..bf3b70a 100644 (file)
--- a/README
+++ b/README
@@ -8,7 +8,7 @@ Features:
   * Proxy support, including authentication and SSL tunneling
   * Client support for Digest, NTLM, and Basic authentication
   * Server support for Digest and Basic authentication
-  * Basic client-side SOAP and XML-RPC support
+  * XML-RPC support
 
 See the documentation in docs/reference/ and the test programs in
 tests/ for simple examples of how to use the code. The
index f04ecc6..ac8c9df 100644 (file)
@@ -3,7 +3,7 @@ dnl *** Initialize automake and set version ***
 dnl *******************************************
 
 AC_PREREQ(2.53)
-AC_INIT(libsoup, 2.2.104)
+AC_INIT(libsoup, 2.3.0)
 AC_CONFIG_SRCDIR(libsoup.pc.in)
 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
 
@@ -11,14 +11,14 @@ AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 AC_PROG_MAKE_SET
 
-SOUP_API_VERSION=2.2
+SOUP_API_VERSION=2.4
 AC_SUBST(SOUP_API_VERSION)
 
 # Increment on interface addition. Reset on removal.
-SOUP_AGE=5
+SOUP_AGE=0
 
 # Increment on interface add, remove, or change.
-SOUP_CURRENT=13
+SOUP_CURRENT=0
 
 # Increment on source change. Reset when CURRENT changes.
 SOUP_REVISION=0
@@ -73,7 +73,7 @@ dnl ***********************
 dnl *** Checks for glib ***
 dnl ***********************
 
-AM_PATH_GLIB_2_0(2.12.0,,,gobject gthread)
+AM_PATH_GLIB_2_0(2.15.0,,,gobject gthread gio)
 
 PKG_CHECK_MODULES(XML, libxml-2.0)
 AC_SUBST(XML_CFLAGS)
@@ -272,6 +272,14 @@ fi
 AC_SUBST(IF_HAVE_PHP)
 AM_CONDITIONAL(HAVE_XMLRPC_EPI_PHP, test "$have_xmlrpc_epi_php" = yes)
 
+dnl *******************************
+dnl *** curl (for regression tests)
+dnl *******************************
+AC_PATH_PROG(CURL, curl, no)
+if test "$CURL" != no; then
+    AC_DEFINE(HAVE_CURL, 1, [Whether or not curl can be used for tests])
+fi
+AM_CONDITIONAL(HAVE_CURL, test "$CURL" != no)
 
 dnl *************************
 dnl *** Output Everything ***
index 0dae970..f34e154 100644 (file)
@@ -29,7 +29,13 @@ HFILE_GLOB=
 CFILE_GLOB=
 
 # Header files to ignore when scanning.
-IGNORE_HFILES= soup.h soup-marshal.h soup-types.h
+IGNORE_HFILES= soup.h soup-marshal.h \
+       soup-message-private.h soup-session-private.h \
+       soup-types.h soup-enum-types.h \
+       soup-auth-basic.h soup-auth-digest.h soup-auth-ntlm.h \
+       soup-connection.h soup-connection-ntlm.h \
+       soup-dns.h soup-auth-manager.h soup-md5-utils.h \
+       soup-message-queue.h soup-path-map.h soup-ssl.h
 
 # Images to copy into HTML directory.
 HTML_IMAGES = 
@@ -51,7 +57,11 @@ GTKDOC_CFLAGS =                              \
 
 GTKDOC_LIBS = $(top_builddir)/libsoup/libsoup-$(SOUP_API_VERSION).la
 
-
 # include common portion ...
 include $(top_srcdir)/gtk-doc.make
 
+# kludges
+tmpl/*.sgml:
+
+clean: clean-am
+       rm -rf tmpl
index ac7caa0..c52f635 100644 (file)
@@ -44,7 +44,7 @@ slightly different behavior:
 </itemizedlist>
 
 <para>
-If you want to do a mix of synchronous and asynchronous I/O, you will
+If you want to do a mix of mainloop-based and blocking I/O, you will
 need to create two different session objects.
 </para>
 
@@ -142,8 +142,10 @@ only need to create the message and it's ready to send:
 
 <para>
 In more complicated cases, you can use various <link
-linkend="SoupMessage">SoupMessage</link> methods to set the request
-headers and body of the message:
+linkend="SoupMessage">SoupMessage</link>, <link
+linkend="SoupMessageHeaders">SoupMessageHeaders</link>, and <link
+linkend="SoupMessageBody">SoupMessageBody</link> methods to set the
+request headers and body of the message:
 </para>
 
 <informalexample><programlisting>
@@ -151,8 +153,8 @@ headers and body of the message:
 
        msg = soup_message_new ("POST", "http://example.com/form.cgi");
        soup_message_set_request (msg, "application/x-www-form-urlencoded",
-                                 SOUP_BUFFER_USER_OWNED, formdata, strlen (formdata));
-       soup_message_add_header (msg->request_headers, "Referer", referring_url);
+                                 SOUP_MEMORY_COPY, formdata, strlen (formdata));
+       soup_message_headers_append (msg->request_headers, "Referer", referring_url);
 </programlisting></informalexample>
 
 <para>
@@ -180,20 +182,18 @@ linkend="soup-session-send-message"><function>soup_session_send_message</functio
 </programlisting></informalexample>
 
 <para>
-<literal>session</literal> can be either a <link
-linkend="SoupSessionSync"><type>SoupSessionSync</type></link> or a
-<link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>;
-if you use <function>soup_session_send_message</function> on an async
-session, it will run the main loop itself until the message is
-complete.
+(If you use <function>soup_session_send_message</function> with a
+<link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>,
+it will run the main loop itself until the message is complete.)
 </para>
 
 <para>
-The return value from <function>soup_session_send</function> is a <link
-linkend="soup-status">soup status code</link>, indicating either a
-transport error that prevented the message from being sent, or the
+The return value from <function>soup_session_send</function> is a
+<link linkend="soup-status">soup status code</link>, indicating either
+transport error that prevented the message from being sent, or the
 HTTP status that was returned by the server in response to the
-message.
+message. (The status is also available as
+<literal>msg->status_code</literal>.)
 </para>
 
 </refsect3>
@@ -202,10 +202,8 @@ message.
 <title>Sending a Message Asynchronously</title>
 
 <para>
-To send a message asynchronously (which can only be done if you're
-using <link
-linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>), use
-<link linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>:
+To send a message asynchronously, use <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>:
 </para>
 
 <informalexample><programlisting>
@@ -215,7 +213,7 @@ linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>), use
 }
 
 static void
-my_callback (SoupMessage *msg, gpointer user_data)
+my_callback (SoupSession, *session, SoupMessage *msg, gpointer user_data)
 {
        /* Handle the response here */
 }
@@ -229,6 +227,15 @@ response be will be read. When the message is complete,
 passed to <function>soup_session_queue_message</function>.
 </para>
 
+<para>
+(If you use <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
+with a <link
+linkend="SoupSessionSync"><type>SoupSessionSync</type></link>, the
+message will be sent in another thread, with the callback eventually
+being invoked in the session's <link linkend="SOUP-SESSION-ASYNC-CONTEXT:CAPS"><literal>SOUP_SESSION_ASYNC_CONTEXT</literal></link>.)
+</para>
+
 </refsect3>
 
 </refsect2>
@@ -245,11 +252,11 @@ asynchronously, you can look at the response fields in the
 status and textual status response from the server.
 <structfield>response_headers</structfield> contains the response
 headers, which you can investigate using <link
-linkend="soup-message-get-header"><function>soup_message_get_header</function></link> and
+linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link> and
 <link
-    linkend="soup-message-foreach-header"><function>soup_message_foreach_header</function></link>.
+    linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
 The response body (if any) is in the
-<structfield>response</structfield> field.
+<structfield>response_body</structfield> field.
 </para>
 
 <para>
@@ -272,17 +279,17 @@ it.
 <title>Intermediate/Automatic Processing</title>
 
 <para>
-You can also connect to various <literal>SoupMessage</literal>
-signals, or set up handlers using <link
-linkend="soup-message-add-handler"><function>soup_message_add_handler</function></link>
-and the other handler methods. Notably, <link
+You can also connect to various <literal>SoupMessage</literal> signals
+to do processing at intermediate stages of HTTP I/O.
+<literal>SoupMessage</literal> also provides two convenience methods,
+<link
 linkend="soup-message-add-header-handler"><function>soup_message_add_header_handler</function></link>,
-<link linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>,
-and
-<link linkend="soup-message-add-status-class-handler"><function>soup_message_add_status_class_handler</function></link>
-allow you to invoke a handler automatically for messages with certain
-response headers or status codes. <type>SoupSession</type> uses
-this internally to handle authentication and redirection.
+and <link
+linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>,
+which allow you to set up a signal handler that will only be invoked
+for messages with certain response headers or status codes.
+<type>SoupSession</type> uses this internally to handle authentication
+and redirection.
 </para>
 
 <para>
@@ -293,10 +300,9 @@ linkend="soup-session-send-message"><function>soup_session_send_message</functio
 
 <para>
 To automatically set up handlers on all messages sent via a session,
-you can create a <link
-linkend="SoupMessageFilter">SoupMessageFilter</link> and attach it to
-the session with <link
-linkend="soup-session-add-filter"><function>soup_session_add_filter</function></link>.
+you can connect to the session's <link
+linkend="SoupSession-request-started"><literal>request_started</literal></link>
+signal, and add handlers to each message from there.
 </para>
 
 </refsect2>
@@ -309,76 +315,36 @@ linkend="soup-session-add-filter"><function>soup_session_add_filter</function></
 authentication for you. If it receives a 401 ("Unauthorized") or 407
 ("Proxy Authentication Required") response, the session will emit the
 <link linkend="SoupSession-authenticate">authenticate</link> signal,
-indicating the authentication type ("Basic", "Digest", or "NTLM") and
-the realm name provided by the server. You should connect to this
-signal and, if possible, fill in the <parameter>username</parameter>
-and <parameter>password</parameter> parameters with authentication
-information. (The session will <function>g_free</function> the strings
-when it is done with them.) If the handler doesn't fill in those
-parameters, then the session will just return the message to the
-application with its 401 or 407 status.
+providing you with a <link
+linkend="SoupAuth"><type>SoupAuth</type></link> object indicating the
+authentication type ("Basic", "Digest", or "NTLM") and the realm name
+provided by the server. If you have a username and password available
+(or can generate one), call <link
+linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
+to give the information to libsoup. The session will automatically
+requeue the message and try it again with that authentication
+information. (If you don't call
+<function>soup_auth_authenticate</function>, the session will just
+return the message to the application with its 401 or 407 status.)
 </para>
 
 <para>
-If the <literal>authenticate</literal> handler returns a username and
-password, but the request still gets an authorization error using that
-information, then the session will emit the <link
-linkend="SoupSession-reauthenticate">reauthenticate</link> signal.
-This lets the application know that the information it provided
-earlier was incorrect, and gives it a chance to try again. If this
+If the server doesn't accept the username and password provided, the
+session will emit <link
+linkend="SoupSession-authenticate">authenticate</link> again, with the
+<literal>retrying</literal> parameter set to <link
+linkend="TRUE:CAPS"><literal>TRUE</literal></link>. This lets the
+application know that the information it provided earlier was
+incorrect, and gives it a chance to try again. If this
 username/password pair also doesn't work, the session will contine to
-emit <literal>reauthenticate</literal> again and again until the
-returned username/password successfully authentications, or until the
-signal handler fails to provide a username, at which point
-<application>libsoup</application> will allow the message to fail
-(with status 401 or 407).
+emit <literal>authenticate</literal> again and again until the
+provided username/password successfully authenticates, or until the
+signal handler fails to call <link
+linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>,
+at which point <application>libsoup</application> will allow the
+message to fail (with status 401 or 407).
 </para>
 
-<para>
-There are basically three ways an application might want to use
-the signals:
-</para>
-
-<itemizedlist>
-    <listitem><para>
-       An interactive application that doesn't cache passwords could
-       just connect both <literal>authenticate</literal> and
-       <literal>reauthenticate</literal> to the same signal handler,
-       which would ask the user for a username and password and then
-       return that to soup. This handler would be called repeatedly
-       until the provided information worked, or until it failed to
-       return any information (eg, because the user hit "Cancel"
-       instead of "OK").
-    </para></listitem>
-
-    <listitem><para>
-       A slightly cleverer interactive application would look in its
-       password cache from the <literal>authenticate</literal>
-       handler, and return a password from there if one was
-       available. If no password was cached, it would just call its
-       <literal>reauthenticate</literal> handler to prompt the user.
-       The <literal>reauthenticate</literal> handler would first
-       clear any cached password for this host, auth type, and realm,
-       then ask the user as in the case above, and then store that
-       information in its cache before returning it to soup. (If the
-       password turns out to be incorrect, then
-       <literal>reauthenticate</literal> will be called again to
-       force it to be uncached.)
-    </para></listitem>
-
-    <listitem><para>
-       A non-interactive program that only has access to cached
-       passwords would only connect to
-       <literal>authenticate</literal>. If the username and password
-       that <literal>authenticate</literal> returns fail, the session
-       will emit <literal>reauthenticate</literal>, but since the
-       application is not listening to that signal, no new username
-       and password will be returned there, so the message will be
-       returned to the application with a 401 or 407 status, which
-       the application can deal with as it needs to.
-    </para></listitem>
-</itemizedlist>
-
 </refsect2>
 
 <refsect2>
@@ -396,12 +362,12 @@ A few sample programs are available in the
     </para></listitem>
 
     <listitem><para>
-       <emphasis role="bold"><literal>dict</literal></emphasis> and
-       <emphasis role="bold"><literal>getbug</literal></emphasis> are trivial
-       demonstrations of the <link
-       linkend="SoupSoapMessage">SOAP</link> and <link
-       linkend="SoupXmlrpcMessage">XMLRPC</link> interfaces,
-       respectively.
+       <emphasis role="bold"><literal>getbug</literal></emphasis> is a trivial
+       demonstration of the <link
+       linkend="libsoup-XMLRPC-Support">XMLRPC</link> interface.
+       (<emphasis
+       role="bold"><literal>xmlrpc-test</literal></emphasis> provides
+       a slightly more complicated example.)
     </para></listitem>
 
     <listitem><para>
index 6c8011f..ce97a91 100644 (file)
     <title>libsoup Tutorial</title>
     <xi:include href="client-howto.xml"/>
     <xi:include href="server-howto.xml"/>
+    <xi:include href="porting-2.2-2.4.xml"/>
   </chapter>
 
   <chapter>
     <title>libsoup API Reference</title>
     <xi:include href="xml/soup-address.xml"/>
     <xi:include href="xml/soup-auth.xml"/>
-    <xi:include href="xml/soup-connection.xml"/>
-    <xi:include href="xml/soup-connection-ntlm.xml"/>
+    <xi:include href="xml/soup-auth-domain.xml"/>
+    <xi:include href="xml/soup-auth-domain-basic.xml"/>
+    <xi:include href="xml/soup-auth-domain-digest.xml"/>
+    <xi:include href="xml/soup-logger.xml"/>
     <xi:include href="xml/soup-message.xml"/>
-    <xi:include href="xml/soup-message-filter.xml"/>
-    <xi:include href="xml/soup-server-message.xml"/>
+    <xi:include href="xml/soup-message-headers.xml"/>
+    <xi:include href="xml/soup-message-body.xml"/>
+    <xi:include href="xml/soup-method.xml"/>
     <xi:include href="xml/soup-server.xml"/>
     <xi:include href="xml/soup-session.xml"/>
     <xi:include href="xml/soup-session-async.xml"/>
     <xi:include href="xml/soup-session-sync.xml"/>
-    <xi:include href="xml/soup-soap-message.xml"/>
-    <xi:include href="xml/soup-soap-response.xml"/>
     <xi:include href="xml/soup-socket.xml"/>
     <xi:include href="xml/soup-status.xml"/>
     <xi:include href="xml/soup-uri.xml"/>
-    <xi:include href="xml/soup-xmlrpc-message.xml"/>
-    <xi:include href="xml/soup-xmlrpc-response.xml"/>
+    <xi:include href="xml/soup-value-utils.xml"/>
+    <xi:include href="xml/soup-xmlrpc.xml"/>
     <xi:include href="xml/soup-misc.xml"/>
   </chapter>
 
-  <chapter>
-    <title>libsoup internals</title>
-    <xi:include href="xml/soup-dns.xml"/>
-    <xi:include href="xml/soup-ssl.xml"/>
-  </chapter>
-
   <index></index>
 </book>
index 9828402..06826ca 100644 (file)
@@ -1,9 +1,4 @@
 <FUNCTION>
-<NAME>soup_dns_lookup_get_address</NAME>
-<RETURNS>struct sockaddr *</RETURNS>
-SoupDNSLookup   *lookup
-</FUNCTION>
-<FUNCTION>
 <NAME>soup_address_get_sockaddr</NAME>
 <RETURNS>struct sockaddr *</RETURNS>
 SoupAddress         *addr,
index c9f24c9..afda49f 100644 (file)
@@ -2,26 +2,13 @@
 <FILE>soup-message</FILE>
 <TITLE>SoupMessage</TITLE>
 SoupMessage
-SoupMessageStatus
-SOUP_MESSAGE_IS_STARTING
-SoupTransferEncoding
-SoupOwnership
-SoupDataBuffer
-SoupMessageCallbackFn
 <SUBSECTION>
 soup_message_new
 soup_message_new_from_uri
 soup_message_set_request
 soup_message_set_response
 <SUBSECTION>
-soup_message_add_header
-soup_message_get_header
-soup_message_get_header_list
-soup_message_foreach_header
-soup_message_remove_header
-soup_message_clear_headers
-<SUBSECTION>
-SoupHttpVersion
+SoupHTTPVersion
 soup_message_set_http_version
 soup_message_get_http_version
 soup_message_get_uri
@@ -32,25 +19,17 @@ soup_message_get_flags
 <SUBSECTION>
 soup_message_set_status
 soup_message_set_status_full
-soup_message_add_chunk
-soup_message_add_final_chunk
-soup_message_pop_chunk
 soup_message_is_keepalive
-soup_message_get_request_encoding
-soup_message_get_response_encoding
 <SUBSECTION>
-SoupHandlerPhase
-soup_message_add_handler
 soup_message_add_header_handler
 soup_message_add_status_code_handler
-soup_message_add_status_class_handler
-soup_message_remove_handler
-<SUBSECTION>
-soup_message_send_request
-soup_message_read_request
-soup_message_io_pause
-soup_message_io_unpause
-soup_message_io_stop
+<SUBSECTION>
+SOUP_MESSAGE_METHOD
+SOUP_MESSAGE_URI
+SOUP_MESSAGE_HTTP_VERSION
+SOUP_MESSAGE_FLAGS
+SOUP_MESSAGE_STATUS_CODE
+SOUP_MESSAGE_REASON_PHRASE
 <SUBSECTION Standard>
 SOUP_MESSAGE
 SOUP_IS_MESSAGE
@@ -60,9 +39,21 @@ SOUP_MESSAGE_CLASS
 SOUP_IS_MESSAGE_CLASS
 SOUP_MESSAGE_GET_CLASS
 SoupMessageClass
-SoupMessagePrivate
-SOUP_MESSAGE_GET_PRIVATE
 <SUBSECTION Private>
+soup_message_wrote_informational
+soup_message_wrote_headers
+soup_message_wrote_chunk
+soup_message_wrote_body
+soup_message_got_informational
+soup_message_got_headers
+soup_message_got_chunk
+soup_message_got_body
+soup_message_finished
+soup_message_restarted
+</SECTION>
+
+<SECTION>
+<FILE>soup-method</FILE>
 SOUP_METHOD_OPTIONS
 SOUP_METHOD_GET
 SOUP_METHOD_HEAD
@@ -79,35 +70,63 @@ SOUP_METHOD_MOVE
 SOUP_METHOD_LOCK
 SOUP_METHOD_UNLOCK
 SOUP_METHOD_PATCH
-SoupMethodId
-soup_method_get_id
-soup_message_wrote_informational
-soup_message_wrote_headers
-soup_message_wrote_chunk
-soup_message_wrote_body
-soup_message_got_informational
-soup_message_got_headers
-soup_message_got_chunk
-soup_message_got_body
-soup_message_finished
-soup_message_restarted
-SoupMessageGetHeadersFn
-SoupMessageParseHeadersFn
-soup_message_cleanup_response
-soup_message_io_client
-soup_message_io_in_progress
-soup_message_io_server
-soup_message_run_handlers
-soup_message_send_request_internal
-soup_message_get_auth
-soup_message_get_proxy_auth
-soup_message_set_auth
-soup_message_set_proxy_auth
+</SECTION>
+
+<SECTION>
+<FILE>soup-message-headers</FILE>
+<TITLE>SoupMessageHeaders</TITLE>
+SoupMessageHeaders
+SoupMessageHeadersType
+soup_message_headers_new
+soup_message_headers_free
+<SUBSECTION>
+soup_message_headers_append
+soup_message_headers_replace
+soup_message_headers_remove
+soup_message_headers_clear
+soup_message_headers_get
+SoupMessageHeadersForeachFunc
+soup_message_headers_foreach
+<SUBSECTION>
+SoupEncoding
+soup_message_headers_get_encoding
+soup_message_headers_set_encoding
+soup_message_headers_get_content_length
+soup_message_headers_set_content_length
+<SUBSECTION>
+SoupExpectation
+soup_message_headers_get_expectations
+soup_message_headers_set_expectations
+</SECTION>
+
+<SECTION>
+<FILE>soup-message-body</FILE>
+<TITLE>SoupMessageBody</TITLE>
+SoupBuffer
+SoupMemoryUse
+soup_buffer_new
+soup_buffer_new_subbuffer
+soup_buffer_copy
+soup_buffer_free
+<SUBSECTION>
+SoupMessageBody
+soup_message_body_new
+soup_message_body_free
+<SUBSECTION>
+soup_message_body_append
+soup_message_body_append_buffer
+soup_message_body_truncate
+soup_message_body_complete
+soup_message_body_flatten
+soup_message_body_get_chunk
+<SUBSECTION Standard>
+SOUP_TYPE_BUFFER
+soup_buffer_get_type
 </SECTION>
 
 <SECTION>
 <FILE>soup-status</FILE>
-SoupStatusClass
+SOUP_STATUS_IS_TRANSPORT_ERROR
 SOUP_STATUS_IS_INFORMATIONAL
 SOUP_STATUS_IS_SUCCESSFUL
 SOUP_STATUS_IS_REDIRECTION
@@ -115,38 +134,48 @@ SOUP_STATUS_IS_CLIENT_ERROR
 SOUP_STATUS_IS_SERVER_ERROR
 SoupKnownStatusCode
 soup_status_get_phrase
-<SUBSECTION Standard>
-SOUP_STATUS_IS_TRANSPORT_ERROR
+<SUBSECTION>
+SOUP_HTTP_ERROR
+<SUBSECTION Private>
+soup_http_error_quark
 </SECTION>
 
 <SECTION>
 <FILE>soup-server</FILE>
 <TITLE>SoupServer</TITLE>
 SoupServer
-SoupServerContext
-SoupServerCallbackFn
-SoupServerUnregisterFn
 soup_server_new
-soup_server_get_protocol
+soup_server_is_https
 soup_server_get_port
 soup_server_get_listener
 soup_server_run
 soup_server_run_async
 soup_server_quit
+soup_server_get_async_context
 <SUBSECTION>
-SoupServerHandler
+SoupServerCallback
 soup_server_add_handler
 soup_server_remove_handler
-soup_server_get_handler
-soup_server_list_handlers
-soup_server_context_get_client_address
-soup_server_context_get_client_host
+<SUBSECTION>
+SoupClientContext
+soup_client_context_get_socket
+soup_client_context_get_address
+soup_client_context_get_host
+soup_client_context_get_auth_domain
+soup_client_context_get_auth_user
+<SUBSECTION>
+soup_server_add_auth_domain
+soup_server_remove_auth_domain
+<SUBSECTION>
+soup_server_pause_message
+soup_server_unpause_message
 <SUBSECTION>
 SOUP_SERVER_PORT
 SOUP_SERVER_INTERFACE
 SOUP_SERVER_SSL_CERT_FILE
 SOUP_SERVER_SSL_KEY_FILE
 SOUP_SERVER_ASYNC_CONTEXT
+SOUP_SERVER_RAW_PATHS
 <SUBSECTION Standard>
 SOUP_SERVER
 SOUP_IS_SERVER
@@ -156,18 +185,87 @@ SOUP_SERVER_CLASS
 SOUP_IS_SERVER_CLASS
 SOUP_SERVER_GET_CLASS
 SoupServerClass
-<SUBSECTION Private>
-soup_server_auth_check_passwd
-soup_server_auth_context_challenge
-soup_server_auth_free
-soup_server_auth_get_user
-soup_server_auth_new
-SoupServerAuthBasic
-SoupServerAuthCallbackFn
-SoupServerAuthContext
-SoupServerAuthDigest
-SoupAuthType
-SoupDigestAlgorithm
+SOUP_TYPE_CLIENT_CONTEXT
+soup_client_context_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain</FILE>
+<TITLE>SoupAuthDomain</TITLE>
+SoupAuthDomain
+<SUBSECTION>
+soup_auth_domain_add_path
+soup_auth_domain_remove_path
+SoupAuthDomainFilter
+soup_auth_domain_set_filter
+soup_auth_domain_get_realm
+<SUBSECTION>
+soup_auth_domain_covers
+soup_auth_domain_accepts
+soup_auth_domain_challenge
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_REALM
+SOUP_AUTH_DOMAIN_PROXY
+SOUP_AUTH_DOMAIN_ADD_PATH
+SOUP_AUTH_DOMAIN_REMOVE_PATH
+SOUP_AUTH_DOMAIN_FILTER
+SOUP_AUTH_DOMAIN_FILTER_DATA
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN
+SOUP_IS_AUTH_DOMAIN
+SOUP_TYPE_AUTH_DOMAIN
+soup_auth_domain_get_type
+SOUP_AUTH_DOMAIN_CLASS
+SOUP_IS_AUTH_DOMAIN_CLASS
+SOUP_AUTH_DOMAIN_GET_CLASS
+SoupAuthDomainClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain-basic</FILE>
+<TITLE>SoupAuthDomainBasic</TITLE>
+SoupAuthDomainBasic
+soup_auth_domain_basic_new
+<SUBSECTION>
+SoupAuthDomainBasicAuthCallback
+soup_auth_domain_basic_set_auth_callback
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK
+SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN_BASIC
+SOUP_IS_AUTH_DOMAIN_BASIC
+SOUP_TYPE_AUTH_DOMAIN_BASIC
+soup_auth_domain_basic_get_type
+SOUP_AUTH_DOMAIN_BASIC_CLASS
+SOUP_IS_AUTH_DOMAIN_BASIC_CLASS
+SOUP_AUTH_DOMAIN_BASIC_GET_CLASS
+SoupAuthDomainBasicClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain-digest</FILE>
+<TITLE>SoupAuthDomainDigest</TITLE>
+SoupAuthDomainDigest
+soup_auth_domain_digest_new
+<SUBSECTION>
+SoupAuthDomainDigestAuthCallback
+soup_auth_domain_digest_set_auth_callback
+soup_auth_domain_digest_encode_password
+<SUBSECTION>
+soup_auth_domain_digest_evil_check_password
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK
+SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN_DIGEST
+SOUP_IS_AUTH_DOMAIN_DIGEST
+SOUP_TYPE_AUTH_DOMAIN_DIGEST
+soup_auth_domain_digest_get_type
+SOUP_AUTH_DOMAIN_DIGEST_CLASS
+SOUP_IS_AUTH_DOMAIN_DIGEST_CLASS
+SOUP_AUTH_DOMAIN_DIGEST_GET_CLASS
+SoupAuthDomainDigestClass
 </SECTION>
 
 <SECTION>
@@ -179,10 +277,11 @@ SOUP_ADDRESS_ANY_PORT
 soup_address_new
 soup_address_new_from_sockaddr
 soup_address_new_any
+<SUBSECTION>
 SoupAddressCallback
 soup_address_resolve_async
-soup_address_resolve_async_full
 soup_address_resolve_sync
+<SUBSECTION>
 soup_address_get_name
 soup_address_get_sockaddr
 soup_address_get_physical
@@ -196,103 +295,27 @@ SOUP_ADDRESS_CLASS
 SOUP_IS_ADDRESS_CLASS
 SOUP_ADDRESS_GET_CLASS
 SoupAddressClass
-</SECTION>
-
-<SECTION>
-<FILE>soup-soap-message</FILE>
-SoupSoapMessage
-<TITLE>SoupSoapMessage</TITLE>
-soup_soap_message_new
-soup_soap_message_new_from_uri
-soup_soap_message_start_envelope
-soup_soap_message_end_envelope
-soup_soap_message_start_body
-soup_soap_message_end_body
-soup_soap_message_start_element
-soup_soap_message_end_element
-soup_soap_message_start_fault
-soup_soap_message_end_fault
-soup_soap_message_start_fault_detail
-soup_soap_message_end_fault_detail
-soup_soap_message_start_header
-soup_soap_message_end_header
-soup_soap_message_start_header_element
-soup_soap_message_end_header_element
-soup_soap_message_write_int
-soup_soap_message_write_double
-soup_soap_message_write_base64
-soup_soap_message_write_time
-soup_soap_message_write_string
-soup_soap_message_write_buffer
-soup_soap_message_set_element_type
-soup_soap_message_set_null
-soup_soap_message_add_attribute
-soup_soap_message_add_namespace
-soup_soap_message_set_default_namespace
-soup_soap_message_set_encoding_style
-soup_soap_message_reset
-soup_soap_message_persist
-soup_soap_message_get_namespace_prefix
-soup_soap_message_get_xml_doc
-soup_soap_message_parse_response
-<SUBSECTION Standard>
-SOUP_IS_SOAP_MESSAGE
-SOUP_IS_SOAP_MESSAGE_CLASS
-SOUP_SOAP_MESSAGE
-SOUP_SOAP_MESSAGE_CLASS
-SOUP_SOAP_MESSAGE_GET_CLASS
-SOUP_TYPE_SOAP_MESSAGE
-SoupSoapMessageClass
-soup_soap_message_get_type
-</SECTION>
-
-<SECTION>
-<FILE>soup-soap-response</FILE>
-<TITLE>SoupSoapResponse</TITLE>
-SoupSoapResponse
-SoupSoapParameter
-soup_soap_response_new
-soup_soap_response_new_from_string
-soup_soap_response_set_method_name
-soup_soap_parameter_get_first_child
-soup_soap_parameter_get_first_child_by_name
-soup_soap_parameter_get_int_value
-soup_soap_parameter_get_name
-soup_soap_parameter_get_next_child
-soup_soap_parameter_get_next_child_by_name
-soup_soap_parameter_get_property
-soup_soap_parameter_get_string_value
-soup_soap_response_from_string
-soup_soap_response_get_first_parameter
-soup_soap_response_get_first_parameter_by_name
-soup_soap_response_get_method_name
-soup_soap_response_get_next_parameter
-soup_soap_response_get_next_parameter_by_name
-soup_soap_response_get_parameters
-<SUBSECTION Standard>
-SOUP_IS_SOAP_RESPONSE
-SOUP_IS_SOAP_RESPONSE_CLASS
-SOUP_SOAP_RESPONSE
-SOUP_SOAP_RESPONSE_CLASS
-SOUP_SOAP_RESPONSE_GET_CLASS
-SOUP_TYPE_SOAP_RESPONSE
-SoupSoapResponseClass
-soup_soap_response_get_type
+<SUBSECTION Private>
+AF_INET6
 </SECTION>
 
 <SECTION>
 <FILE>soup-session</FILE>
 <TITLE>SoupSession</TITLE>
 SoupSession
+<SUBSECTION>
+SoupSessionCallback
 soup_session_queue_message
 soup_session_requeue_message
 soup_session_send_message
-soup_session_abort
-soup_session_add_filter
 soup_session_cancel_message
-soup_session_get_connection
-soup_session_remove_filter
-soup_session_try_prune_connection
+soup_session_abort
+<SUBSECTION>
+soup_session_pause_message
+soup_session_unpause_message
+<SUBSECTION>
+soup_session_get_async_context
+<SUBSECTION>
 SOUP_SESSION_PROXY_URI
 SOUP_SESSION_MAX_CONNS
 SOUP_SESSION_MAX_CONNS_PER_HOST
@@ -309,17 +332,6 @@ SOUP_SESSION_GET_CLASS
 SOUP_TYPE_SESSION
 SoupSessionClass
 soup_session_get_type
-<SUBSECTION Private>
-SoupMessageQueue
-SoupMessageQueueIter
-soup_message_queue_append
-soup_message_queue_destroy
-soup_message_queue_first
-soup_message_queue_free_iter
-soup_message_queue_new
-soup_message_queue_next
-soup_message_queue_remove
-soup_message_queue_remove_message
 </SECTION>
 
 <SECTION>
@@ -360,16 +372,27 @@ soup_session_sync_get_type
 <FILE>soup-auth</FILE>
 <TITLE>SoupAuth</TITLE>
 SoupAuth
-SoupAuthBasic
-SoupAuthDigest
-soup_auth_new_from_header_list
+soup_auth_new
+soup_auth_update
+<SUBSECTION>
+soup_auth_is_for_proxy
 soup_auth_get_scheme_name
+soup_auth_get_host
 soup_auth_get_realm
+soup_auth_get_info
+<SUBSECTION>
 soup_auth_authenticate
 soup_auth_is_authenticated
+<SUBSECTION>
 soup_auth_get_authorization
 soup_auth_get_protection_space
 soup_auth_free_protection_space
+<SUBSECTION>
+SOUP_AUTH_SCHEME_NAME
+SOUP_AUTH_REALM
+SOUP_AUTH_HOST
+SOUP_AUTH_IS_FOR_PROXY
+SOUP_AUTH_IS_AUTHENTICATED
 <SUBSECTION Standard>
 SOUP_AUTH
 SOUP_IS_AUTH
@@ -379,22 +402,6 @@ SOUP_AUTH_CLASS
 SOUP_IS_AUTH_CLASS
 SOUP_AUTH_GET_CLASS
 SoupAuthClass
-SOUP_AUTH_BASIC
-SOUP_IS_AUTH_BASIC
-SOUP_TYPE_AUTH_BASIC
-soup_auth_basic_get_type
-SOUP_AUTH_BASIC_CLASS
-SOUP_IS_AUTH_BASIC_CLASS
-SOUP_AUTH_BASIC_GET_CLASS
-SoupAuthBasicClass
-SOUP_AUTH_DIGEST
-SOUP_IS_AUTH_DIGEST
-SOUP_TYPE_AUTH_DIGEST
-soup_auth_digest_get_type
-SOUP_AUTH_DIGEST_CLASS
-SOUP_IS_AUTH_DIGEST_CLASS
-SOUP_AUTH_DIGEST_GET_CLASS
-SoupAuthDigestClass
 </SECTION>
 
 <SECTION>
@@ -402,31 +409,38 @@ SoupAuthDigestClass
 <TITLE>SoupSocket</TITLE>
 SoupSocket
 soup_socket_new
-soup_socket_connect
+<SUBSECTION>
+SoupSocketCallback
+soup_socket_connect_async
+soup_socket_connect_sync
+<SUBSECTION>
 soup_socket_listen
+<SUBSECTION>
 soup_socket_start_ssl
 soup_socket_start_proxy_ssl
+soup_socket_is_ssl
+<SUBSECTION>
 soup_socket_disconnect
 soup_socket_is_connected
-SoupSocketCallback
-SoupSocketListenerCallback
-soup_socket_client_new_async
-soup_socket_client_new_sync
-soup_socket_server_new
+<SUBSECTION>
 soup_socket_get_local_address
 soup_socket_get_remote_address
+<SUBSECTION>
 SoupSocketIOStatus
 soup_socket_read
 soup_socket_read_until
 soup_socket_write
+<SUBSECTION>
+SOUP_SSL_ERROR
+SoupSSLError
+<SUBSECTION>
+SOUP_SOCKET_LOCAL_ADDRESS
+SOUP_SOCKET_REMOTE_ADDRESS
 SOUP_SOCKET_FLAG_NONBLOCKING
-SOUP_SOCKET_FLAG_NODELAY
-SOUP_SOCKET_FLAG_REUSEADDR
-SOUP_SOCKET_FLAG_CLOEXEC
-SOUP_SOCKET_TIMEOUT
 SOUP_SOCKET_IS_SERVER
 SOUP_SOCKET_SSL_CREDENTIALS
 SOUP_SOCKET_ASYNC_CONTEXT
+SOUP_SOCKET_TIMEOUT
 <SUBSECTION Standard>
 SOUP_SOCKET
 SOUP_IS_SOCKET
@@ -436,260 +450,154 @@ SOUP_SOCKET_CLASS
 SOUP_IS_SOCKET_CLASS
 SOUP_SOCKET_GET_CLASS
 SoupSocketClass
-</SECTION>
-
-<SECTION>
-<FILE>soup-connection-ntlm</FILE>
-<TITLE>SoupConnectionNTLM</TITLE>
-SoupConnectionNTLM
-<SUBSECTION Standard>
-SOUP_CONNECTION_NTLM
-SOUP_IS_CONNECTION_NTLM
-SOUP_TYPE_CONNECTION_NTLM
-soup_connection_ntlm_get_type
-SOUP_CONNECTION_NTLM_CLASS
-SOUP_IS_CONNECTION_NTLM_CLASS
-SOUP_CONNECTION_NTLM_GET_CLASS
-SoupConnectionNTLMClass
-</SECTION>
-
-<SECTION>
-<FILE>soup-connection</FILE>
-<TITLE>SoupConnection</TITLE>
-SoupConnection
-soup_connection_new
-SoupConnectionCallback
-soup_connection_connect_async
-soup_connection_connect_sync
-soup_connection_disconnect
-soup_connection_is_in_use
-soup_connection_last_used
-soup_connection_send_request
-soup_connection_authenticate
-soup_connection_reauthenticate
-soup_connection_release
-soup_connection_reserve
-SOUP_CONNECTION_ORIGIN_URI
-SOUP_CONNECTION_PROXY_URI
-SOUP_CONNECTION_SSL_CREDENTIALS
-SOUP_CONNECTION_MESSAGE_FILTER
-SOUP_CONNECTION_ASYNC_CONTEXT
-SOUP_CONNECTION_TIMEOUT
-<SUBSECTION Standard>
-SOUP_CONNECTION
-SOUP_IS_CONNECTION
-SOUP_TYPE_CONNECTION
-soup_connection_get_type
-SOUP_CONNECTION_CLASS
-SOUP_IS_CONNECTION_CLASS
-SOUP_CONNECTION_GET_CLASS
-SoupConnectionClass
-</SECTION>
-
-<SECTION>
-<FILE>soup-server-message</FILE>
-<TITLE>SoupServerMessage</TITLE>
-SoupServerMessage
-soup_server_message_new
-soup_server_message_get_server
-soup_server_message_set_encoding
-soup_server_message_get_encoding
-soup_server_message_start
-soup_server_message_is_started
-soup_server_message_finish
-soup_server_message_is_finished
-<SUBSECTION Standard>
-SOUP_SERVER_MESSAGE
-SOUP_IS_SERVER_MESSAGE
-SOUP_TYPE_SERVER_MESSAGE
-soup_server_message_get_type
-SOUP_SERVER_MESSAGE_CLASS
-SOUP_IS_SERVER_MESSAGE_CLASS
-SOUP_SERVER_MESSAGE_GET_CLASS
-SoupServerMessageClass
-</SECTION>
-
-<SECTION>
-<FILE>soup-dns</FILE>
-soup_dns_init
-soup_dns_ntop
-SoupDNSLookup
-soup_dns_lookup_name
-soup_dns_lookup_address
-soup_dns_lookup_resolve
-SoupDNSCallback
-soup_dns_lookup_resolve_async
-soup_dns_lookup_cancel
-soup_dns_lookup_get_hostname
-soup_dns_lookup_get_address
-soup_dns_lookup_free
-</SECTION>
-
-<SECTION>
-<FILE>soup-ssl</FILE>
-soup_ssl_supported
-<SUBSECTION>
-SoupSSLType
-SoupSSLCredentials
-soup_ssl_get_client_credentials
-soup_ssl_free_client_credentials
-soup_ssl_get_server_credentials
-soup_ssl_free_server_credentials
-soup_ssl_wrap_iochannel
-SOUP_SSL_ERROR
+<SUBSECTION Private>
 soup_ssl_error_quark
-SoupSocketError
 </SECTION>
 
 <SECTION>
 <FILE>soup-uri</FILE>
-<TITLE>SoupUri</TITLE>
-SoupUri
-SoupProtocol
-SOUP_PROTOCOL_HTTP
-SOUP_PROTOCOL_HTTPS
+<TITLE>SoupURI</TITLE>
+SoupURI
 soup_uri_new_with_base
 soup_uri_new
 soup_uri_to_string
+<SUBSECTION>
 soup_uri_copy
-soup_uri_copy_root
 soup_uri_equal
 soup_uri_free
+<SUBSECTION>
 soup_uri_encode
 soup_uri_decode
+soup_uri_normalize
+<SUBSECTION>
+SOUP_URI_SCHEME_HTTP
+SOUP_URI_SCHEME_HTTPS
 soup_uri_uses_default_port
-</SECTION>
-
-<SECTION>
-<FILE>soup-message-filter</FILE>
-<TITLE>SoupMessageFilter</TITLE>
-soup_message_filter_setup_message
+<SUBSECTION>
+soup_uri_set_scheme
+soup_uri_set_user
+soup_uri_set_password
+soup_uri_set_host
+soup_uri_set_port
+soup_uri_set_path
+soup_uri_set_query
+soup_uri_set_query_from_form
+soup_uri_set_fragment
 <SUBSECTION Standard>
-SOUP_IS_MESSAGE_FILTER
-SOUP_IS_MESSAGE_FILTER_CLASS
-SOUP_MESSAGE_FILTER
-SOUP_MESSAGE_FILTER_CLASS
-SOUP_MESSAGE_FILTER_GET_CLASS
-SOUP_MESSAGE_FILTER_GET_CLASS
-SOUP_TYPE_MESSAGE_FILTER
-SoupMessageFilterClass
-soup_message_filter_get_type
+SOUP_TYPE_URI
+soup_uri_get_type
 </SECTION>
 
 <SECTION>
 <FILE>soup-misc</FILE>
 <TITLE>Soup Miscellaneous Utilities</TITLE>
-soup_base64_decode
-soup_base64_encode
-soup_base64_decode_step
-soup_base64_encode_step
-soup_base64_encode_close
-<SUBSECTION>
-soup_add_idle
-soup_add_io_watch
-soup_add_timeout
-soup_signal_connect_once
+SoupDate
+SoupDateFormat
+soup_date_new
+soup_date_new_from_string
+soup_date_new_from_time_t
+soup_date_new_from_now
+soup_date_to_string
+soup_date_free
 <SUBSECTION>
-soup_date_parse
-soup_date_iso8601_parse
-soup_date_generate
-soup_gmtime
-soup_mktime_utc
+soup_form_decode_urlencoded
+soup_form_encode_urlencoded
+soup_form_encode_urlencoded_list
 <SUBSECTION>
-soup_header_param_copy_token
-soup_header_param_decode_token
-soup_header_param_destroy_hash
-soup_header_param_parse_list
 soup_headers_parse_request
 soup_headers_parse_response
 soup_headers_parse_status_line
 <SUBSECTION>
+soup_header_parse_list
+soup_header_parse_quality_list
+soup_header_free_list
+soup_header_contains
+soup_header_parse_param_list
+soup_header_free_param_list
+<SUBSECTION>
 soup_str_case_equal
 soup_str_case_hash
 <SUBSECTION>
-soup_xml_real_node
+soup_add_idle
+soup_add_io_watch
+soup_add_timeout
+<SUBSECTION>
+soup_ssl_supported
+<SUBSECTION Private>
+soup_date_copy
+soup_signal_connect_once
+SOUP_TYPE_DATE
+soup_date_get_type
 </SECTION>
 
 <SECTION>
-<FILE>soup-xmlrpc-message</FILE>
-<TITLE>SoupXmlrpcMessage</TITLE>
-SoupXmlrpcMessage
-soup_xmlrpc_message_new
-soup_xmlrpc_message_new_from_uri
-soup_xmlrpc_message_from_string
-<SUBSECTION>
-soup_xmlrpc_message_start_call
-soup_xmlrpc_message_end_call
-soup_xmlrpc_message_start_param
-soup_xmlrpc_message_end_param
-<SUBSECTION>
-soup_xmlrpc_message_write_int
-soup_xmlrpc_message_write_boolean
-soup_xmlrpc_message_write_string
-soup_xmlrpc_message_write_double
-soup_xmlrpc_message_write_datetime
-soup_xmlrpc_message_write_base64
-<SUBSECTION>
-soup_xmlrpc_message_start_struct
-soup_xmlrpc_message_end_struct
-soup_xmlrpc_message_start_member
-soup_xmlrpc_message_end_member
-<SUBSECTION>
-soup_xmlrpc_message_start_array
-soup_xmlrpc_message_end_array
-<SUBSECTION>
-soup_xmlrpc_message_to_string
-soup_xmlrpc_message_persist
-<SUBSECTION>
-soup_xmlrpc_message_parse_response
-<SUBSECTION Standard>
-SOUP_IS_XMLRPC_MESSAGE
-SOUP_IS_XMLRPC_MESSAGE_CLASS
-SOUP_TYPE_XMLRPC_MESSAGE
-SOUP_XMLRPC_MESSAGE
-SOUP_XMLRPC_MESSAGE_CLASS
-SOUP_XMLRPC_MESSAGE_GET_CLASS
-SoupXmlrpcMessageClass
-soup_xmlrpc_message_get_type
+<FILE>soup-xmlrpc</FILE>
+<TITLE>XMLRPC Support</TITLE>
+<SUBSECTION>
+soup_xmlrpc_build_method_call
+soup_xmlrpc_request_new
+soup_xmlrpc_parse_method_response
+soup_xmlrpc_extract_method_response
+<SUBSECTION>
+soup_xmlrpc_parse_method_call
+soup_xmlrpc_extract_method_call
+soup_xmlrpc_build_method_response
+soup_xmlrpc_build_fault
+soup_xmlrpc_set_response
+soup_xmlrpc_set_fault
+<SUBSECTION>
+SOUP_XMLRPC_ERROR
+SoupXMLRPCError
+SOUP_XMLRPC_FAULT
+<SUBSECTION Private>
+soup_xmlrpc_error_quark
+soup_xmlrpc_fault_quark
 </SECTION>
 
 <SECTION>
-<FILE>soup-xmlrpc-response</FILE>
-<TITLE>SoupXmlrpcResponse</TITLE>
-SoupXmlrpcResponse
-soup_xmlrpc_response_new
-soup_xmlrpc_response_new_from_string
-soup_xmlrpc_response_from_string
-soup_xmlrpc_response_to_string
-<SUBSECTION>
-SoupXmlrpcValue
-SoupXmlrpcValueType
-soup_xmlrpc_response_is_fault
-soup_xmlrpc_response_get_value
-<SUBSECTION>
-soup_xmlrpc_value_get_type
-soup_xmlrpc_value_get_int
-soup_xmlrpc_value_get_double
-soup_xmlrpc_value_get_boolean
-soup_xmlrpc_value_get_string
-soup_xmlrpc_value_get_datetime
-soup_xmlrpc_value_get_base64
-soup_xmlrpc_value_get_struct
-<SUBSECTION>
-SoupXmlrpcValueArrayIterator
-soup_xmlrpc_value_array_get_iterator
-soup_xmlrpc_value_array_iterator_prev
-soup_xmlrpc_value_array_iterator_next
-soup_xmlrpc_value_array_iterator_get_value
-<SUBSECTION>
-soup_xmlrpc_value_dump
+<FILE>soup-value-utils</FILE>
+<TITLE>GValue Support</TITLE>
+soup_value_hash_new
+soup_value_hash_insert_value
+soup_value_hash_insert
+soup_value_hash_lookup
+<SUBSECTION>
+soup_value_array_from_args
+soup_value_array_to_args
+soup_value_array_insert
+soup_value_array_append
+soup_value_array_get_nth
+<SUBSECTION>
+SOUP_VALUE_SETV
+SOUP_VALUE_GETV
+<SUBSECTION>
+SOUP_TYPE_BYTE_ARRAY
+<SUBSECTION Private>
+soup_byte_array_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-logger</FILE>
+<TITLE>SoupLogger</TITLE>
+SoupLogger
+SoupLoggerLogLevel
+soup_logger_new
+soup_logger_attach
+soup_logger_detach
+<SUBSECTION>
+SoupLoggerFilter
+soup_logger_set_request_filter
+soup_logger_set_response_filter
+<SUBSECTION>
+SoupLoggerPrinter
+soup_logger_set_printer
 <SUBSECTION Standard>
-SOUP_IS_XMLRPC_RESPONSE
-SOUP_IS_XMLRPC_RESPONSE_CLASS
-SOUP_TYPE_XMLRPC_RESPONSE
-SOUP_XMLRPC_RESPONSE
-SOUP_XMLRPC_RESPONSE_CLASS
-SOUP_XMLRPC_RESPONSE_GET_CLASS
-SoupXmlrpcResponseClass
-soup_xmlrpc_response_get_type
+SoupLoggerClass
+soup_logger_get_type
+SOUP_IS_LOGGER
+SOUP_IS_LOGGER_CLASS
+SOUP_LOGGER
+SOUP_LOGGER_CLASS
+SOUP_LOGGER_GET_CLASS
+SOUP_TYPE_LOGGER
 </SECTION>
index 7c6a4d2..9effee1 100644 (file)
@@ -1,37 +1,14 @@
-#include <libsoup/soup-address.h>
-#include <libsoup/soup-auth.h>
-#include <libsoup/soup-auth-basic.h>
-#include <libsoup/soup-auth-digest.h>
-#include <libsoup/soup-connection.h>
-#include <libsoup/soup-connection-ntlm.h>
-#include <libsoup/soup-message.h>
-#include <libsoup/soup-message-filter.h>
-#include <libsoup/soup-server-message.h>
-#include <libsoup/soup-server.h>
-#include <libsoup/soup-session.h>
-#include <libsoup/soup-session-sync.h>
-#include <libsoup/soup-session-async.h>
-#include <libsoup/soup-soap-message.h>
-#include <libsoup/soup-soap-response.h>
-#include <libsoup/soup-socket.h>
-#include <libsoup/soup-xmlrpc-message.h>
-#include <libsoup/soup-xmlrpc-response.h>
+#include <libsoup/soup.h>
 
 soup_address_get_type
 soup_auth_get_type
-soup_auth_basic_get_type
-soup_auth_digest_get_type
-soup_connection_get_type
-soup_connection_ntlm_get_type
+soup_auth_domain_get_type
+soup_auth_domain_basic_get_type
+soup_auth_domain_digest_get_type
 soup_message_get_type
-soup_message_filter_get_type
-soup_server_message_get_type
 soup_server_get_type
 soup_session_get_type
 soup_session_sync_get_type
 soup_session_async_get_type
-soup_soap_message_get_type
-soup_soap_response_get_type
 soup_socket_get_type
-soup_xmlrpc_message_get_type
-soup_xmlrpc_response_get_type
+
diff --git a/docs/reference/porting-2.2-2.4.xml b/docs/reference/porting-2.2-2.4.xml
new file mode 100644 (file)
index 0000000..4e01880
--- /dev/null
@@ -0,0 +1,878 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
+               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-porting-2.2-2.4">
+<refmeta>
+<refentrytitle>libsoup 2.2 to 2.4 porting notes</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Porting notes</refname><refpurpose>Notes on porting from libsoup 2.2 to 2.4</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title>Overview</title>
+
+<para>
+After many API-compatible releases in the 2.2 series,
+<application>libsoup</application> has now changed its API and bumped
+its version number to 2.4. Changes were made for a variety of reasons:
+</para>
+
+<itemizedlist>
+    <listitem><para>
+       To fix bugs and add features that couldn't be done ABI-compatibly.
+    </para></listitem>
+
+    <listitem><para>
+       To make it easier to generate bindings for libsoup for
+       languages other than C.
+    </para></listitem>
+
+    <listitem><para>
+       To clean up ugly/confusing old APIs
+    </para></listitem>
+
+    <listitem><para>
+       To be more glib/gobject/gtk-like in general.
+    </para></listitem>
+</itemizedlist>
+
+</refsect2>
+
+<refsect2>
+<title>SoupMessage</title>
+
+<para>
+<link linkend="SoupMessage"><type>SoupMessage</type></link> has had a
+number of API changes made, mostly to increase its
+language-bindability.
+</para>
+
+<refsect3>
+<title>SoupMessageHeaders</title>
+
+<para>
+    <type>SoupMessage</type>'s
+    <structfield>request_headers</structfield> and
+    <structfield>response_headers</structfield> fields are now an
+    opaque type (<link
+    linkend="SoupMessageHeaders"><type>SoupMessageHeaders</type></link>)
+    rather than being <type>GHashTables</type>. The method names have
+    changed slightly to reflect this:
+</para>
+
+<variablelist>
+    <varlistentry>
+       <term><function>soup_message_add_header</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-message-headers-append"><function>soup_message_headers_append</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_get_header</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_foreach_header</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_remove_header</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-message-headers-remove"><function>soup_message_headers_remove</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_clear_headers</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-message-headers-clear"><function>soup_message_headers_append</function></link>
+       </para></listitem>
+    </varlistentry>
+</variablelist>
+
+<para>
+    <function>soup_message_get_header_list</function> has no equivalent;
+    if multiple copies of a header are present,
+    <function>soup_message_headers_get</function> will return all of
+    them, concatenated together and separated by commas; RFC 2616 says
+    that the two forms (multiple headers, and a single header with
+    comma-separated values) are equivalent; this change to libsoup
+    ensures that applications will treat them as equivalent.
+</para>
+
+<para>
+    In addition, certain important header fields now have
+    dedicated get/set methods:
+</para>
+
+<itemizedlist>
+    <listitem><para>
+       <link linkend="soup-message-headers-get-encoding"><function>soup_message_headers_get_encoding</function></link> / <link linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+    </para></listitem>
+
+    <listitem><para>
+       <link linkend="soup-message-headers-get-content-length"><function>soup_message_headers_get_content_length</function></link> / <link linkend="soup-message-headers-set-content-length"><function>soup_message_headers_set_content_length</function></link>
+    </para></listitem>
+
+    <listitem><para>
+       <link linkend="soup-message-headers-get-expectations"><function>soup_message_headers_get_expectations</function></link> / <link linkend="soup-message-headers-set-expectations"><function>soup_message_headers_set_expectations</function></link>
+    </para></listitem>
+</itemizedlist>
+
+<para>
+    (<literal>soup_message_headers_set_expectation(msg,&#160;SOUP_EXPECTATION_CONTINUE)</literal>
+    replaces the <literal>SOUP_MESSAGE_EXPECT_CONTINUE</literal>
+    message flag).
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>SoupMessageBody</title>
+
+<para>
+    Similarly, the <structfield>request_body</structfield> and
+    <structfield>response</structfield> fields (renamed from
+    <structfield>request</structfield> and <structfield>response</structfield>) are
+    now a new type, <link
+    linkend="SoupMessageBody"><type>SoupMessageBody</type></link>,
+    implemented in terms of <link
+    linkend="SoupBuffer"><type>SoupBuffer</type></link>, a refcounted
+    memory buffer type with clearer semantics than the old
+    <type>SoupDataBuffer</type>/<type>SoupOwnership</type>.
+</para>
+
+<variablelist>
+    <varlistentry>
+       <term><literal>SOUP_BUFFER_STATIC</literal></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="SOUP-MEMORY-STATIC:CAPS"><literal>SOUP_MEMORY_STATIC</literal></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>SOUP_BUFFER_SYSTEM_OWNED</literal></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="SOUP-MEMORY-TAKE:CAPS"><literal>SOUP_MEMORY_TAKE</literal></link>
+           (meaning <application>libsoup</application>
+           should take ownership of the memory from your).
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>SOUP_BUFFER_USER_OWNED</literal></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="SOUP-MEMORY-COPY:CAPS"><literal>SOUP_MEMORY_COPY</literal></link>
+           (meaning <application>libsoup</application>
+           should make a copy of the memory, because you
+           can't make any guarantees about how long it will
+           last.)
+       </para></listitem>
+    </varlistentry>
+</variablelist>
+
+<para>
+    A fourth <type>SoupMemoryUse</type> value is also available: <link
+    linkend="SOUP-MEMORY-TEMPORARY:CAPS"><literal>SOUP_MEMORY_TEMPORARY</literal></link>,
+    which helps to avoid extra copies in some cases.
+    <literal>SOUP_MEMORY_TEMPORARY</literal> means that the memory
+    will last at least as long as the object you are handing it to (a
+    <type>SoupBuffer</type>, <type>SoupMessageBody</type>, or
+    <type>SoupMessage</type>), and so doesn't need to be copied right
+    away, but that if anyone makes a copy of the buffer,
+    <application>libsoup</application> needs to make a new copy of the
+    memory for them at that point, since the original pointer may not
+    remain valid for the lifetime of the new copy.
+</para>
+
+<para>
+    (In the future, there may be additional <type>SoupBuffer</type>
+    and <type>SoupMessageBody</type> methods to work directly with
+    mmapped memory, splicing to file descriptors, etc.)
+</para>
+
+<para>
+    <link
+       linkend="soup-message-set-request"><function>soup_message_set_request</function></link>
+    and <link
+    linkend="soup-message-set-response"><function>soup_message_set_response</function></link>
+    still work roughly like they used to.
+</para>
+
+<para>
+    Unlike the old <structfield>request</structfield> and
+    <structfield>response</structfield> fields, the new
+    <structfield>request_body</structfield> and
+    <structfield>response_body</structfield> fields are not guaranteed
+    to be filled in at all times. (In particular, the
+    <structfield>response_body</structfield> is not filled in until it
+    has been fully read, although you can use <link
+    linkend="soup-message-body-get-chunk"><function>soup_message_body_get_chunk</function></link>
+    to iterate through the chunks before that point if you need to.)
+</para>
+
+<para>
+    When <structfield>request_body</structfield> and
+    <structfield>response_body</structfield> <emphasis>are</emphasis>
+    filled in, they are <literal>'\0'</literal>-terminated for your
+    processing convenience. (The terminating 0 byte is not included in
+    their length.)
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Chunked encoding</title>
+
+<para>
+    The prototype of the <link
+    linkend="SoupMessage-got-chunk"><literal>SoupMessage::got_chunk</literal></link>
+    signal has been changed; it now includes the chunk as a
+    <type>SoupBuffer</type> parameter (rather than storing the chunk
+    data in <literal>msg->response</literal> as in 2.2). <link
+    linkend="SoupMessageFlags"><literal>SOUP_MESSAGE_OVERWRITE_CHUNKS</literal></link>
+    is now somewhat poorly named, but still has essentially the same
+    semantics: if you set it, each chunk will be discarded after it is
+    read, and <literal>msg->response_body</literal> will not be filled
+    in with the complete response at the end of message processing.
+</para>
+
+<para>
+    The API for sending chunked responses from a
+    <type>SoupServer</type> is also slightly different now:
+</para>
+
+<variablelist>
+    <varlistentry>
+       <term><function>soup_server_message_set_encoding</function></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_add_chunk</function></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
+           or <link
+           linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_add_final_chunk</function></term>
+       <listitem><para>
+           &#8594; <link
+           linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
+       </para></listitem>
+    </varlistentry>
+</variablelist>
+
+<para>
+    Since the new chunk-sending APIs require you to explicitly pass
+    the
+    <structfield>request_headers</structfield>/<structfield>request_body</structfield>
+    fields, rather than just assuming you're talking about the
+    response body, in theory it is now possible to use chunked
+    encoding with the request as well. As of the 2.3.0 release this
+    has not yet been tested.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Methods</title>
+
+<para>
+    <type>SoupMessage</type>'s
+    <structfield>method</structfield> field is now an interned
+    string, and you can compare the method directly against
+    the defines such as <link
+    linkend="SOUP-METHOD-GET:CAPS"><literal>SOUP_METHOD_GET</literal></link>
+    (eg, in a <type>SoupServer</type> request handler).
+    <function>soup_method_get_id</function> and the
+    <literal>SOUP_METHOD_ID_*</literal> macros are now gone.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Handlers</title>
+
+<para>
+    <link
+    linkend="soup-message-add-header-handler"><function>soup_message_add_header_handler</function></link>
+    and <link
+    linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>
+    are now just clever wrappers around
+    <function>g_signal_connect</function>. In particular, you now pass
+    a signal name to them rather than a <type>SoupHandlerPhase</type>,
+    and you remove them with the normal signal handler remove methods.
+    However, they still retain the special behavior that if the
+    message has been cancelled or requeued when the time comes for the
+    handler to run, then the handler will be skipped. (Use plain
+    <function>g_signal_connect</function> if you don't want that
+    behavior.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>I/O-related <type>SoupMessage</type> methods</title>
+
+<para>
+    <function>soup_message_io_pause</function> and
+    <function>soup_message_io_unpause</function> have been moved to
+    <type>SoupSession</type> and <type>SoupServer</type>, to better
+    reflect the fact that the session/server control the I/O, and
+    <type>SoupMessage</type> is merely acted-upon by them.
+</para>
+
+<variablelist>
+    <varlistentry>
+       <term><function>soup_message_io_pause</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link> / <link linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><function>soup_message_io_unpause</function></term>
+       <listitem><para>
+           &#8594; <link linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link> / <link linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
+       </para></listitem>
+    </varlistentry>
+</variablelist>
+
+<para>
+    <literal>msg->status</literal> (the I/O status) is now
+    gone as well, because (a) it's really an internal state of
+    <type>SoupSession</type>, and (b) it's too easy to confuse
+    with <literal>msg->status_code</literal> (the HTTP status)
+    anyway. Code that used to check if status was
+    <literal>SOUP_MESSAGE_STATUS_FINISHED</literal> needs to
+    be rewritten to track whether or not the <link
+    linkend="SoupMessage-finished"><literal>finished</literal></link>
+    signal has been emitted.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>HTTP-Version</title>
+
+<para>
+    <type>SoupHttpVersion</type> is now <link
+    linkend="SoupHTTPVersion"><type>SoupHTTPVersion</type></link>
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>SoupSession</title>
+
+<refsect3>
+<title><function>soup_session_queue_message</function> callback</title>
+
+<para>
+    <link
+    linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>'s
+    callback parameter now includes the <type>SoupSession</type> as a
+    parameter, reflecting the fact that it is a
+    <type>SoupSession</type> callback, not a <type>SoupMessage</type>
+    callback. (It has also been renamed, from
+    <type>SoupMessageCallbackFn</type> to <link
+    linkend="SoupSessionCallback"><type>SoupSessionCallback</type></link>.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Authentication</title>
+
+<para>
+    <type>SoupSession</type>'s <literal>authenticate</literal> and
+    <literal>reauthenticate</literal> signals have been merged into a
+    single <link
+    linkend="SoupSession-authenticate"><literal>authenticate</literal></link>
+    signal with a <parameter>retrying</parameter> parameter to indicate if
+    it's the second (or later) try. Also, the signal now includes a
+    <link linkend="SoupAuth"><type>SoupAuth</type></link> directly,
+    and you authenticate by calling <link
+    linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
+    on the auth (rather than passing back a username and password from
+    the signal handler).
+</para>
+</refsect3>
+
+<refsect3>
+<title><type>SoupLogger</type></title>
+
+<para>
+<link linkend="libsoup-SoupLogger"><type>SoupLogger</type></link> is a
+new object that copies the behavior of
+<application>evolution-exchange</application>'s
+<literal>E2K_DEBUG</literal> and its clones. That is, it causes a
+<type>SoupSession</type> to start logging some or all of its HTTP
+traffic to stdout, for debugging purposes.
+</para>
+</refsect3>
+
+<refsect3>
+<title><type>SoupMessageFilter</type></title>
+
+<para>
+    <type>SoupMessageFilter</type> is gone; code that used to use it
+    can now connect to the <link
+    linkend="SoupSession-request-started"><literal>SoupSession::request-started</literal></link>
+    signal to get a chance to act on each message as it is sent.
+    (This is how <type>SoupLogger</type> works.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Internal types</title>
+
+<para>
+    The <type>SoupConnection</type> and <type>SoupMessageQueue</type>
+    types (which should always have been internal to
+    <type>SoupSession</type>) have been removed from the public API.
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>SoupURI</title>
+
+<type>SoupUri</type> has been renamed <link
+linkend="SoupURI"><type>SoupURI</type></link>, and its behavior has
+changed in a few ways:
+
+<itemizedlist>
+    <listitem>
+       <para>
+           It no longer fully-decodes %-encoded URI components. This
+           is necessary to ensure that complicated URIs (eg, URIs
+           that include other URIs as query parameters) can be
+           round-tripped correctly. This corresponds to the old
+           <structfield>broken_encoding</structfield> behavior, but
+           that flag no longer exists, since it is the default and
+           there's no way to turn it off.
+       </para>
+
+       <para>
+           In theory, this is an ABI-breaking change, especially for
+           <link
+           linkend="SoupServer"><type>SoupServer</type></link>s.
+           However, it is unlikely to actually break anything. (And
+           in the <type>SoupServer</type> case, servers now
+           fully-decode the <structfield>path</structfield> component
+           themselves unless you set the <link
+           linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link>
+           flag on the server, so the behavior should still be the
+           same.
+       </para>
+    </listitem>            
+
+    <listitem><para>
+       It uses the RFC3986 parsing rules, including support for IPv6 literal
+       addresses.
+    </para></listitem>
+
+    <listitem>
+       <para>
+           The field formerly called
+           <structfield>protocol</structfield> is now
+           <structfield>scheme</structfield>, to match the spec, and
+           it's an interned string rather than a quark. The names of
+           the predefined values have changed to match:
+       </para>
+
+       <variablelist>
+           <varlistentry>
+               <term><function>SOUP_PROTOCOL_HTTP</function></term>
+               <listitem><para>
+                   &#8594; <link linkend="SOUP-URI-SCHEME-HTTP:CAPS"><literal>SOUP_URI_SCHEME_HTTP</literal></link>
+               </para></listitem>
+           </varlistentry>
+           <varlistentry>
+               <term><function>SOUP_PROTOCOL_HTTPS</function></term>
+               <listitem><para>
+                   &#8594; <link linkend="SOUP-URI-SCHEME-HTTPS:CAPS"><literal>SOUP_URI_SCHEME_HTTPS</literal></link>
+               </para></listitem>
+           </varlistentry>
+       </variablelist>
+    </listitem>
+</itemizedlist>
+
+<para>
+<link linkend="soup-uri-decode"><function>soup_uri_decode</function></link>
+now returns a new string rather than modifying its input string in
+place. The new method <link
+linkend="soup-uri-normalize"><function>soup_uri_normalize</function></link>,
+which removes some, but not all, %-encoding, behaves similarly.
+</para>
+
+<para>
+Finally, <type>SoupURI</type> (as well as most other struct types in
+<application>libsoup</application>) now uses the glib "slice"
+allocator, so any code that uses <link
+linkend="g-new"><function>g_new</function></link> to create
+<type>SoupURI</type>s is wrong. If you want to create a URI "by hand",
+you can call <link
+linkend="soup-uri-new"><function>soup_uri_new</function></link>,
+passing <literal>NULL</literal>, and you will get back an empty
+<type>SoupURI</type>. There are also now methods that can be used to
+set its fields (eg, <link
+linkend="soup-uri-set-scheme"><function>soup_uri_set_scheme</function></link>,
+<link
+linkend="soup-uri-set-path"><function>soup_uri_set_path</function></link>,
+etc) rather than mucking with the fields directly.
+</para>
+
+<refsect3>
+<title>Forms</title>
+
+<para>
+Related to <type>SoupURI</type>, there are some new helper methods for
+dealing with HTML forms. <link
+linkend="soup-form-decode-urlencoded">soup_form_decode_urlencoded</link>
+decodes a URI <structfield>query</structfield> component (or an
+<literal>application/x-www-form-urlencoded</literal> request body)
+into a <type>GHashTable</type>. <link
+linkend="soup-form-encode-urlencoded">soup_form_encode_urlencoded</link>
+reverses the process, allowing you to fill in a
+<literal>uri->query</literal> with a properly-encoded form dataset.
+(<type>SoupURI</type> also provides <link
+linkend="soup-uri-set-query-from-form"><function>soup_uri_set_query_from_form</function></link>
+to help with this.)
+</para>
+</refsect3>
+
+</refsect2>
+
+
+<refsect2>
+<title>XML-RPC and SOAP</title>
+
+<refsect3>
+<title>SOAP</title>
+<para>
+SOAP support has been removed; the existing methods covered only a
+teeny tiny subset of SOAP, which was really only useful to a single
+application. (The code that was formerly in libsoup has been moved to
+that application.). If you were using this code, you can resurrect a
+libsoup-2.4-compatible version of it from revision 1016 of libsoup
+svn.
+</para>
+</refsect3>
+
+<refsect3>
+<title>XML-RPC</title>
+<para>
+The XML-RPC code has been completely rewritten to make it simpler to
+implement XML-RPC clients and servers. (Note: the server-side code has
+not been heavily tested yet.) The <link
+linkend="libsoup-XMLRPC-Support">new XML-RPC API</link> makes use of
+<type>GValue</type>s, with the following type mappings:
+</para>
+
+<variablelist>
+    <varlistentry>
+       <term><literal>int</literal></term>
+       <listitem><para>
+           &#8594; <type>int</type> (<link linkend="G-TYPE-INT:CAPS"><literal>G_TYPE_INT</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>boolean</literal></term>
+       <listitem><para>
+           &#8594; <type>gboolean</type> (<link linkend="G-TYPE-BOOLEAN:CAPS"><literal>G_TYPE_BOOLEAN</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>string</literal></term>
+       <listitem><para>
+           &#8594; <type>char *</type> (<link linkend="G-TYPE-STRING:CAPS"><literal>G_TYPE_STRING</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>double</literal></term>
+       <listitem><para>
+           &#8594; <type>double</type> (<link linkend="G-TYPE-DOUBLE:CAPS"><literal>G_TYPE_DOUBLE</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>dateTime.iso8601</literal></term>
+       <listitem><para>
+           &#8594; <link linkend="SoupDate"><type>SoupDate</type></link> (<link linkend="SOUP-TYPE-DATE:CAPS"><literal>SOUP_TYPE_DATE</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>base64</literal></term>
+       <listitem><para>
+           &#8594; <type>GByteArray</type> (<link linkend="SOUP-TYPE-BYTE-ARRAY:CAPS"><literal>SOUP_TYPE_BYTE_ARRAY</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>struct</literal></term>
+       <listitem><para>
+           &#8594; <type>GHashTable</type> (<link linkend="G-TYPE-HASH-TABLE:CAPS"><literal>G_TYPE_HASH_TABLE</literal></link>)
+       </para></listitem>
+    </varlistentry>
+    <varlistentry>
+       <term><literal>array</literal></term>
+       <listitem><para>
+           &#8594; <type>GValueArray</type> (<link linkend="G-TYPE-VALUE-ARRAY:CAPS"><literal>G_TYPE_VALUE_ARRAY</literal></link>)
+       </para></listitem>
+    </varlistentry>
+</variablelist>
+
+<para>
+<type>SoupDate</type> is discussed below.
+<literal>SOUP_TYPE_BYTE_ARRAY</literal> is just a new
+<type>GType</type> value defined by <application>libsoup</application>
+to represent <type>GByteArray</type>s, which glib does not define a
+<type>GType</type> for.
+</para>
+
+<para>
+<application>libsoup</application> provides some additional <link
+linkend="libsoup-GValue-Support"><type>GValue</type> support
+methods</link> for working with
+<type>GValueArray</type>s, and <type>GHashTable</type>s of
+<type>GValue</type>s, for the XML-RPC <literal>struct</literal> and
+<literal>array</literal> types. Eg, you can use <link
+linkend="soup-value-hash-new"><function>soup_value_hash_new</function></link>
+to create a <type>GHashTable</type> to use with the XML-RPC methods,
+and <link
+linkend="soup-value-hash-insert"><function>soup_value_hash_insert</function></link>
+to add values to it without needing to muck with <type>GValue</type>s
+directly.
+</para>
+
+<para>
+The <literal>getbug</literal> and <literal>xmlrpc-test</literal>
+programs in the <application>libsoup</application> sources provide
+examples of how to use the new API. (Beware that
+<literal>xmlrpc-test</literal>'s use of the API is a little
+complicated because of the way it sends all calls through a single
+<literal>do_xmlrpc</literal> method.)
+</para>
+
+</refsect3>
+</refsect2>
+
+<refsect2>
+<title>SoupServer</title>
+
+<refsect3>
+<title>SoupServer handlers</title>
+
+<para>
+    The prototypes for <link
+    linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>,
+    and for the <link
+    linkend="SoupServerCallback"><type>SoupServer</type>
+    handlers</link> themselves have changed:
+</para>
+
+<informalexample><programlisting>
+typedef void (*SoupServerCallback)     (SoupServer         *server,
+                                       SoupMessage        *msg, 
+                                       const char         *path,
+                                       GHashTable         *query,
+                                       SoupClientContext  *client,
+                                       gpointer            user_data);
+
+void           soup_server_add_handler (SoupServer         *server,
+                                       const char         *path,
+                                       SoupServerCallback  callback,
+                                       gpointer            data,
+                                       GDestroyNotify      destroy);
+</programlisting></informalexample>
+
+<para>
+    <function>soup_server_add_handler</function> no longer takes a
+    <type>SoupServerAuthContext</type> (see the discussion of server
+    authentication below), and the order of the final two arguments
+    has been swapped. (Additionally, <type>SoupServerCallbackFn</type>
+    has been renamed to <type>SoupServerCallback</type>, and the old
+    <parameter>unregister</parameter> parameter of type
+    <type>SoupServerUnregisterFn</type> is now a standard
+    <type>GDestroyNotify</type>. The change to
+    <type>GDestroyNotify</type> and the swapping of the final two
+    arguments is to make the method conform to standard glib/gtk
+    practices.)
+</para>
+
+<para>
+    In <type>SoupServerCallback</type>, several bits of data that used
+    to be part of the <parameter>context</parameter> argument are now
+    provided directly, and <parameter>context</parameter> specifically
+    only contains more specifically-client-related information (such
+    as the <type>SoupSocket</type> that the request arrived on, and
+    information about authentication).
+</para>
+
+<para>
+    <parameter>path</parameter> is the fully %-decoded path component
+    of <parameter>msg</parameter>'s URI, and
+    <parameter>query</parameter> is a hash table containing
+    <parameter>msg</parameter>'s URI's
+    <structfield>query</structfield> component decoded with <link
+    linkend="soup-form-decode-urlencoded">soup_form_decode_urlencoded</link>.
+    These are provided for your convenience; if you need the raw
+    query, you can get it out of <parameter>msg</parameter>'s URI
+    directly. If you need the raw path, you'll need to set the <link
+    linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link>
+    property on the server, which actually changes the behavior of the
+    server with respect to how paths are matched; see the
+    documentation for details.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Server-side authentication</title>
+
+<para>
+    <type>SoupServer</type> authentication has been completely
+    rewritten, with <type>SoupServerAuthContext</type> being replaced
+    with <link
+    linkend="SoupAuthDomain"><type>SoupAuthDomain</type></link>. Among
+    other improvements, you no longer need to have the cleartext
+    password available to check against. See the
+    <type>SoupAuthDomain</type> documentation, the <link
+    linkend="libsoup-server-howto">server tutorial</link>, and
+    <literal>tests/server-auth-test.c</literal>.
+</para>
+</refsect3>
+
+<refsect3>
+<title><literal>Expect: 100-continue</literal> and other early <type>SoupMessage</type> processing</title>
+
+<para>
+    <type>SoupServer</type> now handles
+    "<literal>Expect:&#160;100-continue</literal>" correctly. In
+    particular, if the client passes that header, and your server
+    requires authentication, then authentication will be checked
+    before reading the request body.
+</para>
+
+<para>
+    If you want to do additional pre-request-body handling, you can
+    connect to <type>SoupServer</type>'s <link
+    linkend="SoupServer-request-started"><literal>request_started</literal></link>
+    signal, and connect to the request's <link
+    linkend="SoupMessage-got-headers"><literal>got_headers</literal></link>
+    signal from there. (See the description of
+    <literal>request_started</literal> for information about other
+    related <type>SoupServer</type> signals.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Date header</title>
+
+<para>
+    <type>SoupServer</type> now automatically sets the
+    <literal>Date</literal> header on all responses, as required by
+    RFC 2616.
+</para>
+</refsect3>
+
+<refsect3>
+<title>SoupServerMessage</title>
+
+<para>
+    <type>SoupServerMessage</type> is now merged into
+    <type>SoupMessage</type>.
+    <function>soup_server_message_set_encoding</function> is replaced
+    with <link
+    linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+    as described in the section on <type>SoupMessage</type> above.
+</para>
+</refsect3>
+
+<refsect3>
+<title><function>soup_server_run</function> / <function>soup_server_quit</function></title>
+
+<para>
+    <link
+    linkend="soup-server-run"><function>soup_server_run</function></link>
+    and <link
+    linkend="soup-server-run-async"><function>soup_server_run_async</function></link>
+    no longer <function>g_object_ref</function> the server, and
+    <link
+    linkend="soup-server-quit"><function>soup_server_quit</function></link>
+    no longer unrefs it.
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>Miscellaneous</title>
+
+<refsect3>
+<title>SoupDate</title>
+
+<para>
+    The new <link linkend="SoupDate"><type>SoupDate</type></link> type
+    replaces the old <function>soup_date_*</function> methods, and has
+    an improved (more liberal) date parser.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Header parsing</title>
+
+<para>
+    <literal>soup-headers.h</literal> now has a few additional methods
+    for parsing list-type headers.
+</para>
+</refsect3>
+
+<refsect3>
+<title>SoupAddress, SoupSocket</title>
+
+<para>
+    <type>SoupSocket</type> has had various simplifications made to
+    reflect the fact that this is specifically libsoup's socket
+    implementation, not some random generic socket API.
+</para>
+
+<para>
+    Various <type>SoupAddress</type> and <type>SoupSocket</type>
+    methods now take arguments of the new <link
+    linkend="GCancellable"><type>GCancellable</type></link> type, from
+    libgio. When porting old code, you can just pass
+    <literal>NULL</literal> for these. (<link
+    linkend="soup-address-resolve-async"><function>soup_address_resolve_async</function></link>
+    also takes another new argument, a <link
+    linkend="GMainContext"><type>GMainContext</type></link> that
+    you'll want to pass <literal>NULL</literal> for.) If you pass a
+    <type>GCancellable</type>, you can use it to cleanly cancel the
+    address resolution / socket operation.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Base64 methods</title>
+
+<para>
+    The deprecated base64 methods are now gone; use glib's base64
+    methods instead.
+</para>
+</refsect3>
+
+</refsect2>
+
+</refentry>
index 2d7971b..76c1918 100644 (file)
@@ -21,17 +21,6 @@ most of your interactions with libsoup. In this case, <link
 linkend="SoupServer"><type>SoupServer</type></link>.
 </para>
 
-<warning>
-    <para>
-       Note that <type>SoupServer</type> isn't as polished as
-       <type>SoupSession</type>, and thus not as stable, and the APIs
-       will likely change in incompatible (but not
-       difficult-to-port-to) ways in the future to make things nicer.
-       We apologize in advance for the inconvenience.
-    </para>
-</warning>
-
-
 <para>
 You create the server with <link
 linkend="soup-server-new"><function>soup_server_new</function></link>,
@@ -83,6 +72,16 @@ various additional options:
            other than the main thread.
        </para></listitem>
     </varlistentry>
+    <varlistentry>
+       <term><link linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term>
+       <listitem><para>
+           Set this to <literal>TRUE</literal> if you don't want
+           <application>libsoup</application> to decode %-encoding
+           in the Request-URI. (Eg, because you need to treat
+           <literal>"/foo/bar"</literal> and
+           <literal>"/foo%2Fbar"</literal> as different paths.
+       </para></listitem>
+    </varlistentry>
 </variablelist>
 
 </refsect2>
@@ -100,8 +99,8 @@ to set a callback to handle certain URI paths.
 </para>
 
 <informalexample><programlisting>
-soup_server_add_handler (server, "/foo", NULL, server_callback,
-                        unregister_callback, data);
+       soup_server_add_handler (server, "/foo", server_callback,
+                                data, destroy_notify);
 </programlisting></informalexample>
 
 <para>
@@ -131,18 +130,28 @@ A handler callback looks something like this:
 
 <informalexample><programlisting>
 static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
+server_callback (SoupServer        *server,
+                SoupMessage       *msg, 
+                const char        *path,
+                GHashTable        *query,
+                SoupClientContext *client,
+                gpointer           user_data)
 {
        ...
 }
 </programlisting></informalexample>
 
 <para>
-<literal>msg</literal> is the request that has been received.
-<literal>data</literal> is the same data that was passed to <link
+<literal>msg</literal> is the request that has been received and
+<literal>user_data</literal> is the data that was passed to <link
 linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>.
-The <link>context</link> argument contains some additional information
-related to the request.
+<literal>path</literal> is the path (from <literal>msg</literal>'s
+URI), and <literal>query</literal> contains the result of parsing the
+URI query field. (It is <literal>NULL</literal> if there was no
+query.) <literal>client</literal> is a <link
+linkend="SoupClientContext"><type>SoupClientContext</type></link>,
+which contains additional information about the client (including its
+IP address, and whether or not it used HTTP authentication).
 </para>
 
 <para>
@@ -151,10 +160,10 @@ completely finished processing the message when you return from the
 callback, and that it can therefore begin sending the response. If you
 are not ready to send a response immediately (eg, you have to contact
 another server, or wait for data from a database), you must call <link
-linkend="soup-message-io-pause"><function>soup_message_io_pause</function></link>
+linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
 on the message before returning from the callback. This will delay
 sending a response until you call <link
-linkend="soup-message-io-unpause"><function>soup_message_io_unpause</function></link>.
+linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>.
 (You must also connect to the <link
 linkend="SoupMessage-finished">finished</link> signal on the message
 in this case, so that you can break off processing if the client
@@ -166,10 +175,8 @@ To set the response status, call <link
 linkend="soup-message-set-status"><function>soup_message_set_status</function></link>
 or <link
 linkend="soup-message-set-status-full"><function>soup_message_set_status_full</function></link>.
-If the response requires a body, the callback must call <link
-linkend="soup-server-message-set-encoding"><function>soup_server_message_set_encoding</function></link>
-to indicate whether it will provide the response all at once with
-<literal>Content-Length</literal> encoding, or in pieces with
+If the response requires a body, you must decide whether to use
+<literal>Content-Length</literal> encoding (the default), or
 <literal>chunked</literal> encoding.
 </para>
 
@@ -184,29 +191,34 @@ data available at once.
 
 <informalexample><programlisting>
 static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
+server_callback (SoupServer        *server,
+                SoupMessage       *msg, 
+                const char        *path,
+                GHashTable        *query,
+                SoupClientContext *client,
+                gpointer           user_data)
 {
-       MyServerData *server_data = data;
-       SoupUri *uri = soup_message_get_uri (msg);
+       MyServerData *server_data = user_data;
        const char *mime_type;
        GByteArray *body;
 
-       if (context->method_id != SOUP_METHOD_ID_GET) {
+       if (msg->method != SOUP_METHOD_GET) {
                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
                return;
        }
 
-       body = g_hash_table_lookup (server_data->bodies, uri->path);
-       mime_type = g_hash_table_lookup (server_data->mime_types, uri->path);
+       /* This is somewhat silly. Presumably your server will do
+        * something more interesting.
+        */
+       body = g_hash_table_lookup (server_data->bodies, path);
+       mime_type = g_hash_table_lookup (server_data->mime_types, path);
        if (!body || !mime_type) {
                soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
                return;
        }
 
        soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg),
-                                         SOUP_TRANSFER_CONTENT_LENGTH);
-       soup_message_set_response (msg, mime_type, SOUP_BUFFER_USER_OWNED,
+       soup_message_set_response (msg, mime_type, SOUP_MEMORY_COPY,
                                   body->data, body->len);
 }
 </programlisting></informalexample>
@@ -219,16 +231,22 @@ server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
 <para>
 If you want to supply the response body in chunks as it becomes
 available, use <literal>chunked</literal> encoding instead. In this
-case, call <link
-linkend="soup-message-add-chunk"><function>soup_message_add_chunk</function></link> with
-each chunk of the response body as it becomes available, and call
-<link
-linkend="soup-message-add-final-chunk"><function>soup_message_add_final_chunk</function></link>
+case, first call <link
+linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>&#160;<literal>(msg->response_headers,&#160;<link
+linkend="SoupEncoding">SOUP_ENCODING_CHUNKED</link>)</literal>
+to tell <application>libsoup</application> that you'll be using
+chunked encoding. Then call <link
+linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
+(or <link
+linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>)
+on <literal>msg->response_body</literal> with each chunk of the
+response body as it becomes available, and call <link
+linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
 when the response is complete. After each of these calls, you must
 also call <link
-linkend="soup-message-io-unpause"><function>soup_message_io_unpause</function></link> to
-cause the chunk to be sent. (You do not normally need to call 
-<link linkend="soup-message-io-pause"><function>soup_message_io_pause</function></link>,
+linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
+to cause the chunk to be sent. (You do not normally need to call <link
+linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>,
 because I/O is automatically paused when doing a
 <literal>chunked</literal> transfer if no chunks are available.)
 </para>
@@ -239,7 +257,9 @@ linkend="SoupMessage-finished">finished</link> signal on the message,
 so that you will be notified if the client disconnects between two
 chunks; <type>SoupServer</type> will unref the message if that
 happens, so you must stop adding new chunks to the response at that
-point.
+point. (An alternate possibility is to write each new chunk only when
+the <link linkend="SoupMessage-wrote-chunk">wrote_chunk</link> signal
+is emitted indicating that the previous one was written successfully.)
 </para>
 
 <para>
@@ -257,59 +277,92 @@ using <literal>chunked</literal> encoding.
 
 <para>
 To have <link linkend="SoupServer"><type>SoupServer</type></link>
-handle HTTP authentication for you, pass a <link
-linkend="SoupAuthContext"><type>SoupAuthContext</type></link> to <link
-linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>:
+handle HTTP authentication for you, create a <link
+linkend="SoupAuthDomainBasic"><type>SoupAuthDomainBasic</type></link>
+or <link
+linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>,
+and pass it to <link
+linkend="soup-server-add-auth-domain"><function>soup_server_add_auth_domain</function></link>:
 </para>
 
 <informalexample><programlisting>
-SoupServerAuthContext auth_ctx;
-
-auth_ctx.types            = SOUP_AUTH_TYPE_BASIC;
-auth_ctx.callback         = auth_callback;
-auth_ctx.user_data        = data;
-auth_ctx.basic_info.realm = "My Realm";
-
-soup_server_add_handler (server, "/bar", &amp;auth_ctx, server_callback,
-                        unregister_callback, data);
+       SoupAuthDomain *domain;
+
+       domain = soup_auth_domain_basic_new (
+               SOUP_AUTH_DOMAIN_REALM, "My Realm",
+               SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+               SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, auth_data,
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/foo",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/bar/private",
+               NULL);
+       soup_server_add_auth_domain (server, domain);
+       g_object_unref (domain);
 </programlisting></informalexample>
 
 <para>
-Then, every request that matches that handler will be passed to the
-<literal>auth_callback</literal> first before being passed to the
-<literal>server_callback</literal>:
+Then, every request under one of the auth domain's paths will be
+passed to the <literal>auth_callback</literal> first before being
+passed to the <literal>server_callback</literal>:
 </para>
 
 <informalexample><programlisting>
 static gboolean
-auth_callback (SoupServerAuthContext *auth_ctx, SoupServerAuth *auth,
-              SoupMessage *msg, gpointer user_data)
+auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
+              const char *username, const char *password,
+              gpointer user_data)
 {
        MyServerData *server_data = user_data;
-       const char *username, *password;
+       MyUserData *user;
 
-       if (!auth)
+       user = my_server_data_lookup_user (server_data, username);
+       if (!user)
                return FALSE;
 
-       username = soup_server_auth_get_user (auth);
-       password = g_hash_table_lookup (server_data->passwords, username);
-       if (!password)
-               return FALSE;
-
-       return soup_server_auth_check_passwd (auth, password);
+       /* FIXME: Don't do this. Keeping a cleartext password database
+        * is bad.
+        */
+       return strcmp (password, user->password) == 0;
 }
 </programlisting></informalexample>
 
 <para>
-The <literal>auth</literal> parameter indicates the authentication
-information passed by the client. If no
-<literal>WWW-Authenticate</literal> header was present, this will be
-<literal>NULL</literal>, so we return <literal>FALSE</literal> from
-the callback to indicate that the server should return a <literal>401
-Unauthorized</literal> response. If it is non-<literal>NULL</literal>,
-we extract the username from it, and compare it against our stored
-password. Assuming it matches, we return <literal>TRUE</literal>, and
-the server callback is then invoked normally.
+The <link
+linkend="SoupAuthDomainBasicAuthCallback"><type>SoupAuthDomainBasicAuthCallback</type></link>
+is given the username and password from the
+<literal>Authorization</literal> header and must determine, in some
+server-specific manner, whether or not to accept them. (In this
+example we compare the password against a cleartext password database,
+but it would be better to store the password somehow encoded, as in
+the UNIX password database. Alternatively, you may need to delegate
+the password check to PAM or some other service.)
+</para>
+
+<para>
+If you are using Digest authentication, note that <link
+linkend="SoupAuthDomainDigestAuthCallback"><type>SoupAuthDomainDigestAuthCallback</type></link>
+works completely differently (since the server doesn't receive the
+cleartext password from the client in that case, so there's no way to
+compare it directly). See the documentation for <link
+linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>
+for more details.
+</para>
+
+<para>
+You can have multiple <type>SoupAuthDomain</type>s attached to a
+<literal>SoupServer</literal>, either in separate parts of the path
+hierarchy, or overlapping. (Eg, you might want to accept either Basic
+or Digest authentication for a given path.) When more than one auth
+domain covers a given path, the request will be accepted if the user
+authenticates successfully against <emphasis>any</emphasis> of the
+domains.
+</para>
+
+<para>
+If you want to require authentication for some requests under a
+certain path, but not all of them (eg, you want to authenticate
+<literal>PUT</literal>s, but not <literal>GET</literal>s), use a 
+<link
+linkend="SoupAuthDomainFilter"><type>SoupAuthDomainFilter</type></link>.
 </para>
 
 </refsect2>
diff --git a/docs/reference/tmpl/libsoup-unused.sgml b/docs/reference/tmpl/libsoup-unused.sgml
deleted file mode 100644 (file)
index f4901ef..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-<!-- ##### SECTION ./tmpl/soup-auth-basic.sgml:Long_Description ##### -->
-<para>
-This implements the Basic HTTP Authentication mechanism, as described
-in RFC 2617. It is created automatically by #SoupSession when needed.
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-basic.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-basic.sgml:Short_Description ##### -->
-HTTP Basic Authentication
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-basic.sgml:Title ##### -->
-SoupAuthBasic
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-digest.sgml:Long_Description ##### -->
-<para>
-This implements the Digest HTTP Authentication mechanism, as described
-in RFC 2617. It is created automatically by #SoupSession when needed.
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-digest.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-digest.sgml:Short_Description ##### -->
-HTTP Digest Authentication
-
-
-<!-- ##### SECTION ./tmpl/soup-auth-digest.sgml:Title ##### -->
-SoupAuthDigest
-
-
-<!-- ##### SECTION ./tmpl/soup-md5-utils.sgml:Long_Description ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-md5-utils.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-md5-utils.sgml:Short_Description ##### -->
-MD5 hash utilities
-
-
-<!-- ##### SECTION ./tmpl/soup-md5-utils.sgml:Title ##### -->
-soup-md5-utils
-
-
-<!-- ##### SECTION ./tmpl/soup-message-filter.sgml.sgml:Long_Description ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-filter.sgml.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-filter.sgml.sgml:Short_Description ##### -->
-
-
-
-<!-- ##### SECTION ./tmpl/soup-message-filter.sgml.sgml:Stability_Level ##### -->
-
-
-
-<!-- ##### SECTION ./tmpl/soup-message-filter.sgml.sgml:Title ##### -->
-Soup Miscellaneous Utilities
-
-
-<!-- ##### SECTION ./tmpl/soup-message-private.sgml:Long_Description ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-private.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-private.sgml:Short_Description ##### -->
-
-
-
-<!-- ##### SECTION ./tmpl/soup-message-private.sgml:Title ##### -->
-soup-message-private
-
-
-<!-- ##### SECTION ./tmpl/soup-message-queue.sgml:Long_Description ##### -->
-<para>
-#SoupMessageQueue maintains the queue of pending messages in a
-#SoupSession.
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-queue.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-message-queue.sgml:Short_Description ##### -->
-Message queue object
-
-
-<!-- ##### SECTION ./tmpl/soup-message-queue.sgml:Title ##### -->
-soup-message-queue
-
-
-<!-- ##### SECTION ./tmpl/soup-server-auth.sgml:Long_Description ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-server-auth.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/soup-server-auth.sgml:Short_Description ##### -->
-Server-side authentication structures
-
-
-<!-- ##### SECTION ./tmpl/soup-server-auth.sgml:Title ##### -->
-soup-server-auth
-
-
-<!-- ##### ENUM SoupAuthType ##### -->
-<para>
-
-</para>
-
-@SOUP_AUTH_TYPE_BASIC: 
-@SOUP_AUTH_TYPE_DIGEST: 
-
-<!-- ##### ENUM SoupDigestAlgorithm ##### -->
-<para>
-
-</para>
-
-@SOUP_ALGORITHM_MD5: 
-@SOUP_ALGORITHM_MD5_SESS: 
-
-<!-- ##### STRUCT SoupMD5Context ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### USER_FUNCTION SoupMessageGetHeadersFn ##### -->
-<para>
-
-</para>
-
-@msg: 
-@headers: 
-@encoding: 
-@user_data: 
-
-<!-- ##### USER_FUNCTION SoupMessageParseHeadersFn ##### -->
-<para>
-
-</para>
-
-@msg: 
-@headers: 
-@header_len: 
-@encoding: 
-@content_len: 
-@user_data: 
-@Returns: 
-
-<!-- ##### TYPEDEF SoupMessageQueue ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### STRUCT SoupMessageQueueIter ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### STRUCT SoupServerAuthBasic ##### -->
-<para>
-
-</para>
-
-@type: 
-@user: 
-@passwd: 
-
-<!-- ##### USER_FUNCTION SoupServerAuthCallbackFn ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@auth: 
-@msg: 
-@data: 
-@Returns: 
-
-<!-- ##### STRUCT SoupServerAuthContext ##### -->
-<para>
-
-</para>
-
-@types: 
-@callback: 
-@user_data: 
-
-<!-- ##### STRUCT SoupServerAuthDigest ##### -->
-<para>
-
-</para>
-
-@type: 
-@algorithm: 
-@integrity: 
-@realm: 
-@user: 
-@nonce: 
-@nonce_count: 
-@cnonce: 
-@digest_uri: 
-@digest_response: 
-@request_method: 
-
-<!-- ##### FUNCTION soup_md5_final ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@digest: 
-
-<!-- ##### FUNCTION soup_md5_final_hex ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@digest: 
-
-<!-- ##### FUNCTION soup_md5_init ##### -->
-<para>
-
-</para>
-
-@ctx: 
-
-<!-- ##### FUNCTION soup_md5_update ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@buf: 
-@len: 
-
-<!-- ##### FUNCTION soup_message_cleanup_response ##### -->
-<para>
-
-</para>
-
-@req: 
-
-<!-- ##### FUNCTION soup_message_finished ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_got_body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_got_chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_got_headers ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_got_informational ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_io_client ##### -->
-<para>
-
-</para>
-
-@msg: 
-@sock: 
-@get_headers_cb: 
-@parse_headers_cb: 
-@user_data: 
-
-<!-- ##### FUNCTION soup_message_io_server ##### -->
-<para>
-
-</para>
-
-@msg: 
-@sock: 
-@get_headers_cb: 
-@parse_headers_cb: 
-@user_data: 
-
-<!-- ##### FUNCTION soup_message_queue_append ##### -->
-<para>
-
-</para>
-
-@queue: 
-@msg: 
-
-<!-- ##### FUNCTION soup_message_queue_destroy ##### -->
-<para>
-
-</para>
-
-@queue: 
-
-<!-- ##### FUNCTION soup_message_queue_first ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-<!-- ##### FUNCTION soup_message_queue_free_iter ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-
-<!-- ##### FUNCTION soup_message_queue_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-<!-- ##### FUNCTION soup_message_queue_next ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-<!-- ##### FUNCTION soup_message_queue_remove ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-<!-- ##### FUNCTION soup_message_queue_remove_message ##### -->
-<para>
-
-</para>
-
-@queue: 
-@msg: 
-
-<!-- ##### FUNCTION soup_message_restarted ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_run_handlers ##### -->
-<para>
-
-</para>
-
-@msg: 
-@phase: 
-
-<!-- ##### FUNCTION soup_message_wrote_body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_wrote_chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_wrote_headers ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_message_wrote_informational ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### FUNCTION soup_server_auth_check_passwd ##### -->
-<para>
-
-</para>
-
-@auth: 
-@passwd: 
-@Returns: 
-
-<!-- ##### FUNCTION soup_server_auth_context_challenge ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@msg: 
-@header_name: 
-
-<!-- ##### FUNCTION soup_server_auth_free ##### -->
-<para>
-
-</para>
-
-@auth: 
-
-<!-- ##### FUNCTION soup_server_auth_get_user ##### -->
-<para>
-
-</para>
-
-@auth: 
-@Returns: 
-
-<!-- ##### FUNCTION soup_server_auth_new ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@auth_hdrs: 
-@msg: 
-@Returns: 
-
diff --git a/docs/reference/tmpl/soup-address.sgml b/docs/reference/tmpl/soup-address.sgml
deleted file mode 100644 (file)
index 5cebb3b..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupAddress
-
-<!-- ##### SECTION Short_Description ##### -->
-Higher-level IP address object
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupAddress represents the address of a TCP connection endpoint; both
-the IP address and the port. (It is somewhat like an object-oriented
-version of struct #sockaddr.)
-</para>
-
-<para>
-If libsoup was built with IPv6 support, #SoupAddress will allow both
-IPv4 and IPv6 addresses.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupAddress ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SIGNAL SoupAddress::dns-result ##### -->
-<para>
-
-</para>
-
-@addr: 
-@status: 
-
-<!-- ##### ENUM SoupAddressFamily ##### -->
-<para>
-
-</para>
-
-@SOUP_ADDRESS_FAMILY_IPV4: 
-@SOUP_ADDRESS_FAMILY_IPV6: 
-
-<!-- ##### MACRO SOUP_ADDRESS_ANY_PORT ##### -->
-<para>
-
-</para>
-
-
-
-<!-- ##### FUNCTION soup_address_new ##### -->
-<para>
-
-</para>
-
-@name: 
-@port: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_new_from_sockaddr ##### -->
-<para>
-
-</para>
-
-@sa: 
-@len: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_new_any ##### -->
-<para>
-
-</para>
-
-@family: 
-@port: 
-@Returns: 
-
-
-<!-- ##### USER_FUNCTION SoupAddressCallback ##### -->
-<para>
-
-</para>
-
-@addr: 
-@status: 
-@data: 
-
-
-<!-- ##### FUNCTION soup_address_resolve_async ##### -->
-<para>
-
-</para>
-
-@addr: 
-@callback: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_address_resolve_async_full ##### -->
-<para>
-
-</para>
-
-@addr: 
-@async_context: 
-@callback: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_address_resolve_sync ##### -->
-<para>
-
-</para>
-
-@addr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_get_name ##### -->
-<para>
-
-</para>
-
-@addr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_get_sockaddr ##### -->
-<para>
-
-</para>
-
-@addr: 
-@len: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_get_physical ##### -->
-<para>
-
-</para>
-
-@addr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_address_get_port ##### -->
-<para>
-
-</para>
-
-@addr: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-auth.sgml b/docs/reference/tmpl/soup-auth.sgml
deleted file mode 100644 (file)
index e612ffb..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupAuth
-
-<!-- ##### SECTION Short_Description ##### -->
-HTTP Authentication support
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupAuth objects store the authentication data associated with a
-given bit of webspace. They are created and maintained automatically
-by #SoupSession.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-#SoupSession, #SoupConnectionNTLM
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupAuth ##### -->
-<para>
-The abstract base class for handling authentication. Specific HTTP
-Authentication mechanisms are implemented by its subclasses. (NTLM
-authentication, which works quite differently from normal HTTP
-authentication, is handled by #SoupConnectionNTLM.)
-</para>
-
-
-<!-- ##### STRUCT SoupAuthBasic ##### -->
-<para>
-An object representing Basic HTTP authentication.
-</para>
-
-
-<!-- ##### STRUCT SoupAuthDigest ##### -->
-<para>
-An object representing Digest HTTP authentication.
-</para>
-
-
-<!-- ##### FUNCTION soup_auth_new_from_header_list ##### -->
-<para>
-
-</para>
-
-@vals: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_get_scheme_name ##### -->
-<para>
-
-</para>
-
-@auth: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_get_realm ##### -->
-<para>
-
-</para>
-
-@auth: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_authenticate ##### -->
-<para>
-
-</para>
-
-@auth: 
-@username: 
-@password: 
-
-
-<!-- ##### FUNCTION soup_auth_is_authenticated ##### -->
-<para>
-
-</para>
-
-@auth: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_get_authorization ##### -->
-<para>
-
-</para>
-
-@auth: 
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_get_protection_space ##### -->
-<para>
-
-</para>
-
-@auth: 
-@source_uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_auth_free_protection_space ##### -->
-<para>
-
-</para>
-
-@auth: 
-@space: 
-
-
diff --git a/docs/reference/tmpl/soup-connection-ntlm.sgml b/docs/reference/tmpl/soup-connection-ntlm.sgml
deleted file mode 100644 (file)
index f72ad2a..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupConnectionNTLM
-
-<!-- ##### SECTION Short_Description ##### -->
-NTLM-authenticated connection
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupSession automatically creates #SoupConnectionNTLM rather than
-#SoupConnection if you set the %SOUP_SESSION_USE_NTLM flag.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupConnectionNTLM ##### -->
-<para>
-
-</para>
-
-
diff --git a/docs/reference/tmpl/soup-connection.sgml b/docs/reference/tmpl/soup-connection.sgml
deleted file mode 100644 (file)
index 3af9305..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupConnection
-
-<!-- ##### SECTION Short_Description ##### -->
-a single possibly-persistent HTTP connection
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupConnection represents a single connection to an HTTP server
-(possibly via a proxy). Connection objects are created and destroyed
-automatically by #SoupSession.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupConnection ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SIGNAL SoupConnection::authenticate ##### -->
-<para>
-
-</para>
-
-@conn: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-<!-- ##### SIGNAL SoupConnection::connect-result ##### -->
-<para>
-
-</para>
-
-@conn: 
-@status: 
-
-<!-- ##### SIGNAL SoupConnection::disconnected ##### -->
-<para>
-
-</para>
-
-@conn: 
-
-<!-- ##### SIGNAL SoupConnection::reauthenticate ##### -->
-<para>
-
-</para>
-
-@conn: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-<!-- ##### ARG SoupConnection:async-context ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupConnection:message-filter ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupConnection:origin-uri ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupConnection:proxy-uri ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupConnection:ssl-creds ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupConnection:timeout ##### -->
-<para>
-
-</para>
-
-<!-- ##### FUNCTION soup_connection_new ##### -->
-<para>
-
-</para>
-
-@propname1: 
-@Varargs: 
-@Returns: 
-
-
-<!-- ##### USER_FUNCTION SoupConnectionCallback ##### -->
-<para>
-
-</para>
-
-@conn: 
-@status: 
-@data: 
-
-
-<!-- ##### FUNCTION soup_connection_connect_async ##### -->
-<para>
-
-</para>
-
-@conn: 
-@callback: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_connection_connect_sync ##### -->
-<para>
-
-</para>
-
-@conn: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_connection_disconnect ##### -->
-<para>
-
-</para>
-
-@conn: 
-
-
-<!-- ##### FUNCTION soup_connection_is_in_use ##### -->
-<para>
-
-</para>
-
-@conn: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_connection_last_used ##### -->
-<para>
-
-</para>
-
-@conn: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_connection_send_request ##### -->
-<para>
-
-</para>
-
-@conn: 
-@req: 
-
-
-<!-- ##### FUNCTION soup_connection_authenticate ##### -->
-<para>
-
-</para>
-
-@conn: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-
-<!-- ##### FUNCTION soup_connection_reauthenticate ##### -->
-<para>
-
-</para>
-
-@conn: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-
-<!-- ##### FUNCTION soup_connection_release ##### -->
-<para>
-
-</para>
-
-@conn: 
-
-
-<!-- ##### FUNCTION soup_connection_reserve ##### -->
-<para>
-
-</para>
-
-@conn: 
-
-
-<!-- ##### MACRO SOUP_CONNECTION_ORIGIN_URI ##### -->
-<para>
-An alias for the "origin-uri" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_CONNECTION_PROXY_URI ##### -->
-<para>
-An alias for the "proxy-uri" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_CONNECTION_SSL_CREDENTIALS ##### -->
-<para>
-An alias for the "ssl-creds" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_CONNECTION_MESSAGE_FILTER ##### -->
-<para>
-An alias for the "message-filter" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_CONNECTION_ASYNC_CONTEXT ##### -->
-<para>
-An alias for the "async-context" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_CONNECTION_TIMEOUT ##### -->
-<para>
-An alias for the "timeout" property.
-</para>
-
-
-
diff --git a/docs/reference/tmpl/soup-dns.sgml b/docs/reference/tmpl/soup-dns.sgml
deleted file mode 100644 (file)
index bc13b52..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-dns
-
-<!-- ##### SECTION Short_Description ##### -->
-Low-level DNS routines
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### FUNCTION soup_dns_init ##### -->
-<para>
-
-</para>
-
-
-
-<!-- ##### FUNCTION soup_dns_ntop ##### -->
-<para>
-
-</para>
-
-@sa: 
-@Returns: 
-
-
-<!-- ##### TYPEDEF SoupDNSLookup ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_dns_lookup_name ##### -->
-<para>
-
-</para>
-
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_address ##### -->
-<para>
-
-</para>
-
-@sockaddr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_resolve ##### -->
-<para>
-
-</para>
-
-@lookup: 
-@Returns: 
-
-
-<!-- ##### USER_FUNCTION SoupDNSCallback ##### -->
-<para>
-
-</para>
-
-@lookup: 
-@success: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_resolve_async ##### -->
-<para>
-
-</para>
-
-@lookup: 
-@async_context: 
-@callback: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_cancel ##### -->
-<para>
-
-</para>
-
-@lookup: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_get_hostname ##### -->
-<para>
-
-</para>
-
-@lookup: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_get_address ##### -->
-<para>
-
-</para>
-
-@lookup: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_dns_lookup_free ##### -->
-<para>
-
-</para>
-
-@lookup: 
-
-
diff --git a/docs/reference/tmpl/soup-md5-utils.sgml b/docs/reference/tmpl/soup-md5-utils.sgml
deleted file mode 100644 (file)
index afb42b2..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-md5-utils
-
-<!-- ##### SECTION Short_Description ##### -->
-MD5 hash utilities
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### STRUCT SoupMD5Context ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_md5_init ##### -->
-<para>
-
-</para>
-
-@ctx: 
-
-
-<!-- ##### FUNCTION soup_md5_update ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@buf: 
-@len: 
-
-
-<!-- ##### FUNCTION soup_md5_final ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@digest: 
-
-
diff --git a/docs/reference/tmpl/soup-message-filter.sgml b/docs/reference/tmpl/soup-message-filter.sgml
deleted file mode 100644 (file)
index f1c02af..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupMessageFilter
-
-<!-- ##### SECTION Short_Description ##### -->
-Automatic message processing
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### FUNCTION soup_message_filter_setup_message ##### -->
-<para>
-
-</para>
-
-@filter: 
-@msg: 
-
-
diff --git a/docs/reference/tmpl/soup-message-private.sgml b/docs/reference/tmpl/soup-message-private.sgml
deleted file mode 100644 (file)
index 581531f..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-message-private
-
-<!-- ##### SECTION Short_Description ##### -->
-
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### FUNCTION soup_message_run_handlers ##### -->
-<para>
-
-</para>
-
-@msg: 
-@phase: 
-
-
-<!-- ##### FUNCTION soup_message_cleanup_response ##### -->
-<para>
-
-</para>
-
-@req: 
-
-
-<!-- ##### USER_FUNCTION SoupMessageGetHeadersFn ##### -->
-<para>
-
-</para>
-
-@msg: 
-@headers: 
-@encoding: 
-@user_data: 
-
-
-<!-- ##### USER_FUNCTION SoupMessageParseHeadersFn ##### -->
-<para>
-
-</para>
-
-@msg: 
-@headers: 
-@header_len: 
-@encoding: 
-@content_len: 
-@user_data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_io_client ##### -->
-<para>
-
-</para>
-
-@msg: 
-@sock: 
-@get_headers_cb: 
-@parse_headers_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_io_server ##### -->
-<para>
-
-</para>
-
-@msg: 
-@sock: 
-@get_headers_cb: 
-@parse_headers_cb: 
-@user_data: 
-
-
diff --git a/docs/reference/tmpl/soup-message-queue.sgml b/docs/reference/tmpl/soup-message-queue.sgml
deleted file mode 100644 (file)
index 468f0d6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-message-queue
-
-<!-- ##### SECTION Short_Description ##### -->
-Message queue object
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupMessageQueue maintains the queue of pending messages in a
-#SoupSession.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### TYPEDEF SoupMessageQueue ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### STRUCT SoupMessageQueueIter ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_message_queue_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_queue_append ##### -->
-<para>
-
-</para>
-
-@queue: 
-@msg: 
-
-
-<!-- ##### FUNCTION soup_message_queue_first ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_queue_next ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_queue_remove ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_queue_free_iter ##### -->
-<para>
-
-</para>
-
-@queue: 
-@iter: 
-
-
-<!-- ##### FUNCTION soup_message_queue_destroy ##### -->
-<para>
-
-</para>
-
-@queue: 
-
-
-<!-- ##### FUNCTION soup_message_queue_remove_message ##### -->
-<para>
-
-</para>
-
-@queue: 
-@msg: 
-
-
diff --git a/docs/reference/tmpl/soup-message.sgml b/docs/reference/tmpl/soup-message.sgml
deleted file mode 100644 (file)
index 195a394..0000000
+++ /dev/null
@@ -1,522 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupMessage
-
-<!-- ##### SECTION Short_Description ##### -->
-An HTTP request and response.
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupMessage ##### -->
-<para>
-
-</para>
-
-@method: 
-@status_code: 
-@reason_phrase: 
-@request: 
-@request_headers: 
-@response: 
-@response_headers: 
-@status: 
-
-<!-- ##### SIGNAL SoupMessage::finished ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::got-body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::got-chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::got-headers ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::got-informational ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::restarted ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::wrote-body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::wrote-chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::wrote-headers ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### SIGNAL SoupMessage::wrote-informational ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-<!-- ##### ENUM SoupMessageStatus ##### -->
-<para>
-
-</para>
-
-@SOUP_MESSAGE_STATUS_IDLE: 
-@SOUP_MESSAGE_STATUS_QUEUED: 
-@SOUP_MESSAGE_STATUS_CONNECTING: 
-@SOUP_MESSAGE_STATUS_RUNNING: 
-@SOUP_MESSAGE_STATUS_FINISHED: 
-
-<!-- ##### MACRO SOUP_MESSAGE_IS_STARTING ##### -->
-
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### ENUM SoupTransferEncoding ##### -->
-<para>
-
-</para>
-
-@SOUP_TRANSFER_UNKNOWN: 
-@SOUP_TRANSFER_CHUNKED: 
-@SOUP_TRANSFER_CONTENT_LENGTH: 
-@SOUP_TRANSFER_BYTERANGES: 
-@SOUP_TRANSFER_NONE: 
-@SOUP_TRANSFER_EOF: 
-
-<!-- ##### ENUM SoupOwnership ##### -->
-<para>
-
-</para>
-
-@SOUP_BUFFER_SYSTEM_OWNED: 
-@SOUP_BUFFER_USER_OWNED: 
-@SOUP_BUFFER_STATIC: 
-
-<!-- ##### STRUCT SoupDataBuffer ##### -->
-<para>
-
-</para>
-
-@owner: 
-@body: 
-@length: 
-
-<!-- ##### USER_FUNCTION SoupMessageCallbackFn ##### -->
-<para>
-
-</para>
-
-@req: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_new ##### -->
-<para>
-
-</para>
-
-@method: 
-@uri_string: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_new_from_uri ##### -->
-<para>
-
-</para>
-
-@method: 
-@uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_set_request ##### -->
-<para>
-
-</para>
-
-@msg: 
-@content_type: 
-@req_owner: 
-@req_body: 
-@req_length: 
-
-
-<!-- ##### FUNCTION soup_message_set_response ##### -->
-<para>
-
-</para>
-
-@msg: 
-@content_type: 
-@resp_owner: 
-@resp_body: 
-@resp_length: 
-
-
-<!-- ##### FUNCTION soup_message_add_header ##### -->
-<para>
-
-</para>
-
-@hash: 
-@name: 
-@value: 
-
-
-<!-- ##### FUNCTION soup_message_get_header ##### -->
-<para>
-
-</para>
-
-@hash: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_get_header_list ##### -->
-<para>
-
-</para>
-
-@hash: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_foreach_header ##### -->
-<para>
-
-</para>
-
-@hash: 
-@func: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_remove_header ##### -->
-<para>
-
-</para>
-
-@hash: 
-@name: 
-
-
-<!-- ##### FUNCTION soup_message_clear_headers ##### -->
-<para>
-
-</para>
-
-@hash: 
-
-
-<!-- ##### ENUM SoupHttpVersion ##### -->
-<para>
-
-</para>
-
-@SOUP_HTTP_1_0: 
-@SOUP_HTTP_1_1: 
-
-<!-- ##### FUNCTION soup_message_set_http_version ##### -->
-<para>
-
-</para>
-
-@msg: 
-@version: 
-
-
-<!-- ##### FUNCTION soup_message_get_http_version ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_get_uri ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_set_uri ##### -->
-<para>
-
-</para>
-
-@msg: 
-@uri: 
-
-
-<!-- ##### ENUM SoupMessageFlags ##### -->
-<para>
-
-</para>
-
-@SOUP_MESSAGE_NO_REDIRECT: 
-@SOUP_MESSAGE_OVERWRITE_CHUNKS: 
-@SOUP_MESSAGE_EXPECT_CONTINUE: 
-
-<!-- ##### FUNCTION soup_message_set_flags ##### -->
-<para>
-
-</para>
-
-@msg: 
-@flags: 
-
-
-<!-- ##### FUNCTION soup_message_get_flags ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_set_status ##### -->
-<para>
-
-</para>
-
-@msg: 
-@status_code: 
-
-
-<!-- ##### FUNCTION soup_message_set_status_full ##### -->
-<para>
-
-</para>
-
-@msg: 
-@status_code: 
-@reason_phrase: 
-
-
-<!-- ##### FUNCTION soup_message_add_chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-@owner: 
-@body: 
-@length: 
-
-
-<!-- ##### FUNCTION soup_message_add_final_chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_message_pop_chunk ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_is_keepalive ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_get_request_encoding ##### -->
-<para>
-
-</para>
-
-@msg: 
-@content_length: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_message_get_response_encoding ##### -->
-<para>
-
-</para>
-
-@msg: 
-@content_length: 
-@Returns: 
-
-
-<!-- ##### ENUM SoupHandlerPhase ##### -->
-<para>
-
-</para>
-
-@SOUP_HANDLER_POST_REQUEST: 
-@SOUP_HANDLER_PRE_BODY: 
-@SOUP_HANDLER_BODY_CHUNK: 
-@SOUP_HANDLER_POST_BODY: 
-
-<!-- ##### FUNCTION soup_message_add_handler ##### -->
-<para>
-
-</para>
-
-@msg: 
-@phase: 
-@handler_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_add_header_handler ##### -->
-<para>
-
-</para>
-
-@msg: 
-@header: 
-@phase: 
-@handler_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_add_status_code_handler ##### -->
-<para>
-
-</para>
-
-@msg: 
-@status_code: 
-@phase: 
-@handler_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_add_status_class_handler ##### -->
-<para>
-
-</para>
-
-@msg: 
-@status_class: 
-@phase: 
-@handler_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_remove_handler ##### -->
-<para>
-
-</para>
-
-@msg: 
-@phase: 
-@handler_cb: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_message_send_request ##### -->
-<para>
-
-</para>
-
-@req: 
-@sock: 
-@is_via_proxy: 
-
-
-<!-- ##### FUNCTION soup_message_read_request ##### -->
-<para>
-
-</para>
-
-@req: 
-@sock: 
-
-
-<!-- ##### FUNCTION soup_message_io_pause ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_message_io_unpause ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_message_io_stop ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
diff --git a/docs/reference/tmpl/soup-misc.sgml b/docs/reference/tmpl/soup-misc.sgml
deleted file mode 100644 (file)
index 7f949d0..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-misc
-
-<!-- ##### SECTION Short_Description ##### -->
-Miscellaneous functions
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### FUNCTION soup_base64_decode ##### -->
-<para>
-
-</para>
-
-@text: 
-@out_len: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_base64_encode ##### -->
-<para>
-
-</para>
-
-@text: 
-@len: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_base64_decode_step ##### -->
-<para>
-
-</para>
-
-@in: 
-@len: 
-@out: 
-@state: 
-@save: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_base64_encode_step ##### -->
-<para>
-
-</para>
-
-@in: 
-@len: 
-@break_lines: 
-@out: 
-@state: 
-@save: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_base64_encode_close ##### -->
-<para>
-
-</para>
-
-@in: 
-@inlen: 
-@break_lines: 
-@out: 
-@state: 
-@save: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_add_idle ##### -->
-<para>
-
-</para>
-
-@async_context: 
-@function: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_add_io_watch ##### -->
-<para>
-
-</para>
-
-@async_context: 
-@chan: 
-@condition: 
-@function: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_add_timeout ##### -->
-<para>
-
-</para>
-
-@async_context: 
-@interval: 
-@function: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_signal_connect_once ##### -->
-<para>
-
-</para>
-
-@instance: 
-@detailed_signal: 
-@c_handler: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_date_parse ##### -->
-<para>
-
-</para>
-
-@timestamp: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_date_iso8601_parse ##### -->
-<para>
-
-</para>
-
-@timestamp: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_date_generate ##### -->
-<para>
-
-</para>
-
-@when: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_gmtime ##### -->
-<para>
-
-</para>
-
-@when: 
-@tm: 
-
-
-<!-- ##### FUNCTION soup_mktime_utc ##### -->
-<para>
-
-</para>
-
-@tm: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_header_param_copy_token ##### -->
-<para>
-
-</para>
-
-@tokens: 
-@t: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_header_param_decode_token ##### -->
-<para>
-
-</para>
-
-@in: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_header_param_destroy_hash ##### -->
-<para>
-
-</para>
-
-@table: 
-
-
-<!-- ##### FUNCTION soup_header_param_parse_list ##### -->
-<para>
-
-</para>
-
-@header: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_headers_parse_request ##### -->
-<para>
-
-</para>
-
-@str: 
-@len: 
-@dest: 
-@req_method: 
-@req_path: 
-@ver: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_headers_parse_response ##### -->
-<para>
-
-</para>
-
-@str: 
-@len: 
-@dest: 
-@ver: 
-@status_code: 
-@reason_phrase: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_headers_parse_status_line ##### -->
-<para>
-
-</para>
-
-@status_line: 
-@ver: 
-@status_code: 
-@reason_phrase: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_str_case_equal ##### -->
-<para>
-
-</para>
-
-@v1: 
-@v2: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_str_case_hash ##### -->
-<para>
-
-</para>
-
-@key: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xml_real_node ##### -->
-<para>
-
-</para>
-
-@node: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-server-auth.sgml b/docs/reference/tmpl/soup-server-auth.sgml
deleted file mode 100644 (file)
index cbe22e3..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-server-auth
-
-<!-- ##### SECTION Short_Description ##### -->
-Server-side authentication structures
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### STRUCT SoupServerAuthContext ##### -->
-<para>
-
-</para>
-
-@types: 
-@callback: 
-@user_data: 
-
-<!-- ##### USER_FUNCTION SoupServerAuthCallbackFn ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@auth: 
-@msg: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_auth_context_challenge ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@msg: 
-@header_name: 
-
-
-<!-- ##### ENUM SoupAuthType ##### -->
-<para>
-
-</para>
-
-@SOUP_AUTH_TYPE_BASIC: 
-@SOUP_AUTH_TYPE_DIGEST: 
-
-<!-- ##### STRUCT SoupServerAuthBasic ##### -->
-<para>
-
-</para>
-
-@type: 
-@user: 
-@passwd: 
-
-<!-- ##### ENUM SoupDigestAlgorithm ##### -->
-<para>
-
-</para>
-
-@SOUP_ALGORITHM_MD5: 
-@SOUP_ALGORITHM_MD5_SESS: 
-
-<!-- ##### STRUCT SoupServerAuthDigest ##### -->
-<para>
-
-</para>
-
-@type: 
-@algorithm: 
-@integrity: 
-@realm: 
-@user: 
-@nonce: 
-@nonce_count: 
-@cnonce: 
-@digest_uri: 
-@digest_response: 
-@request_method: 
-
-<!-- ##### FUNCTION soup_server_auth_new ##### -->
-<para>
-
-</para>
-
-@auth_ctx: 
-@auth_hdrs: 
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_auth_free ##### -->
-<para>
-
-</para>
-
-@auth: 
-
-
-<!-- ##### FUNCTION soup_server_auth_get_user ##### -->
-<para>
-
-</para>
-
-@auth: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_auth_check_passwd ##### -->
-<para>
-
-</para>
-
-@auth: 
-@passwd: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-server-message.sgml b/docs/reference/tmpl/soup-server-message.sgml
deleted file mode 100644 (file)
index d5ed721..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupServerMessage
-
-<!-- ##### SECTION Short_Description ##### -->
-Server-side #SoupMessage
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupServerMessage ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_server_message_new ##### -->
-<para>
-
-</para>
-
-@server: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_message_get_server ##### -->
-<para>
-
-</para>
-
-@smsg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_message_set_encoding ##### -->
-<para>
-
-</para>
-
-@smsg: 
-@encoding: 
-
-
-<!-- ##### FUNCTION soup_server_message_get_encoding ##### -->
-<para>
-
-</para>
-
-@smsg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_message_start ##### -->
-<para>
-
-</para>
-
-@smsg: 
-
-
-<!-- ##### FUNCTION soup_server_message_is_started ##### -->
-<para>
-
-</para>
-
-@smsg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_message_finish ##### -->
-<para>
-
-</para>
-
-@smsg: 
-
-
-<!-- ##### FUNCTION soup_server_message_is_finished ##### -->
-<para>
-
-</para>
-
-@smsg: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-server.sgml b/docs/reference/tmpl/soup-server.sgml
deleted file mode 100644 (file)
index 29babd8..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupServer
-
-<!-- ##### SECTION Short_Description ##### -->
-HTTP server
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupServer implements a simple HTTP server.
-</para>
-
-<para>
-This API is less stable than the soup client API, and will most likely
-change in the next release.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupServer ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### ARG SoupServer:async-context ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupServer:interface ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupServer:port ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupServer:ssl-cert-file ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupServer:ssl-key-file ##### -->
-<para>
-
-</para>
-
-<!-- ##### STRUCT SoupServerContext ##### -->
-<para>
-
-</para>
-
-@msg: 
-@path: 
-@method_id: 
-@auth: 
-@server: 
-@handler: 
-@sock: 
-
-<!-- ##### USER_FUNCTION SoupServerCallbackFn ##### -->
-<para>
-
-</para>
-
-@context: 
-@msg: 
-@user_data: 
-
-
-<!-- ##### USER_FUNCTION SoupServerUnregisterFn ##### -->
-<para>
-
-</para>
-
-@server: 
-@handler: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_server_new ##### -->
-<para>
-
-</para>
-
-@optname1: 
-@Varargs: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_get_protocol ##### -->
-<para>
-
-</para>
-
-@serv: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_get_port ##### -->
-<para>
-
-</para>
-
-@serv: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_get_listener ##### -->
-<para>
-
-</para>
-
-@serv: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_run ##### -->
-<para>
-
-</para>
-
-@serv: 
-
-
-<!-- ##### FUNCTION soup_server_run_async ##### -->
-<para>
-
-</para>
-
-@serv: 
-
-
-<!-- ##### FUNCTION soup_server_quit ##### -->
-<para>
-
-</para>
-
-@serv: 
-
-
-<!-- ##### TYPEDEF SoupServerHandler ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_server_add_handler ##### -->
-<para>
-
-</para>
-
-@serv: 
-@path: 
-@auth_ctx: 
-@callback: 
-@unreg: 
-@data: 
-
-
-<!-- ##### FUNCTION soup_server_remove_handler ##### -->
-<para>
-
-</para>
-
-@serv: 
-@path: 
-
-
-<!-- ##### FUNCTION soup_server_get_handler ##### -->
-<para>
-
-</para>
-
-@serv: 
-@path: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_list_handlers ##### -->
-<para>
-
-</para>
-
-@serv: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_context_get_client_address ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_server_context_get_client_host ##### -->
-<para>
-
-</para>
-
-@ctx: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_SERVER_PORT ##### -->
-<para>
-An alias for the "port" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SERVER_INTERFACE ##### -->
-<para>
-An alias for the "interface" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SERVER_SSL_CERT_FILE ##### -->
-<para>
-An alias for the "ssl-cert-file" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SERVER_SSL_KEY_FILE ##### -->
-<para>
-An alias for the "ssl-key-file" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SERVER_ASYNC_CONTEXT ##### -->
-<para>
-An alias for the "async-context" property.
-</para>
-
-
-
diff --git a/docs/reference/tmpl/soup-session-async.sgml b/docs/reference/tmpl/soup-session-async.sgml
deleted file mode 100644 (file)
index 5453324..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSessionAsync
-
-<!-- ##### SECTION Short_Description ##### -->
-Soup session for asynchronous (main-loop-based) I/O.
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupSessionASync is an implementation of #SoupSession that uses
-non-blocking I/O via the glib main loop. It is intended for use in
-single-threaded programs.
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSessionAsync ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_session_async_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_session_async_new_with_options ##### -->
-<para>
-
-</para>
-
-@optname1: 
-@Varargs: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-session-sync.sgml b/docs/reference/tmpl/soup-session-sync.sgml
deleted file mode 100644 (file)
index 63e3093..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSessionSync
-
-<!-- ##### SECTION Short_Description ##### -->
-Soup session for blocking I/O in multithreaded programs.
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-#SoupSessionSync is an implementation of #SoupSession that uses
-synchronous I/O, intended for use in multi-threaded programs.
-</para>
-
-<para>
-Note that you cannot use soup_session_queue_message() with a
-synchronous session. You can only use soup_session_send_message().
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSessionSync ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_session_sync_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_session_sync_new_with_options ##### -->
-<para>
-
-</para>
-
-@optname1: 
-@Varargs: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-session.sgml b/docs/reference/tmpl/soup-session.sgml
deleted file mode 100644 (file)
index 4c5d80d..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSession
-
-<!-- ##### SECTION Short_Description ##### -->
-Soup session state object
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSession ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SIGNAL SoupSession::authenticate ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-<!-- ##### SIGNAL SoupSession::reauthenticate ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-@auth_type: 
-@auth_realm: 
-@username: 
-@password: 
-
-<!-- ##### ARG SoupSession:async-context ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:max-conns ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:max-conns-per-host ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:proxy-uri ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:ssl-ca-file ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:timeout ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSession:use-ntlm ##### -->
-<para>
-
-</para>
-
-<!-- ##### FUNCTION soup_session_queue_message ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-@callback: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_session_requeue_message ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-
-
-<!-- ##### FUNCTION soup_session_send_message ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_session_abort ##### -->
-<para>
-
-</para>
-
-@session: 
-
-
-<!-- ##### FUNCTION soup_session_add_filter ##### -->
-<para>
-
-</para>
-
-@session: 
-@filter: 
-
-
-<!-- ##### FUNCTION soup_session_cancel_message ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-
-
-<!-- ##### FUNCTION soup_session_get_connection ##### -->
-<para>
-
-</para>
-
-@session: 
-@msg: 
-@try_pruning: 
-@is_new: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_session_remove_filter ##### -->
-<para>
-
-</para>
-
-@session: 
-@filter: 
-
-
-<!-- ##### FUNCTION soup_session_try_prune_connection ##### -->
-<para>
-
-</para>
-
-@session: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_SESSION_PROXY_URI ##### -->
-<para>
-An alias for the "proxy-uri" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_MAX_CONNS ##### -->
-<para>
-An alias for the "max-conns" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_MAX_CONNS_PER_HOST ##### -->
-<para>
-An alias for the "max-conns-per-host" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_USE_NTLM ##### -->
-<para>
-An alias for the "use-ntlm" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_SSL_CA_FILE ##### -->
-<para>
-An alias for the "ssl-ca-file" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_ASYNC_CONTEXT ##### -->
-<para>
-An alias for the "async-context" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SESSION_TIMEOUT ##### -->
-<para>
-An alias for the "timeout" property.
-</para>
-
-
-
diff --git a/docs/reference/tmpl/soup-soap-message.sgml b/docs/reference/tmpl/soup-soap-message.sgml
deleted file mode 100644 (file)
index 72c83ed..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSoapMessage
-
-<!-- ##### SECTION Short_Description ##### -->
-A SOAP request
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSoapMessage ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_soap_message_new ##### -->
-<para>
-
-</para>
-
-@method: 
-@uri_string: 
-@standalone: 
-@xml_encoding: 
-@env_prefix: 
-@env_uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_message_new_from_uri ##### -->
-<para>
-
-</para>
-
-@method: 
-@uri: 
-@standalone: 
-@xml_encoding: 
-@env_prefix: 
-@env_uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_envelope ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_envelope ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_body ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_element ##### -->
-<para>
-
-</para>
-
-@msg: 
-@name: 
-@prefix: 
-@ns_uri: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_element ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_fault ##### -->
-<para>
-
-</para>
-
-@msg: 
-@faultcode: 
-@faultstring: 
-@faultfactor: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_fault ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_fault_detail ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_fault_detail ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_header ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_header ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_start_header_element ##### -->
-<para>
-
-</para>
-
-@msg: 
-@name: 
-@must_understand: 
-@actor_uri: 
-@prefix: 
-@ns_uri: 
-
-
-<!-- ##### FUNCTION soup_soap_message_end_header_element ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_int ##### -->
-<para>
-
-</para>
-
-@msg: 
-@i: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_double ##### -->
-<para>
-
-</para>
-
-@msg: 
-@d: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_base64 ##### -->
-<para>
-
-</para>
-
-@msg: 
-@string: 
-@len: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_time ##### -->
-<para>
-
-</para>
-
-@msg: 
-@timeval: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_string ##### -->
-<para>
-
-</para>
-
-@msg: 
-@string: 
-
-
-<!-- ##### FUNCTION soup_soap_message_write_buffer ##### -->
-<para>
-
-</para>
-
-@msg: 
-@buffer: 
-@len: 
-
-
-<!-- ##### FUNCTION soup_soap_message_set_element_type ##### -->
-<para>
-
-</para>
-
-@msg: 
-@xsi_type: 
-
-
-<!-- ##### FUNCTION soup_soap_message_set_null ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_add_attribute ##### -->
-<para>
-
-</para>
-
-@msg: 
-@name: 
-@value: 
-@prefix: 
-@ns_uri: 
-
-
-<!-- ##### FUNCTION soup_soap_message_add_namespace ##### -->
-<para>
-
-</para>
-
-@msg: 
-@prefix: 
-@ns_uri: 
-
-
-<!-- ##### FUNCTION soup_soap_message_set_default_namespace ##### -->
-<para>
-
-</para>
-
-@msg: 
-@ns_uri: 
-
-
-<!-- ##### FUNCTION soup_soap_message_set_encoding_style ##### -->
-<para>
-
-</para>
-
-@msg: 
-@enc_style: 
-
-
-<!-- ##### FUNCTION soup_soap_message_reset ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_persist ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_soap_message_get_namespace_prefix ##### -->
-<para>
-
-</para>
-
-@msg: 
-@ns_uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_message_get_xml_doc ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_message_parse_response ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-soap-response.sgml b/docs/reference/tmpl/soup-soap-response.sgml
deleted file mode 100644 (file)
index 0bde3c0..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSoapResponse
-
-<!-- ##### SECTION Short_Description ##### -->
-A SOAP response
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSoapResponse ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### TYPEDEF SoupSoapParameter ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_soap_response_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_new_from_string ##### -->
-<para>
-
-</para>
-
-@xmlstr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_set_method_name ##### -->
-<para>
-
-</para>
-
-@response: 
-@method_name: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_first_child ##### -->
-<para>
-
-</para>
-
-@param: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_first_child_by_name ##### -->
-<para>
-
-</para>
-
-@param: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_int_value ##### -->
-<para>
-
-</para>
-
-@param: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_name ##### -->
-<para>
-
-</para>
-
-@param: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_next_child ##### -->
-<para>
-
-</para>
-
-@param: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_next_child_by_name ##### -->
-<para>
-
-</para>
-
-@param: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_property ##### -->
-<para>
-
-</para>
-
-@param: 
-@prop_name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_parameter_get_string_value ##### -->
-<para>
-
-</para>
-
-@param: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_from_string ##### -->
-<para>
-
-</para>
-
-@response: 
-@xmlstr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_first_parameter ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_first_parameter_by_name ##### -->
-<para>
-
-</para>
-
-@response: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_method_name ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_next_parameter ##### -->
-<para>
-
-</para>
-
-@response: 
-@from: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_next_parameter_by_name ##### -->
-<para>
-
-</para>
-
-@response: 
-@from: 
-@name: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_soap_response_get_parameters ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-socket.sgml b/docs/reference/tmpl/soup-socket.sgml
deleted file mode 100644 (file)
index 0a86e70..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupSocket
-
-<!-- ##### SECTION Short_Description ##### -->
-a network socket
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupSocket ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SIGNAL SoupSocket::connect-result ##### -->
-<para>
-
-</para>
-
-@sock: 
-@status: 
-
-<!-- ##### SIGNAL SoupSocket::disconnected ##### -->
-<para>
-
-</para>
-
-@sock: 
-
-<!-- ##### SIGNAL SoupSocket::new-connection ##### -->
-<para>
-
-</para>
-
-@sock: 
-@new: 
-
-<!-- ##### SIGNAL SoupSocket::readable ##### -->
-<para>
-
-</para>
-
-@sock: 
-
-<!-- ##### SIGNAL SoupSocket::writable ##### -->
-<para>
-
-</para>
-
-@sock: 
-
-<!-- ##### ARG SoupSocket:async-context ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:cloexec ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:is-server ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:nodelay ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:non-blocking ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:reuseaddr ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:ssl-creds ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG SoupSocket:timeout ##### -->
-<para>
-
-</para>
-
-<!-- ##### FUNCTION soup_socket_new ##### -->
-<para>
-
-</para>
-
-@optname1: 
-@Varargs: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_connect ##### -->
-<para>
-
-</para>
-
-@sock: 
-@remote_addr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_listen ##### -->
-<para>
-
-</para>
-
-@sock: 
-@local_addr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_start_ssl ##### -->
-<para>
-
-</para>
-
-@sock: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_start_proxy_ssl ##### -->
-<para>
-
-</para>
-
-@sock: 
-@ssl_host: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_disconnect ##### -->
-<para>
-
-</para>
-
-@sock: 
-
-
-<!-- ##### FUNCTION soup_socket_is_connected ##### -->
-<para>
-
-</para>
-
-@sock: 
-@Returns: 
-
-
-<!-- ##### USER_FUNCTION SoupSocketCallback ##### -->
-<para>
-
-</para>
-
-@sock: 
-@status: 
-@user_data: 
-
-
-<!-- ##### USER_FUNCTION SoupSocketListenerCallback ##### -->
-<para>
-
-</para>
-
-@listener: 
-@sock: 
-@user_data: 
-
-
-<!-- ##### FUNCTION soup_socket_client_new_async ##### -->
-<para>
-
-</para>
-
-@hostname: 
-@port: 
-@ssl_creds: 
-@callback: 
-@user_data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_client_new_sync ##### -->
-<para>
-
-</para>
-
-@hostname: 
-@port: 
-@ssl_creds: 
-@status_ret: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_server_new ##### -->
-<para>
-
-</para>
-
-@local_addr: 
-@ssl_creds: 
-@callback: 
-@user_data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_get_local_address ##### -->
-<para>
-
-</para>
-
-@sock: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_get_remote_address ##### -->
-<para>
-
-</para>
-
-@sock: 
-@Returns: 
-
-
-<!-- ##### ENUM SoupSocketIOStatus ##### -->
-<para>
-
-</para>
-
-@SOUP_SOCKET_OK: 
-@SOUP_SOCKET_WOULD_BLOCK: 
-@SOUP_SOCKET_EOF: 
-@SOUP_SOCKET_ERROR: 
-
-<!-- ##### FUNCTION soup_socket_read ##### -->
-<para>
-
-</para>
-
-@sock: 
-@buffer: 
-@len: 
-@nread: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_read_until ##### -->
-<para>
-
-</para>
-
-@sock: 
-@buffer: 
-@len: 
-@boundary: 
-@boundary_len: 
-@nread: 
-@got_boundary: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_socket_write ##### -->
-<para>
-
-</para>
-
-@sock: 
-@buffer: 
-@len: 
-@nwrote: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_SOCKET_FLAG_NONBLOCKING ##### -->
-<para>
-An alias for the "non-blocking" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_FLAG_NODELAY ##### -->
-<para>
-An alias for the "nodelay" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_FLAG_REUSEADDR ##### -->
-<para>
-An alias for the "reuseaddr" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_FLAG_CLOEXEC ##### -->
-<para>
-An alias for the "cloexec" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_TIMEOUT ##### -->
-<para>
-An alias for the "timeout" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_IS_SERVER ##### -->
-<para>
-An alias for the "is-server" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_SSL_CREDENTIALS ##### -->
-<para>
-An alias for the "ssl-creds" property.
-</para>
-
-
-
-<!-- ##### MACRO SOUP_SOCKET_ASYNC_CONTEXT ##### -->
-<para>
-An alias for the "async-context" property.
-</para>
-
-
-
diff --git a/docs/reference/tmpl/soup-ssl.sgml b/docs/reference/tmpl/soup-ssl.sgml
deleted file mode 100644 (file)
index 714c88e..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-ssl
-
-<!-- ##### SECTION Short_Description ##### -->
-SSL/TLS handling
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### VARIABLE soup_ssl_supported ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### ENUM SoupSSLType ##### -->
-<para>
-
-</para>
-
-@SOUP_SSL_TYPE_CLIENT: 
-@SOUP_SSL_TYPE_SERVER: 
-
-<!-- ##### TYPEDEF SoupSSLCredentials ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_ssl_get_client_credentials ##### -->
-<para>
-
-</para>
-
-@ca_file: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_ssl_free_client_credentials ##### -->
-<para>
-
-</para>
-
-@creds: 
-
-
-<!-- ##### FUNCTION soup_ssl_get_server_credentials ##### -->
-<para>
-
-</para>
-
-@cert_file: 
-@key_file: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_ssl_free_server_credentials ##### -->
-<para>
-
-</para>
-
-@creds: 
-
-
-<!-- ##### FUNCTION soup_ssl_wrap_iochannel ##### -->
-<para>
-
-</para>
-
-@sock: 
-@type: 
-@remote_host: 
-@creds: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_SSL_ERROR ##### -->
-<para>
-
-</para>
-
-
-
-<!-- ##### FUNCTION soup_ssl_error_quark ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### ENUM SoupSocketError ##### -->
-<para>
-
-</para>
-
-@SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ: 
-@SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE: 
-@SOUP_SSL_ERROR_CERTIFICATE: 
-
diff --git a/docs/reference/tmpl/soup-status.sgml b/docs/reference/tmpl/soup-status.sgml
deleted file mode 100644 (file)
index b5d8f17..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-status
-
-<!-- ##### SECTION Short_Description ##### -->
-HTTP and Soup status codes
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### ENUM SoupStatusClass ##### -->
-<para>
-
-</para>
-
-@SOUP_STATUS_CLASS_TRANSPORT_ERROR: 
-@SOUP_STATUS_CLASS_INFORMATIONAL: 
-@SOUP_STATUS_CLASS_SUCCESS: 
-@SOUP_STATUS_CLASS_REDIRECT: 
-@SOUP_STATUS_CLASS_CLIENT_ERROR: 
-@SOUP_STATUS_CLASS_SERVER_ERROR: 
-
-<!-- ##### MACRO SOUP_STATUS_IS_INFORMATIONAL ##### -->
-<para>
-
-</para>
-
-@status: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_STATUS_IS_SUCCESSFUL ##### -->
-<para>
-
-</para>
-
-@status: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_STATUS_IS_REDIRECTION ##### -->
-<para>
-
-</para>
-
-@status: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_STATUS_IS_CLIENT_ERROR ##### -->
-<para>
-
-</para>
-
-@status: 
-@Returns: 
-
-
-<!-- ##### MACRO SOUP_STATUS_IS_SERVER_ERROR ##### -->
-<para>
-
-</para>
-
-@status: 
-@Returns: 
-
-
-<!-- ##### ENUM SoupKnownStatusCode ##### -->
-<para>
-
-</para>
-
-@SOUP_STATUS_NONE: 
-@SOUP_STATUS_CANCELLED: 
-@SOUP_STATUS_CANT_RESOLVE: 
-@SOUP_STATUS_CANT_RESOLVE_PROXY: 
-@SOUP_STATUS_CANT_CONNECT: 
-@SOUP_STATUS_CANT_CONNECT_PROXY: 
-@SOUP_STATUS_SSL_FAILED: 
-@SOUP_STATUS_IO_ERROR: 
-@SOUP_STATUS_MALFORMED: 
-@SOUP_STATUS_TRY_AGAIN: 
-@SOUP_STATUS_CONTINUE: 
-@SOUP_STATUS_SWITCHING_PROTOCOLS: 
-@SOUP_STATUS_PROCESSING: 
-@SOUP_STATUS_OK: 
-@SOUP_STATUS_CREATED: 
-@SOUP_STATUS_ACCEPTED: 
-@SOUP_STATUS_NON_AUTHORITATIVE: 
-@SOUP_STATUS_NO_CONTENT: 
-@SOUP_STATUS_RESET_CONTENT: 
-@SOUP_STATUS_PARTIAL_CONTENT: 
-@SOUP_STATUS_MULTI_STATUS: 
-@SOUP_STATUS_MULTIPLE_CHOICES: 
-@SOUP_STATUS_MOVED_PERMANENTLY: 
-@SOUP_STATUS_FOUND: 
-@SOUP_STATUS_MOVED_TEMPORARILY: 
-@SOUP_STATUS_SEE_OTHER: 
-@SOUP_STATUS_NOT_MODIFIED: 
-@SOUP_STATUS_USE_PROXY: 
-@SOUP_STATUS_NOT_APPEARING_IN_THIS_PROTOCOL: 
-@SOUP_STATUS_TEMPORARY_REDIRECT: 
-@SOUP_STATUS_BAD_REQUEST: 
-@SOUP_STATUS_UNAUTHORIZED: 
-@SOUP_STATUS_PAYMENT_REQUIRED: 
-@SOUP_STATUS_FORBIDDEN: 
-@SOUP_STATUS_NOT_FOUND: 
-@SOUP_STATUS_METHOD_NOT_ALLOWED: 
-@SOUP_STATUS_NOT_ACCEPTABLE: 
-@SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 
-@SOUP_STATUS_PROXY_UNAUTHORIZED: 
-@SOUP_STATUS_REQUEST_TIMEOUT: 
-@SOUP_STATUS_CONFLICT: 
-@SOUP_STATUS_GONE: 
-@SOUP_STATUS_LENGTH_REQUIRED: 
-@SOUP_STATUS_PRECONDITION_FAILED: 
-@SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE: 
-@SOUP_STATUS_REQUEST_URI_TOO_LONG: 
-@SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE: 
-@SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: 
-@SOUP_STATUS_INVALID_RANGE: 
-@SOUP_STATUS_EXPECTATION_FAILED: 
-@SOUP_STATUS_UNPROCESSABLE_ENTITY: 
-@SOUP_STATUS_LOCKED: 
-@SOUP_STATUS_FAILED_DEPENDENCY: 
-@SOUP_STATUS_INTERNAL_SERVER_ERROR: 
-@SOUP_STATUS_NOT_IMPLEMENTED: 
-@SOUP_STATUS_BAD_GATEWAY: 
-@SOUP_STATUS_SERVICE_UNAVAILABLE: 
-@SOUP_STATUS_GATEWAY_TIMEOUT: 
-@SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 
-@SOUP_STATUS_INSUFFICIENT_STORAGE: 
-@SOUP_STATUS_NOT_EXTENDED: 
-
-<!-- ##### FUNCTION soup_status_get_phrase ##### -->
-<para>
-
-</para>
-
-@status_code: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-uri.sgml b/docs/reference/tmpl/soup-uri.sgml
deleted file mode 100644 (file)
index a3814d7..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-soup-uri
-
-<!-- ##### SECTION Short_Description ##### -->
-URIs
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupUri ##### -->
-<para>
-
-</para>
-
-@protocol: 
-@user: 
-@passwd: 
-@host: 
-@port: 
-@path: 
-@query: 
-@fragment: 
-@broken_encoding: 
-
-<!-- ##### TYPEDEF SoupProtocol ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### MACRO SOUP_PROTOCOL_HTTP ##### -->
-<para>
-
-</para>
-
-
-
-<!-- ##### MACRO SOUP_PROTOCOL_HTTPS ##### -->
-<para>
-
-</para>
-
-
-
-<!-- ##### FUNCTION soup_uri_new_with_base ##### -->
-<para>
-
-</para>
-
-@base: 
-@uri_string: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_new ##### -->
-<para>
-
-</para>
-
-@uri_string: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_to_string ##### -->
-<para>
-
-</para>
-
-@uri: 
-@just_path: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_copy ##### -->
-<para>
-
-</para>
-
-@uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_copy_root ##### -->
-<para>
-
-</para>
-
-@uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_equal ##### -->
-<para>
-
-</para>
-
-@uri1: 
-@uri2: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_free ##### -->
-<para>
-
-</para>
-
-@uri: 
-
-
-<!-- ##### FUNCTION soup_uri_encode ##### -->
-<para>
-
-</para>
-
-@part: 
-@escape_extra: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_uri_decode ##### -->
-<para>
-
-</para>
-
-@part: 
-
-
-<!-- ##### FUNCTION soup_uri_uses_default_port ##### -->
-<para>
-
-</para>
-
-@uri: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-xmlrpc-message.sgml b/docs/reference/tmpl/soup-xmlrpc-message.sgml
deleted file mode 100644 (file)
index 93e4ce6..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupXmlrpcMessage
-
-<!-- ##### SECTION Short_Description ##### -->
-An XML-RPC Message
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupXmlrpcMessage ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_new ##### -->
-<para>
-
-</para>
-
-@uri_string: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_new_from_uri ##### -->
-<para>
-
-</para>
-
-@uri: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_from_string ##### -->
-<para>
-
-</para>
-
-@message: 
-@xmlstr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_start_call ##### -->
-<para>
-
-</para>
-
-@msg: 
-@method_name: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_end_call ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_start_param ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_end_param ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_int ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Param2: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_boolean ##### -->
-<para>
-
-</para>
-
-@msg: 
-@b: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_string ##### -->
-<para>
-
-</para>
-
-@msg: 
-@str: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_double ##### -->
-<para>
-
-</para>
-
-@msg: 
-@d: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_datetime ##### -->
-<para>
-
-</para>
-
-@msg: 
-@timeval: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_write_base64 ##### -->
-<para>
-
-</para>
-
-@msg: 
-@buf: 
-@len: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_start_struct ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_end_struct ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_start_member ##### -->
-<para>
-
-</para>
-
-@msg: 
-@name: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_end_member ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_start_array ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_end_array ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_to_string ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_persist ##### -->
-<para>
-
-</para>
-
-@msg: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_message_parse_response ##### -->
-<para>
-
-</para>
-
-@msg: 
-@Returns: 
-
-
diff --git a/docs/reference/tmpl/soup-xmlrpc-response.sgml b/docs/reference/tmpl/soup-xmlrpc-response.sgml
deleted file mode 100644 (file)
index 2f678b2..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-<!-- ##### SECTION Title ##### -->
-SoupXmlrpcResponse
-
-<!-- ##### SECTION Short_Description ##### -->
-An XML-RPC response
-
-<!-- ##### SECTION Long_Description ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION See_Also ##### -->
-<para>
-
-</para>
-
-<!-- ##### SECTION Stability_Level ##### -->
-
-
-<!-- ##### STRUCT SoupXmlrpcResponse ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_xmlrpc_response_new ##### -->
-<para>
-
-</para>
-
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_response_new_from_string ##### -->
-<para>
-
-</para>
-
-@xmlstr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_response_from_string ##### -->
-<para>
-
-</para>
-
-@response: 
-@xmlstr: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_response_to_string ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
-<!-- ##### TYPEDEF SoupXmlrpcValue ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### ENUM SoupXmlrpcValueType ##### -->
-<para>
-
-</para>
-
-@SOUP_XMLRPC_VALUE_TYPE_BAD: 
-@SOUP_XMLRPC_VALUE_TYPE_INT: 
-@SOUP_XMLRPC_VALUE_TYPE_BOOLEAN: 
-@SOUP_XMLRPC_VALUE_TYPE_STRING: 
-@SOUP_XMLRPC_VALUE_TYPE_DOUBLE: 
-@SOUP_XMLRPC_VALUE_TYPE_DATETIME: 
-@SOUP_XMLRPC_VALUE_TYPE_BASE64: 
-@SOUP_XMLRPC_VALUE_TYPE_STRUCT: 
-@SOUP_XMLRPC_VALUE_TYPE_ARRAY: 
-
-<!-- ##### FUNCTION soup_xmlrpc_response_is_fault ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_response_get_value ##### -->
-<para>
-
-</para>
-
-@response: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_type ##### -->
-<para>
-
-</para>
-
-@value: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_int ##### -->
-<para>
-
-</para>
-
-@value: 
-@i: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_double ##### -->
-<para>
-
-</para>
-
-@value: 
-@b: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_boolean ##### -->
-<para>
-
-</para>
-
-@value: 
-@b: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_string ##### -->
-<para>
-
-</para>
-
-@value: 
-@str: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_datetime ##### -->
-<para>
-
-</para>
-
-@value: 
-@timeval: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_base64 ##### -->
-<para>
-
-</para>
-
-@value: 
-@data: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_get_struct ##### -->
-<para>
-
-</para>
-
-@value: 
-@table: 
-@Returns: 
-
-
-<!-- ##### TYPEDEF SoupXmlrpcValueArrayIterator ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_array_get_iterator ##### -->
-<para>
-
-</para>
-
-@value: 
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_array_iterator_prev ##### -->
-<para>
-
-</para>
-
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_array_iterator_next ##### -->
-<para>
-
-</para>
-
-@iter: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_array_iterator_get_value ##### -->
-<para>
-
-</para>
-
-@iter: 
-@value: 
-@Returns: 
-
-
-<!-- ##### FUNCTION soup_xmlrpc_value_dump ##### -->
-<para>
-
-</para>
-
-@value: 
-
-
index d281079..2979eed 100644 (file)
@@ -16,54 +16,73 @@ INCLUDES =                          \
 MARSHAL_GENERATED = soup-marshal.c soup-marshal.h
 
 soup-marshal.h: soup-marshal.list
-       ( @GLIB_GENMARSHAL@ --prefix=soup_marshal $(srcdir)/soup-marshal.list --header > soup-marshal.tmp \
+       ( $(GLIB_GENMARSHAL) --prefix=soup_marshal $(srcdir)/soup-marshal.list --header > soup-marshal.tmp \
        && mv soup-marshal.tmp soup-marshal.h ) \
        || ( rm -f soup-marshal.tmp && exit 1 )
 
 soup-marshal.c: soup-marshal.h
-       ( (echo '#include "soup-marshal.h"'; @GLIB_GENMARSHAL@ --prefix=soup_marshal $(srcdir)/soup-marshal.list --body) > soup-marshal.tmp \
+       ( (echo '#include "soup-marshal.h"'; $(GLIB_GENMARSHAL) --prefix=soup_marshal $(srcdir)/soup-marshal.list --body) > soup-marshal.tmp \
        && mv soup-marshal.tmp soup-marshal.c ) \
        || ( rm -f soup-marshal.tmp && exit 1 )
 
+soup-enum-types.h: $(soup_headers)
+       ( cd $(srcdir) && $(GLIB_MKENUMS) --template soup-enum-types.h.tmpl \
+               $(soup_headers) ) > soup-enum-types.h.tmp \
+       && mv soup-enum-types.h.tmp soup-enum-types.h \
+       || rm -f soup-enum-type.h.tmp
+
+soup-enum-types.c: $(libsoupinclude_HEADERS)
+       ( cd $(srcdir) && $(GLIB_MKENUMS) --template soup-enum-types.c.tmpl \
+               $(soup_headers) ) > soup-enum-types.c.tmp \
+       && mv soup-enum-types.c.tmp soup-enum-types.c \
+       || rm -f soup-enum-type.c.tmp
+
 BUILT_SOURCES = $(MARSHAL_GENERATED)
 
 CLEANFILES = $(MARSHAL_GENERATED)
 
-libsoupincludedir = $(includedir)/libsoup-2.2/libsoup
+DISTCLEANFILES = soup-enum-types.h soup-enum-types.c
 
-libsoupinclude_HEADERS =       \
+libsoupincludedir = $(includedir)/libsoup-$(SOUP_API_VERSION)/libsoup
+
+soup_headers =                 \
        soup.h                  \
        soup-address.h          \
-       soup-connection.h       \
+       soup-auth.h             \
+       soup-auth-domain.h      \
+       soup-auth-domain-basic.h  \
+       soup-auth-domain-digest.h \
        soup-date.h             \
+       soup-form.h             \
        soup-headers.h          \
+       soup-logger.h           \
        soup-message.h          \
-       soup-message-filter.h   \
-       soup-message-queue.h    \
+       soup-message-body.h     \
+       soup-message-headers.h  \
        soup-method.h           \
        soup-misc.h             \
        soup-portability.h      \
-       soup-server-auth.h      \
-       soup-server-message.h   \
        soup-server.h           \
        soup-session.h          \
        soup-session-async.h    \
        soup-session-sync.h     \
-       soup-soap-message.h     \
-       soup-soap-response.h    \
        soup-socket.h           \
        soup-status.h           \
        soup-types.h            \
        soup-uri.h              \
-       soup-xmlrpc-message.h   \
-       soup-xmlrpc-response.h
+       soup-value-utils.h      \
+       soup-xmlrpc.h
+
+libsoupinclude_HEADERS =       \
+       $(soup_headers)         \
+       soup-enum-types.h
 
-lib_LTLIBRARIES = libsoup-2.2.la
+lib_LTLIBRARIES = libsoup-2.4.la
 
-libsoup_2_2_la_LDFLAGS =       \
+libsoup_2_4_la_LDFLAGS =       \
        -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) -no-undefined
 
-libsoup_2_2_la_LIBADD =                        \
+libsoup_2_4_la_LIBADD =                        \
        $(GLIB_LIBS)                    \
        $(XML_LIBS)                     \
        $(LIBGNUTLS_LIBS_STATIC)        \
@@ -71,49 +90,56 @@ libsoup_2_2_la_LIBADD =                     \
        $(LIBGCRYPT_LIBS)               \
        $(LIBWS2_32)
 
-libsoup_2_2_la_SOURCES =               \
+libsoup_2_4_la_SOURCES =               \
        $(MARSHAL_GENERATED)            \
        soup-address.c                  \
-       soup-auth.h                     \
        soup-auth.c                     \
        soup-auth-basic.h               \
        soup-auth-basic.c               \
        soup-auth-digest.h              \
        soup-auth-digest.c              \
+       soup-auth-ntlm.h                \
+       soup-auth-ntlm.c                \
+       soup-auth-domain.c              \
+       soup-auth-domain-basic.c        \
+       soup-auth-domain-digest.c       \
+       soup-auth-manager.h             \
+       soup-auth-manager.c             \
+       soup-connection.h               \
        soup-connection.c               \
        soup-connection-ntlm.h          \
        soup-connection-ntlm.c          \
        soup-date.c                     \
+       soup-enum-types.c               \
        soup-dns.h                      \
        soup-dns.c                      \
+       soup-form.c                     \
        soup-gnutls.c                   \
        soup-headers.c                  \
-       soup-md5-utils.h                \
-       soup-md5-utils.c                \
+       soup-logger.c                   \
        soup-message.c                  \
+       soup-message-body.c             \
        soup-message-client-io.c        \
-       soup-message-filter.c           \
-       soup-message-handlers.c         \
+       soup-message-headers.c          \
        soup-message-io.c               \
        soup-message-private.h          \
+       soup-message-queue.h            \
        soup-message-queue.c            \
        soup-message-server-io.c        \
-       soup-method.c                   \
        soup-misc.c                     \
        soup-nossl.c                    \
+       soup-path-map.h                 \
+       soup-path-map.c                 \
        soup-server.c                   \
-       soup-server-auth.c              \
-       soup-server-message.c           \
        soup-session.c                  \
        soup-session-async.c            \
+       soup-session-private.h          \
        soup-session-sync.c             \
-       soup-soap-message.c             \
-       soup-soap-response.c            \
        soup-socket.c                   \
        soup-ssl.h                      \
        soup-status.c                   \
        soup-uri.c                      \
-       soup-xmlrpc-message.c           \
-       soup-xmlrpc-response.c
+       soup-value-utils.c              \
+       soup-xmlrpc.c
 
 EXTRA_DIST= soup-marshal.list
index 20f54cb..628f4ba 100644 (file)
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
-#include <signal.h>
 
 #include "soup-address.h"
 #include "soup-dns.h"
 #define INADDR_NONE -1
 #endif
 
+/**
+ * SECTION:soup-address
+ * @short_description: DNS support
+ *
+ * #SoupAddress represents the address of a TCP connection endpoint;
+ * both the IP address and the port. (It is somewhat like an
+ * object-oriented version of struct sockaddr.)
+ *
+ * If libsoup was built with IPv6 support, #SoupAddress will allow
+ * both IPv4 and IPv6 addresses.
+ **/
+
 typedef struct {
        struct sockaddr *sockaddr;
 
@@ -99,13 +110,6 @@ typedef struct {
        memcpy (SOUP_ADDRESS_GET_DATA (priv), data, length)
 
 
-enum {
-       DNS_RESULT,
-       LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
 G_DEFINE_TYPE (SoupAddress, soup_address, G_TYPE_OBJECT)
 
 static void
@@ -146,26 +150,6 @@ soup_address_class_init (SoupAddressClass *address_class)
        /* virtual method override */
        object_class->finalize = finalize;
 
-       /* signals */
-
-       /**
-        * SoupAddress::dns-result:
-        * @addr: the #SoupAddress
-        * @status: the DNS status code
-        *
-        * Emitted when an address resolution completes. (This is used
-        * internally by soup_address_resolve_async() itself.)
-        **/
-       signals[DNS_RESULT] =
-               g_signal_new ("dns_result",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupAddressClass, dns_result),
-                             NULL, NULL,
-                             soup_marshal_NONE__INT,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
-
 #ifdef G_OS_WIN32
        /* This hopefully is a good place to call WSAStartup */
        {
@@ -248,6 +232,13 @@ soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
  **/
 
 /**
+ * SOUP_ADDRESS_ANY_PORT:
+ *
+ * This can be passed to any #SoupAddress method that expects a port,
+ * to indicate that you don't care what port is used.
+ **/
+
+/**
  * soup_address_new_any:
  * @family: the address family
  * @port: the port number (usually %SOUP_ADDRESS_ANY_PORT)
@@ -363,91 +354,124 @@ soup_address_get_port (SoupAddress *addr)
 
 
 static void
-update_address (SoupDNSLookup *lookup, gboolean success, gpointer user_data)
+update_address (SoupAddress *addr, SoupDNSLookup *lookup)
 {
-       SoupAddress *addr = user_data;
        SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
 
-       if (success) {
-               if (!priv->name)
-                       priv->name = soup_dns_lookup_get_hostname (lookup);
+       if (!priv->name)
+               priv->name = soup_dns_lookup_get_hostname (lookup);
 
-               if (!priv->sockaddr) {
-                       priv->sockaddr = soup_dns_lookup_get_address (lookup);
-                       SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
-               }
+       if (!priv->sockaddr) {
+               priv->sockaddr = soup_dns_lookup_get_address (lookup);
+               SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
        }
+}
+
+typedef struct {
+       SoupAddress         *addr;
+       SoupAddressCallback  callback;
+       gpointer             callback_data;
+} SoupAddressResolveAsyncData;
+
+static void
+lookup_resolved (SoupDNSLookup *lookup, guint status, gpointer user_data)
+{
+       SoupAddressResolveAsyncData *res_data = user_data;
+       SoupAddress *addr;
+       SoupAddressCallback callback;
+       gpointer callback_data;
+
+       addr = res_data->addr;
+       callback = res_data->callback;
+       callback_data = res_data->callback_data;
+       g_free (res_data);
 
-       g_signal_emit (addr, signals[DNS_RESULT], 0, success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE);
+       if (status == SOUP_STATUS_OK)
+               update_address (addr, lookup);
+
+       if (callback)
+               callback (addr, status, callback_data);
+
+       g_object_unref (addr);
 }
 
 /**
- * soup_address_resolve_async:
- * @addr: a #SoupAddress
- * @callback: callback to call with the result
- * @user_data: data for @callback
+ * SoupAddressCallback:
+ * @addr: the #SoupAddress that was resolved
+ * @status: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED
+ * @data: the user data that was passed to
+ * soup_address_resolve_async()
  *
- * Asynchronously resolves the missing half of @addr. (Its IP address
- * if it was created with soup_address_new(), or its hostname if it
- * was created with soup_address_new_from_sockaddr() or
- * soup_address_new_any().) @callback will be called when the
- * resolution finishes (successfully or not).
+ * The callback function passed to soup_address_resolve_async().
  **/
-void
-soup_address_resolve_async (SoupAddress *addr,
-                           SoupAddressCallback callback,
-                           gpointer user_data)
-{
-       soup_address_resolve_async_full (addr, NULL, callback, user_data);
-}
 
 /**
- * soup_address_resolve_async_full:
+ * soup_address_resolve_async:
  * @addr: a #SoupAddress
  * @async_context: the #GMainContext to call @callback from
+ * @cancellable: a #GCancellable object, or %NULL
  * @callback: callback to call with the result
  * @user_data: data for @callback
  *
- * Like soup_address_resolve_async(), but calls @callback from
- * the given @async_context.
+ * Asynchronously resolves the missing half of @addr. (Its IP address
+ * if it was created with soup_address_new(), or its hostname if it
+ * was created with soup_address_new_from_sockaddr() or
+ * soup_address_new_any().)
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * resolution. @callback will still be invoked in this case, with a
+ * status of %SOUP_STATUS_CANCELLED.
  **/
 void
-soup_address_resolve_async_full (SoupAddress *addr, GMainContext *async_context,
-                                SoupAddressCallback callback,
-                                gpointer user_data)
+soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context,
+                           GCancellable *cancellable,
+                           SoupAddressCallback callback, gpointer user_data)
 {
        SoupAddressPrivate *priv;
+       SoupAddressResolveAsyncData *res_data;
 
        g_return_if_fail (SOUP_IS_ADDRESS (addr));
        priv = SOUP_ADDRESS_GET_PRIVATE (addr);
 
-       if (callback) {
-               soup_signal_connect_once (addr, "dns_result",
-                                         G_CALLBACK (callback), user_data);
-       }
+       res_data = g_new (SoupAddressResolveAsyncData, 1);
+       res_data->addr          = addr;
+       res_data->callback      = callback;
+       res_data->callback_data = user_data;
 
-       soup_dns_lookup_resolve_async (priv->lookup, async_context, update_address, addr);
+       g_object_ref (addr);
+       soup_dns_lookup_resolve_async (priv->lookup, async_context, cancellable,
+                                      lookup_resolved, res_data);
 }
 
 /**
  * soup_address_resolve_sync:
  * @addr: a #SoupAddress
+ * @cancellable: a #GCancellable object, or %NULL
  *
  * Synchronously resolves the missing half of @addr, as with
  * soup_address_resolve_async().
  *
- * Return value: %SOUP_STATUS_OK or %SOUP_STATUS_CANT_RESOLVE
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * resolution. soup_address_resolve_sync() will then return a status
+ * of %SOUP_STATUS_CANCELLED.
+ *
+ * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED.
  **/
 guint
-soup_address_resolve_sync (SoupAddress *addr)
+soup_address_resolve_sync (SoupAddress *addr, GCancellable *cancellable)
 {
        SoupAddressPrivate *priv;
-       gboolean success;
+       guint status;
 
        g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
        priv = SOUP_ADDRESS_GET_PRIVATE (addr);
 
-       success = soup_dns_lookup_resolve (priv->lookup);
-       update_address (priv->lookup, success, addr);
-       return success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE;
+       g_object_ref (addr);
+       status = soup_dns_lookup_resolve (priv->lookup, cancellable);
+       if (status == SOUP_STATUS_OK)
+               update_address (addr, priv->lookup);
+       g_object_unref (addr);
+       return status;
 }
index 68357b0..92e71fe 100644 (file)
@@ -8,6 +8,8 @@
 
 #include <sys/types.h>
 
+#include <gio/gio.h>
+
 #include <libsoup/soup-portability.h>
 #include <libsoup/soup-types.h>
 
@@ -28,42 +30,29 @@ struct SoupAddress {
 typedef struct {
        GObjectClass parent_class;
 
-       /* signals */
-       void (*dns_result) (SoupAddress *addr, guint status);
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupAddressClass;
 
-/* This is messy, but gtk-doc doesn't understand if the #if occurs
- * inside the typedef.
- */
-#ifdef AF_INET6
+/* gtk-doc gets confused if there's an #ifdef inside the typedef */
+#ifndef AF_INET6
+#define AF_INET6 -1
+#endif
+
 typedef enum {
        SOUP_ADDRESS_FAMILY_IPV4 = AF_INET,
        SOUP_ADDRESS_FAMILY_IPV6 = AF_INET6
 } SoupAddressFamily;
-#else
-typedef enum {
-       SOUP_ADDRESS_FAMILY_IPV4 = AF_INET,
-       SOUP_ADDRESS_FAMILY_IPV6 = -1
-} SoupAddressFamily;
+
+#if AF_INET6 == -1
+#undef AF_INET6
 #endif
 
-/**
- * SOUP_ADDRESS_ANY_PORT:
- *
- * This can be passed to any #SoupAddress method that expects a port,
- * to indicate that you don't care what port is used.
- **/
 #define SOUP_ADDRESS_ANY_PORT 0
 
-/**
- * SoupAddressCallback:
- * @addr: the #SoupAddress that was resolved
- * @status: %SOUP_STATUS_OK or %SOUP_STATUS_CANT_RESOLVE
- * @data: the user data that was passed to
- * soup_address_resolve_async()
- *
- * The callback function passed to soup_address_resolve_async().
- **/
 typedef void   (*SoupAddressCallback)            (SoupAddress         *addr,
                                                  guint                status,
                                                  gpointer             data);
@@ -78,13 +67,12 @@ SoupAddress     *soup_address_new_any            (SoupAddressFamily    family,
                                                  guint                port);
 
 void             soup_address_resolve_async      (SoupAddress         *addr,
-                                                 SoupAddressCallback  callback,
-                                                 gpointer             user_data);
-void             soup_address_resolve_async_full (SoupAddress         *addr,
                                                  GMainContext        *async_context,
+                                                 GCancellable        *cancellable,
                                                  SoupAddressCallback  callback,
                                                  gpointer             user_data);
-guint            soup_address_resolve_sync       (SoupAddress         *addr);
+guint            soup_address_resolve_sync       (SoupAddress         *addr,
+                                                 GCancellable        *cancellable);
 
 const char      *soup_address_get_name           (SoupAddress         *addr);
 const char      *soup_address_get_physical       (SoupAddress         *addr);
index 003bda6..35f4708 100644 (file)
@@ -17,8 +17,8 @@
 #include "soup-misc.h"
 #include "soup-uri.h"
 
-static void construct (SoupAuth *auth, GHashTable *auth_params);
-static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
 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);
@@ -54,8 +54,9 @@ soup_auth_basic_class_init (SoupAuthBasicClass *auth_basic_class)
        g_type_class_add_private (auth_basic_class, sizeof (SoupAuthBasicPrivate));
 
        auth_class->scheme_name = "Basic";
+       auth_class->strength = 1;
 
-       auth_class->construct = construct;
+       auth_class->update = update;
        auth_class->get_protection_space = get_protection_space;
        auth_class->authenticate = authenticate;
        auth_class->is_authenticated = is_authenticated;
@@ -65,14 +66,26 @@ soup_auth_basic_class_init (SoupAuthBasicClass *auth_basic_class)
 }
 
 
-static void
-construct (SoupAuth *auth, GHashTable *auth_params)
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
 {
-       ;
+       SoupAuthBasicPrivate *priv = SOUP_AUTH_BASIC_GET_PRIVATE (auth);
+
+       /* If we're updating a pre-existing auth, the
+        * username/password must be bad now, so forget it.
+        * Other than that, there's nothing to do here.
+        */
+       if (priv->token) {
+               memset (priv->token, 0, strlen (priv->token));
+               g_free (priv->token);
+               priv->token = NULL;
+       }
+
+       return TRUE;
 }
 
 static GSList *
-get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
 {
        char *space, *p;
 
index 3841e98..8ad5f85 100644 (file)
 
 #include "soup-auth-digest.h"
 #include "soup-headers.h"
-#include "soup-md5-utils.h"
 #include "soup-message.h"
+#include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-uri.h"
 
-static void construct (SoupAuth *auth, GHashTable *auth_params);
-static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
 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);
 
-typedef enum {
-       QOP_NONE     = 0,
-       QOP_AUTH     = 1 << 0,
-       QOP_AUTH_INT = 1 << 1
-} QOPType;
-
-typedef enum {
-       ALGORITHM_MD5      = 1 << 0,
-       ALGORITHM_MD5_SESS = 1 << 1
-} AlgorithmType;
-
 typedef struct {
-       char          *user;
-       char           hex_a1[33];
+       char                    *user;
+       char                     hex_urp[33];
+       char                     hex_a1[33];
 
        /* These are provided by the server */
-       char          *nonce;
-       QOPType        qop_options;
-       AlgorithmType  algorithm;
-       char          *domain;
+       char                    *nonce;
+       char                    *opaque;
+       SoupAuthDigestQop        qop_options;
+       SoupAuthDigestAlgorithm  algorithm;
+       char                    *domain;
 
        /* These are generated by the client */
-       char          *cnonce;
-       int            nc;
-       QOPType        qop;
+       char                    *cnonce;
+       int                      nc;
+       SoupAuthDigestQop        qop;
 } SoupAuthDigestPrivate;
 #define SOUP_AUTH_DIGEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestPrivate))
 
+static void recompute_hex_a1 (SoupAuthDigestPrivate *priv);
+
 G_DEFINE_TYPE (SoupAuthDigest, soup_auth_digest, SOUP_TYPE_AUTH)
 
 static void
@@ -76,6 +69,9 @@ finalize (GObject *object)
        if (priv->cnonce)
                g_free (priv->cnonce);
 
+       memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
+       memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+
        G_OBJECT_CLASS (soup_auth_digest_parent_class)->finalize (object);
 }
 
@@ -88,9 +84,10 @@ soup_auth_digest_class_init (SoupAuthDigestClass *auth_digest_class)
        g_type_class_add_private (auth_digest_class, sizeof (SoupAuthDigestPrivate));
 
        auth_class->scheme_name = "Digest";
+       auth_class->strength = 5;
 
        auth_class->get_protection_space = get_protection_space;
-       auth_class->construct = construct;
+       auth_class->update = update;
        auth_class->authenticate = authenticate;
        auth_class->is_authenticated = is_authenticated;
        auth_class->get_authorization = get_authorization;
@@ -98,91 +95,118 @@ soup_auth_digest_class_init (SoupAuthDigestClass *auth_digest_class)
        object_class->finalize = finalize;
 }
 
-typedef struct {
-       const char *name;
-       guint type;
-} DataType;
-
-static DataType qop_types[] = {
-       { "auth",     QOP_AUTH     },
-       { "auth-int", QOP_AUTH_INT },
-       { NULL, 0 }
-};
-
-static DataType algorithm_types[] = {
-       { "MD5",      ALGORITHM_MD5      },
-       { "MD5-sess", ALGORITHM_MD5_SESS },
-       { NULL, 0 }
-};
-
-static guint
-decode_data_type (DataType *dtype, const char *name)
+SoupAuthDigestAlgorithm
+soup_auth_digest_parse_algorithm (const char *algorithm)
 {
-        int i;
-
-       if (!name)
-               return 0;
-
-        for (i = 0; dtype[i].name; i++) {
-                if (!g_ascii_strcasecmp (dtype[i].name, name))
-                       return dtype[i].type;
-        }
-
-       return 0;
+       if (!algorithm)
+               return SOUP_AUTH_DIGEST_ALGORITHM_NONE;
+       else if (!g_ascii_strcasecmp (algorithm, "MD5"))
+               return SOUP_AUTH_DIGEST_ALGORITHM_MD5;
+       else if (!g_ascii_strcasecmp (algorithm, "MD5-sess"))
+               return SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS;
+       else
+               return -1;
 }
 
-static inline guint
-decode_qop (const char *name)
+char *
+soup_auth_digest_get_algorithm (SoupAuthDigestAlgorithm algorithm)
 {
-       return decode_data_type (qop_types, name);
+       if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5)
+               return g_strdup ("MD5");
+       else if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS)
+               return g_strdup ("MD5-sess");
+       else
+               return NULL;
 }
 
-static inline guint
-decode_algorithm (const char *name)
+SoupAuthDigestQop
+soup_auth_digest_parse_qop (const char *qop)
 {
-       return decode_data_type (algorithm_types, name);
+       GSList *qop_values, *iter;
+       SoupAuthDigestQop out = 0;
+
+       if (qop) {
+               qop_values = soup_header_parse_list (qop);
+               for (iter = qop_values; iter; iter = iter->next) {
+                       if (!g_ascii_strcasecmp (iter->data, "auth"))
+                               out |= SOUP_AUTH_DIGEST_QOP_AUTH;
+                       else if (!g_ascii_strcasecmp (iter->data, "auth-int"))
+                               out |= SOUP_AUTH_DIGEST_QOP_AUTH_INT;
+                       else
+                               out = -1;
+               }
+               soup_header_free_list (qop_values);
+       }
+
+       return out;
 }
 
-static void
-construct (SoupAuth *auth, GHashTable *auth_params)
+char *
+soup_auth_digest_get_qop (SoupAuthDigestQop qop)
 {
-       SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
-       char *tmp, *ptr;
+       GString *out;
+
+       out = g_string_new (NULL);
+       if (qop & SOUP_AUTH_DIGEST_QOP_AUTH)
+               g_string_append (out, "auth");
+       if (qop & SOUP_AUTH_DIGEST_QOP_AUTH_INT) {
+               if (qop & SOUP_AUTH_DIGEST_QOP_AUTH)
+                       g_string_append (out, ",");
+               g_string_append (out, "auth-int");
+       }
 
-       priv->nc = 1;
-       /* We're just going to do qop=auth for now */
-       priv->qop = QOP_AUTH;
+       return g_string_free (out, FALSE);
+}
 
-       priv->domain = soup_header_param_copy_token (auth_params, "domain");
-       priv->nonce = soup_header_param_copy_token (auth_params, "nonce");
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+       SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+       const char *stale;
+       guint qop_options;
+       gboolean ok = TRUE;
 
-       tmp = soup_header_param_copy_token (auth_params, "qop");
-       ptr = tmp;
+       g_free (priv->domain);
+       g_free (priv->nonce);
+       g_free (priv->opaque);
 
-       while (ptr && *ptr) {
-               char *token;
+       priv->nc = 1;
 
-               token = soup_header_param_decode_token ((char **)&ptr);
-               if (token)
-                       priv->qop_options |= decode_qop (token);
-               g_free (token);
+       priv->domain = g_strdup (g_hash_table_lookup (auth_params, "domain"));
+       priv->nonce = g_strdup (g_hash_table_lookup (auth_params, "nonce"));
+       priv->opaque = g_strdup (g_hash_table_lookup (auth_params, "opaque"));
 
-               if (*ptr == ',')
-                       ptr++;
-       }
-       g_free (tmp);
+       qop_options = soup_auth_digest_parse_qop (g_hash_table_lookup (auth_params, "qop"));
+       /* We're just going to do qop=auth for now */
+       if (qop_options == -1 || !(qop_options & SOUP_AUTH_DIGEST_QOP_AUTH))
+               ok = FALSE;
+       priv->qop = SOUP_AUTH_DIGEST_QOP_AUTH;
+
+       priv->algorithm = soup_auth_digest_parse_algorithm (g_hash_table_lookup (auth_params, "algorithm"));
+       if (priv->algorithm == -1)
+               ok = FALSE;
+
+       stale = g_hash_table_lookup (auth_params, "stale");
+       if (stale && !g_ascii_strcasecmp (stale, "TRUE") && *priv->hex_urp)
+               recompute_hex_a1 (priv);
+       else {
+               g_free (priv->user);
+               priv->user = NULL;
+               g_free (priv->cnonce);
+               priv->cnonce = NULL;
+               memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
+               memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+        }
 
-       tmp = soup_header_param_copy_token (auth_params, "algorithm");
-       priv->algorithm = decode_algorithm (tmp);
-       g_free (tmp);
+       return ok;
 }
 
 static GSList *
-get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
 {
        SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
        GSList *space = NULL;
-       SoupUri *uri;
+       SoupURI *uri;
        char **dvec, *d, *dir, *slash;
        int dix;
 
@@ -200,7 +224,7 @@ get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
                        dir = g_strdup (d);
                else {
                        uri = soup_uri_new (d);
-                       if (uri && uri->protocol == source_uri->protocol &&
+                       if (uri && uri->scheme == source_uri->scheme &&
                            uri->port == source_uri->port &&
                            !strcmp (uri->host, source_uri->host))
                                dir = g_strdup (uri->path);
@@ -223,16 +247,72 @@ get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
        return space;
 }
 
+void
+soup_auth_digest_compute_hex_urp (const char *username,
+                                 const char *realm,
+                                 const char *password,
+                                 char        hex_urp[33])
+{
+       GChecksum *checksum;
+
+       checksum = g_checksum_new (G_CHECKSUM_MD5);
+       g_checksum_update (checksum, username, strlen (username));
+       g_checksum_update (checksum, ":", 1);
+       g_checksum_update (checksum, realm, strlen (realm));
+       g_checksum_update (checksum, ":", 1);
+       g_checksum_update (checksum, password, strlen (password));
+       strncpy (hex_urp, g_checksum_get_string (checksum), 33);
+       g_checksum_free (checksum);
+}
+
+void
+soup_auth_digest_compute_hex_a1 (const char              *hex_urp,
+                                SoupAuthDigestAlgorithm  algorithm,
+                                const char              *nonce,
+                                const char              *cnonce,
+                                char                     hex_a1[33])
+{
+       if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5) {
+               /* In MD5, A1 is just user:realm:password, so hex_A1
+                * is just hex_urp.
+                */
+               /* You'd think you could say "sizeof (hex_a1)" here,
+                * but you'd be wrong.
+                */
+               memcpy (hex_a1, hex_urp, 33);
+       } else {
+               GChecksum *checksum;
+
+               /* In MD5-sess, A1 is hex_urp:nonce:cnonce */
+
+               checksum = g_checksum_new (G_CHECKSUM_MD5);
+               g_checksum_update (checksum, hex_urp, strlen (hex_urp));
+               g_checksum_update (checksum, ":", 1);
+               g_checksum_update (checksum, nonce, strlen (nonce));
+               g_checksum_update (checksum, ":", 1);
+               g_checksum_update (checksum, cnonce, strlen (cnonce));
+               strncpy (hex_a1, g_checksum_get_string (checksum), 33);
+               g_checksum_free (checksum);
+       }
+}
+
+static void
+recompute_hex_a1 (SoupAuthDigestPrivate *priv)
+{
+       soup_auth_digest_compute_hex_a1 (priv->hex_urp,
+                                        priv->algorithm,
+                                        priv->nonce,
+                                        priv->cnonce,
+                                        priv->hex_a1);
+}
+
 static void
 authenticate (SoupAuth *auth, const char *username, const char *password)
 {
        SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
-       SoupMD5Context ctx;
-       guchar d[16];
        char *bgen;
 
-       g_return_if_fail (username != NULL);
-
+       /* Create client nonce */
        bgen = g_strdup_printf ("%p:%lu:%lu",
                                auth,
                                (unsigned long) getpid (),
@@ -242,32 +322,13 @@ authenticate (SoupAuth *auth, const char *username, const char *password)
 
        priv->user = g_strdup (username);
 
-       /* compute A1 */
-       soup_md5_init (&ctx);
-
-       soup_md5_update (&ctx, username, strlen (username));
-
-       soup_md5_update (&ctx, ":", 1);
-       soup_md5_update (&ctx, auth->realm, strlen (auth->realm));
-       soup_md5_update (&ctx, ":", 1);
-       if (password)
-               soup_md5_update (&ctx, password, strlen (password));
-
-       if (priv->algorithm == ALGORITHM_MD5_SESS) {
-               soup_md5_final (&ctx, d);
-
-               soup_md5_init (&ctx);
-               soup_md5_update (&ctx, d, 16);
-               soup_md5_update (&ctx, ":", 1);
-               soup_md5_update (&ctx, priv->nonce,
-                                strlen (priv->nonce));
-               soup_md5_update (&ctx, ":", 1);
-               soup_md5_update (&ctx, priv->cnonce,
-                                strlen (priv->cnonce));
-       }
+       /* compute "URP" (user:realm:password) */
+       soup_auth_digest_compute_hex_urp (username, auth->realm,
+                                         password ? password : "",
+                                         priv->hex_urp);
 
-       /* hexify A1 */
-       soup_md5_final_hex (&ctx, priv->hex_a1);
+       /* And compute A1 from that */
+       recompute_hex_a1 (priv);
 }
 
 static gboolean
@@ -276,125 +337,134 @@ is_authenticated (SoupAuth *auth)
        return SOUP_AUTH_DIGEST_GET_PRIVATE (auth)->cnonce != NULL;
 }
 
-static char *
-compute_response (SoupAuthDigestPrivate *priv, SoupMessage *msg)
+void
+soup_auth_digest_compute_response (const char        *method,
+                                  const char        *uri,
+                                  const char        *hex_a1,
+                                  SoupAuthDigestQop  qop,
+                                  const char        *nonce,
+                                  const char        *cnonce,
+                                  int                nc,
+                                  char               response[33])
 {
-       char hex_a2[33], o[33];
-       SoupMD5Context md5;
-       char *url;
-       const SoupUri *uri;
-
-       uri = soup_message_get_uri (msg);
-       g_return_val_if_fail (uri != NULL, NULL);
-       url = soup_uri_to_string (uri, TRUE);
+       char hex_a2[33];
+       GChecksum *checksum;
 
        /* compute A2 */
-       soup_md5_init (&md5);
-       soup_md5_update (&md5, msg->method, strlen (msg->method));
-       soup_md5_update (&md5, ":", 1);
-       soup_md5_update (&md5, url, strlen (url));
+       checksum = g_checksum_new (G_CHECKSUM_MD5);
+       g_checksum_update (checksum, method, strlen (method));
+       g_checksum_update (checksum, ":", 1);
+       g_checksum_update (checksum, uri, strlen (uri));
+       strncpy (hex_a2, g_checksum_get_string (checksum), 33);
+       g_checksum_free (checksum);
 
-       g_free (url);
-
-       if (priv->qop == QOP_AUTH_INT) {
-               /* FIXME: Actually implement. Ugh. */
-               soup_md5_update (&md5, ":", 1);
-               soup_md5_update (&md5, "00000000000000000000000000000000", 32);
+       /* compute KD */
+       checksum = g_checksum_new (G_CHECKSUM_MD5);
+       g_checksum_update (checksum, hex_a1, strlen (hex_a1));
+       g_checksum_update (checksum, ":", 1);
+       g_checksum_update (checksum, nonce, strlen (nonce));
+       g_checksum_update (checksum, ":", 1);
+
+       if (qop) {
+               char tmp[9];
+
+               snprintf (tmp, 9, "%.8x", nc);
+               g_checksum_update (checksum, tmp, strlen (tmp));
+               g_checksum_update (checksum, ":", 1);
+               g_checksum_update (checksum, cnonce, strlen (cnonce));
+               g_checksum_update (checksum, ":", 1);
+
+               if (qop != SOUP_AUTH_DIGEST_QOP_AUTH)
+                       g_assert_not_reached ();
+               g_checksum_update (checksum, "auth", strlen ("auth"));
+               g_checksum_update (checksum, ":", 1);
        }
 
-       /* now hexify A2 */
-       soup_md5_final_hex (&md5, hex_a2);
+       g_checksum_update (checksum, hex_a2, 32);
+       strncpy (response, g_checksum_get_string (checksum), 33);
+       g_checksum_free (checksum);
+}
 
-       /* compute KD */
-       soup_md5_init (&md5);
-       soup_md5_update (&md5, priv->hex_a1, 32);
-       soup_md5_update (&md5, ":", 1);
-       soup_md5_update (&md5, priv->nonce,
-                        strlen (priv->nonce));
-       soup_md5_update (&md5, ":", 1);
+static void
+authentication_info_cb (SoupMessage *msg, gpointer data)
+{
+       SoupAuth *auth = data;
+       SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+       const char *header;
+       GHashTable *auth_params;
+       char *nextnonce;
 
-       if (priv->qop) {
-               char *tmp;
-
-               tmp = g_strdup_printf ("%.8x", priv->nc);
-
-               soup_md5_update (&md5, tmp, strlen (tmp));
-               g_free (tmp);
-               soup_md5_update (&md5, ":", 1);
-               soup_md5_update (&md5, priv->cnonce,
-                                strlen (priv->cnonce));
-               soup_md5_update (&md5, ":", 1);
-
-               if (priv->qop == QOP_AUTH)
-                       tmp = "auth";
-               else if (priv->qop == QOP_AUTH_INT)
-                       tmp = "auth-int";
-               else
-                       g_assert_not_reached ();
+       if (auth != soup_message_get_auth (msg))
+               return;
 
-               soup_md5_update (&md5, tmp, strlen (tmp));
-               soup_md5_update (&md5, ":", 1);
-       }
+       header = soup_message_headers_get (msg->response_headers,
+                                           soup_auth_is_for_proxy (auth) ?
+                                           "Proxy-Authentication-Info" :
+                                           "Authentication-Info");
+       g_return_if_fail (header != NULL);
 
-       soup_md5_update (&md5, hex_a2, 32);
-       soup_md5_final_hex (&md5, o);
+       auth_params = soup_header_parse_param_list (header);
+       if (!auth_params)
+               return;
 
-       return g_strdup (o);
+       nextnonce = g_strdup (g_hash_table_lookup (auth_params, "nextnonce"));
+       if (nextnonce) {
+               g_free (priv->nonce);
+               priv->nonce = nextnonce;
+       }
+
+       soup_header_free_param_list (auth_params);
 }
 
 static char *
 get_authorization (SoupAuth *auth, SoupMessage *msg)
 {
        SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
-       char *response;
-       char *qop = NULL;
-       char *nc;
+       char response[33], *token;
        char *url;
-       char *out;
-       const SoupUri *uri;
+       GString *out;
+       SoupURI *uri;
 
        uri = soup_message_get_uri (msg);
        g_return_val_if_fail (uri != NULL, NULL);
        url = soup_uri_to_string (uri, TRUE);
 
-       response = compute_response (priv, msg);
-
-       if (priv->qop == QOP_AUTH)
-               qop = "auth";
-       else if (priv->qop == QOP_AUTH_INT)
-               qop = "auth-int";
-       else
-               g_assert_not_reached ();
-
-       nc = g_strdup_printf ("%.8x", priv->nc);
+       soup_auth_digest_compute_response (msg->method, url, priv->hex_a1,
+                                          priv->qop, priv->nonce,
+                                          priv->cnonce, priv->nc,
+                                          response);
 
-       out = g_strdup_printf (
-               "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", %s%s%s "
-               "%s%s%s %s%s%s uri=\"%s\", response=\"%s\"",
-               priv->user,
-               auth->realm,
-               priv->nonce,
+       out = g_string_new ("Digest ");
 
-               priv->qop ? "cnonce=\"" : "",
-               priv->qop ? priv->cnonce : "",
-               priv->qop ? "\"," : "",
+       /* FIXME: doesn't deal with quotes in the %s strings */
+       g_string_append_printf (out, "username=\"%s\", realm=\"%s\", "
+                               "nonce=\"%s\", uri=\"%s\", response=\"%s\"",
+                               priv->user, auth->realm, priv->nonce,
+                               url, response);
 
-               priv->qop ? "nc=" : "",
-               priv->qop ? nc : "",
-               priv->qop ? "," : "",
+       if (priv->opaque)
+               g_string_append_printf (out, ", opaque=\"%s\"", priv->opaque);
 
-               priv->qop ? "qop=" : "",
-               priv->qop ? qop : "",
-               priv->qop ? "," : "",
+       if (priv->qop) {
+               char *qop = soup_auth_digest_get_qop (priv->qop);
 
-               url,
-               response);
+               g_string_append_printf (out, ", cnonce=\"%s\", nc=\"%.8x\", qop=\"%s\"",
+                                       priv->cnonce, priv->nc, qop);
+               g_free (qop);
+       }
 
-       g_free (response);
        g_free (url);
-       g_free (nc);
 
        priv->nc++;
 
-       return out;
+       token = g_string_free (out, FALSE);
+
+       soup_message_add_header_handler (msg,
+                                        "got_headers",
+                                        soup_auth_is_for_proxy (auth) ?
+                                        "Proxy-Authentication-Info" :
+                                        "Authentication-Info",
+                                        G_CALLBACK (authentication_info_cb),
+                                        auth);
+       return token;
 }
index b8bf8fa..453c40c 100644 (file)
@@ -27,4 +27,41 @@ typedef struct {
 
 GType soup_auth_digest_get_type (void);
 
+/* Utility routines (also used by SoupAuthDomainDigest) */
+
+typedef enum {
+       SOUP_AUTH_DIGEST_ALGORITHM_NONE,
+       SOUP_AUTH_DIGEST_ALGORITHM_MD5,
+       SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS
+} SoupAuthDigestAlgorithm;
+
+typedef enum {
+       SOUP_AUTH_DIGEST_QOP_AUTH     = 1 << 0,
+       SOUP_AUTH_DIGEST_QOP_AUTH_INT = 1 << 1
+} SoupAuthDigestQop;
+
+SoupAuthDigestAlgorithm  soup_auth_digest_parse_algorithm (const char *algorithm);
+char                    *soup_auth_digest_get_algorithm   (SoupAuthDigestAlgorithm algorithm);
+
+SoupAuthDigestQop        soup_auth_digest_parse_qop       (const char *qop);
+char                    *soup_auth_digest_get_qop         (SoupAuthDigestQop qop);
+
+void soup_auth_digest_compute_hex_urp  (const char              *username,
+                                       const char              *realm,
+                                       const char              *password,
+                                       char                     hex_urp[33]);
+void soup_auth_digest_compute_hex_a1   (const char              *hex_urp,
+                                       SoupAuthDigestAlgorithm  algorithm,
+                                       const char              *nonce,
+                                       const char              *cnonce,
+                                       char                     hex_a1[33]);
+void soup_auth_digest_compute_response (const char              *method,
+                                       const char              *uri,
+                                       const char              *hex_a1,
+                                       SoupAuthDigestQop        qop,
+                                       const char              *nonce,
+                                       const char              *cnonce,
+                                       int                      nc,
+                                       char                     response[33]);
+
 #endif /*SOUP_AUTH_DIGEST_H*/
diff --git a/libsoup/soup-auth-domain-basic.c b/libsoup/soup-auth-domain-basic.c
new file mode 100644 (file)
index 0000000..c3c64e2
--- /dev/null
@@ -0,0 +1,294 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain-basic.c: HTTP Basic Authentication (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-domain-basic.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+
+/**
+ * SECTION:soup-auth-domain-basic
+ * @short_description: Server-side "Basic" authentication
+ *
+ * #SoupAuthDomainBasic handles the server side of HTTP "Basic" (ie,
+ * cleartext password) authentication.
+ **/
+
+enum {
+       PROP_0,
+
+       PROP_AUTH_CALLBACK,
+       PROP_AUTH_DATA,
+
+       LAST_PROP
+};
+
+typedef struct {
+       SoupAuthDomainBasicAuthCallback auth_callback;
+       gpointer auth_data;
+       GDestroyNotify auth_dnotify;
+} SoupAuthDomainBasicPrivate;
+
+#define SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicPrivate))
+
+G_DEFINE_TYPE (SoupAuthDomainBasic, soup_auth_domain_basic, SOUP_TYPE_AUTH_DOMAIN)
+
+static char *accepts   (SoupAuthDomain *domain,
+                       SoupMessage    *msg,
+                       const char     *header);
+static char *challenge (SoupAuthDomain *domain,
+                       SoupMessage    *msg);
+
+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
+soup_auth_domain_basic_init (SoupAuthDomainBasic *basic)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
+
+       G_OBJECT_CLASS (soup_auth_domain_basic_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_basic_class_init (SoupAuthDomainBasicClass *basic_class)
+{
+       SoupAuthDomainClass *auth_domain_class =
+               SOUP_AUTH_DOMAIN_CLASS (basic_class);
+       GObjectClass *object_class = G_OBJECT_CLASS (basic_class);
+
+       g_type_class_add_private (basic_class, sizeof (SoupAuthDomainBasicPrivate));
+
+       auth_domain_class->accepts   = accepts;
+       auth_domain_class->challenge = challenge;
+
+       object_class->finalize     = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       g_object_class_install_property (
+               object_class, PROP_AUTH_CALLBACK,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK,
+                                     "Authentication callback",
+                                     "Password-checking callback",
+                                     G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_AUTH_DATA,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA,
+                                     "Authentication callback data",
+                                     "Data to pass to authentication callback",
+                                     G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_AUTH_CALLBACK:
+               priv->auth_callback = g_value_get_pointer (value);
+               break;
+       case PROP_AUTH_DATA:
+               if (priv->auth_dnotify) {
+                       priv->auth_dnotify (priv->auth_data);
+                       priv->auth_dnotify = NULL;
+               }
+               priv->auth_data = g_value_get_pointer (value);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_AUTH_CALLBACK:
+               g_value_set_pointer (value, priv->auth_callback);
+               break;
+       case PROP_AUTH_DATA:
+               g_value_set_pointer (value, priv->auth_data);
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * soup_auth_domain_basic_new:
+ * @optname1: name of first option, or %NULL
+ * @...: option name/value pairs
+ *
+ * Creates a #SoupAuthDomainBasic. You must set the
+ * %SOUP_AUTH_DOMAIN_REALM parameter, to indicate the realm name to be
+ * returned with the authentication challenge to the client. Other
+ * parameters are optional.
+ *
+ * Return value: the new #SoupAuthDomain
+ **/
+SoupAuthDomain *
+soup_auth_domain_basic_new (const char *optname1, ...)
+{
+       SoupAuthDomain *domain;
+       va_list ap;
+
+       va_start (ap, optname1);
+       domain = (SoupAuthDomain *)g_object_new_valist (SOUP_TYPE_AUTH_DOMAIN_BASIC,
+                                                       optname1, ap);
+       va_end (ap);
+
+       g_return_val_if_fail (soup_auth_domain_get_realm (domain) != NULL, NULL);
+
+       return domain;
+}
+
+/**
+ * SoupAuthDomainBasicAuthCallback:
+ * @domain: the domain
+ * @msg: the message being authenticated
+ * @username: the username provided by the client
+ * @password: the password provided by the client
+ * @user_data: the data passed to soup_auth_domain_basic_set_auth_callback()
+ *
+ * Callback used by #SoupAuthDomainBasic for authentication purposes.
+ * The application should verify that @username and @password and valid
+ * and return %TRUE or %FALSE.
+ *
+ * If you are maintaining your own password database (rather than
+ * using the password to authenticate against some other system like
+ * PAM or a remote server), you should make sure you know what you are
+ * doing. In particular, don't store cleartext passwords, or
+ * easily-computed hashes of cleartext passwords, even if you don't
+ * care that much about the security of your server, because users
+ * will frequently use the same password for multiple sites, and so
+ * compromising any site with a cleartext (or easily-cracked) password
+ * database may give attackers access to other more-interesting sites
+ * as well.
+ *
+ * Return value: %TRUE if @username and @password are valid
+ **/
+
+/**
+ * soup_auth_domain_basic_set_auth_callback:
+ * @domain: the domain
+ * @callback: the callback
+ * @user_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @user_data when @domain
+ * is destroyed
+ *
+ * Sets the callback that @domain will use to authenticate incoming
+ * requests. For each request containing authorization, @domain will
+ * invoke the callback, and then either accept or reject the request
+ * based on @callback's return value.
+ *
+ * You can also set the auth callback by setting the
+ * %SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK and
+ * %SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA properties, which can also be
+ * used to set the callback at construct time.
+ **/
+void
+soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain,
+                                         SoupAuthDomainBasicAuthCallback callback,
+                                         gpointer        user_data,
+                                         GDestroyNotify  dnotify)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
+
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
+
+       priv->auth_callback = callback;
+       priv->auth_data = user_data;
+       priv->auth_dnotify = dnotify;
+
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK);
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA);
+}
+
+static void
+pw_free (char *pw)
+{
+       memset (pw, 0, strlen (pw));
+       g_free (pw);
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+       SoupAuthDomainBasicPrivate *priv =
+               SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
+       char *decoded, *colon;
+       gsize len, plen;
+       char *username, *password;
+       gboolean ok = FALSE;
+
+       if (!priv->auth_callback)
+               return NULL;
+
+       if (strncmp (header, "Basic ", 6) != 0)
+               return NULL;
+
+       decoded = (char *)g_base64_decode (header + 6, &len);
+       if (!decoded)
+               return NULL;
+
+       colon = memchr (decoded, ':', len);
+       if (!colon) {
+               pw_free (decoded);
+               return NULL;
+       }
+       *colon = '\0';
+       plen = len - (colon - decoded) - 1;
+
+       password = g_strndup (colon + 1, plen);
+       memset (colon + 1, 0, plen);
+       username = decoded;
+
+       ok = priv->auth_callback (domain, msg, username, password,
+                                 priv->auth_data);
+       pw_free (password);
+
+       if (ok)
+               return username;
+       else {
+               g_free (username);
+               return NULL;
+       }
+}
+
+static char *
+challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+       /* FIXME: if realm has '"'s or '\'s in it, need to escape them */
+       return g_strdup_printf ("Basic realm=\"%s\"",
+                               soup_auth_domain_get_realm (domain));
+}
diff --git a/libsoup/soup-auth-domain-basic.h b/libsoup/soup-auth-domain-basic.h
new file mode 100644 (file)
index 0000000..66ccb29
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_BASIC_H
+#define SOUP_AUTH_DOMAIN_BASIC_H 1
+
+#include <libsoup/soup-auth-domain.h>
+
+#define SOUP_TYPE_AUTH_DOMAIN_BASIC            (soup_auth_domain_basic_get_type ())
+#define SOUP_AUTH_DOMAIN_BASIC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasic))
+#define SOUP_AUTH_DOMAIN_BASIC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicClass))
+#define SOUP_IS_AUTH_DOMAIN_BASIC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC))
+#define SOUP_IS_AUTH_DOMAIN_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC))
+#define SOUP_AUTH_DOMAIN_BASIC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicClass))
+
+typedef struct {
+       SoupAuthDomain parent;
+
+} SoupAuthDomainBasic;
+
+typedef struct {
+       SoupAuthDomainClass parent_class;
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
+} SoupAuthDomainBasicClass;
+
+#define SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK "auth-callback"
+#define SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA     "auth-data"
+
+GType soup_auth_domain_basic_get_type (void);
+
+SoupAuthDomain *soup_auth_domain_basic_new (const char *optname1,
+                                           ...) G_GNUC_NULL_TERMINATED;
+
+typedef        gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain *domain,
+                                                    SoupMessage    *msg,
+                                                    const char     *username,
+                                                    const char     *password,
+                                                    gpointer        user_data);
+
+void      soup_auth_domain_basic_set_auth_callback  (SoupAuthDomain *domain,
+                                                    SoupAuthDomainBasicAuthCallback callback,
+                                                    gpointer        user_data,
+                                                    GDestroyNotify  dnotify);
+
+#endif /* SOUP_AUTH_DOMAIN_BASIC_H */
diff --git a/libsoup/soup-auth-domain-digest.c b/libsoup/soup-auth-domain-digest.c
new file mode 100644 (file)
index 0000000..b303ce8
--- /dev/null
@@ -0,0 +1,454 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain-digest.c: HTTP Digest Authentication (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-auth-domain-digest.h"
+#include "soup-auth-digest.h"
+#include "soup-headers.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth-domain-digest
+ * @short_description: Server-side "Digest" authentication
+ *
+ * #SoupAuthDomainBasic handles the server side of HTTP "Digest"
+ * authentication.
+ **/
+
+enum {
+       PROP_0,
+
+       PROP_AUTH_CALLBACK,
+       PROP_AUTH_DATA,
+
+       LAST_PROP
+};
+
+typedef struct {
+       SoupAuthDomainDigestAuthCallback auth_callback;
+       gpointer auth_data;
+       GDestroyNotify auth_dnotify;
+
+} SoupAuthDomainDigestPrivate;
+
+#define SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestPrivate))
+
+G_DEFINE_TYPE (SoupAuthDomainDigest, soup_auth_domain_digest, SOUP_TYPE_AUTH_DOMAIN)
+
+static char *accepts   (SoupAuthDomain *domain,
+                       SoupMessage    *msg,
+                       const char     *header);
+static char *challenge (SoupAuthDomain *domain,
+                       SoupMessage    *msg);
+
+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
+soup_auth_domain_digest_init (SoupAuthDomainDigest *digest)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
+
+       G_OBJECT_CLASS (soup_auth_domain_digest_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_digest_class_init (SoupAuthDomainDigestClass *digest_class)
+{
+       SoupAuthDomainClass *auth_domain_class =
+               SOUP_AUTH_DOMAIN_CLASS (digest_class);
+       GObjectClass *object_class = G_OBJECT_CLASS (digest_class);
+
+       g_type_class_add_private (digest_class, sizeof (SoupAuthDomainDigestPrivate));
+
+       auth_domain_class->accepts   = accepts;
+       auth_domain_class->challenge = challenge;
+
+       object_class->finalize     = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       g_object_class_install_property (
+               object_class, PROP_AUTH_CALLBACK,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK,
+                                     "Authentication callback",
+                                     "Password-finding callback",
+                                     G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_AUTH_DATA,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA,
+                                     "Authentication callback data",
+                                     "Data to pass to authentication callback",
+                                     G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_AUTH_CALLBACK:
+               priv->auth_callback = g_value_get_pointer (value);
+               break;
+       case PROP_AUTH_DATA:
+               if (priv->auth_dnotify) {
+                       priv->auth_dnotify (priv->auth_data);
+                       priv->auth_dnotify = NULL;
+               }
+               priv->auth_data = g_value_get_pointer (value);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_AUTH_CALLBACK:
+               g_value_set_pointer (value, priv->auth_callback);
+               break;
+       case PROP_AUTH_DATA:
+               g_value_set_pointer (value, priv->auth_data);
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * soup_auth_domain_digest_new:
+ * @optname1: name of first option, or %NULL
+ * @...: option name/value pairs
+ *
+ * Creates a #SoupAuthDomainDigest. You must set the
+ * %SOUP_AUTH_DOMAIN_REALM parameter, to indicate the realm name to be
+ * returned with the authentication challenge to the client. Other
+ * parameters are optional.
+ *
+ * Return value: the new #SoupAuthDomain
+ **/
+SoupAuthDomain *
+soup_auth_domain_digest_new (const char *optname1, ...)
+{
+       SoupAuthDomain *domain;
+       va_list ap;
+
+       va_start (ap, optname1);
+       domain = (SoupAuthDomain *)g_object_new_valist (SOUP_TYPE_AUTH_DOMAIN_DIGEST,
+                                                       optname1, ap);
+       va_end (ap);
+
+       g_return_val_if_fail (soup_auth_domain_get_realm (domain) != NULL, NULL);
+
+       return domain;
+}
+
+/**
+ * SoupAuthDomainDigestAuthCallback:
+ * @domain: the domain
+ * @msg: the message being authenticated
+ * @username: the username provided by the client
+ * @user_data: the data passed to soup_auth_domain_digest_set_auth_callback()
+ *
+ * Callback used by #SoupAuthDomainDigest for authentication purposes.
+ * The application should look up @username in its password database,
+ * and return the corresponding encoded password (see
+ * soup_auth_domain_digest_encode_password()).
+ *
+ * Return value: the encoded password, or %NULL if @username is not a
+ * valid user. @domain will free the password when it is done with it.
+ **/
+
+/**
+ * soup_auth_domain_digest_set_auth_callback:
+ * @domain: the domain
+ * @callback: the callback
+ * @user_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @user_data when @domain
+ * is destroyed
+ *
+ * Sets the callback that @domain will use to authenticate incoming
+ * requests. For each request containing authorization, @domain will
+ * invoke the callback, and then either accept or reject the request
+ * based on @callback's return value.
+ *
+ * You can also set the auth callback by setting the
+ * %SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK and
+ * %SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA properties, which can also be
+ * used to set the callback at construct time.
+ **/
+void
+soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain,
+                                          SoupAuthDomainDigestAuthCallback callback,
+                                          gpointer        user_data,
+                                          GDestroyNotify  dnotify)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+
+       if (priv->auth_dnotify)
+               priv->auth_dnotify (priv->auth_data);
+
+       priv->auth_callback = callback;
+       priv->auth_data = user_data;
+       priv->auth_dnotify = dnotify;
+
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK);
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA);
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+       GHashTable *params;
+       const char *uri, *qop, *realm, *username;
+       const char *nonce, *nc, *cnonce, *response;
+       char *hex_urp, hex_a1[33], computed_response[33], *ret_user;
+       int nonce_count;
+       SoupURI *dig_uri, *req_uri;
+       gboolean accept = FALSE;
+
+       if (!priv->auth_callback)
+               return NULL;
+
+       if (strncmp (header, "Digest ", 7) != 0)
+               return NULL;
+
+       params = soup_header_parse_param_list (header + 7);
+       if (!params)
+               return NULL;
+
+       /* Check uri */
+       uri = g_hash_table_lookup (params, "uri");
+       if (!uri)
+               goto DONE;
+
+       req_uri = soup_message_get_uri (msg);
+       dig_uri = soup_uri_new (uri);
+       if (dig_uri) {
+               if (!soup_uri_equal (dig_uri, req_uri)) {
+                       soup_uri_free (dig_uri);
+                       goto DONE;
+               }
+               soup_uri_free (dig_uri);
+       } else {        
+               char *req_path;
+
+               req_path = soup_uri_to_string (req_uri, TRUE);
+               if (strcmp (uri, req_path) != 0) {
+                       g_free (req_path);
+                       goto DONE;
+               }
+               g_free (req_path);
+       }
+
+       /* Check qop; we only support "auth" for now */
+       qop = g_hash_table_lookup (params, "qop");
+       if (!qop || strcmp (qop, "auth") != 0)
+               goto DONE;
+
+       /* Check realm */
+       realm = g_hash_table_lookup (params, "realm");
+       if (!realm || strcmp (realm, soup_auth_domain_get_realm (domain)) != 0)
+               goto DONE;
+
+       username = g_hash_table_lookup (params, "username");
+       if (!username)
+               goto DONE;
+       nonce = g_hash_table_lookup (params, "nonce");
+       if (!nonce)
+               goto DONE;
+       nc = g_hash_table_lookup (params, "nc");
+       if (!nc)
+               goto DONE;
+       nonce_count = atoi (nc);
+       if (nonce_count <= 0)
+               goto DONE;
+       cnonce = g_hash_table_lookup (params, "cnonce");
+       if (!cnonce)
+               goto DONE;
+       response = g_hash_table_lookup (params, "response");
+       if (!response)
+               goto DONE;
+
+       hex_urp = priv->auth_callback (domain, msg, username, priv->auth_data);
+       if (hex_urp) {
+               soup_auth_digest_compute_hex_a1 (hex_urp,
+                                                SOUP_AUTH_DIGEST_ALGORITHM_MD5,
+                                                nonce, cnonce, hex_a1);
+               g_free (hex_urp);
+               soup_auth_digest_compute_response (msg->method, uri, hex_a1,
+                                                  SOUP_AUTH_DIGEST_QOP_AUTH,
+                                                  nonce, cnonce, nonce_count,
+                                                  computed_response);
+               accept = (strcmp (response, computed_response) == 0);
+       }
+
+ DONE:
+       ret_user = accept ? g_strdup (username) : NULL;
+       soup_header_free_param_list (params);
+       return ret_user;
+}
+
+static char *
+challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+       GString *str;
+
+       str = g_string_new ("Digest ");
+
+       /* FIXME: escape realm */
+       g_string_append_printf (str, "realm=\"%s\", ", 
+                               soup_auth_domain_get_realm (domain));
+
+       g_string_append_printf (str, "nonce=\"%lu%lu\", ", 
+                               (unsigned long) msg,
+                               (unsigned long) time (0));
+
+       g_string_append_printf (str, "qop=\"auth\", ");
+       g_string_append_printf (str, "algorithm=\"MD5\"");
+
+       return g_string_free (str, FALSE);
+}
+
+/**
+ * soup_auth_domain_digest_encode_password:
+ * @username: a username
+ * @realm: an auth realm name
+ * @password: the password for @username in @realm
+ *
+ * Encodes the username/realm/password triplet for Digest
+ * authentication. (That is, it returns a stringified MD5 hash of
+ * @username, @realm, and @password concatenated together). This is
+ * the form that is needed as the return value of
+ * #SoupAuthDomainDigest's auth handler.
+ *
+ * For security reasons, you should store the encoded hash, rather
+ * than storing the cleartext password itself and calling this method
+ * only when you need to verify it. This way, if your server is
+ * compromised, the attackers will not gain access to cleartext
+ * passwords which might also be usable at other sites. (Note also
+ * that the encoded password returned by this method is identical to
+ * the encoded password stored in an Apache .htdigest file.)
+ *
+ * Return value: the encoded password
+ **/
+char *
+soup_auth_domain_digest_encode_password (const char *username,
+                                        const char *realm,
+                                        const char *password)
+{
+       char hex_urp[33];
+
+       soup_auth_digest_compute_hex_urp (username, realm, password, hex_urp);
+       return g_strdup (hex_urp);
+}
+
+static char *
+evil_auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
+                   const char *username, gpointer encoded_password)
+{
+       return g_strdup (encoded_password);
+}
+
+/**
+ * soup_auth_domain_digest_evil_check_password:
+ * @domain: the auth domain
+ * @msg: the possibly-authenticated request
+ * @username: the username to check @msg against
+ * @password: the password to check @msg against
+ *
+ * Checks if @msg correctly authenticates @username via @password in
+ * @domain.
+ *
+ * Don't use this method; it's evil. It requires you to have a
+ * cleartext password database, which means that if your server is
+ * compromised, the attackers will have access to all of your users'
+ * passwords, which may also be their passwords on other servers. It
+ * is much better to store the passwords encoded in some format (eg,
+ * via soup_auth_domain_digest_encode_password() when using Digest
+ * authentication), so that if the server is compromised, the
+ * attackers won't be able to use the encoded passwords elsewhere.
+ *
+ * At any rate, even if you do have a cleartext password database, you
+ * still don't need to use this method, as you can just call
+ * soup_auth_domain_digest_encode_password() on the cleartext password
+ * from the #SoupAuthDomainDigestAuthCallback anyway. This method
+ * really only exists so as not to break certain libraries written
+ * against libsoup 2.2 whose public APIs depend on the existence of a
+ * "check this password against this request" method.
+ *
+ * Return value: %TRUE if @msg matches @username and @password,
+ * %FALSE if not.
+ **/
+gboolean
+soup_auth_domain_digest_evil_check_password (SoupAuthDomain *domain,
+                                            SoupMessage    *msg,
+                                            const char     *username,
+                                            const char     *password)
+{
+       SoupAuthDomainDigestPrivate *priv =
+               SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+       char *encoded_password;
+       const char *header;
+       SoupAuthDomainDigestAuthCallback old_callback;
+       gpointer old_data;
+       char *matched_username;
+
+       encoded_password = soup_auth_domain_digest_encode_password (
+               username, soup_auth_domain_get_realm (domain), password);
+
+       old_callback = priv->auth_callback;
+       old_data = priv->auth_data;
+
+       priv->auth_callback = evil_auth_callback;
+       priv->auth_data = encoded_password;
+
+       header = soup_message_headers_get (msg->request_headers, "Authorization");
+       matched_username = accepts (domain, msg, header);
+
+       priv->auth_callback = old_callback;
+       priv->auth_data = old_data;
+
+       g_free (encoded_password);
+
+       if (matched_username) {
+               g_free (matched_username);
+               return TRUE;
+       } else
+               return FALSE;
+}
diff --git a/libsoup/soup-auth-domain-digest.h b/libsoup/soup-auth-domain-digest.h
new file mode 100644 (file)
index 0000000..8f8ae10
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_DIGEST_H
+#define SOUP_AUTH_DOMAIN_DIGEST_H 1
+
+#include <libsoup/soup-auth-domain.h>
+
+#define SOUP_TYPE_AUTH_DOMAIN_DIGEST            (soup_auth_domain_digest_get_type ())
+#define SOUP_AUTH_DOMAIN_DIGEST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigest))
+#define SOUP_AUTH_DOMAIN_DIGEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestClass))
+#define SOUP_IS_AUTH_DOMAIN_DIGEST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST))
+#define SOUP_IS_AUTH_DOMAIN_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST))
+#define SOUP_AUTH_DOMAIN_DIGEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestClass))
+
+typedef struct {
+       SoupAuthDomain parent;
+
+} SoupAuthDomainDigest;
+
+typedef struct {
+       SoupAuthDomainClass parent_class;
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
+} SoupAuthDomainDigestClass;
+
+#define SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK "auth-callback"
+#define SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA     "auth-data"
+
+GType soup_auth_domain_digest_get_type (void);
+
+SoupAuthDomain *soup_auth_domain_digest_new (const char *optname1,
+                                           ...) G_GNUC_NULL_TERMINATED;
+
+typedef        char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain *domain,
+                                                   SoupMessage    *msg,
+                                                   const char     *username,
+                                                   gpointer        user_data);
+
+void    soup_auth_domain_digest_set_auth_callback  (SoupAuthDomain *domain,
+                                                   SoupAuthDomainDigestAuthCallback callback,
+                                                   gpointer        user_data,
+                                                   GDestroyNotify  dnotify);
+
+char   *soup_auth_domain_digest_encode_password    (const char     *username,
+                                                   const char     *realm,
+                                                   const char     *password);
+
+
+gboolean soup_auth_domain_digest_evil_check_password (SoupAuthDomain *domain,
+                                                     SoupMessage    *msg,
+                                                     const char     *username,
+                                                     const char     *password);
+
+
+#endif /* SOUP_AUTH_DOMAIN_DIGEST_H */
diff --git a/libsoup/soup-auth-domain.c b/libsoup/soup-auth-domain.c
new file mode 100644 (file)
index 0000000..8ff77bb
--- /dev/null
@@ -0,0 +1,415 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain.c: HTTP Authentication Domain (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-domain.h"
+#include "soup-message.h"
+#include "soup-path-map.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth-domain
+ * @short_description: Server-side authentication
+ * @see_also: #SoupServer
+ *
+ * A #SoupAuthDomain manages authentication for all or part of a
+ * #SoupServer. To make a server require authentication, first create
+ * an appropriate subclass of #SoupAuthDomain, and then add it to the
+ * server with soup_server_add_auth_domain().
+ *
+ * In order for an auth domain to have any effect, you must add one or
+ * more paths to it (via soup_auth_domain_add_path() or the
+ * %SOUP_AUTH_DOMAIN_ADD_PATH property). To require authentication for
+ * all requests, add the path "/".
+ *
+ * If you need greater control over which requests should and
+ * shouldn't be authenticated, add paths covering everything you
+ * <emphasis>might</emphasis> want authenticated, and then use a
+ * filter (soup_auth_domain_set_filter()) to bypass authentication for
+ * those requests that don't need it.
+ **/
+
+enum {
+       PROP_0,
+
+       PROP_REALM,
+       PROP_PROXY,
+       PROP_ADD_PATH,
+       PROP_REMOVE_PATH,
+       PROP_FILTER,
+       PROP_FILTER_DATA,
+
+       LAST_PROP
+};
+
+typedef struct {
+       char *realm;
+       gboolean proxy;
+       SoupAuthDomainFilter filter;
+       gpointer filter_data;
+       GDestroyNotify filter_dnotify;
+       SoupPathMap *paths;
+} SoupAuthDomainPrivate;
+
+#define SOUP_AUTH_DOMAIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainPrivate))
+
+G_DEFINE_TYPE (SoupAuthDomain, soup_auth_domain, G_TYPE_OBJECT)
+
+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
+soup_auth_domain_init (SoupAuthDomain *domain)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       priv->paths = soup_path_map_new (NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+       g_free (priv->realm);
+       soup_path_map_free (priv->paths);
+
+       if (priv->filter_dnotify)
+               priv->filter_dnotify (priv->filter_data);
+
+       G_OBJECT_CLASS (soup_auth_domain_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_class_init (SoupAuthDomainClass *auth_domain_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (auth_domain_class);
+
+       g_type_class_add_private (auth_domain_class, sizeof (SoupAuthDomainPrivate));
+
+       object_class->finalize = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       g_object_class_install_property (
+               object_class, PROP_REALM,
+               g_param_spec_string (SOUP_AUTH_DOMAIN_REALM,
+                                    "Realm",
+                                    "The realm of this auth domain",
+                                    NULL,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_PROXY,
+               g_param_spec_boolean (SOUP_AUTH_DOMAIN_PROXY,
+                                     "Proxy",
+                                     "Whether or not this is a proxy auth domain",
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_ADD_PATH,
+               g_param_spec_string (SOUP_AUTH_DOMAIN_ADD_PATH,
+                                    "Add a path",
+                                    "Add a path covered by this auth domain",
+                                    NULL,
+                                    G_PARAM_WRITABLE));
+       g_object_class_install_property (
+               object_class, PROP_REMOVE_PATH,
+               g_param_spec_string (SOUP_AUTH_DOMAIN_REMOVE_PATH,
+                                    "Remove a path",
+                                    "Remove a path covered by this auth domain",
+                                    NULL,
+                                    G_PARAM_WRITABLE));
+       g_object_class_install_property (
+               object_class, PROP_FILTER,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_FILTER,
+                                     "Filter",
+                                     "A filter for deciding whether or not to require authentication",
+                                     G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_FILTER_DATA,
+               g_param_spec_pointer (SOUP_AUTH_DOMAIN_FILTER_DATA,
+                                     "Filter data",
+                                     "Data to pass to filter",
+                                     G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomain *auth_domain = SOUP_AUTH_DOMAIN (object);
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_REALM:
+               g_free (priv->realm);
+               priv->realm = g_value_dup_string (value);
+               break;
+       case PROP_PROXY:
+               priv->proxy = g_value_get_boolean (value);
+               break;
+       case PROP_ADD_PATH:
+               soup_auth_domain_add_path (auth_domain,
+                                          g_value_get_string (value));
+               break;
+       case PROP_REMOVE_PATH:
+               soup_auth_domain_remove_path (auth_domain,
+                                             g_value_get_string (value));
+               break;
+       case PROP_FILTER:
+               priv->filter = g_value_get_pointer (value);
+               break;
+       case PROP_FILTER_DATA:
+               if (priv->filter_dnotify) {
+                       priv->filter_dnotify (priv->filter_data);
+                       priv->filter_dnotify = NULL;
+               }
+               priv->filter_data = g_value_get_pointer (value);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_REALM:
+               g_value_set_string (value, priv->realm);
+               break;
+       case PROP_PROXY:
+               g_value_set_boolean (value, priv->proxy);
+               break;
+       case PROP_FILTER:
+               g_value_set_pointer (value, priv->filter);
+               break;
+       case PROP_FILTER_DATA:
+               g_value_set_pointer (value, priv->filter_data);
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * soup_auth_domain_add_path:
+ * @domain: a #SoupAuthDomain
+ * @path: the path to add to @domain
+ *
+ * Adds @path to @domain, such that requests under @path on @domain's
+ * server will require authentication (unless overridden by
+ * soup_auth_domain_remove_path() or soup_auth_domain_set_filter()).
+ *
+ * You can also add paths by setting the %SOUP_AUTH_DOMAIN_ADD_PATH
+ * property, which can also be used to add one or more paths at
+ * construct time.
+ **/
+void
+soup_auth_domain_add_path (SoupAuthDomain *domain, const char *path)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       soup_path_map_add (priv->paths, path, GINT_TO_POINTER (TRUE));
+}
+
+/**
+ * soup_auth_domain_remove_path:
+ * @domain: a #SoupAuthDomain
+ * @path: the path to remove from @domain
+ *
+ * Removes @path from @domain, such that requests under @path on
+ * @domain's server will NOT require authentication.
+ *
+ * This is not simply an undo-er for soup_auth_domain_add_path(); it
+ * can be used to "carve out" a subtree that does not require
+ * authentication inside a hierarchy that does. Note also that unlike
+ * with soup_auth_domain_add_path(), this cannot be overridden by
+ * adding a filter, as filters can only bypass authentication that
+ * would otherwise be required, not require it where it would
+ * otherwise be unnecessary.
+ *
+ * You can also remove paths by setting the
+ * %SOUP_AUTH_DOMAIN_REMOVE_PATH property, which can also be used to
+ * remove one or more paths at construct time.
+ **/
+void
+soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       soup_path_map_add (priv->paths, path, GINT_TO_POINTER (FALSE));
+}
+
+/**
+ * soup_auth_domain_set_filter:
+ * @domain: a #SoupAuthDomain
+ * @filter: the auth filter for @domain
+ * @filter_data: data to pass to @filter
+ * @dnotify: destroy notifier to free @filter_data when @domain
+ * is destroyed
+ *
+ * Adds @filter as an authentication filter to @domain. The filter
+ * gets a chance to bypass authentication for certain requests that
+ * would otherwise require it. Eg, it might check the message's path
+ * in some way that is too complicated to do via the other methods, or
+ * it might check the message's method, and allow GETs but not PUTs.
+ *
+ * The filter function returns %TRUE if the request should still
+ * require authentication, or %FALSE if authentication is unnecessary
+ * for this request.
+ *
+ * To help prevent security holes, your filter should return %TRUE by
+ * default, and only return %FALSE under specifically-tested
+ * circumstances, rather than the other way around. Eg, in the example
+ * above, where you want to authenticate PUTs but not GETs, you should
+ * check if the method is GET and return %FALSE in that case, and then
+ * return %TRUE for all other methods (rather than returning %TRUE for
+ * PUT and %FALSE for all other methods). This way if it turned out
+ * (now or later) that some paths supported additional methods besides
+ * GET and PUT, those methods would default to being NOT allowed for
+ * unauthenticated users.
+ *
+ * You can also set the filter by setting the %SOUP_AUTH_DOMAIN_FILTER
+ * and %SOUP_AUTH_DOMAIN_FILTER_DATA properties, which can also be
+ * used to set the filter at construct time.
+ **/
+void
+soup_auth_domain_set_filter (SoupAuthDomain *domain,
+                            SoupAuthDomainFilter filter,
+                            gpointer        filter_data,
+                            GDestroyNotify  dnotify)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       if (priv->filter_dnotify)
+               priv->filter_dnotify (priv->filter_data);
+
+       priv->filter = filter;
+       priv->filter_data = filter_data;
+       priv->filter_dnotify = dnotify;
+
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_FILTER);
+       g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_FILTER_DATA);
+}
+
+/**
+ * soup_auth_domain_get_realm:
+ * @domain: a #SoupAuthDomain
+ *
+ * Gets the realm name associated with @domain
+ *
+ * Return value: @domain's realm
+ **/
+const char *
+soup_auth_domain_get_realm (SoupAuthDomain *domain)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+       return priv->realm;
+}
+
+/**
+ * soup_auth_domain_covers:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Checks if @domain requires @msg to be authenticated (according to
+ * its paths and filter function). This does not actually look at
+ * whether @msg *is* authenticated, merely whether or not is needs to
+ * be.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ *
+ * Return value: %TRUE if @domain requires @msg to be authenticated
+ **/
+gboolean
+soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+       const char *path;
+
+       path = soup_message_get_uri (msg)->path;
+       if (!soup_path_map_lookup (priv->paths, path))
+               return FALSE;
+
+       if (priv->filter && !priv->filter (domain, msg, priv->filter_data))
+               return FALSE;
+       else
+               return TRUE;
+}
+
+/**
+ * soup_auth_domain_accepts:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Checks if @msg contains appropriate authorization for @domain to
+ * accept it. Mirroring soup_auth_domain_covers(), this does not check
+ * whether or not @domain *cares* if @msg is authorized.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ *
+ * Return value: the username that @msg has authenticated as, if in
+ * fact it has authenticated. %NULL otherwise.
+ **/
+char *
+soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+       const char *header;
+
+       header = soup_message_headers_get (msg->request_headers,
+                                           priv->proxy ?
+                                           "Proxy-Authorization" :
+                                           "Authorization");
+       if (!header)
+               return NULL;
+       return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->accepts (domain, msg, header);
+}
+
+/**
+ * soup_auth_domain_challenge:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg,
+ * requesting that the client authenticate, and sets @msg's status
+ * accordingly.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ **/
+void
+soup_auth_domain_challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+       SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+       char *challenge;
+
+       challenge = SOUP_AUTH_DOMAIN_GET_CLASS (domain)->challenge (domain, msg);
+       soup_message_set_status (msg, priv->proxy ?
+                                SOUP_STATUS_PROXY_UNAUTHORIZED :
+                                SOUP_STATUS_UNAUTHORIZED);
+       soup_message_headers_append (msg->response_headers,
+                                    priv->proxy ?
+                                    "Proxy-Authenticate" :
+                                    "WWW-Authenticate",
+                                    challenge);
+       g_free (challenge);
+}
diff --git a/libsoup/soup-auth-domain.h b/libsoup/soup-auth-domain.h
new file mode 100644 (file)
index 0000000..8b903f7
--- /dev/null
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_H
+#define SOUP_AUTH_DOMAIN_H 1
+
+#include <libsoup/soup-types.h>
+
+#define SOUP_TYPE_AUTH_DOMAIN            (soup_auth_domain_get_type ())
+#define SOUP_AUTH_DOMAIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomain))
+#define SOUP_AUTH_DOMAIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainClass))
+#define SOUP_IS_AUTH_DOMAIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN))
+#define SOUP_IS_AUTH_DOMAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN))
+#define SOUP_AUTH_DOMAIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainClass))
+
+struct SoupAuthDomain {
+       GObject parent;
+
+};
+
+typedef struct {
+       GObjectClass parent_class;
+
+       char *   (*accepts)   (SoupAuthDomain  *domain,
+                              SoupMessage     *msg,
+                              const char      *header);
+       char *   (*challenge) (SoupAuthDomain  *domain,
+                              SoupMessage     *msg);
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
+} SoupAuthDomainClass;
+
+#define SOUP_AUTH_DOMAIN_REALM       "realm"
+#define SOUP_AUTH_DOMAIN_PROXY       "proxy"
+#define SOUP_AUTH_DOMAIN_ADD_PATH    "add-path"
+#define SOUP_AUTH_DOMAIN_REMOVE_PATH "remove-path"
+#define SOUP_AUTH_DOMAIN_FILTER      "filter"
+#define SOUP_AUTH_DOMAIN_FILTER_DATA "filter-data"
+
+typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *, SoupMessage *, gpointer);
+
+GType       soup_auth_domain_get_type    (void);
+
+void        soup_auth_domain_add_path    (SoupAuthDomain       *domain,
+                                         const char           *path);
+void        soup_auth_domain_remove_path (SoupAuthDomain       *domain,
+                                         const char           *path);
+
+void        soup_auth_domain_set_filter  (SoupAuthDomain       *domain,
+                                         SoupAuthDomainFilter  filter,
+                                         gpointer              filter_data,
+                                         GDestroyNotify        dnotify);
+
+const char *soup_auth_domain_get_realm   (SoupAuthDomain       *domain);
+
+gboolean    soup_auth_domain_covers      (SoupAuthDomain       *domain,
+                                         SoupMessage          *msg);
+char       *soup_auth_domain_accepts     (SoupAuthDomain       *domain,
+                                         SoupMessage          *msg);
+void        soup_auth_domain_challenge   (SoupAuthDomain       *domain,
+                                         SoupMessage          *msg);
+
+#endif /* SOUP_AUTH_DOMAIN_H */
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
new file mode 100644 (file)
index 0000000..6417197
--- /dev/null
@@ -0,0 +1,451 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-manager.c: SoupAuth manager for SoupSession
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-manager.h"
+#include "soup-headers.h"
+#include "soup-message-private.h"
+#include "soup-path-map.h"
+#include "soup-session.h"
+#include "soup-session-private.h"
+#include "soup-uri.h"
+
+static void session_request_started (SoupSession *session, SoupMessage *msg,
+                                    SoupSocket *socket, gpointer data);
+
+struct SoupAuthManager {
+       SoupSession *session;
+       GPtrArray *auth_types;
+
+       SoupAuth *proxy_auth;
+       GHashTable *auth_hosts;
+};
+
+typedef struct {
+       SoupURI     *root_uri;
+       SoupPathMap *auth_realms;      /* path -> scheme:realm */
+       GHashTable  *auths;            /* scheme:realm -> SoupAuth */
+} SoupAuthHost;
+
+/* temporary until we fix this to index hosts by SoupAddress */
+extern guint     soup_uri_host_hash  (gconstpointer  key);
+extern gboolean  soup_uri_host_equal (gconstpointer  v1,
+                                     gconstpointer  v2);
+extern SoupURI  *soup_uri_copy_root  (SoupURI *uri);
+
+SoupAuthManager *
+soup_auth_manager_new (SoupSession *session)
+{
+       SoupAuthManager *manager;
+
+       manager = g_slice_new0 (SoupAuthManager);
+       manager->session = session;
+       manager->auth_types = g_ptr_array_new ();
+       manager->auth_hosts = g_hash_table_new (soup_uri_host_hash,
+                                               soup_uri_host_equal);
+
+       g_signal_connect (session, "request_started",
+                         G_CALLBACK (session_request_started), manager);
+       return manager;
+}
+
+static gboolean
+foreach_free_host (gpointer key, gpointer value, gpointer data)
+{
+       SoupAuthHost *host = value;
+
+       if (host->auth_realms)
+               soup_path_map_free (host->auth_realms);
+       if (host->auths)
+               g_hash_table_destroy (host->auths);
+
+       soup_uri_free (host->root_uri);
+       g_slice_free (SoupAuthHost, host);
+
+       return TRUE;
+}
+
+void
+soup_auth_manager_free (SoupAuthManager *manager)
+{
+       int i;
+
+       g_signal_handlers_disconnect_by_func (
+               manager->session,
+               G_CALLBACK (session_request_started), manager);
+
+       for (i = 0; i < manager->auth_types->len; i++)
+               g_type_class_unref (manager->auth_types->pdata[i]);
+       g_ptr_array_free (manager->auth_types, TRUE);
+
+       g_hash_table_foreach_remove (manager->auth_hosts, foreach_free_host, NULL);
+       g_hash_table_destroy (manager->auth_hosts);
+
+       if (manager->proxy_auth)
+               g_object_unref (manager->proxy_auth);
+
+       g_slice_free (SoupAuthManager, manager);
+}
+
+static int
+auth_type_compare_func (gconstpointer a, gconstpointer b)
+{
+       SoupAuthClass *auth1 = (SoupAuthClass *)a;
+       SoupAuthClass *auth2 = (SoupAuthClass *)b;
+
+       return auth2->strength - auth1->strength;
+}
+
+void
+soup_auth_manager_add_type (SoupAuthManager *manager, GType type)
+{
+       SoupAuthClass *auth_class;
+
+       g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
+
+       auth_class = g_type_class_ref (type);
+       g_ptr_array_add (manager->auth_types, auth_class);
+       g_ptr_array_sort (manager->auth_types, auth_type_compare_func);
+}
+
+void
+soup_auth_manager_remove_type (SoupAuthManager *manager, GType type)
+{
+       SoupAuthClass *auth_class;
+       int i;
+
+       g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
+
+       auth_class = g_type_class_peek (type);
+       for (i = 0; i < manager->auth_types->len; i++) {
+               if (manager->auth_types->pdata[i] == (gpointer)auth_class) {
+                       g_ptr_array_remove_index (manager->auth_types, i);
+                       g_type_class_unref (auth_class);
+                       return;
+               }
+       }
+}
+
+static inline const char *
+auth_header_for_message (SoupMessage *msg)
+{
+       if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
+               return soup_message_headers_get (msg->response_headers,
+                                                "Proxy-Authenticate");
+       } else {
+               return soup_message_headers_get (msg->response_headers,
+                                                "WWW-Authenticate");
+       }
+}
+
+static char *
+extract_challenge (const char *challenges, const char *scheme)
+{
+       GSList *items, *i;
+       int schemelen = strlen (scheme);
+       char *item, *space, *equals;
+       GString *challenge;
+
+       /* The relevant grammar:
+        *
+        * WWW-Authenticate   = 1#challenge
+        * Proxy-Authenticate = 1#challenge
+        * challenge          = auth-scheme 1#auth-param
+        * auth-scheme        = token
+        * auth-param         = token "=" ( token | quoted-string )
+        *
+        * The fact that quoted-strings can contain commas, equals
+        * signs, and auth scheme names makes it tricky to "cheat" on
+        * the parsing. We just use soup_header_parse_list(), and then
+        * reassemble the pieces after we find the one we want.
+        */
+
+       items = soup_header_parse_list (challenges);
+
+       /* First item will start with the scheme name, followed by a
+        * space and then the first auth-param.
+        */
+       for (i = items; i; i = i->next) {
+               item = i->data;
+               if (!g_ascii_strncasecmp (item, scheme, schemelen) &&
+                   g_ascii_isspace (item[schemelen]))
+                       break;
+       }
+       if (!i)
+               return NULL;
+
+       /* The challenge extends from this item until the end, or until
+        * the next item that has a space before an equals sign.
+        */
+       challenge = g_string_new (item);
+       for (i = i->next; i; i = i->next) {
+               item = i->data;
+               space = strpbrk (item, " \t");
+               equals = strchr (item, '=');
+               if (!equals || (space && equals > space))
+                       break;
+
+               g_string_append (challenge, ", ");
+               g_string_append (challenge, item);
+       }
+
+       soup_header_free_list (items);
+       return g_string_free (challenge, FALSE);
+}
+
+static SoupAuth *
+create_auth (SoupAuthManager *manager, SoupMessage *msg)
+{
+       const char *header;
+       SoupAuthClass *auth_class;
+       char *challenge = NULL;
+       SoupAuth *auth;
+       int i;
+
+       header = auth_header_for_message (msg);
+       if (!header)
+               return NULL;
+
+       for (i = manager->auth_types->len - 1; i >= 0; i--) {
+               auth_class = manager->auth_types->pdata[i];
+               challenge = extract_challenge (header, auth_class->scheme_name);
+               if (challenge)
+                       break;
+       }
+       if (!challenge)
+               return NULL;
+
+       auth = soup_auth_new (G_TYPE_FROM_CLASS (auth_class), msg, challenge);
+       g_free (challenge);
+       return auth;
+}
+
+static gboolean
+check_auth (SoupAuthManager *manager, SoupMessage *msg, SoupAuth *auth)
+{
+       const char *header;
+       char *challenge;
+       gboolean ok;
+
+       header = auth_header_for_message (msg);
+       if (!header)
+               return FALSE;
+
+       challenge = extract_challenge (header, soup_auth_get_scheme_name (auth));
+       if (!challenge)
+               return FALSE;
+
+       ok = soup_auth_update (auth, msg, challenge);
+       g_free (challenge);
+       return ok;
+}
+
+static SoupAuthHost *
+get_auth_host_for_message (SoupAuthManager *manager, SoupMessage *msg)
+{
+       SoupAuthHost *host;
+       SoupURI *source = soup_message_get_uri (msg);
+
+       host = g_hash_table_lookup (manager->auth_hosts, source);
+       if (host)
+               return host;
+
+       host = g_slice_new0 (SoupAuthHost);
+       host->root_uri = soup_uri_copy_root (source);
+       g_hash_table_insert (manager->auth_hosts, host->root_uri, host);
+
+       return host;
+}
+
+static SoupAuth *
+lookup_auth (SoupAuthManager *manager, SoupMessage *msg)
+{
+       SoupAuthHost *host;
+       const char *path, *realm;
+
+       host = get_auth_host_for_message (manager, msg);
+       if (!host->auth_realms)
+               return NULL;
+
+       path = soup_message_get_uri (msg)->path;
+       if (!path)
+               path = "/";
+       realm = soup_path_map_lookup (host->auth_realms, path);
+       if (realm)
+               return g_hash_table_lookup (host->auths, realm);
+       else
+               return NULL;
+}
+
+static gboolean
+authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
+                  SoupMessage *msg, gboolean prior_auth_failed,
+                  gboolean proxy)
+{
+       SoupURI *uri;
+
+       if (soup_auth_is_authenticated (auth))
+               return TRUE;
+
+       if (proxy) {
+               g_object_get (G_OBJECT (manager->session),
+                             SOUP_SESSION_PROXY_URI, &uri,
+                             NULL);
+       } else
+               uri = soup_uri_copy (soup_message_get_uri (msg));
+
+       if (uri->password && !prior_auth_failed) {
+               soup_auth_authenticate (auth, uri->user, uri->password);
+               soup_uri_free (uri);
+               return TRUE;
+       }
+       soup_uri_free (uri);
+
+       soup_session_emit_authenticate (manager->session,
+                                       msg, auth, prior_auth_failed);
+       return soup_auth_is_authenticated (auth);
+}
+
+static gboolean
+update_auth (SoupAuthManager *manager, SoupMessage *msg)
+{
+       SoupAuthHost *host;
+       SoupAuth *auth, *prior_auth, *old_auth;
+       const char *path;
+       char *auth_info, *old_auth_info;
+       GSList *pspace, *p;
+       gboolean prior_auth_failed = FALSE;
+
+       host = get_auth_host_for_message (manager, msg);
+
+       /* See if we used auth last time */
+       prior_auth = soup_message_get_auth (msg);
+       if (prior_auth && check_auth (manager, msg, prior_auth)) {
+               auth = prior_auth;
+               if (!soup_auth_is_authenticated (auth))
+                       prior_auth_failed = TRUE;
+       } else {
+               auth = create_auth (manager, msg);
+               if (!auth)
+                       return FALSE;
+       }
+       auth_info = soup_auth_get_info (auth);
+
+       if (!host->auth_realms) {
+               host->auth_realms = soup_path_map_new (g_free);
+               host->auths = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                    g_free, g_object_unref);
+       }
+
+       /* Record where this auth realm is used. */
+       pspace = soup_auth_get_protection_space (auth, soup_message_get_uri (msg));
+       for (p = pspace; p; p = p->next) {
+               path = p->data;
+               old_auth_info = soup_path_map_lookup (host->auth_realms, path);
+               if (old_auth_info) {
+                       if (!strcmp (old_auth_info, auth_info))
+                               continue;
+                       soup_path_map_remove (host->auth_realms, path);
+               }
+
+               soup_path_map_add (host->auth_realms, path,
+                                  g_strdup (auth_info));
+       }
+       soup_auth_free_protection_space (auth, pspace);
+
+       /* Now, make sure the auth is recorded. (If there's a
+        * pre-existing auth, we keep that rather than the new one,
+        * since the old one might already be authenticated.)
+        */
+       old_auth = g_hash_table_lookup (host->auths, auth_info);
+       if (old_auth) {
+               g_free (auth_info);
+               if (auth != old_auth && auth != prior_auth) {
+                       g_object_unref (auth);
+                       auth = old_auth;
+               }
+       } else {
+               g_hash_table_insert (host->auths, auth_info, auth);
+       }
+
+       /* If we need to authenticate, try to do it. */
+       return authenticate_auth (manager, auth, msg,
+                                 prior_auth_failed, FALSE);
+}
+
+static gboolean
+update_proxy_auth (SoupAuthManager *manager, SoupMessage *msg)
+{
+       SoupAuth *prior_auth;
+       gboolean prior_auth_failed = FALSE;
+
+       /* See if we used auth last time */
+       prior_auth = soup_message_get_proxy_auth (msg);
+       if (prior_auth && check_auth (manager, msg, prior_auth)) {
+               if (!soup_auth_is_authenticated (prior_auth))
+                       prior_auth_failed = TRUE;
+       }
+
+       if (!manager->proxy_auth) {
+               manager->proxy_auth = create_auth (manager, msg);
+               if (!manager->proxy_auth)
+                       return FALSE;
+       }
+
+       /* If we need to authenticate, try to do it. */
+       return authenticate_auth (manager, manager->proxy_auth, msg,
+                                 prior_auth_failed, TRUE);
+}
+
+static void
+authorize_handler (SoupMessage *msg, gpointer user_data)
+{
+       SoupAuthManager *manager = user_data;
+
+       if (update_auth (manager, msg))
+               soup_session_requeue_message (manager->session, msg);
+}
+
+static void
+proxy_authorize_handler (SoupMessage *msg, gpointer user_data)
+{
+       SoupAuthManager *manager = user_data;
+
+       if (update_proxy_auth (manager, msg))
+               soup_session_requeue_message (manager->session, msg);
+}
+
+static void
+session_request_started (SoupSession *session, SoupMessage *msg,
+                        SoupSocket *socket, gpointer data)
+{
+       SoupAuthManager *manager = data;
+       SoupAuth *auth;
+
+       auth = lookup_auth (manager, msg);
+       if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE))
+               auth = NULL;
+       soup_message_set_auth (msg, auth);
+       soup_message_add_status_code_handler (
+               msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
+               G_CALLBACK (authorize_handler), manager);
+
+       auth = manager->proxy_auth;
+       if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE))
+               auth = NULL;
+       soup_message_set_proxy_auth (msg, auth);
+       soup_message_add_status_code_handler (
+               msg, "got_body", SOUP_STATUS_PROXY_UNAUTHORIZED,
+               G_CALLBACK (proxy_authorize_handler), manager);
+}
+
+
diff --git a/libsoup/soup-auth-manager.h b/libsoup/soup-auth-manager.h
new file mode 100644 (file)
index 0000000..89b7bed
--- /dev/null
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_AUTH_MANAGER_H
+#define SOUP_AUTH_MANAGER_H 1
+
+#include "soup-types.h"
+#include "soup-auth.h"
+
+G_BEGIN_DECLS
+
+typedef struct SoupAuthManager SoupAuthManager;
+
+SoupAuthManager *soup_auth_manager_new         (SoupSession     *session);
+
+void             soup_auth_manager_add_type    (SoupAuthManager *manager,
+                                               GType            type);
+void             soup_auth_manager_remove_type (SoupAuthManager *manager,
+                                               GType            type);
+
+void             soup_auth_manager_free        (SoupAuthManager *manager);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_MANAGER_H */
diff --git a/libsoup/soup-auth-ntlm.c b/libsoup/soup-auth-ntlm.c
new file mode 100644 (file)
index 0000000..6383311
--- /dev/null
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-ntlm.c: HTTP NTLM Authentication helper
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-ntlm.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
+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);
+
+typedef struct {
+       char *username, *password;
+} SoupAuthNTLMPrivate;
+#define SOUP_AUTH_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMPrivate))
+
+G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH)
+
+static void
+soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (object);
+
+       g_free (priv->username);
+       if (priv->password) {
+               memset (priv->password, 0, strlen (priv->password));
+               g_free (priv->password);
+       }
+
+       G_OBJECT_CLASS (soup_auth_ntlm_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
+{
+       SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_ntlm_class);
+       GObjectClass *object_class = G_OBJECT_CLASS (auth_ntlm_class);
+
+       g_type_class_add_private (auth_ntlm_class, sizeof (SoupAuthNTLMPrivate));
+
+       auth_class->scheme_name = "NTLM";
+       auth_class->strength = 3;
+
+       auth_class->update = update;
+       auth_class->get_protection_space = get_protection_space;
+       auth_class->authenticate = authenticate;
+       auth_class->is_authenticated = is_authenticated;
+       auth_class->get_authorization = get_authorization;
+
+       object_class->finalize = finalize;
+}
+
+SoupAuth *
+soup_auth_ntlm_new (const char *realm, const char *host)
+{
+       SoupAuth *auth;
+
+       auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
+                            SOUP_AUTH_REALM, realm,
+                            SOUP_AUTH_HOST, host,
+                            NULL);
+       return auth;
+}
+
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+       g_return_val_if_reached (FALSE);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+       g_return_val_if_reached (NULL);
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+       SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+       g_return_if_fail (username != NULL);
+       g_return_if_fail (password != NULL);
+
+       priv->username = g_strdup (username);
+       priv->password = g_strdup (password);
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+       return SOUP_AUTH_NTLM_GET_PRIVATE (auth)->password != NULL;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+       g_return_val_if_reached (NULL);
+}
+
+const char *
+soup_auth_ntlm_get_username (SoupAuth *auth)
+{
+       SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+       return priv->username;
+}
+
+const char *
+soup_auth_ntlm_get_password (SoupAuth *auth)
+{
+       SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+       return priv->password;
+}
diff --git a/libsoup/soup-auth-ntlm.h b/libsoup/soup-auth-ntlm.h
new file mode 100644 (file)
index 0000000..f494c02
--- /dev/null
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, 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 {
+       SoupAuth parent;
+
+} SoupAuthNTLM;
+
+typedef struct {
+       SoupAuthClass parent_class;
+
+} SoupAuthNTLMClass;
+
+GType soup_auth_ntlm_get_type (void);
+
+SoupAuth   *soup_auth_ntlm_new          (const char *realm,
+                                        const char *host);
+const char *soup_auth_ntlm_get_username (SoupAuth   *auth);
+const char *soup_auth_ntlm_get_password (SoupAuth   *auth);
+
+#endif /* SOUP_AUTH_NTLM_H */
index 51ad491..3079281 100644 (file)
 #include "soup-auth-basic.h"
 #include "soup-auth-digest.h"
 #include "soup-headers.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth
+ * @short_description: HTTP client-side authentication support
+ * @see_also: #SoupSession
+ *
+ * #SoupAuth objects store the authentication data associated with a
+ * given bit of web space. They are created automatically by
+ * #SoupSession.
+ **/
+
+/**
+ * SoupAuth:
+ *
+ * The abstract base class for handling authentication. Specific HTTP
+ * Authentication mechanisms are implemented by its subclasses, but
+ * applications never need to be aware of the specific subclasses
+ * being used.
+ **/
+
+typedef struct {
+       gboolean proxy;
+       char *host;
+
+} SoupAuthPrivate;
+#define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
 
 G_DEFINE_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
 
+enum {
+       PROP_0,
+
+       PROP_SCHEME_NAME,
+       PROP_REALM,
+       PROP_HOST,
+       PROP_IS_FOR_PROXY,
+       PROP_IS_AUTHENTICATED,
+
+       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
 finalize (GObject *object)
 {
        SoupAuth *auth = SOUP_AUTH (object);
+       SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (auth);
 
        g_free (auth->realm);
+       g_free (priv->host);
 
        G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
 }
@@ -33,7 +79,48 @@ soup_auth_class_init (SoupAuthClass *auth_class)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (auth_class);
 
+       g_type_class_add_private (auth_class, sizeof (SoupAuthPrivate));
+
        object_class->finalize = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       /* properties */
+       g_object_class_install_property (
+               object_class, PROP_SCHEME_NAME,
+               g_param_spec_string (SOUP_AUTH_SCHEME_NAME,
+                                    "Scheme name",
+                                    "Authentication scheme name",
+                                    NULL,
+                                    G_PARAM_READABLE));
+       g_object_class_install_property (
+               object_class, PROP_REALM,
+               g_param_spec_string (SOUP_AUTH_REALM,
+                                    "Realm",
+                                    "Authentication realm",
+                                    NULL,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_HOST,
+               g_param_spec_string (SOUP_AUTH_HOST,
+                                    "Host",
+                                    "Authentication host",
+                                    NULL,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_IS_FOR_PROXY,
+               g_param_spec_boolean (SOUP_AUTH_IS_FOR_PROXY,
+                                     "For Proxy",
+                                     "Whether or not the auth is for a proxy server",
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_IS_AUTHENTICATED,
+               g_param_spec_boolean (SOUP_AUTH_IS_AUTHENTICATED,
+                                     "Authenticated",
+                                     "Whether or not the auth is authenticated",
+                                     FALSE,
+                                     G_PARAM_READABLE));
 }
 
 static void
@@ -41,108 +128,208 @@ soup_auth_init (SoupAuth *auth)
 {
 }
 
-typedef GType (*GTypeFunc) (void);
-
-typedef struct {
-       const char *name;
-       int         len;
-       GTypeFunc   type_func;
-       int         strength;
-} AuthScheme; 
-
-static AuthScheme known_auth_schemes [] = {
-       { "Basic",  sizeof ("Basic") - 1,  soup_auth_basic_get_type,  0 },
-       { "Digest", sizeof ("Digest") - 1, soup_auth_digest_get_type, 3 },
-       { NULL }
-};
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupAuth *auth = SOUP_AUTH (object);
+       SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_REALM:
+               auth->realm = g_value_dup_string (value);
+               break;
+       case PROP_HOST:
+               priv->host = g_value_dup_string (value);
+               break;
+       case PROP_IS_FOR_PROXY:
+               priv->proxy = g_value_get_boolean (value);
+               break;
+       default:
+               break;
+       }
+}
 
-/* FIXME: it should be possible to register new auth schemes! */
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupAuth *auth = SOUP_AUTH (object);
+       SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_SCHEME_NAME:
+               g_value_set_string (value, soup_auth_get_scheme_name (auth));
+               break;
+       case PROP_REALM:
+               g_value_set_string (value, soup_auth_get_realm (auth));
+               break;
+       case PROP_HOST:
+               g_value_set_string (value, soup_auth_get_host (auth));
+               break;
+       case PROP_IS_FOR_PROXY:
+               g_value_set_boolean (value, priv->proxy);
+               break;
+       case PROP_IS_AUTHENTICATED:
+               g_value_set_boolean (value, soup_auth_is_authenticated (auth));
+               break;
+       default:
+               break;
+       }
+}
 
 /**
- * soup_auth_new_from_header_list:
- * @vals: a list of WWW-Authenticate headers from a server response
+ * soup_auth_new:
+ * @type: the type of auth to create (a subtype of #SoupAuth)
+ * @msg: the #SoupMessage the auth is being created for
+ * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
  *
- * Creates a #SoupAuth value based on the strongest available
- * supported auth type in @vals.
+ * Creates a new #SoupAuth of type @type with the information from
+ * @msg and @auth_header.
  *
- * Return value: the new #SoupAuth, or %NULL if none could be created.
+ * This is called by #SoupSession; you will normally not create auths
+ * yourself.
+ *
+ * Return value: the new #SoupAuth, or %NULL if it could not be
+ * created
  **/
 SoupAuth *
-soup_auth_new_from_header_list (const GSList *vals)
+soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
 {
-       char *header = NULL, *realm;
-       AuthScheme *scheme = NULL, *iter;
-       SoupAuth *auth = NULL;
+       SoupAuth *auth;
        GHashTable *params;
+       const char *scheme, *realm;
 
-       g_return_val_if_fail (vals != NULL, NULL);
-
-       while (vals) {
-               char *tryheader = vals->data;
-
-               for (iter = known_auth_schemes; iter->name; iter++) {
-                       if (!g_ascii_strncasecmp (tryheader, iter->name, 
-                                                 iter->len) &&
-                           (!tryheader[iter->len] ||
-                            g_ascii_isspace (tryheader[iter->len]))) {
-                               if (!scheme || 
-                                   scheme->strength < iter->strength) {
-                                       header = tryheader;
-                                       scheme = iter;
-                               }
+       g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+       g_return_val_if_fail (auth_header != NULL, NULL);
 
-                               break;
-                       }
-               }
+       auth = g_object_new (type,
+                            SOUP_AUTH_IS_FOR_PROXY, (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED),
+                            SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
+                            NULL);
 
-               vals = vals->next;
+       scheme = soup_auth_get_scheme_name (auth);
+       if (strncmp (auth_header, scheme, strlen (scheme)) != 0) {
+               g_object_unref (auth);
+               return NULL;
        }
 
-       if (!scheme)
+       params = soup_header_parse_param_list (auth_header + strlen (scheme));
+       if (!params) {
+               g_object_unref (auth);
                return NULL;
+       }
 
-       params = soup_header_param_parse_list (header + scheme->len);
-       if (!params)
-               return NULL;
-       realm = soup_header_param_copy_token (params, "realm");
+       realm = g_hash_table_lookup (params, "realm");
        if (!realm) {
-               soup_header_param_destroy_hash (params);
+               soup_header_free_param_list (params);
+               g_object_unref (auth);
                return NULL;
        }
 
-       auth = g_object_new (scheme->type_func (), NULL);
-       auth->realm = realm;
+       auth->realm = g_strdup (realm);
 
-       SOUP_AUTH_GET_CLASS (auth)->construct (auth, params);
-       soup_header_param_destroy_hash (params);
+       if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
+               g_object_unref (auth);
+               auth = NULL;
+       }
+       soup_header_free_param_list (params);
        return auth;
 }
 
 /**
+ * soup_auth_update:
+ * @auth: a #SoupAuth
+ * @msg: the #SoupMessage @auth is being updated for
+ * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
+ *
+ * Updates @auth with the information from @msg and @auth_header,
+ * possibly un-authenticating it. As with soup_auth_new(), this is
+ * normally only used by #SoupSession.
+ *
+ * Return value: %TRUE if @auth is still a valid (but potentially
+ * unauthenticated) #SoupAuth. %FALSE if something about @auth_params
+ * could not be parsed or incorporated into @auth at all.
+ **/
+gboolean
+soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
+{
+       GHashTable *params;
+       const char *scheme, *realm;
+       gboolean was_authenticated, success;
+
+       g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+       g_return_val_if_fail (auth_header != NULL, FALSE);
+
+       scheme = soup_auth_get_scheme_name (auth);
+       if (strncmp (auth_header, scheme, strlen (scheme)) != 0)
+               return FALSE;
+
+       params = soup_header_parse_param_list (auth_header + strlen (scheme));
+       if (!params)
+               return FALSE;
+
+       realm = g_hash_table_lookup (params, "realm");
+       if (!realm || strcmp (realm, auth->realm) != 0) {
+               soup_header_free_param_list (params);
+               return FALSE;
+       }
+
+       was_authenticated = soup_auth_is_authenticated (auth);
+       success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
+       if (was_authenticated != soup_auth_is_authenticated (auth))
+               g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
+       soup_header_free_param_list (params);
+       return success;
+}
+
+/**
  * soup_auth_authenticate:
  * @auth: a #SoupAuth
  * @username: the username provided by the user or client
  * @password: the password provided by the user or client
  *
- * This is called by the session after requesting a username and
- * password from the application. @auth will take the information
- * and do whatever scheme-specific processing is needed.
+ * Call this on an auth to authenticate it; normally this will cause
+ * the auth's message to be requeued with the new authentication info.
  **/
 void
 soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
 {
+       gboolean was_authenticated;
+
        g_return_if_fail (SOUP_IS_AUTH (auth));
-       g_return_if_fail (username != NULL);
-       g_return_if_fail (password != NULL);
+       g_return_if_fail (username != NULL || password == NULL);
 
+       was_authenticated = soup_auth_is_authenticated (auth);
        SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
+       if (was_authenticated != soup_auth_is_authenticated (auth))
+               g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
+}
+
+/**
+ * soup_auth_is_for_proxy:
+ * @auth: a #SoupAuth
+ *
+ * Tests whether or not @auth is associated with a proxy server rather
+ * than an "origin" server.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+soup_auth_is_for_proxy (SoupAuth *auth)
+{
+       g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
+
+       return SOUP_AUTH_GET_PRIVATE (auth)->proxy;
 }
 
 /**
  * soup_auth_get_scheme_name:
  * @auth: a #SoupAuth
  *
- * Returns @auth's scheme name. (Eg, "Basic")
+ * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
  *
  * Return value: the scheme name
  **/
@@ -155,10 +342,30 @@ soup_auth_get_scheme_name (SoupAuth *auth)
 }
 
 /**
+ * soup_auth_get_host:
+ * @auth: a #SoupAuth
+ *
+ * Returns the host that @auth is associated with.
+ *
+ * Return value: the hostname
+ **/
+const char *
+soup_auth_get_host (SoupAuth *auth)
+{
+       g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+       return SOUP_AUTH_GET_PRIVATE (auth)->host;
+}
+
+
+/**
  * soup_auth_get_realm:
  * @auth: a #SoupAuth
  *
- * Returns @auth's realm.
+ * Returns @auth's realm. This is an identifier that distinguishes
+ * separate authentication spaces on a given server, and may be some
+ * string that is meaningful to the user. (Although it is probably not
+ * localized.)
  *
  * Return value: the realm name
  **/
@@ -174,10 +381,10 @@ soup_auth_get_realm (SoupAuth *auth)
  * soup_auth_get_info:
  * @auth: a #SoupAuth
  *
- * Gets an identifier for @auth. #SoupAuth objects from the same
- * server with the same identifier refer to the same authentication
- * domain (eg, the URLs associated with them take the same usernames
- * and passwords).
+ * Gets an opaque identifier for @auth, for use as a hash key or the
+ * like. #SoupAuth objects from the same server with the same
+ * identifier refer to the same authentication domain (eg, the URLs
+ * associated with them take the same usernames and passwords).
  *
  * Return value: the identifier
  **/
@@ -242,7 +449,7 @@ soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
  * soup_auth_free_protection_space().
  **/
 GSList *
-soup_auth_get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+soup_auth_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
 {
        g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
        g_return_val_if_fail (source_uri != NULL, NULL);
index 0feda0c..50e3849 100644 (file)
@@ -7,6 +7,7 @@
 #define SOUP_AUTH_H 1
 
 #include <libsoup/soup-types.h>
+#include <libsoup/soup-headers.h>
 
 #define SOUP_TYPE_AUTH            (soup_auth_get_type ())
 #define SOUP_AUTH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH, SoupAuth))
 #define SOUP_IS_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH))
 #define SOUP_AUTH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH, SoupAuthClass))
 
-typedef struct {
+struct SoupAuth {
        GObject parent;
 
        char *realm;
-} SoupAuth;
+};
 
 typedef struct {
        GObjectClass parent_class;
 
-       const char *scheme_name;
+       const char  *scheme_name;
+       guint        strength;
 
-       void         (*construct)            (SoupAuth      *auth,
+       gboolean     (*update)               (SoupAuth      *auth,
+                                             SoupMessage   *msg,
                                              GHashTable    *auth_params);
 
        GSList *     (*get_protection_space) (SoupAuth      *auth,
-                                             const SoupUri *source_uri);
+                                             SoupURI       *source_uri);
 
        void         (*authenticate)         (SoupAuth      *auth,
                                              const char    *username,
@@ -39,14 +42,31 @@ typedef struct {
 
        char *       (*get_authorization)    (SoupAuth      *auth,
                                              SoupMessage   *msg);
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupAuthClass;
 
-GType       soup_auth_get_type              (void);
+#define SOUP_AUTH_SCHEME_NAME      "scheme-name"
+#define SOUP_AUTH_REALM            "realm"
+#define SOUP_AUTH_HOST             "host"
+#define SOUP_AUTH_IS_FOR_PROXY     "is-for-proxy"
+#define SOUP_AUTH_IS_AUTHENTICATED "is-authenticated"
 
+GType       soup_auth_get_type              (void);
 
-SoupAuth   *soup_auth_new_from_header_list  (const GSList  *vals);
+SoupAuth   *soup_auth_new                   (GType          type,
+                                            SoupMessage   *msg,
+                                            const char    *auth_header);
+gboolean    soup_auth_update                (SoupAuth      *auth,
+                                            SoupMessage   *msg,
+                                            const char    *auth_header);
 
+gboolean    soup_auth_is_for_proxy          (SoupAuth      *auth);
 const char *soup_auth_get_scheme_name       (SoupAuth      *auth);
+const char *soup_auth_get_host              (SoupAuth      *auth);
 const char *soup_auth_get_realm             (SoupAuth      *auth);
 char       *soup_auth_get_info              (SoupAuth      *auth);
 
@@ -59,7 +79,7 @@ char       *soup_auth_get_authorization     (SoupAuth      *auth,
                                             SoupMessage   *msg);
 
 GSList     *soup_auth_get_protection_space  (SoupAuth      *auth,
-                                            const SoupUri *source_uri);
+                                            SoupURI       *source_uri);
 void        soup_auth_free_protection_space (SoupAuth      *auth,
                                             GSList        *space);
 
index 1ffb66b..efd1570 100644 (file)
@@ -13,7 +13,9 @@
 #include <string.h>
 
 #include "soup-connection-ntlm.h"
+#include "soup-auth-ntlm.h"
 #include "soup-message.h"
+#include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-uri.h"
 
@@ -79,10 +81,10 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
 {
        SoupConnectionNTLM *ntlm = user_data;
        SoupConnectionNTLMPrivate *priv = SOUP_CONNECTION_NTLM_GET_PRIVATE (ntlm);
-       const GSList *headers;
+       SoupAuth *auth;
        const char *val;
        char *nonce, *header;
-       char *username, *domain_username = NULL, *password = NULL;
+       const char *username = NULL, *password = NULL;
        char *slash, *domain;
 
        if (priv->state > SOUP_CONNECTION_NTLM_SENT_REQUEST) {
@@ -94,15 +96,11 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
                goto done;
        }
 
-       headers = soup_message_get_header_list (msg->response_headers,
-                                               "WWW-Authenticate");
-       while (headers) {
-               val = headers->data;
-               if (!strncmp (val, "NTLM ", 5))
-                       break;
-               headers = headers->next;
-       }
-       if (!headers) {
+       val = soup_message_headers_get (msg->response_headers,
+                                       "WWW-Authenticate");
+       if (val)
+               val = strstr (val, "NTLM ");
+       if (!val) {
                priv->state = SOUP_CONNECTION_NTLM_FAILED;
                goto done;
        }
@@ -112,36 +110,32 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
                goto done;
        }
 
-       soup_connection_authenticate (SOUP_CONNECTION (ntlm), msg,
-                                     "NTLM", domain,
-                                     &domain_username, &password);
-       if (!domain_username || !password) {
+       auth = soup_auth_ntlm_new (domain, soup_message_get_uri (msg)->host);
+       soup_connection_authenticate (SOUP_CONNECTION (ntlm), msg, auth, FALSE);
+       username = soup_auth_ntlm_get_username (auth);
+       password = soup_auth_ntlm_get_password (auth);
+       if (!username || !password) {
                g_free (nonce);
                g_free (domain);
-               g_free (domain_username);
-               g_free (password);
+               g_object_unref (auth);
                goto done;
        }
 
-       slash = strpbrk (domain_username, "\\/");
+       slash = strpbrk (username, "\\/");
        if (slash) {
                g_free (domain);
+               domain = g_strdup (username);
+               slash = strpbrk (domain, "\\/");
                *slash = '\0';
-               domain = domain_username;
                username = slash + 1;
-               domain_username = NULL;
-       } else
-               username = domain_username;
+       }
 
        header = soup_ntlm_response (nonce, username, password, NULL, domain);
-       g_free (domain_username);
-       g_free (password);
        g_free (domain);
        g_free (nonce);
+       g_object_unref (auth);
 
-       soup_message_remove_header (msg->request_headers, "Authorization");
-       soup_message_add_header (msg->request_headers,
-                                "Authorization", header);
+       soup_message_headers_replace (msg->request_headers, "Authorization", header);
        g_free (header);
        priv->state = SOUP_CONNECTION_NTLM_RECEIVED_CHALLENGE;
 
@@ -149,7 +143,7 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
        /* 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");
+       soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
 }
 
 static void
@@ -158,7 +152,7 @@ ntlm_authorize_post (SoupMessage *msg, gpointer conn)
        SoupConnectionNTLMPrivate *priv = SOUP_CONNECTION_NTLM_GET_PRIVATE (conn);
 
        if (priv->state == SOUP_CONNECTION_NTLM_RECEIVED_CHALLENGE &&
-           soup_message_get_header (msg->request_headers, "Authorization")) {
+           soup_message_headers_get (msg->request_headers, "Authorization")) {
                /* We just added the last Auth header, so restart it. */
                priv->state = SOUP_CONNECTION_NTLM_SENT_RESPONSE;
 
@@ -182,10 +176,8 @@ ntlm_cleanup_msg (SoupMessage *msg, gpointer conn)
        /* Do this when the message is restarted, in case it's
         * restarted on a different connection.
         */
-       soup_message_remove_handler (msg, SOUP_HANDLER_PRE_BODY,
-                                    ntlm_authorize_pre, conn);
-       soup_message_remove_handler (msg, SOUP_HANDLER_POST_BODY,
-                                    ntlm_authorize_post, conn);
+       g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, conn);
+       g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, conn);
 }
 
 static void
@@ -196,21 +188,21 @@ send_request (SoupConnection *conn, SoupMessage *req)
        if (priv->state == SOUP_CONNECTION_NTLM_NEW) {
                char *header = soup_ntlm_request ();
 
-               soup_message_remove_header (req->request_headers,
-                                           "Authorization");
-               soup_message_add_header (req->request_headers,
-                                        "Authorization", header);
+               soup_message_headers_replace (req->request_headers,
+                                             "Authorization", header);
                g_free (header);
                priv->state = SOUP_CONNECTION_NTLM_SENT_REQUEST;
        }
 
-       soup_message_add_status_code_handler (req, SOUP_STATUS_UNAUTHORIZED,
-                                             SOUP_HANDLER_PRE_BODY,
-                                             ntlm_authorize_pre, conn);
+       soup_message_add_status_code_handler (req, "got_headers",
+                                             SOUP_STATUS_UNAUTHORIZED,
+                                             G_CALLBACK (ntlm_authorize_pre),
+                                             conn);
 
-       soup_message_add_status_code_handler (req, SOUP_STATUS_UNAUTHORIZED,
-                                             SOUP_HANDLER_POST_BODY,
-                                             ntlm_authorize_post, conn);
+       soup_message_add_status_code_handler (req, "got_body",
+                                             SOUP_STATUS_UNAUTHORIZED,
+                                             G_CALLBACK (ntlm_authorize_post),
+                                             conn);
 
        g_signal_connect (req, "restarted",
                          G_CALLBACK (ntlm_cleanup_msg), conn);
index f3c3270..4dc6d42 100644 (file)
@@ -22,7 +22,6 @@
 #include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
-#include "soup-message-filter.h"
 #include "soup-misc.h"
 #include "soup-socket.h"
 #include "soup-ssl.h"
@@ -43,12 +42,11 @@ typedef struct {
         * connected to, which will be proxy_uri if there's a proxy
         * and origin_uri if not.
         */
-       SoupUri     *proxy_uri, *origin_uri, *conn_uri;
+       SoupURI     *proxy_uri, *origin_uri, *conn_uri;
        gpointer     ssl_creds;
 
        SoupConnectionMode  mode;
 
-       SoupMessageFilter *filter;
        GMainContext      *async_context;
 
        SoupMessage *cur_req;
@@ -63,8 +61,8 @@ G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
 enum {
        CONNECT_RESULT,
        DISCONNECTED,
+       REQUEST_STARTED,
        AUTHENTICATE,
-       REAUTHENTICATE,
        LAST_SIGNAL
 };
 
@@ -76,7 +74,6 @@ enum {
        PROP_ORIGIN_URI,
        PROP_PROXY_URI,
        PROP_SSL_CREDS,
-       PROP_MESSAGE_FILTER,
        PROP_ASYNC_CONTEXT,
        PROP_TIMEOUT,
 
@@ -108,8 +105,6 @@ finalize (GObject *object)
        if (priv->origin_uri)
                soup_uri_free (priv->origin_uri);
 
-       if (priv->filter)
-               g_object_unref (priv->filter);
        if (priv->async_context)
                g_main_context_unref (priv->async_context);
 
@@ -145,14 +140,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
 
        /* signals */
 
-       /**
-        * SoupConnection::connect-result:
-        * @conn: the connection
-        * @status: the status
-        *
-        * Emitted when a connection attempt succeeds or fails. This
-        * is used internally by soup_connection_connect_async().
-        **/
        signals[CONNECT_RESULT] =
                g_signal_new ("connect_result",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -162,14 +149,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                              soup_marshal_NONE__INT,
                              G_TYPE_NONE, 1,
                              G_TYPE_INT);
-
-       /**
-        * SoupConnection::disconnected:
-        * @conn: the connection
-        *
-        * Emitted when the connection's socket is disconnected, for
-        * whatever reason.
-        **/
        signals[DISCONNECTED] =
                g_signal_new ("disconnected",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -178,62 +157,26 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                              NULL, NULL,
                              soup_marshal_NONE__NONE,
                              G_TYPE_NONE, 0);
-
-       /**
-        * SoupConnection::authenticate:
-        * @conn: the connection
-        * @msg: the #SoupMessage being sent
-        * @auth_type: the authentication type
-        * @auth_realm: the realm being authenticated to
-        * @username: the signal handler should set this to point to
-        * the provided username
-        * @password: the signal handler should set this to point to
-        * the provided password
-        *
-        * Emitted when the connection requires authentication.
-        * (#SoupConnectionNTLM makes use of this.)
-        **/
-       signals[AUTHENTICATE] =
-               g_signal_new ("authenticate",
+       signals[REQUEST_STARTED] =
+               g_signal_new ("request-started",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupConnectionClass, authenticate),
+                             G_STRUCT_OFFSET (SoupConnectionClass, request_started),
                              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);
-
-       /**
-        * SoupConnection::reauthenticate:
-        * @conn: the connection
-        * @msg: the #SoupMessage being sent
-        * @auth_type: the authentication type
-        * @auth_realm: the realm being authenticated to
-        * @username: the signal handler should set this to point to
-        * the provided username
-        * @password: the signal handler should set this to point to
-        * the provided password
-        *
-        * Emitted when the authentication data acquired by a previous
-        * %authenticate or %reauthenticate signal fails.
-        **/
-       signals[REAUTHENTICATE] =
-               g_signal_new ("reauthenticate",
+                             soup_marshal_NONE__OBJECT,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_MESSAGE);
+       signals[AUTHENTICATE] =
+               g_signal_new ("authenticate",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupConnectionClass, reauthenticate),
+                             G_STRUCT_OFFSET (SoupConnectionClass, authenticate),
                              NULL, NULL,
-                             soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
-                             G_TYPE_NONE, 5,
+                             soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+                             G_TYPE_NONE, 3,
                              SOUP_TYPE_MESSAGE,
-                             G_TYPE_STRING,
-                             G_TYPE_STRING,
-                             G_TYPE_POINTER,
-                             G_TYPE_POINTER);
+                             SOUP_TYPE_AUTH,
+                             G_TYPE_BOOLEAN);
 
        /* properties */
        g_object_class_install_property (
@@ -255,12 +198,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                                      "Opaque SSL credentials for this connection",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
        g_object_class_install_property (
-               object_class, PROP_MESSAGE_FILTER,
-               g_param_spec_pointer (SOUP_CONNECTION_MESSAGE_FILTER,
-                                     "Message filter",
-                                     "Message filter object for this connection",
-                                     G_PARAM_READWRITE));
-       g_object_class_install_property (
                object_class, PROP_ASYNC_CONTEXT,
                g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
                                      "Async GMainContext",
@@ -328,7 +265,7 @@ set_property (GObject *object, guint prop_id,
                if (priv->proxy_uri) {
                        priv->conn_uri = priv->proxy_uri;
                        if (priv->origin_uri &&
-                           priv->origin_uri->protocol == SOUP_PROTOCOL_HTTPS)
+                           priv->origin_uri->scheme == SOUP_URI_SCHEME_HTTPS)
                                priv->mode = SOUP_CONNECTION_MODE_TUNNEL;
                        else
                                priv->mode = SOUP_CONNECTION_MODE_PROXY;
@@ -341,13 +278,6 @@ set_property (GObject *object, guint prop_id,
        case PROP_SSL_CREDS:
                priv->ssl_creds = g_value_get_pointer (value);
                break;
-       case PROP_MESSAGE_FILTER:
-               if (priv->filter)
-                       g_object_unref (priv->filter);
-               priv->filter = g_value_get_pointer (value);
-               if (priv->filter)
-                       g_object_ref (priv->filter);
-               break;
        case PROP_ASYNC_CONTEXT:
                priv->async_context = g_value_get_pointer (value);
                if (priv->async_context)
@@ -380,9 +310,6 @@ get_property (GObject *object, guint prop_id,
        case PROP_SSL_CREDS:
                g_value_set_pointer (value, priv->ssl_creds);
                break;
-       case PROP_MESSAGE_FILTER:
-               g_value_set_pointer (value, priv->filter ? g_object_ref (priv->filter) : NULL);
-               break;
        case PROP_ASYNC_CONTEXT:
                g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
                break;
@@ -399,7 +326,7 @@ set_current_request (SoupConnectionPrivate *priv, SoupMessage *req)
 {
        g_return_if_fail (priv->cur_req == NULL);
 
-       req->status = SOUP_MESSAGE_STATUS_RUNNING;
+       soup_message_set_io_status (req, SOUP_MESSAGE_IO_STATUS_RUNNING);
        priv->cur_req = req;
        priv->in_use = TRUE;
        g_object_add_weak_pointer (G_OBJECT (req), (gpointer)&priv->cur_req);
@@ -458,7 +385,8 @@ tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
 
        if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                if (soup_socket_start_proxy_ssl (priv->socket,
-                                                priv->origin_uri->host))
+                                                priv->origin_uri->host,
+                                                NULL))
                        priv->connected = TRUE;
                else
                        status = SOUP_STATUS_SSL_FAILED;
@@ -512,8 +440,8 @@ socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
        if (!SOUP_STATUS_IS_SUCCESSFUL (status))
                goto done;
 
-       if (priv->conn_uri->protocol == SOUP_PROTOCOL_HTTPS) {
-               if (!soup_socket_start_ssl (sock)) {
+       if (priv->conn_uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+               if (!soup_socket_start_ssl (sock, NULL)) {
                        status = SOUP_STATUS_SSL_FAILED;
                        goto done;
                }
@@ -567,14 +495,13 @@ soup_connection_connect_async (SoupConnection *conn,
        }
 
        addr = soup_address_new (priv->conn_uri->host, priv->conn_uri->port);
-
        priv->socket =
-               soup_socket_new (SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+               soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
+                                SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
                                 SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
                                 NULL);
-       soup_socket_connect (priv->socket, addr);
-       soup_signal_connect_once (priv->socket, "connect_result",
-                                 G_CALLBACK (socket_connect_result), conn);
+       soup_socket_connect_async (priv->socket, NULL,
+                                  socket_connect_result, conn);
        g_signal_connect (priv->socket, "disconnected",
                          G_CALLBACK (socket_disconnected), conn);
 
@@ -600,27 +527,26 @@ soup_connection_connect_sync (SoupConnection *conn)
        priv = SOUP_CONNECTION_GET_PRIVATE (conn);
        g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
 
+       addr = soup_address_new (priv->conn_uri->host,
+                                priv->conn_uri->port);
        priv->socket =
-               soup_socket_new (SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+               soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
+                                SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
                                 SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
                                 SOUP_SOCKET_TIMEOUT, priv->timeout,
                                 NULL);
 
-       addr = soup_address_new (priv->conn_uri->host,
-                                priv->conn_uri->port);
-
-       status = soup_socket_connect (priv->socket, addr);
+       status = soup_socket_connect_sync (priv->socket, NULL);
        g_object_unref (addr);
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (status))
                goto fail;
-
                
        g_signal_connect (priv->socket, "disconnected",
                          G_CALLBACK (socket_disconnected), conn);
 
-       if (priv->conn_uri->protocol == SOUP_PROTOCOL_HTTPS) {
-               if (!soup_socket_start_ssl (priv->socket)) {
+       if (priv->conn_uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+               if (!soup_socket_start_ssl (priv->socket, NULL)) {
                        status = SOUP_STATUS_SSL_FAILED;
                        goto fail;
                }
@@ -648,7 +574,8 @@ soup_connection_connect_sync (SoupConnection *conn)
 
                if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                        if (!soup_socket_start_proxy_ssl (priv->socket,
-                                                        priv->origin_uri->host))
+                                                         priv->origin_uri->host,
+                                                         NULL))
                                status = SOUP_STATUS_SSL_FAILED;
                }
        }
@@ -722,7 +649,8 @@ soup_connection_disconnect (SoupConnection *conn)
                 * all we need to do to get the message requeued in
                 * this case is to change its status.
                 */
-               priv->cur_req->status = SOUP_MESSAGE_STATUS_QUEUED;
+               soup_message_set_io_status (priv->cur_req,
+                                           SOUP_MESSAGE_IO_STATUS_QUEUED);
                return;
        }
 
@@ -742,6 +670,14 @@ soup_connection_disconnect (SoupConnection *conn)
         */
 }
 
+SoupSocket *
+soup_connection_get_socket (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+
+       return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
+}
+
 /**
  * soup_connection_is_in_use:
  * @conn: a connection
@@ -783,12 +719,11 @@ send_request (SoupConnection *conn, SoupMessage *req)
 
        if (req != priv->cur_req) {
                set_current_request (priv, req);
-               if (priv->filter)
-                       soup_message_filter_setup_message (priv->filter, req);
+               g_signal_emit (conn, signals[REQUEST_STARTED], 0, req);
        }
 
-       soup_message_send_request_internal (req, priv->socket, conn,
-                                           priv->mode == SOUP_CONNECTION_MODE_PROXY);
+       soup_message_send_request (req, priv->socket, conn,
+                                  priv->mode == SOUP_CONNECTION_MODE_PROXY);
 }
 
 /**
@@ -846,44 +781,15 @@ soup_connection_release (SoupConnection *conn)
  * soup_connection_authenticate:
  * @conn: a #SoupConnection
  * @msg: the message to authenticate
- * @auth_type: type of authentication to use
- * @auth_realm: authentication realm
- * @username: on successful return, will contain the username to
- * authenticate with
- * @password: on successful return, will contain the password to
- * authenticate with
+ * @auth: the #SoupAuth to authenticate
+ * @retrying: %TRUE if this is the second or later try
  *
  * Emits the %authenticate signal on @conn. For use by #SoupConnection
  * subclasses.
  **/
 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_connection_reauthenticate:
- * @conn: a #SoupConnection
- * @msg: the message to authenticate
- * @auth_type: type of authentication to use
- * @auth_realm: authentication realm
- * @username: on successful return, will contain the username to
- * authenticate with
- * @password: on successful return, will contain the password to
- * authenticate with
- *
- * Emits the %reauthenticate signal on @conn. For use by
- * #SoupConnection subclasses.
- **/
-void
-soup_connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
-                               const char *auth_type, const char *auth_realm,
-                               char **username, char **password)
+                             SoupAuth *auth, gboolean retrying)
 {
-       g_signal_emit (conn, signals[REAUTHENTICATE], 0,
-                      msg, auth_type, auth_realm, username, password);
+       g_signal_emit (conn, signals[AUTHENTICATE], 0, msg, auth, retrying);
 }
index b36d5fa..98f20d6 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <time.h>
 
-#include <libsoup/soup-types.h>
+#include "soup-types.h"
 
 G_BEGIN_DECLS
 
@@ -19,24 +19,22 @@ G_BEGIN_DECLS
 #define SOUP_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONNECTION))
 #define SOUP_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION, SoupConnectionClass))
 
-struct SoupConnection {
+typedef struct {
        GObject parent;
 
-};
+} SoupConnection;
 
 typedef struct {
        GObjectClass parent_class;
 
        /* signals */
-       void (*connect_result) (SoupConnection *, guint);
-       void (*disconnected)   (SoupConnection *);
+       void (*connect_result)  (SoupConnection *, guint);
+       void (*disconnected)    (SoupConnection *);
+
+       void (*request_started) (SoupConnection *, SoupMessage *);
 
-       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);
+       void (*authenticate)    (SoupConnection *, SoupMessage *,
+                                SoupAuth *, gboolean);
 
        /* methods */
        void (*send_request) (SoupConnection *, SoupMessage *);
@@ -45,14 +43,6 @@ typedef struct {
 GType soup_connection_get_type (void);
 
 
-/**
- * SoupConnectionCallback:
- * @conn: the #SoupConnection
- * @status: an HTTP status code indicating success or failure
- * @data: the data passed to soup_connection_connect_async()
- *
- * The callback function passed to soup_connection_connect_async().
- **/
 typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
                                                guint             status,
                                                gpointer          data);
@@ -61,7 +51,6 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_ORIGIN_URI      "origin-uri"
 #define SOUP_CONNECTION_PROXY_URI       "proxy-uri"
 #define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
-#define SOUP_CONNECTION_MESSAGE_FILTER  "message-filter"
 #define SOUP_CONNECTION_ASYNC_CONTEXT   "async-context"
 #define SOUP_CONNECTION_TIMEOUT                "timeout"
 
@@ -75,6 +64,8 @@ guint           soup_connection_connect_sync   (SoupConnection   *conn);
 
 void            soup_connection_disconnect     (SoupConnection   *conn);
 
+SoupSocket     *soup_connection_get_socket     (SoupConnection   *conn);
+
 gboolean        soup_connection_is_in_use      (SoupConnection   *conn);
 time_t          soup_connection_last_used      (SoupConnection   *conn);
 
@@ -87,16 +78,8 @@ void            soup_connection_release        (SoupConnection   *conn);
 /* 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);
+                                               SoupAuth         *auth,
+                                               gboolean          retrying);
 
 G_END_DECLS
 
index 6f92e74..563849e 100644 (file)
@@ -1,8 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * soup-date.c: Date/time functions
+ * soup-date.c: Date/time handling
  *
  * Copyright (C) 2005, Novell, Inc.
+ * Copyright (C) 2007, Red Hat, Inc.
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include "soup-date.h"
 
+/**
+ * SoupDate:
+ * @year: the year, 1 to 9999
+ * @month: the month, 1 to 12
+ * @day: day of the month, 1 to 31
+ * @hour: hour of the day, 0 to 23
+ * @minute: minute, 0 to 59
+ * @second: second, 0 to 59 (or up to 61 in the case of leap seconds)
+ * @utc: %TRUE if the date is in UTC
+ * @offset: offset from UTC
+
+ * A date and time. The date is assumed to be in the (proleptic)
+ * Gregorian calendar. The time is in UTC if @utc is %TRUE. Otherwise,
+ * the time is a local time, and @offset gives the offset from UTC in
+ * minutes (such that adding @offset to the time would give the
+ * correct UTC time). If @utc is %FALSE and @offset is 0, then the
+ * %SoupDate represents a "floating" time with no associated timezone
+ * information.
+ **/
+
 /* Do not internationalize */
 static const char *months[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -26,197 +47,478 @@ static const char *days[] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 };
 
-static int
-parse_month (const char *month)
+static const int days_before[] = {
+       0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+};
+
+GType
+soup_date_get_type (void)
+{
+       static GType type = 0;
+
+       if (type == 0) {
+               type = g_boxed_type_register_static (
+                       g_intern_static_string ("SoupDate"),
+                       (GBoxedCopyFunc)soup_date_copy,
+                       (GBoxedFreeFunc)soup_date_free);
+       }
+       return type;
+}
+
+/**
+ * soup_date_new:
+ * @year: the year (1-9999)
+ * @month: the month (1-12)
+ * @day: the day of the month (1-31, as appropriate for @month)
+ * @hour: the hour (0-23)
+ * @minute: the minute (0-59)
+ * @second: the second (0-59)
+ *
+ * Creates a #SoupDate representing the indicated time, UTC.
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new (int year, int month, int day, 
+              int hour, int minute, int second)
+{
+       SoupDate *date = g_slice_new (SoupDate);
+
+       date->year   = year;
+       date->month  = month;
+       date->day    = day;
+       date->hour   = hour;
+       date->minute = minute;
+       date->second = second;
+       date->utc    = TRUE;
+       date->offset = 0;
+
+       return date;
+}
+
+/**
+ * soup_date_new_from_now:
+ * @offset_seconds: offset from current time
+ *
+ * Creates a #SoupDate representing a time @offset_seconds after the
+ * current time (or before it, if @offset_seconds is negative). If
+ * offset_seconds is 0, returns the current time.
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new_from_now (int offset_seconds)
+{
+       return soup_date_new_from_time_t (time (NULL) + offset_seconds);
+}
+
+static gboolean
+parse_iso8601_date (SoupDate *date, const char *date_string)
+{
+       gulong val;
+
+       if (strlen (date_string) < 15)
+               return FALSE;
+       if (date_string[4] == '-' &&
+           date_string[7] == '-' &&
+           date_string[10] == 'T') {
+               /* YYYY-MM-DD */
+               date->year  = atoi (date_string);
+               date->month = atoi (date_string + 5);
+               date->day   = atoi (date_string + 8);
+               date_string += 11;
+       } else if (date_string[8] == 'T') {
+               /* YYYYMMDD */
+               val = atoi (date_string);
+               date->year = val / 10000;
+               date->month = (val % 10000) / 100;
+               date->day = val % 100;
+               date_string += 9;
+       } else
+               return FALSE;
+
+       if (strlen (date_string) >= 8 &&
+           date_string[2] == ':' && date_string[5] == ':') {
+               /* HH:MM:SS */
+               date->hour   = atoi (date_string);
+               date->minute = atoi (date_string + 3);
+               date->second = atoi (date_string + 6);
+               date_string += 8;
+       } else if (strlen (date_string) >= 6) {
+               /* HHMMSS */
+               val = strtoul (date_string, (char **)&date_string, 10);
+               date->hour   = val / 10000;
+               date->minute = (val % 10000) / 100;
+               date->second = val % 100;
+       } else
+               return FALSE;
+
+       if (*date_string == '.')
+               strtoul (date_string + 1, (char **)&date_string, 10);
+
+       if (*date_string == 'Z') {
+               date_string++;
+               date->utc = TRUE;
+               date->offset = 0;
+       } else if (*date_string == '+' || *date_string == '-') {
+               int sign = (*date_string == '+') ? -1 : 1;
+               val = strtoul (date_string + 1, (char **)&date_string, 10);
+               if (*date_string == ':')
+                       val = 60 * val + strtoul (date_string + 1, (char **)&date_string, 10);
+               else
+                       val = 60 * (val / 100) + (val % 100);
+               date->offset = sign * val;
+               date->utc = sign && !val;
+       }
+
+       return !*date_string;
+}
+
+static inline gboolean
+parse_day (SoupDate *date, const char **date_string)
+{
+       char *end;
+
+       date->day = strtoul (*date_string, &end, 10);
+       if (end == (char *)date_string)
+               return FALSE;
+
+       while (*end == ' ' || *end == '-')
+               end++;
+       *date_string = end;
+       return TRUE;
+}
+
+static inline gboolean
+parse_month (SoupDate *date, const char **date_string)
 {
        int i;
 
        for (i = 0; i < G_N_ELEMENTS (months); i++) {
-               if (!strncmp (month, months[i], 3))
-                       return i;
+               if (!strncmp (*date_string, months[i], 3)) {
+                       date->month = i + 1;
+                       *date_string += 3;
+                       while (**date_string == ' ' || **date_string == '-')
+                               (*date_string)++;
+                       return TRUE;
+               }
        }
-       return -1;
+       return FALSE;
+}
+
+static inline gboolean
+parse_year (SoupDate *date, const char **date_string)
+{
+       char *end;
+
+       date->year = strtoul (*date_string, &end, 10);
+       if (end == (char *)date_string)
+               return FALSE;
+
+       if (end == (char *)*date_string + 2) {
+               if (date->year < 70)
+                       date->year += 2000;
+               else
+                       date->year += 1900;
+       } else if (end == (char *)*date_string + 3)
+               date->year += 1900;
+
+       while (*end == ' ' || *end == '-')
+               end++;
+       *date_string = end;
+       return TRUE;
+}
+
+static inline gboolean
+parse_time (SoupDate *date, const char **date_string)
+{
+       char *p;
+
+       date->hour = strtoul (*date_string, &p, 10);
+       if (*p++ != ':')
+               return FALSE;
+       date->minute = strtoul (p, &p, 10);
+       if (*p++ != ':')
+               return FALSE;
+       date->second = strtoul (p, &p, 10);
+
+       while (*p == ' ')
+               p++;
+       *date_string = p;
+       return TRUE;
+}
+
+static inline gboolean
+parse_timezone (SoupDate *date, const char **date_string)
+{
+       if (**date_string == '+' || **date_string == '-') {
+               gulong val;
+               int sign = (**date_string == '+') ? -1 : 1;
+               val = strtoul (*date_string + 1, (char **)date_string, 10);
+               if (**date_string != ':')
+                       return FALSE;
+               val = 60 * val + strtoul (*date_string + 1, (char **)date_string, 10);
+               date->offset = sign * val;
+               date->utc = sign && !val;
+       } else if (**date_string == 'Z') {
+               date->offset = 0;
+               date->utc = TRUE;
+               (*date_string)++;
+       } else if (!strcmp (*date_string, "GMT") ||
+                  !strcmp (*date_string, "UTC")) {
+               date->offset = 0;
+               date->utc = TRUE;
+               (*date_string) += 3;
+       } else if (strchr ("ECMP", **date_string) &&
+                  ((*date_string)[1] == 'D' || (*date_string)[1] == 'S') &&
+                  (*date_string)[2] == 'T') {
+               date->offset = -60 * (5 * strcspn ("ECMP", *date_string));
+               if ((*date_string)[1] == 'D')
+                       date->offset += 60;
+               date->utc = FALSE;
+       } else if (!**date_string) {
+               date->utc = FALSE;
+               date->offset = 0;
+       } else
+               return FALSE;
+       return TRUE;
+}
+
+static gboolean
+parse_textual_date (SoupDate *date, const char *date_string)
+{
+       /* If it starts with a word, it must be a weekday, which we skip */
+       while (g_ascii_isalpha (*date_string))
+               date_string++;
+       if (*date_string == ',')
+               date_string++;
+       while (g_ascii_isspace (*date_string))
+               date_string++;
+
+       /* If there's now another word, this must be an asctime-date */
+       if (g_ascii_isalpha (*date_string)) {
+               /* (Sun) Nov  6 08:49:37 1994 */
+               if (!parse_month (date, &date_string) ||
+                   !parse_day (date, &date_string) ||
+                   !parse_time (date, &date_string) ||
+                   !parse_year (date, &date_string))
+                       return FALSE;
+
+               /* There shouldn't be a timezone, but check anyway */
+               parse_timezone (date, &date_string);
+       } else {
+               /* Non-asctime date, so some variation of
+                * (Sun,) 06 Nov 1994 08:49:37 GMT
+                */
+               if (!parse_day (date, &date_string) ||
+                   !parse_month (date, &date_string) ||
+                   !parse_year (date, &date_string) ||
+                   !parse_time (date, &date_string))
+                       return FALSE;
+
+               /* This time there *should* be a timezone, but we
+                * survive if there isn't.
+                */
+               parse_timezone (date, &date_string);
+       }
+       return TRUE;
+}
+
+static int
+days_in_month (int month, int year)
+{
+       return days_before[month + 1] - days_before[month] +
+               (((year % 4 == 0) && month == 2) ? 1 : 0);
 }
 
 /**
- * soup_mktime_utc:
- * @tm: the UTC time
+ * SoupDateFormat:
+ * @SOUP_DATE_HTTP: RFC 1123 format, used by the HTTP "Date" header. Eg
+ * "Sun, 06 Nov 1994 08:49:37 GMT"
+ * @SOUP_DATE_COOKIE: The format for the "Expires" timestamp in the
+ * Netscape cookie specification. Eg, "Sun, 06-Nov-1994 08:49:37 GMT".
+ * @SOUP_DATE_RFC2822: RFC 2822 format, eg "Sun, 6 Nov 1994 09:49:37 -0100"
+ * @SOUP_DATE_ISO8601_COMPACT: ISO 8601 date/time with no optional
+ * punctuation. Eg, "19941106T094937-0100".
+ * @SOUP_DATE_ISO8601_FULL: ISO 8601 date/time with all optional
+ * punctuation. Eg, "1994-11-06T09:49:37-01:00".
+ * @SOUP_DATE_ISO8601_XMLRPC: ISO 8601 date/time as used by XML-RPC.
+ * Eg, "19941106T09:49:37".
+ * @SOUP_DATE_ISO8601: An alias for @SOUP_DATE_ISO8601_FULL.
  *
- * Converts @tm to a #time_t. Unlike with mktime(), @tm is interpreted
- * as being a UTC time.
+ * Date formats that soup_date_to_string() can use.
  *
- * Return value: @tm as a #time_t
+ * @SOUP_DATE_HTTP and @SOUP_DATE_COOKIE always coerce the time to
+ * UTC. @SOUP_DATE_ISO8601_XMLRPC uses the time as given, ignoring the
+ * offset completely. @SOUP_DATE_RFC2822 and the other ISO 8601
+ * variants use the local time, appending the offset information if
+ * available.
+ *
+ * This enum may be extended with more values in future releases.
  **/
-time_t
-soup_mktime_utc (struct tm *tm)
+
+/**
+ * soup_date_new_from_string:
+ * @date_string: the date in some plausible format
+ *
+ * Parses @date_string and tries to extract a date from it. This
+ * recognizes all of the "HTTP-date" formats from RFC 2616, all ISO
+ * 8601 formats containing both a time and a date, RFC 2822 dates,
+ * and reasonable approximations thereof. (Eg, it is lenient about
+ * whitespace, leading "0"s, etc.)
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new_from_string (const char *date_string)
 {
-#if HAVE_TIMEGM
-       return timegm (tm);
-#else
-       time_t tt;
-       static const int days_before[] = {
-               0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
-       };
-
-       /* We check the month because (a) if we don't, the
-        * days_before[] part below may access random memory, and (b)
-        * soup_date_parse() doesn't check the return value of
-        * parse_month(). The caller is responsible for ensuring the
-        * sanity of everything else.
+       SoupDate *date = g_slice_new (SoupDate);
+       gboolean success;
+
+       while (g_ascii_isspace (*date_string))
+               date_string++;
+
+       /* If it starts with a digit, it's either an ISO 8601 date, or
+        * an RFC2822 date without the optional weekday; in the later
+        * case, there will be a month name later on, so look for one
+        * of the month-start letters.
         */
-       if (tm->tm_mon < 0 || tm->tm_mon > 11)
-               return (time_t)-1;
-
-       tt = (tm->tm_year - 70) * 365;
-       tt += (tm->tm_year - 68) / 4;
-       tt += days_before[tm->tm_mon] + tm->tm_mday - 1;
-       if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
-               tt--;
-       tt = ((((tt * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
-       
-       return tt;
-#endif
+       if (g_ascii_isdigit (*date_string) &&
+           !strpbrk (date_string, "JFMASOND"))
+               success = parse_iso8601_date (date, date_string);
+       else
+               success = parse_textual_date (date, date_string);
+
+       if (!success) {
+               g_slice_free (SoupDate, date);
+               return NULL;
+       }
+
+       if (date->year < 1 || date->year > 9999 ||
+           date->month < 1 || date->month > 12 ||
+           date->day < 1 ||
+           date->day > days_in_month (date->month, date->year) ||
+           date->hour < 0 || date->hour > 23 ||
+           date->minute < 0 || date->minute > 59 ||
+           date->second < 0 || date->second > 59) {
+               g_slice_free (SoupDate, date);
+               return NULL;
+       } else
+               return date;
 }
 
 /**
- * soup_gmtime:
+ * soup_date_new_from_time_t:
  * @when: a #time_t
- * @tm: a struct tm to be filled in with the expansion of @when
  *
- * Expands @when into @tm (as a UTC time). This is just a wrapper
- * around gmtime_r() (or gmtime() on lame platforms). (The Microsoft C
- * library on Windows doesn't have gmtime_r(), but its gmtime() is in
- * fact thread-safe as it uses a per-thread buffer, so it's not
- * totally lame ;-)
+ * Creates a #SoupDate corresponding to @when
+ *
+ * Return value: a new #SoupDate
  **/
-void
-soup_gmtime (const time_t *when, struct tm *tm)
+SoupDate *
+soup_date_new_from_time_t (time_t when)
 {
+       struct tm tm;
+
 #ifdef HAVE_GMTIME_R
-       gmtime_r (when, tm);
+       gmtime_r (&when, &tm);
 #else
-       *tm = *gmtime (when);
+       tm = *gmtime (when);
 #endif
+
+       return soup_date_new (tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                             tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+static const char *
+soup_date_weekday (SoupDate *date)
+{
+       int day;
+
+       /* Proleptic Gregorian 0001-01-01 was a Monday, which
+        * corresponds to 1 in the days[] array. So we take the
+        * number of days since 0000-12-31, modulo 7.
+        */
+       day = (date->year - 1) * 365 + ((date->year - 1) / 4);
+       day += days_before[date->month] + date->day;
+       if (date->year % 4 == 0 && date->month > 2)
+               day++;
+
+       return days[day % 7];
 }
 
 /**
- * soup_date_parse:
- * @timestamp: a timestamp, in any of the allowed HTTP 1.1 formats
+ * soup_date_to_string:
+ * @date: a #SoupDate
+ * @format: the format to generate the date in
  *
- * Parses @timestamp and returns its value as a #time_t.
+ * Converts @date to a string in the format described by @format.
  *
- * Return value: the #time_t corresponding to @timestamp, or -1 if
- * @timestamp couldn't be parsed.
+ * Return value: @date as a string
  **/
-time_t
-soup_date_parse (const char *timestamp)
+char *
+soup_date_to_string (SoupDate *date, SoupDateFormat format)
 {
-       struct tm tm;
-       int len = strlen (timestamp);
-
-       if (len < 4)
-               return (time_t)-1;
-
-       switch (timestamp[3]) {
-       case ',':
-               /* rfc1123-date = wkday "," SP date1 SP time SP "GMT"
-                * date1        = 2DIGIT SP month SP 4DIGIT
-                * time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
-                *
-                * eg, "Sun, 06 Nov 1994 08:49:37 GMT"
-                */
-               if (len != 29 || strcmp (timestamp + 25, " GMT") != 0)
-                       return (time_t)-1;
-
-               tm.tm_mday = atoi (timestamp + 5);
-               tm.tm_mon = parse_month (timestamp + 8);
-               tm.tm_year = atoi (timestamp + 12) - 1900;
-               tm.tm_hour = atoi (timestamp + 17);
-               tm.tm_min = atoi (timestamp + 20);
-               tm.tm_sec = atoi (timestamp + 23);
-               break;
-
-       case ' ':
-               /* asctime-date = wkday SP date3 SP time SP 4DIGIT
-                * date3        = month SP ( 2DIGIT | ( SP 1DIGIT ))
-                * time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
-                *
-                * eg, "Sun Nov  6 08:49:37 1994"
-                */
-               if (len != 24)
-                       return (time_t)-1;
+       /* FIXME: offset, 8601 zones, etc */
+
+       switch (format) {
+       case SOUP_DATE_HTTP:
+               /* "Sun, 06 Nov 1994 08:49:37 GMT" */
+               return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d GMT",
+                                       soup_date_weekday (date), date->day,
+                                       months[date->month - 1],
+                                       date->year, date->hour, date->minute,
+                                       date->second);
 
-               tm.tm_mon = parse_month (timestamp + 4);
-               tm.tm_mday = atoi (timestamp + 8);
-               tm.tm_hour = atoi (timestamp + 11);
-               tm.tm_min = atoi (timestamp + 14);
-               tm.tm_sec = atoi (timestamp + 17);
-               tm.tm_year = atoi (timestamp + 20) - 1900;
-               break;
+       case SOUP_DATE_COOKIE:
+               /* "Sun, 06-Nov-1994 08:49:37 GMT" */
+               return g_strdup_printf ("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
+                                       soup_date_weekday (date), date->day,
+                                       months[date->month - 1],
+                                       date->year, date->hour, date->minute,
+                                       date->second);
+
+       case SOUP_DATE_ISO8601_COMPACT:
+               return g_strdup_printf ("%04d%02d%02dT%02d%02d%02d",
+                                       date->year, date->month, date->day,
+                                       date->hour, date->minute, date->second);
+       case SOUP_DATE_ISO8601_FULL:
+               return g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d",
+                                       date->year, date->month, date->day,
+                                       date->hour, date->minute, date->second);
+       case SOUP_DATE_ISO8601_XMLRPC:
+               return g_strdup_printf ("%04d%02d%02dT%02d:%02d:%02d",
+                                       date->year, date->month, date->day,
+                                       date->hour, date->minute, date->second);
 
        default:
-               /* rfc850-date  = weekday "," SP date2 SP time SP "GMT"
-                * date2        = 2DIGIT "-" month "-" 2DIGIT
-                * time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
-                *
-                * eg, "Sunday, 06-Nov-94 08:49:37 GMT"
-                */
-               timestamp = strchr (timestamp, ',');
-               if (timestamp == NULL || strlen (timestamp) != 24 || strcmp (timestamp + 20, " GMT") != 0)
-                       return (time_t)-1;
-
-               tm.tm_mday = atoi (timestamp + 2);
-               tm.tm_mon = parse_month (timestamp + 5);
-               tm.tm_year = atoi (timestamp + 9);
-               if (tm.tm_year < 70)
-                       tm.tm_year += 100;
-               tm.tm_hour = atoi (timestamp + 12);
-               tm.tm_min = atoi (timestamp + 15);
-               tm.tm_sec = atoi (timestamp + 18);
-               break;
+               return NULL;
        }
-
-       return soup_mktime_utc (&tm);
 }
 
 /**
- * soup_date_generate:
- * @when: the time to generate a timestamp for
- *
- * Generates an HTTP 1.1 Date header corresponding to @when.
+ * soup_date_copy:
+ * @date: a #SoupDate
  *
- * Return value: the timestamp, which the caller must free.
+ * Copies @date.
  **/
-char *
-soup_date_generate (time_t when)
+SoupDate *
+soup_date_copy (SoupDate *date)
 {
-       struct tm tm;
-
-       soup_gmtime (&when, &tm);
+       SoupDate *copy = g_slice_new (SoupDate);
 
-       /* RFC1123 format, eg, "Sun, 06 Nov 1994 08:49:37 GMT" */
-       return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d GMT",
-                               days[tm.tm_wday], tm.tm_mday,
-                               months[tm.tm_mon], tm.tm_year + 1900,
-                               tm.tm_hour, tm.tm_min, tm.tm_sec);
+       memcpy (copy, date, sizeof (SoupDate));
+       return copy;
 }
 
 /**
- * soup_date_iso8601_parse:
- * @timestamp: an ISO8601 timestamp
- *
- * Converts @timestamp to a %time_t value. @timestamp can be in any of the
- * iso8601 formats that specify both a date and a time.
+ * soup_date_free:
+ * @date: a #SoupDate
  *
- * Return value: the %time_t corresponding to @timestamp, or -1 on error.
+ * Frees @date.
  **/
-time_t
-soup_date_iso8601_parse (const char *timestamp)
+void
+soup_date_free (SoupDate *date)
 {
-       GTimeVal timeval;
-
-       if (!g_time_val_from_iso8601 (timestamp, &timeval))
-               return (time_t) -1;
-
-       return (time_t) timeval.tv_sec;
+       g_slice_free (SoupDate, date);
 }
index 29bdf48..106b082 100644 (file)
@@ -1,23 +1,57 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2007 Red Hat, Inc.
  */
 
 #ifndef SOUP_DATE_H
 #define SOUP_DATE_H 1
 
 #include <time.h>
-#include <glib/gmacros.h>
+#include <libsoup/soup-types.h>
 
 G_BEGIN_DECLS
 
-time_t  soup_mktime_utc         (struct tm *tm);
-void    soup_gmtime             (const time_t *when, struct tm *tm);
-
-time_t  soup_date_parse         (const char *timestamp);
-char   *soup_date_generate      (time_t when);
-
-time_t  soup_date_iso8601_parse (const char *timestamp);
+typedef struct {
+       int      year;
+       int      month;
+       int      day;
+
+       int      hour;
+       int      minute;
+       int      second;
+
+       gboolean utc;
+       int      offset;
+} SoupDate;
+
+typedef enum {
+       SOUP_DATE_HTTP = 1,
+       SOUP_DATE_COOKIE,
+       SOUP_DATE_RFC2822,
+       SOUP_DATE_ISO8601_COMPACT,
+       SOUP_DATE_ISO8601_FULL,
+       SOUP_DATE_ISO8601 = SOUP_DATE_ISO8601_FULL,
+       SOUP_DATE_ISO8601_XMLRPC
+} SoupDateFormat;
+
+GType soup_date_get_type (void);
+#define SOUP_TYPE_DATE (soup_date_get_type ())
+
+SoupDate *soup_date_new             (int             year,
+                                    int             month,
+                                    int             day, 
+                                    int             hour,
+                                    int             minute,
+                                    int             second);
+SoupDate *soup_date_new_from_string (const char     *date_string);
+SoupDate *soup_date_new_from_time_t (time_t          when);
+SoupDate *soup_date_new_from_now    (int             offset_seconds);
+
+char     *soup_date_to_string       (SoupDate       *date,
+                                    SoupDateFormat  format);
+SoupDate *soup_date_copy            (SoupDate       *date);
+void      soup_date_free            (SoupDate       *date);
 
 G_END_DECLS
 
index 6ab3f4f..31d1d20 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "soup-dns.h"
 #include "soup-misc.h"
+#include "soup-status.h"
 
 #ifndef INET_ADDRSTRLEN
 #  define INET_ADDRSTRLEN 16
@@ -122,7 +123,7 @@ typedef struct {
 
        gboolean resolved;
        GThread *resolver_thread;
-       GSList *lookups;
+       GSList *async_lookups;
 } SoupDNSCacheEntry;
 
 static GHashTable *soup_dns_cache;
@@ -132,10 +133,9 @@ struct SoupDNSLookup {
        SoupDNSCacheEntry *entry;
 
        GMainContext *async_context;
+       GCancellable *cancellable;
        SoupDNSCallback callback;
        gpointer user_data;
-
-       gboolean running;
 };
 
 static GMutex *soup_dns_lock;
@@ -226,7 +226,7 @@ soup_dns_cache_entry_unref (SoupDNSCacheEntry *entry)
                 * have reached zero. So no cleanup needed there.
                 */
 
-               g_free (entry);
+               g_slice_free (SoupDNSCacheEntry, entry);
        }
 }
 
@@ -235,7 +235,7 @@ soup_dns_cache_entry_new (const char *name)
 {
        SoupDNSCacheEntry *entry;
 
-       entry = g_new0 (SoupDNSCacheEntry, 1);
+       entry = g_slice_new0 (SoupDNSCacheEntry);
        entry->entry_name = g_strdup (name);
        entry->ref_count = 2; /* One for the caller, one for the cache */
        soup_dns_cache_entry_set_from_phys (entry);
@@ -320,8 +320,10 @@ resolve_address (SoupDNSCacheEntry *entry)
        retval = getaddrinfo (entry->hostname, NULL, &hints, &res);
        if (retval == 0) {
                entry->sockaddr = g_memdup (res->ai_addr, res->ai_addrlen);
+               entry->resolved = TRUE;
                freeaddrinfo (res);
-       }
+       } else
+               entry->resolved = (retval != EAI_AGAIN);
 
 #else /* !HAVE_GETADDRINFO */
 
@@ -336,7 +338,10 @@ resolve_address (SoupDNSCacheEntry *entry)
                sin.sin_family = AF_INET;
                memcpy (&sin.sin_addr, h->h_addr_list[0], sizeof (struct in_addr));
                entry->sockaddr = g_memdup (&sin, sizeof (struct sockaddr_in));
-       }
+               entry->resolved = TRUE;
+       } else
+               entry->resolved = (h || h_errno != TRY_AGAIN);
+
 
        g_mutex_unlock (soup_gethost_lock);
 
@@ -363,10 +368,13 @@ resolve_name (SoupDNSCacheEntry *entry)
 #endif
                );
 
-       if (retval == 0)
+       if (retval == 0) {
                entry->hostname = name;
-       else
+               entry->resolved = TRUE;
+       } else {
                g_free (name);
+               entry->resolved = (retval != EAI_AGAIN);
+       }
 
 #else /* !HAVE_GETNAMEINFO */
 
@@ -377,8 +385,11 @@ resolve_name (SoupDNSCacheEntry *entry)
 
        if (sin->sin_family == AF_INET) {
                h = gethostbyaddr (&sin->sin_addr, sizeof (sin->sin_addr), AF_INET);
-               if (h)
+               if (h) {
                        entry->hostname = g_strdup (h->h_name);
+                       entry->resolved = TRUE;
+               } else
+                       entry->resolved = (h_errno != TRY_AGAIN);
        }
 
        g_mutex_unlock (soup_gethost_lock);
@@ -425,7 +436,7 @@ soup_dns_lookup_name (const char *name)
                        entry->resolved = TRUE;
        }
 
-       lookup = g_new0 (SoupDNSLookup, 1);
+       lookup = g_slice_new0 (SoupDNSLookup);
        lookup->entry = entry;
        g_mutex_unlock (soup_dns_lock);
 
@@ -459,25 +470,37 @@ soup_dns_lookup_address (struct sockaddr *sockaddr)
                entry = soup_dns_cache_entry_new (name); // FIXME
        g_free (name);
 
-       lookup = g_new0 (SoupDNSLookup, 1);
+       lookup = g_slice_new0 (SoupDNSLookup);
        lookup->entry = entry;
        g_mutex_unlock (soup_dns_lock);
 
        return lookup;
 }
 
+static inline guint
+resolve_status (SoupDNSCacheEntry *entry, GCancellable *cancellable)
+{
+       if (entry->hostname && entry->sockaddr)
+               return SOUP_STATUS_OK;
+       else if (g_cancellable_is_cancelled (cancellable))
+               return SOUP_STATUS_CANCELLED;
+       else
+               return SOUP_STATUS_CANT_RESOLVE;
+}
+
+static void async_cancel (GCancellable *cancellable, gpointer user_data);
+
 static gboolean
 do_async_callback (gpointer user_data)
 {
        SoupDNSLookup *lookup = user_data;
+       SoupDNSCacheEntry *entry = lookup->entry;
+       GCancellable *cancellable = lookup->cancellable;
 
-       if (lookup->running) {
-               SoupDNSCacheEntry *entry = lookup->entry;
-               gboolean success = (entry->hostname != NULL && entry->sockaddr != NULL);
-
-               lookup->running = FALSE;
-               lookup->callback (lookup, success, lookup->user_data);
-       }
+       lookup->callback (lookup, resolve_status (entry, cancellable),
+                         lookup->user_data);
+       if (cancellable)
+               g_signal_handlers_disconnect_by_func (cancellable, async_cancel, lookup);
 
        return FALSE;
 }
@@ -486,27 +509,26 @@ static gpointer
 resolver_thread (gpointer user_data)
 {
        SoupDNSCacheEntry *entry = user_data;
-       GSList *lookups;
+       GSList *async_lookups;
        SoupDNSLookup *lookup;
 
        if (entry->hostname == NULL)
                resolve_name (entry);
-       if (entry->sockaddr == NULL)
+       else if (entry->sockaddr == NULL)
                resolve_address (entry);
 
-       entry->resolved = TRUE;
        entry->resolver_thread = NULL;
 
        g_mutex_lock (soup_dns_lock);
-       lookups = entry->lookups;
-       entry->lookups = NULL;
+       async_lookups = entry->async_lookups;
+       entry->async_lookups = NULL;
        g_mutex_unlock (soup_dns_lock);
 
        g_cond_broadcast (soup_dns_cond);
 
-       while (lookups) {
-               lookup = lookups->data;
-               lookups = g_slist_remove (lookups, lookup);
+       while (async_lookups) {
+               lookup = async_lookups->data;
+               async_lookups = g_slist_remove (async_lookups, lookup);
 
                soup_add_idle (lookup->async_context, do_async_callback, lookup);
        }
@@ -515,52 +537,100 @@ resolver_thread (gpointer user_data)
        return NULL;
 }
 
+static void
+sync_cancel (GCancellable *cancellable, gpointer user_data)
+{
+       /* We can't actually cancel the resolver thread. So we just
+        * wake up the blocking thread, which will see that
+        * @cancellable has been cancelled and then stop waiting for
+        * the result. If the resolver thread eventually finishes,
+        * its result will make it to the cache.
+        */
+       g_cond_broadcast (soup_dns_cond);
+}
+
 /**
  * soup_dns_lookup_resolve:
  * @lookup: a #SoupDNSLookup
+ * @cancellable: a #GCancellable, or %NULL
  *
- * Synchronously resolves @lookup. You can cancel a pending resolution
- * using soup_dns_lookup_cancel().
+ * Synchronously resolves @lookup.
  *
- * Return value: success or failure.
+ * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED
  **/
-gboolean
-soup_dns_lookup_resolve (SoupDNSLookup *lookup)
+guint
+soup_dns_lookup_resolve (SoupDNSLookup *lookup, GCancellable *cancellable)
 {
        SoupDNSCacheEntry *entry = lookup->entry;
+       guint cancel_id = 0;
 
        g_mutex_lock (soup_dns_lock);
 
-       lookup->running = TRUE;
+       if (!entry->resolved) {
+               if (!entry->resolver_thread) {
+                       soup_dns_cache_entry_ref (entry);
+                       entry->resolver_thread =
+                               g_thread_create (resolver_thread, entry,
+                                                FALSE, NULL);
+               }
 
-       if (!entry->resolved && !entry->resolver_thread) {
-               soup_dns_cache_entry_ref (entry);
-               entry->resolver_thread =
-                       g_thread_create (resolver_thread, entry, FALSE, NULL);
+               if (cancellable) {
+                       cancel_id = g_signal_connect (cancellable, "cancelled",
+                                                     G_CALLBACK (sync_cancel),
+                                                     NULL);
+               }
        }
 
-       while (!entry->resolved && lookup->running)
+       while (entry->resolver_thread &&
+              !g_cancellable_is_cancelled (cancellable))
                g_cond_wait (soup_dns_cond, soup_dns_lock);
 
-       lookup->running = FALSE;
+       if (cancel_id)
+               g_signal_handler_disconnect (cancellable, cancel_id);
+
+       g_mutex_unlock (soup_dns_lock);
+
+       return resolve_status (entry, cancellable);
+}
+
+static void
+async_cancel (GCancellable *cancellable, gpointer user_data)
+{
+       SoupDNSLookup *lookup = user_data;
+       SoupDNSCacheEntry *entry = lookup->entry;
+
+       /* We can't actually cancel the resolver thread. So we just
+        * remove @lookup from the list of pending async lookups and
+        * invoke its callback now. If the resolver thread eventually
+        * finishes, its result will make it to the cache.
+        */
+       g_mutex_lock (soup_dns_lock);
+
+       if (g_slist_find (entry->async_lookups, lookup)) {
+               entry->async_lookups = g_slist_remove (entry->async_lookups,
+                                                      lookup);
+               soup_add_idle (lookup->async_context, do_async_callback, lookup);
+       }
 
        g_mutex_unlock (soup_dns_lock);
-       return entry->hostname != NULL && entry->sockaddr != NULL;
 }
 
 /**
  * soup_dns_lookup_resolve_async:
  * @lookup: a #SoupDNSLookup
  * @async_context: #GMainContext to call @callback in
+ * @cancellable: a #GCancellable, or %NULL
  * @callback: callback to call when @lookup is resolved
  * @user_data: data to pass to @callback;
  *
  * Tries to asynchronously resolve @lookup. Invokes @callback when it
- * has succeeded or failed. You can cancel a pending resolution using
- * soup_dns_lookup_cancel().
+ * has succeeded or failed.
  **/
 void
-soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_context,
+soup_dns_lookup_resolve_async (SoupDNSLookup *lookup,
+                              GMainContext *async_context,
+                              GCancellable *cancellable,
                               SoupDNSCallback callback, gpointer user_data)
 {
        SoupDNSCacheEntry *entry = lookup->entry;
@@ -568,16 +638,23 @@ soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_contex
        g_mutex_lock (soup_dns_lock);
 
        lookup->async_context = async_context;
+       lookup->cancellable = cancellable;
        lookup->callback = callback;
        lookup->user_data = user_data;
-       lookup->running = TRUE;
 
        if (!entry->resolved) {
-               entry->lookups = g_slist_prepend (entry->lookups, lookup);
+               entry->async_lookups = g_slist_prepend (entry->async_lookups,
+                                                       lookup);
+               if (cancellable) {
+                       g_signal_connect (cancellable, "cancelled",
+                                         G_CALLBACK (async_cancel), lookup);
+               }
+
                if (!entry->resolver_thread) {
                        soup_dns_cache_entry_ref (entry);
                        entry->resolver_thread =
-                               g_thread_create (resolver_thread, entry, FALSE, NULL);
+                               g_thread_create (resolver_thread, entry,
+                                                FALSE, NULL);
                }
        } else
                soup_add_idle (lookup->async_context, do_async_callback, lookup);
@@ -586,28 +663,6 @@ soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_contex
 }
 
 /**
- * soup_dns_lookup_cancel:
- * @lookup: a #SoupDNSLookup
- *
- * Cancels @lookup. If @lookup was running synchronously in another
- * thread, it will immediately return %FALSE. If @lookup was running
- * asynchronously, its callback function will not be called.
- **/
-void
-soup_dns_lookup_cancel (SoupDNSLookup *lookup)
-{
-       /* We never really cancel the DNS lookup itself (since GThread
-        * doesn't have a kill function, and it might mess up
-        * underlying resolver data anyway). But clearing lookup->running
-        * and broadcasting on soup_dns_cond will immediately stop any
-        * blocking synchronous lookups, and clearing lookup->running
-        * will also make sure that its async callback is never invoked.
-        */
-       lookup->running = FALSE;
-       g_cond_broadcast (soup_dns_cond);
-}
-
-/**
  * soup_dns_lookup_get_hostname:
  * @lookup: a #SoupDNSLookup
  *
@@ -642,14 +697,12 @@ soup_dns_lookup_get_address (SoupDNSLookup *lookup)
  * soup_dns_lookup_free:
  * @lookup: a #SoupDNSLookup
  *
- * Frees @lookup. If @lookup is still running, it will be canceled
- * first.
+ * Frees @lookup. It is an error to cancel a lookup while it is
+ * running.
  **/
 void
 soup_dns_lookup_free (SoupDNSLookup *lookup)
 {
-       if (lookup->running)
-               soup_dns_lookup_cancel (lookup);
        soup_dns_cache_entry_unref (lookup->entry);
-       g_free (lookup);
+       g_slice_free (SoupDNSLookup, lookup);
 }
index 2a8bb0f..6519a9c 100644 (file)
@@ -7,6 +7,7 @@
 #define SOUP_DNS_H
 
 #include <glib.h>
+#include <gio/gio.h>
 #include <sys/types.h>
 
 #include <libsoup/soup-portability.h>
 void             soup_dns_init                 (void);
 char            *soup_dns_ntop                 (struct sockaddr *sa);
 
-/**
- * SoupDNSLookup:
- *
- * An opaque type that represents a DNS lookup operation.
- **/
 typedef struct SoupDNSLookup SoupDNSLookup;
 
 SoupDNSLookup   *soup_dns_lookup_name          (const char  *name);
 SoupDNSLookup   *soup_dns_lookup_address       (struct sockaddr *sockaddr);
 void             soup_dns_lookup_free          (SoupDNSLookup   *lookup);
 
-/**
- * SoupDNSCallback:
- * @lookup: the completed lookup
- * @success: %TRUE if @lookup completed successfully, %FALSE if it failed
- * @user_data: the data passed to soup_dns_lookup_resolve_async()
- *
- * The callback function passed to soup_dns_lookup_resolve_async().
- **/
-typedef void (*SoupDNSCallback) (SoupDNSLookup *lookup, gboolean success, gpointer user_data);
-
-gboolean         soup_dns_lookup_resolve       (SoupDNSLookup   *lookup);
+typedef void (*SoupDNSCallback) (SoupDNSLookup *lookup, guint status, gpointer user_data);
+
+guint            soup_dns_lookup_resolve       (SoupDNSLookup   *lookup,
+                                               GCancellable    *cancellable);
 void             soup_dns_lookup_resolve_async (SoupDNSLookup   *lookup,
                                                GMainContext    *async_context,
+                                               GCancellable    *cancellable,
                                                SoupDNSCallback  callback,
                                                gpointer         user_data);
-void             soup_dns_lookup_cancel        (SoupDNSLookup   *lookup);
 
 char            *soup_dns_lookup_get_hostname  (SoupDNSLookup   *lookup);
 struct sockaddr *soup_dns_lookup_get_address   (SoupDNSLookup   *lookup);
diff --git a/libsoup/soup-enum-types.c.tmpl b/libsoup/soup-enum-types.c.tmpl
new file mode 100644 (file)
index 0000000..ea10782
--- /dev/null
@@ -0,0 +1,33 @@
+/*** BEGIN file-header ***/
+#include "soup.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type (void)
+{
+       static GType etype = 0;
+       if (G_UNLIKELY (etype == 0)) {
+               static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+                       { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+                       { 0, NULL, NULL }
+               };
+               etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+       }
+       return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/libsoup/soup-enum-types.h.tmpl b/libsoup/soup-enum-types.h.tmpl
new file mode 100644 (file)
index 0000000..e18d7e0
--- /dev/null
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __SOUP_ENUM_TYPES_H__
+#define __SOUP_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define SOUP_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __SOUP_ENUM_TYPES_H__ */
+/*** END file-tail ***/
diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c
new file mode 100644 (file)
index 0000000..299fc88
--- /dev/null
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* soup-form.c : utility functions for HTML forms */
+
+/*
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-form.h"
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static gboolean
+form_decode (char *part)
+{
+       unsigned char *s, *d;
+
+       s = d = (unsigned char *)part;
+       do {
+               if (*s == '%') {
+                       if (!g_ascii_isxdigit (s[1]) ||
+                           !g_ascii_isxdigit (s[2]))
+                               return FALSE;
+                       *d++ = HEXCHAR (s);
+                       s += 2;
+               } else if (*s == '+')
+                       *d++ = ' ';
+               else
+                       *d++ = *s;
+       } while (*s++);
+
+       return TRUE;
+}
+
+/**
+ * soup_form_decode_urlencoded:
+ * @encoded_form: data of type "application/x-www-form-urlencoded"
+ *
+ * Decodes @form, which is an urlencoded dataset as defined in the
+ * HTML 4.01 spec.
+ *
+ * Return value: a hash table containing the name/value pairs from
+ * @encoded_form, which you can free with g_hash_table_destroy().
+ **/
+GHashTable *
+soup_form_decode_urlencoded (const char *encoded_form)
+{
+       GHashTable *form_data_set;
+       char **pairs, *eq, *name, *value;
+       int i;
+
+       form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                              g_free, NULL);
+       pairs = g_strsplit (encoded_form, "&", -1);
+       for (i = 0; pairs[i]; i++) {
+               name = pairs[i];
+               eq = strchr (name, '=');
+               if (!form_decode (name)) {
+                       g_free (name);
+                       continue;
+               }
+               if (eq) {
+                       *eq = '\0';
+                       value = eq + 1;
+               } else
+                       value = NULL;
+
+               g_hash_table_insert (form_data_set, name, value);
+       }
+       g_free (pairs);
+
+       return form_data_set;
+}
+
+static void
+append_form_encoded (GString *str, const char *in)
+{
+       const unsigned char *s = (const unsigned char *)in;
+
+       while (*s) {
+               if (*s == ' ') {
+                       g_string_append_c (str, '+');
+                       s++;
+               } else if (!g_ascii_isalnum (*s))
+                       g_string_append_printf (str, "%%%02X", (int)*s++);
+               else
+                       g_string_append_c (str, *s++);
+       }
+}
+
+static void
+encode_pair (GString *str, const char *name, const char *value)
+{
+       if (str->len)
+               g_string_append_c (str, '&');
+       append_form_encoded (str, name);
+       g_string_append_c (str, '=');
+       append_form_encoded (str, value);
+}
+
+static void
+hash_encode_foreach (gpointer name, gpointer value, gpointer str)
+{
+       encode_pair (str, name, value);
+}
+
+/**
+ * soup_form_encode_urlencoded:
+ * @form_data_set: a hash table containing name/value pairs
+ *
+ * Encodes @form_data_set into a value of type
+ * "application/x-www-form-urlencoded", as defined in the HTML 4.01
+ * spec.
+ *
+ * Note that the spec states that "The control names/values are listed
+ * in the order they appear in the document." Since
+ * soup_form_encode_urlencoded() takes a hash table, this cannot be
+ * enforced; if you care about the ordering of the form fields, use
+ * soup_form_encode_urlencoded_list().
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode_urlencoded (GHashTable *form_data_set)
+{
+       GString *str = g_string_new (NULL);
+
+       g_hash_table_foreach (form_data_set, hash_encode_foreach, str);
+       return g_string_free (str, FALSE);
+}
+
+static void
+datalist_encode_foreach (GQuark key_id, gpointer value, gpointer str)
+{
+       encode_pair (str, g_quark_to_string (key_id), value);
+}
+
+/**
+ * soup_form_encode_urlencoded_list:
+ * @form_data_set: a hash table containing name/value pairs
+ *
+ * Encodes @form_data_set into a value of type
+ * "application/x-www-form-urlencoded", as defined in the HTML 4.01
+ * spec. Unlike soup_form_encode_urlencoded(), this preserves the
+ * ordering of the form elements, which may be required in some
+ * situations.
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode_urlencoded_list (GData **form_data_set)
+{
+       GString *str = g_string_new (NULL);
+
+       g_datalist_foreach (form_data_set, datalist_encode_foreach, str);
+       return g_string_free (str, FALSE);
+}
diff --git a/libsoup/soup-form.h b/libsoup/soup-form.h
new file mode 100644 (file)
index 0000000..9502244
--- /dev/null
@@ -0,0 +1,20 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef  SOUP_FORM_H
+#define  SOUP_FORM_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+GHashTable *soup_form_decode_urlencoded      (const char  *encoded_form);
+
+char       *soup_form_encode_urlencoded      (GHashTable  *form_data_set);
+char       *soup_form_encode_urlencoded_list (GData      **form_data_set);
+
+G_END_DECLS
+
+#endif /* SOUP_FORM_H */
index c4b66f0..b402850 100644 (file)
 #include "soup-ssl.h"
 #include "soup-misc.h"
 
+/**
+ * soup_ssl_supported:
+ *
+ * Can be used to test if libsoup was compiled with ssl support.
+ **/
 gboolean soup_ssl_supported = TRUE;
 
 #define DH_BITS 1024
@@ -309,7 +314,7 @@ soup_gnutls_free (GIOChannel *channel)
        g_io_channel_unref (chan->real_sock);
        gnutls_deinit (chan->session);
        g_free (chan->hostname);
-       g_free (chan);
+       g_slice_free (SoupGNUTLSChannel, chan);
 }
 
 static GIOStatus
@@ -412,7 +417,7 @@ soup_ssl_wrap_iochannel (GIOChannel *sock, SoupSSLType type,
 
        gnutls_transport_set_ptr (session, GINT_TO_POINTER (sockfd));
 
-       chan = g_new0 (SoupGNUTLSChannel, 1);
+       chan = g_slice_new0 (SoupGNUTLSChannel);
        chan->fd = sockfd;
        chan->real_sock = sock;
        chan->session = session;
@@ -475,7 +480,7 @@ soup_ssl_get_client_credentials (const char *ca_file)
        if (!soup_gnutls_inited)
                soup_gnutls_init ();
 
-       creds = g_new0 (SoupSSLCredentials, 1);
+       creds = g_slice_new0 (SoupSSLCredentials);
        gnutls_certificate_allocate_credentials (&creds->creds);
 
        if (ca_file) {
@@ -507,7 +512,7 @@ void
 soup_ssl_free_client_credentials (SoupSSLCredentials *creds)
 {
        gnutls_certificate_free_credentials (creds->creds);
-       g_free (creds);
+       g_slice_free (SoupSSLCredentials, creds);
 }
 
 /**
@@ -535,7 +540,7 @@ soup_ssl_get_server_credentials (const char *cert_file, const char *key_file)
                        return NULL;
        }
 
-       creds = g_new0 (SoupSSLCredentials, 1);
+       creds = g_slice_new0 (SoupSSLCredentials);
        gnutls_certificate_allocate_credentials (&creds->creds);
 
        if (gnutls_certificate_set_x509_key_file (creds->creds,
@@ -562,7 +567,7 @@ void
 soup_ssl_free_server_credentials (SoupSSLCredentials *creds)
 {
        gnutls_certificate_free_credentials (creds->creds);
-       g_free (creds);
+       g_slice_free (SoupSSLCredentials, creds);
 }
 
 #endif /* HAVE_SSL */
index dd7b516..1e69b02 100644 (file)
 #include "soup-misc.h"
 
 static gboolean
-soup_headers_parse (const char *str, 
-                   int         len, 
-                   GHashTable *dest)
+soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest)
 {
-       const char *end = str + len;
-       const char *name_start, *name_end, *value_start, *value_end;
-       char *name, *value, *eol, *sol;
-       GSList *hdrs;
+       const char *headers_start;
+       char *headers_copy, *name, *name_end, *value, *value_end;
+       char *eol, *sol;
+       gboolean success = FALSE;
 
        /* Technically, the grammar does allow NUL bytes in the
         * headers, but this is probably a bug, and if it's not, we
@@ -36,39 +34,46 @@ soup_headers_parse (const char *str,
         */
 
        /* Skip over the Request-Line / Status-Line */
-       value_end = memchr (str, '\n', len);
-       if (!value_end)
+       headers_start = memchr (str, '\n', len);
+       if (!headers_start)
                return FALSE;
 
-       while (value_end < end - 1) {
-               name_start = value_end + 1;
-               name_end = memchr (name_start, ':', end - name_start);
+       /* We work on a copy of the headers, which we can write '\0's
+        * into, so that we don't have to individually g_strndup and
+        * then g_free each header name and value.
+        */
+       headers_copy = g_strndup (headers_start, len - (headers_start - str));
+       value_end = headers_copy;
+
+       while (*(value_end + 1)) {
+               name = value_end + 1;
+               name_end = strchr (name, ':');
                if (!name_end)
-                       return FALSE;
+                       goto done;
 
                /* Find the end of the value; ie, an end-of-line that
                 * isn't followed by a continuation line.
                 */
-               value_end = memchr (name_start, '\n', end - name_start);
+               value = name_end + 1;
+               value_end = strchr (name, '\n');
                if (!value_end || value_end < name_end)
-                       return FALSE;
-               while (value_end != end - 1 &&
-                      (*(value_end + 1) == ' ' || *(value_end + 1) == '\t')) {
-                       value_end = memchr (value_end + 1, '\n', end - value_end);
+                       goto done;
+               while (*(value_end + 1) == ' ' || *(value_end + 1) == '\t') {
+                       value_end = strchr (value_end + 1, '\n');
                        if (!value_end)
-                               return FALSE;
+                               goto done;
                }
 
-               name = g_strndup (name_start, name_end - name_start);
+               *name_end = '\0';
+               *value_end = '\0';
 
-               value_start = name_end + 1;
-               while (value_start < value_end &&
-                      (*value_start == ' ' || *value_start == '\t' ||
-                       *value_start == '\r' || *value_start == '\n'))
-                       value_start++;
-               value = g_strndup (value_start, value_end - value_start);
+               /* Skip leading whitespace */
+               while (value < value_end &&
+                      (*value == ' ' || *value == '\t' ||
+                       *value == '\r' || *value == '\n'))
+                       value++;
 
-               /* Collapse continuation lines inside value */
+               /* Collapse continuation lines */
                while ((eol = strchr (value, '\n'))) {
                        /* find start of next line */
                        sol = eol + 1;
@@ -91,46 +96,46 @@ soup_headers_parse (const char *str,
                        eol--;
                *eol = '\0';
 
-               hdrs = g_hash_table_lookup (dest, name);
-               hdrs = g_slist_append (hdrs, value);
-               if (!hdrs->next)
-                       g_hash_table_insert (dest, name, hdrs);
-               else
-                       g_free (name);
+               soup_message_headers_append (dest, name, value);
         }
+       success = TRUE;
 
-       return TRUE;
+done:
+       g_free (headers_copy);
+       return success;
 }
 
 /**
  * soup_headers_parse_request:
  * @str: the header string (including the trailing blank line)
  * @len: length of @str up to (but not including) the terminating blank line.
- * @dest: #GHashTable to store the header values in
+ * @req_headers: #SoupMessageHeaders to store the header values in
  * @req_method: if non-%NULL, will be filled in with the request method
  * @req_path: if non-%NULL, will be filled in with the request path
  * @ver: if non-%NULL, will be filled in with the HTTP version
  *
  * Parses the headers of an HTTP request in @str and stores the
- * results in @req_method, @req_path, @ver, and @dest.
+ * results in @req_method, @req_path, @ver, and @req_headers.
  *
- * Beware that @dest may be modified even on failure.
+ * Beware that @req_headers may be modified even on failure.
  *
- * Return value: success or failure.
+ * Return value: %SOUP_STATUS_OK if the headers could be parsed, or an
+ * HTTP error to be returned to the client if they could not be.
  **/
-gboolean
-soup_headers_parse_request (const char       *str, 
-                           int               len, 
-                           GHashTable       *dest, 
-                           char            **req_method,
-                           char            **req_path,
-                           SoupHttpVersion  *ver) 
+guint
+soup_headers_parse_request (const char          *str, 
+                           int                  len, 
+                           SoupMessageHeaders  *req_headers,
+                           char               **req_method,
+                           char               **req_path,
+                           SoupHTTPVersion     *ver) 
 {
-       const char *method, *method_end, *path, *path_end, *version, *headers;
-       int minor_version;
+       const char *method, *method_end, *path, *path_end;
+       const char *version, *version_end, *headers;
+       unsigned long major_version, minor_version;
+       char *p;
 
-       if (!str || !*str)
-               return FALSE;
+       g_return_val_if_fail (str && *str, SOUP_STATUS_MALFORMED);
 
        /* RFC 2616 4.1 "servers SHOULD ignore any empty line(s)
         * received where a Request-Line is expected."
@@ -148,43 +153,47 @@ soup_headers_parse_request (const char       *str,
        while (method_end < str + len && *method_end != ' ' && *method_end != '\t')
                method_end++;
        if (method_end >= str + len)
-               return FALSE;
+               return SOUP_STATUS_BAD_REQUEST;
 
        path = method_end;
        while (path < str + len && (*path == ' ' || *path == '\t'))
                path++;
        if (path >= str + len)
-               return FALSE;
+               return SOUP_STATUS_BAD_REQUEST;
 
        path_end = path;
        while (path_end < str + len && *path_end != ' ' && *path_end != '\t')
                path_end++;
        if (path_end >= str + len)
-               return FALSE;
+               return SOUP_STATUS_BAD_REQUEST;
 
        version = path_end;
        while (version < str + len && (*version == ' ' || *version == '\t'))
                version++;
        if (version + 8 >= str + len)
-               return FALSE;
-
-       /* FIXME: we want SoupServer to return
-        * SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED here
-        */
-       if (strncmp (version, "HTTP/1.", 7) != 0)
-               return FALSE;
-       minor_version = version[7] - '0';
+               return SOUP_STATUS_BAD_REQUEST;
+
+       if (strncmp (version, "HTTP/", 5) != 0 ||
+           !g_ascii_isdigit (version[5]))
+               return SOUP_STATUS_BAD_REQUEST;
+       major_version = strtoul (version + 5, &p, 10);
+       if (*p != '.' || !g_ascii_isdigit (p[1]))
+               return SOUP_STATUS_BAD_REQUEST;
+       minor_version = strtoul (p + 1, &p, 10);
+       version_end = p;
+       if (major_version != 1)
+               return SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
        if (minor_version < 0 || minor_version > 1)
-               return FALSE;
+               return SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
 
-       headers = version + 8;
+       headers = version_end;
        while (headers < str + len && (*headers == '\r' || *headers == ' '))
                headers++;
        if (headers >= str + len || *headers != '\n')
-               return FALSE;
+               return SOUP_STATUS_BAD_REQUEST;
 
-       if (!soup_headers_parse (str, len, dest)) 
-               return FALSE;
+       if (!soup_headers_parse (str, len, req_headers)) 
+               return SOUP_STATUS_BAD_REQUEST;
 
        if (req_method)
                *req_method = g_strndup (method, method_end - method);
@@ -193,7 +202,7 @@ soup_headers_parse_request (const char       *str,
        if (ver)
                *ver = (minor_version == 0) ? SOUP_HTTP_1_0 : SOUP_HTTP_1_1;
 
-       return TRUE;
+       return SOUP_STATUS_OK;
 }
 
 /**
@@ -212,22 +221,29 @@ soup_headers_parse_request (const char       *str,
  **/
 gboolean
 soup_headers_parse_status_line (const char       *status_line,
-                               SoupHttpVersion  *ver,
+                               SoupHTTPVersion  *ver,
                                guint            *status_code,
                                char            **reason_phrase)
 {
-       guint minor_version, code;
+       unsigned long major_version, minor_version, code;
        const char *code_start, *code_end, *phrase_start, *phrase_end;
+       char *p;
 
-       if (strncmp (status_line, "HTTP/1.", 7) != 0)
+       if (strncmp (status_line, "HTTP/", 5) != 0 ||
+           !g_ascii_isdigit (status_line[5]))
+               return FALSE;
+       major_version = strtoul (status_line + 5, &p, 10);
+       if (*p != '.' || !g_ascii_isdigit (p[1]))
+               return FALSE;
+       minor_version = strtoul (p + 1, &p, 10);
+       if (major_version != 1)
                return FALSE;
-       minor_version = status_line[7] - '0';
        if (minor_version < 0 || minor_version > 1)
                return FALSE;
        if (ver)
                *ver = (minor_version == 0) ? SOUP_HTTP_1_0 : SOUP_HTTP_1_1;
 
-       code_start = status_line + 8;
+       code_start = p;
        while (*code_start == ' ' || *code_start == '\t')
                code_start++;
        code_end = code_start;
@@ -258,31 +274,31 @@ soup_headers_parse_status_line (const char       *status_line,
  * soup_headers_parse_response:
  * @str: the header string (including the trailing blank line)
  * @len: length of @str up to (but not including) the terminating blank line.
- * @dest: #GHashTable to store the header values in
+ * @headers: #SoupMessageheaders to store the header values in
  * @ver: if non-%NULL, will be filled in with the HTTP version
  * @status_code: if non-%NULL, will be filled in with the status code
  * @reason_phrase: if non-%NULL, will be filled in with the reason
  * phrase
  *
  * Parses the headers of an HTTP response in @str and stores the
- * results in @ver, @status_code, @reason_phrase, and @dest.
+ * results in @ver, @status_code, @reason_phrase, and @headers.
  *
- * Beware that @dest may be modified even on failure.
+ * Beware that @headers may be modified even on failure.
  *
  * Return value: success or failure.
  **/
 gboolean
-soup_headers_parse_response (const char       *str, 
-                            int               len, 
-                            GHashTable       *dest,
-                            SoupHttpVersion  *ver,
-                            guint            *status_code,
-                            char            **reason_phrase)
+soup_headers_parse_response (const char          *str, 
+                            int                  len, 
+                            SoupMessageHeaders  *headers,
+                            SoupHTTPVersion     *ver,
+                            guint               *status_code,
+                            char               **reason_phrase)
 {
        if (!str || !*str)
                return FALSE;
 
-       if (!soup_headers_parse (str, len, dest)) 
+       if (!soup_headers_parse (str, len, headers)) 
                return FALSE;
 
        if (!soup_headers_parse_status_line (str, 
@@ -296,149 +312,314 @@ soup_headers_parse_response (const char       *str,
 
 
 /*
- * HTTP parameterized header parsing
+ * Parsing of specific HTTP header types
  */
 
-char *
-soup_header_param_copy_token (GHashTable *tokens, char *t)
+static const char *
+skip_lws (const char *s)
 {
-       char *data;
-
-       g_return_val_if_fail (tokens, NULL);
-       g_return_val_if_fail (t, NULL);
-
-       if ( (data = g_hash_table_lookup (tokens, t)))
-               return g_strdup (data);
-       else
-               return NULL;
+       while (g_ascii_isspace (*s))
+               s++;
+       return s;
 }
 
-static void
-decode_lwsp (char **in)
+static const char *
+unskip_lws (const char *s, const char *start)
 {
-       char *inptr = *in;
-
-       while (isspace (*inptr))
-               inptr++;
-
-       *in = inptr;
+       while (s > start && g_ascii_isspace (*(s - 1)))
+               s--;
+       return s;
 }
 
-static char *
-decode_quoted_string (char **in)
+static const char *
+skip_commas (const char *s)
 {
-       char *inptr = *in;
-       char *out = NULL, *outptr;
-       int outlen;
-       int c;
-
-       decode_lwsp (&inptr);
-       if (*inptr == '"') {
-               char *intmp;
-               int skip = 0;
-
-                /* first, calc length */
-                inptr++;
-                intmp = inptr;
-                while ( (c = *intmp++) && c != '"') {
-                        if (c == '\\' && *intmp) {
-                                intmp++;
-                                skip++;
-                        }
-                }
-
-                outlen = intmp - inptr - skip;
-                out = outptr = g_malloc (outlen + 1);
-
-                while ( (c = *inptr++) && c != '"') {
-                        if (c == '\\' && *inptr) {
-                                c = *inptr++;
-                        }
-                        *outptr++ = c;
-                }
-                *outptr = 0;
-        }
-
-        *in = inptr;
-
-        return out;
+       /* The grammar allows for multiple commas */
+       while (g_ascii_isspace (*s) || *s == ',')
+               s++;
+       return s;
 }
 
-char *
-soup_header_param_decode_token (char **in)
+static const char *
+skip_item (const char *s)
 {
-       char *inptr = *in;
-       char *start;
+       gboolean quoted = FALSE;
+       const char *start = s;
+
+       /* A list item ends at the last non-whitespace character
+        * before a comma which is not inside a quoted-string. Or at
+        * the end of the string.
+        */
 
-       decode_lwsp (&inptr);
-       start = inptr;
+       while (*s) {
+               if (*s == '"')
+                       quoted = !quoted;
+               else if (quoted) {
+                       if (*s == '\\' && *(s + 1))
+                               s++;
+               } else {
+                       if (*s == ',')
+                               break;
+               }
+               s++;
+       }
 
-       while (*inptr && *inptr != '=' && *inptr != ',')
-               inptr++;
+       return unskip_lws (s, start);
+}
 
-       if (inptr > start) {
-               *in = inptr;
-               return g_strndup (start, inptr - start);
+/**
+ * soup_header_parse_list:
+ * @header: a header value
+ *
+ * Parses a header whose content is described by RFC2616 as
+ * "#something", where "something" does not itself contain commas,
+ * except as part of quoted-strings.
+ *
+ * Return value: a #GSList of list elements, as allocated strings
+ **/
+GSList *
+soup_header_parse_list (const char *header)
+{
+       GSList *list = NULL;
+       const char *end;
+
+       header = skip_commas (header);
+       while (*header) {
+               end = skip_item (header);
+               list = g_slist_prepend (list, g_strndup (header, end - header));
+               header = skip_commas (end);
        }
-       else
-               return NULL;
+
+       return g_slist_reverse (list);
 }
 
-static char *
-decode_value (char **in)
+typedef struct {
+       char *item;
+       double qval;
+} QualityItem;
+
+static int
+sort_by_qval (const void *a, const void *b)
 {
-       char *inptr = *in;
+       QualityItem *qia = (QualityItem *)a;
+       QualityItem *qib = (QualityItem *)b;
 
-       decode_lwsp (&inptr);
-       if (*inptr == '"')
-               return decode_quoted_string (in);
+       if (qia->qval == qib->qval)
+               return 0;
+       else if (qia->qval < qib->qval)
+               return 1;
        else
-               return soup_header_param_decode_token (in);
+               return -1;
 }
 
-GHashTable *
-soup_header_param_parse_list (const char *header)
+/**
+ * soup_header_parse_quality_list:
+ * @header: a header value
+ * @unacceptable: on return, will contain a list of unacceptable
+ * values
+ *
+ * Parses a header whose content is a list of items with optional
+ * "qvalue"s (eg, Accept, Accept-Charset, Accept-Encoding,
+ * Accept-Language, TE).
+ *
+ * If @unacceptable is not %NULL, then on return, it will contain the
+ * items with qvalue 0. Either way, those items will be removed from
+ * the main list.
+ *
+ * Return value: a #GSList of acceptable values (as allocated
+ * strings), highest-qvalue first.
+ **/
+GSList *
+soup_header_parse_quality_list (const char *header, GSList **unacceptable)
 {
-       char *ptr;
-       gboolean added = FALSE;
-       GHashTable *params = g_hash_table_new (soup_str_case_hash, 
-                                              soup_str_case_equal);
-
-       ptr = (char *) header;
-       while (ptr && *ptr) {
-               char *name;
-               char *value;
-
-               name = soup_header_param_decode_token (&ptr);
-               if (*ptr == '=') {
-                       ptr++;
-                       value = decode_value (&ptr);
-                       g_hash_table_insert (params, name, value);
-                       added = TRUE;
+       GSList *unsorted;
+       QualityItem *array;
+       GSList *sorted, *iter;
+       char *item, *semi;
+       const char *param, *equal, *value;
+       double qval;
+       int n;
+
+       if (unacceptable)
+               *unacceptable = NULL;
+
+       unsorted = soup_header_parse_list (header);
+       array = g_new0 (QualityItem, g_slist_length (unsorted));
+       for (iter = unsorted, n = 0; iter; iter = iter->next) {
+               item = iter->data;
+               qval = 1.0;
+               for (semi = strchr (item, ';'); semi; semi = strchr (semi + 1, ';')) {
+                       param = skip_lws (semi + 1);
+                       if (*param != 'q')
+                               continue;
+                       equal = skip_lws (param + 1);
+                       if (!equal || *equal != '=')
+                               continue;
+                       value = skip_lws (equal + 1);
+                       if (!value)
+                               continue;
+
+                       if (value[0] != '0' && value[0] != '1')
+                               continue;
+                       qval = (double)(value[0] - '0');
+                       if (value[0] == '0' && value[1] == '.') {
+                               if (g_ascii_isdigit (value[2]))
+                                       qval += (double)(value[2] - '0') / 10;
+                               if (g_ascii_isdigit (value[3]))
+                                       qval += (double)(value[3] - '0') / 100;
+                               if (g_ascii_isdigit (value[4]))
+                                       qval += (double)(value[4] - '0') / 1000;
+                       }
+
+                       *semi = '\0';
+                       break;
                }
 
-               if (*ptr == ',')
-                       ptr++;
+               if (qval == 0.0) {
+                       if (unacceptable) {
+                               *unacceptable = g_slist_prepend (*unacceptable,
+                                                                item);
+                       }
+               } else {
+                       array[n].item = item;
+                       array[n].qval = qval;
+                       n++;
+               }
        }
+       g_slist_free (unsorted);
+
+       qsort (array, n, sizeof (QualityItem), sort_by_qval);
+       sorted = NULL;
+       while (n--)
+               sorted = g_slist_prepend (sorted, array[n].item);
+       g_free (array);
 
-       if (!added) {
-               g_hash_table_destroy (params);
-               params = NULL;
+       return sorted;
+}
+
+/**
+ * soup_header_free_list:
+ * @list: a #GSList returned from soup_header_parse_list() or
+ * soup_header_parse_quality_list()
+ *
+ * Frees @list.
+ **/
+void
+soup_header_free_list (GSList *list)
+{
+       GSList *l;
+
+       for (l = list; l; l = l->next)
+               g_free (l->data);
+       g_slist_free (l);
+}
+
+/**
+ * soup_header_contains:
+ * @header: An HTTP header suitable for parsing with
+ * soup_header_parse_list()
+ * @token: a token
+ *
+ * Parses @header to see if it contains the token @token (matched
+ * case-insensitively). Note that this can't be used with lists
+ * that have qvalues.
+ *
+ * Return value: whether or not @header contains @token
+ **/
+gboolean
+soup_header_contains (const char *header, const char *token)
+{
+       const char *end;
+       guint len = strlen (token);
+
+       header = skip_commas (header);
+       while (*header) {
+               end = skip_item (header);
+               if (end - header == len &&
+                   !g_ascii_strncasecmp (header, token, len))
+                       return TRUE;
+               header = skip_commas (end);
        }
 
-       return params;
+       return FALSE;
 }
 
 static void
-destroy_param_hash_elements (gpointer key, gpointer value, gpointer user_data)
+decode_quoted_string (char *quoted_string)
 {
-       g_free (key);
-       g_free (value);
+       char *src, *dst;
+
+       src = quoted_string + 1;
+       dst = quoted_string;
+       while (*src && *src != '"') {
+               if (*src == '\\' && *(src + 1))
+                       src++;
+               *dst++ = *src++;
+       }
+       *dst = '\0';
 }
 
+/**
+ * soup_header_parse_param_list:
+ * @header: a header value
+ *
+ * Parses a header which is a list of something like
+ *   token [ "=" ( token | quoted-string ) ]
+ *
+ * Tokens that don't have an associated value will still be added to
+ * the resulting hash table, but with a %NULL value.
+ * 
+ * Return value: a #GHashTable of list elements.
+ **/
+GHashTable *
+soup_header_parse_param_list (const char *header)
+{
+       GHashTable *params;
+       GSList *list, *iter;
+       char *item, *eq, *name_end, *value;
+
+       list = soup_header_parse_list (header);
+       if (!list)
+               return NULL;
+
+       params = g_hash_table_new_full (soup_str_case_hash, 
+                                       soup_str_case_equal,
+                                       g_free, NULL);
+
+       for (iter = list; iter; iter = iter->next) {
+               item = iter->data;
+
+               eq = strchr (item, '=');
+               if (eq) {
+                       name_end = (char *)unskip_lws (eq, item);
+                       if (name_end == item) {
+                               /* That's no good... */
+                               g_free (item);
+                               continue;
+                       }
+
+                       *name_end = '\0';
+
+                       value = (char *)skip_lws (eq + 1);
+                       if (*value == '"')
+                               decode_quoted_string (value);
+               } else
+                       value = NULL;
+
+               g_hash_table_insert (params, item, value);
+       }
+
+       return params;
+}
+
+/**
+ * soup_header_free_param_list:
+ * @param_list: a #GHashTable returned from soup_header_parse_param_list()
+ *
+ * Frees @param_list.
+ **/
 void
-soup_header_param_destroy_hash (GHashTable *table)
+soup_header_free_param_list (GHashTable *param_list)
 {
-       g_hash_table_foreach (table, destroy_param_hash_elements, NULL);
-       g_hash_table_destroy (table);
+       g_hash_table_destroy (param_list);
 }
index 428ab94..d040e75 100644 (file)
@@ -13,35 +13,37 @@ G_BEGIN_DECLS
 
 /* HTTP Header Parsing */
 
-gboolean    soup_headers_parse_request      (const char       *str, 
-                                            int               len, 
-                                            GHashTable       *dest, 
-                                            char            **req_method,
-                                            char            **req_path,
-                                            SoupHttpVersion  *ver);
-
-gboolean    soup_headers_parse_status_line  (const char        *status_line,
-                                            SoupHttpVersion  *ver,
-                                            guint            *status_code,
-                                            char            **reason_phrase);
-
-gboolean    soup_headers_parse_response     (const char       *str, 
-                                            int               len, 
-                                            GHashTable       *dest,
-                                            SoupHttpVersion  *ver,
-                                            guint            *status_code,
-                                            char            **reason_phrase);
-
-/* HTTP parameterized header parsing */
-
-char       *soup_header_param_decode_token  (char            **in);
-
-GHashTable *soup_header_param_parse_list    (const char       *header);
-
-char       *soup_header_param_copy_token    (GHashTable       *tokens, 
-                                            char             *t);
-
-void        soup_header_param_destroy_hash  (GHashTable       *table);
+guint       soup_headers_parse_request      (const char          *str,
+                                            int                  len,
+                                            SoupMessageHeaders  *req_headers,
+                                            char               **req_method,
+                                            char               **req_path,
+                                            SoupHTTPVersion     *ver);
+
+gboolean    soup_headers_parse_status_line  (const char          *status_line,
+                                            SoupHTTPVersion     *ver,
+                                            guint               *status_code,
+                                            char               **reason_phrase);
+
+gboolean    soup_headers_parse_response     (const char          *str,
+                                            int                  len,
+                                            SoupMessageHeaders  *headers,
+                                            SoupHTTPVersion     *ver,
+                                            guint               *status_code,
+                                            char               **reason_phrase);
+
+/* Individual header parsing */
+
+GSList     *soup_header_parse_list          (const char       *header);
+GSList     *soup_header_parse_quality_list  (const char       *header,
+                                            GSList          **unacceptable);
+void        soup_header_free_list           (GSList           *list);
+
+gboolean    soup_header_contains            (const char       *header,
+                                            const char       *token);
+
+GHashTable *soup_header_parse_param_list    (const char       *header);
+void        soup_header_free_param_list     (GHashTable       *param_list);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c
new file mode 100644 (file)
index 0000000..a822134
--- /dev/null
@@ -0,0 +1,638 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-logger.c
+ *
+ * Copyright (C) 2001-2004 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "soup-logger.h"
+#include "soup-message.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-logger
+ * @short_description: Debug logging support
+ *
+ * #SoupLogger watches a #SoupSession and logs the HTTP traffic that
+ * it generates, for debugging purposes. Many applications use an
+ * environment variable to determine whether or not to use
+ * #SoupLogger, and to determine the amount of debugging output.
+ *
+ * To use #SoupLogger, first create a logger with soup_logger_new(),
+ * optionally configure it with soup_logger_set_request_filter(),
+ * soup_logger_set_response_filter(), and soup_logger_set_printer(),
+ * and then attach it to a session (or multiple sessions) with
+ * soup_logger_attach().
+ *
+ * By default, the debugging output is sent to %stdout, and looks
+ * something like:
+ *
+ * <informalexample><screen>
+ * > POST /unauth HTTP/1.1
+ * > Soup-Debug-Timestamp: 1200171744
+ * > Soup-Debug: session 1 (0x612190), msg 1 (0x617000), conn 1 (0x612220)
+ * > Host: localhost
+ * > Content-Type: text/plain
+ * > Connection: close
+ * > 
+ * > This is a test.
+ *   
+ * &lt; HTTP/1.1 201 Created
+ * &lt; Soup-Debug-Timestamp: 1200171744
+ * &lt; Soup-Debug: msg 1 (0x617000)
+ * &lt; Date: Sun, 12 Jan 2008 21:02:24 GMT
+ * &lt; Content-Length: 0
+ * </screen></informalexample>
+ *
+ * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as
+ * a #time_t) when the request was sent, or the response fully
+ * received.
+ *
+ * The <literal>Soup-Debug</literal> line gives further debugging
+ * information about the #SoupSession, #SoupMessage, and #SoupSocket
+ * involved; the hex numbers are the addresses of the objects in
+ * question (which may be useful if you are running in a debugger).
+ * The decimal IDs are simply counters that uniquely identify objects
+ * across the lifetime of the #SoupLogger. In particular, this can be
+ * used to identify when multiple messages are sent across the same
+ * connection.
+ **/
+
+G_DEFINE_TYPE (SoupLogger, soup_logger, G_TYPE_OBJECT)
+
+typedef struct {
+       /* We use a mutex so that if requests are being run in
+        * multiple threads, we don't mix up the output.
+        */
+       GMutex             *lock;
+
+       GQuark              tag;
+       GHashTable         *ids;
+
+       SoupLoggerLogLevel  level;
+       int                 max_body_size;
+
+       SoupLoggerFilter    request_filter;
+       gpointer            request_filter_data;
+       GDestroyNotify      request_filter_dnotify;
+
+       SoupLoggerFilter    response_filter;
+       gpointer            response_filter_data;
+       GDestroyNotify      response_filter_dnotify;
+
+       SoupLoggerPrinter   printer;
+       gpointer            printer_data;
+       GDestroyNotify      printer_dnotify;
+       char                direction;
+} SoupLoggerPrivate;
+#define SOUP_LOGGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_LOGGER, SoupLoggerPrivate))
+
+static void
+soup_logger_init (SoupLogger *logger)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       priv->lock = g_mutex_new ();
+       priv->tag = g_quark_from_static_string (g_strdup_printf ("SoupLogger-%p", logger));
+       priv->ids = g_hash_table_new (NULL, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (object);
+
+       g_hash_table_destroy (priv->ids);
+
+       if (priv->request_filter_dnotify)
+               priv->request_filter_dnotify (priv->request_filter_data);
+       if (priv->response_filter_dnotify)
+               priv->response_filter_dnotify (priv->response_filter_data);
+       if (priv->printer_dnotify)
+               priv->printer_dnotify (priv->printer_data);
+
+       g_mutex_free (priv->lock);
+
+       G_OBJECT_CLASS (soup_logger_parent_class)->finalize (object);
+}
+
+static void
+soup_logger_class_init (SoupLoggerClass *logger_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (logger_class);
+
+       g_type_class_add_private (logger_class, sizeof (SoupLoggerPrivate));
+
+       object_class->finalize = finalize;
+}
+
+/**
+ * SoupLoggerLogLevel:
+ * @SOUP_LOGGER_LOG_NONE: No logging
+ * @SOUP_LOGGER_LOG_MINIMAL: Log the Request-Line or Status-Line and
+ * the Soup-Debug pseudo-headers
+ * @SOUP_LOGGER_LOG_HEADERS: Log the full request/response headers
+ * @SOUP_LOGGER_LOG_BODY: Log the full headers and request/response
+ * bodies.
+ *
+ * Describes the level of logging output to provide.
+ **/
+
+/**
+ * soup_logger_new:
+ * @level: the debug level
+ * @max_body_size: the maximum body size to output, or -1
+ *
+ * Creates a new #SoupLogger with the given debug level. If @level is
+ * %SOUP_LOGGER_LOG_BODY, @max_body_size gives the maximum number of
+ * bytes of the body that will be logged. (-1 means "no limit".)
+ *
+ * If you need finer control over what message parts are and aren't
+ * logged, use soup_logger_set_request_filter() and
+ * soup_logger_set_response_filter().
+ **/
+SoupLogger *
+soup_logger_new (SoupLoggerLogLevel level, int max_body_size) 
+{
+       SoupLogger *logger;
+       SoupLoggerPrivate *priv;
+
+       logger = g_object_new (SOUP_TYPE_LOGGER, NULL);
+
+       priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       priv->level = level;
+       priv->max_body_size = max_body_size;
+
+       return logger;
+}
+
+/**
+ * SoupLoggerFilter:
+ * @logger: the #SoupLogger
+ * @msg: the message being logged
+ * @user_data: the data passed to soup_logger_set_request_filter()
+ * or soup_logger_set_response_filter()
+ *
+ * The prototype for a logging filter. The filter callback will be
+ * invoked for each request or response, and should analyze it and
+ * return a #SoupLoggerLogLevel value indicating how much of the
+ * message to log. Eg, it might choose between %SOUP_LOGGER_LOG_BODY
+ * and %SOUP_LOGGER_LOG_HEADERS depending on the Content-Type.
+ *
+ * Return value: a #SoupLoggerLogLevel value indicating how much of
+ * the message to log
+ **/
+
+/**
+ * soup_logger_set_request_filter:
+ * @logger: a #SoupLogger
+ * @request_filter: the callback for request debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given request.
+ * For each HTTP request @logger will invoke @request_filter to
+ * determine how much (if any) of that request to log. (If you do not
+ * set a request filter, @logger will just always log requests at the
+ * level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_request_filter (SoupLogger       *logger,
+                               SoupLoggerFilter  request_filter,
+                               gpointer          filter_data,
+                               GDestroyNotify    destroy)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       priv->request_filter         = request_filter;
+       priv->request_filter_data    = filter_data;
+       priv->request_filter_dnotify = destroy;
+}
+
+/**
+ * soup_logger_set_response_filter:
+ * @logger: a #SoupLogger
+ * @response_filter: the callback for response debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given response.
+ * For each HTTP response @logger will invoke @response_filter to
+ * determine how much (if any) of that response to log. (If you do not
+ * set a response filter, @logger will just always log responses at
+ * the level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_response_filter (SoupLogger       *logger,
+                                SoupLoggerFilter  response_filter,
+                                gpointer          filter_data,
+                                GDestroyNotify    destroy)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       priv->response_filter         = response_filter;
+       priv->response_filter_data    = filter_data;
+       priv->response_filter_dnotify = destroy;
+}
+
+/**
+ * SoupLoggerPrinter:
+ * @logger: the #SoupLogger
+ * @level: the level of the information being printed.
+ * @direction: a single-character prefix to @data
+ * @data: data to print
+ * @user_data: the data passed to soup_logger_set_printer()
+ *
+ * The prototype for a custom printing callback.
+ *
+ * @level indicates what kind of information is being printed. Eg, it
+ * will be %SOUP_LOGGER_LOG_HEADERS if @data is header data.
+ *
+ * @direction is either '<', '>', or ' ', and @data is the single line
+ * to print; the printer is expected to add a terminating newline.
+ *
+ * To get the effect of the default printer, you would do:
+ *
+ * <informalexample><programlisting>
+ *     printf ("%c %s\n", direction, data);
+ * </programlisting></informalexample>
+ **/
+
+/**
+ * soup_logger_set_printer:
+ * @logger: a #SoupLogger
+ * @printer: the callback for printing logging output
+ * @printer_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @printer_data
+ *
+ * Sets up an alternate log printing routine, if you don't want
+ * the log to go to %stdout.
+ **/
+void
+soup_logger_set_printer (SoupLogger        *logger,
+                        SoupLoggerPrinter  printer,
+                        gpointer           printer_data,
+                        GDestroyNotify     destroy)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       priv->printer         = printer;
+       priv->printer_data    = printer_data;
+       priv->printer_dnotify = destroy;
+}
+
+static guint
+soup_logger_get_id (SoupLogger *logger, gpointer object)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       return GPOINTER_TO_UINT (g_object_get_qdata (object, priv->tag));
+}
+
+static guint
+soup_logger_set_id (SoupLogger *logger, gpointer object)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       gpointer klass = G_OBJECT_GET_CLASS (object);
+       gpointer id;
+
+       id = g_hash_table_lookup (priv->ids, klass);
+       id = (char *)id + 1;
+       g_hash_table_insert (priv->ids, klass, id);
+
+       g_object_set_qdata (object, priv->tag, id);
+       return GPOINTER_TO_UINT (id);
+}
+
+static void
+soup_logger_clear_id (SoupLogger *logger, gpointer object)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       g_object_set_qdata (object, priv->tag, NULL);
+}
+
+static void request_started (SoupSession *session, SoupMessage *msg,
+                            SoupSocket *socket, gpointer user_data);
+
+/**
+ * soup_logger_attach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Sets @logger to watch @session and print debug information for
+ * its messages.
+ *
+ * (The session will take a reference on @logger, which will be
+ * removed when you call soup_logger_detach(), or when the session is
+ * destroyed.)
+ **/
+void
+soup_logger_attach (SoupLogger  *logger,
+                   SoupSession *session)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       if (!soup_logger_get_id (logger, session))
+               soup_logger_set_id (logger, session);
+       g_signal_connect (session, "request_started",
+                         G_CALLBACK (request_started), logger);
+
+       g_object_set_qdata_full (G_OBJECT (session), priv->tag,
+                                g_object_ref (logger),
+                                g_object_unref);
+}
+
+/**
+ * soup_logger_detach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Stops @logger from watching @session.
+ **/
+void
+soup_logger_detach (SoupLogger  *logger,
+                   SoupSession *session)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       g_signal_handlers_disconnect_by_func (session, request_started, logger);
+
+       g_object_set_qdata (G_OBJECT (session), priv->tag, NULL);
+}
+
+static void
+soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
+                  const char *format, ...)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       va_list args;
+       char *data, *line, *end;
+
+       va_start (args, format);
+       data = g_strdup_vprintf (format, args);
+       va_end (args);
+
+       if (level == SOUP_LOGGER_LOG_BODY && priv->max_body_size > 0) {
+               if (strlen (data) > priv->max_body_size + 6)
+                       strcpy (data + priv->max_body_size, "\n[...]");
+       }
+
+       line = data;
+       do {
+               end = strchr (line, '\n');
+               if (end)
+                       *end = '\0';
+               if (priv->printer) {
+                       priv->printer (logger, level, priv->direction,
+                                      line, priv->printer_data);
+               } else
+                       printf ("%c %s\n", priv->direction, line);
+
+               line = end + 1;
+       } while (end && *line);
+
+       g_free (data);
+}
+
+static void
+print_header (const char *name, const char *value, gpointer logger)
+{
+       if (!g_ascii_strcasecmp (name, "Authorization") &&
+           !g_ascii_strncasecmp (value, "Basic ", 6)) {
+               char *decoded, *p;
+               gsize len;
+
+               decoded = (char *)g_base64_decode (value + 6, &len);
+               if (!decoded)
+                       decoded = g_strdup (value);
+               p = strchr (decoded, ':');
+               if (p) {
+                       while (++p < decoded + len)
+                               *p = '*';
+               }
+               soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS,
+                                  "%s: Basic [%.*s]", name, len, decoded);
+               g_free (decoded);
+       } else {
+               soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS,
+                                  "%s: %s", name, value);
+       }
+}
+
+static void
+print_request (SoupLogger *logger, SoupMessage *msg,
+              SoupSession *session, SoupSocket *socket,
+              gboolean restarted)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       SoupLoggerLogLevel log_level;
+       SoupURI *uri;
+
+       if (priv->request_filter) {
+               log_level = priv->request_filter (logger, msg,
+                                                 priv->request_filter_data);
+       } else
+               log_level = priv->level;
+
+       if (log_level == SOUP_LOGGER_LOG_NONE)
+               return;
+
+       priv->direction = '>';
+
+       uri = soup_message_get_uri (msg);
+       if (msg->method == SOUP_METHOD_CONNECT) {
+               soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                                  "CONNECT %s:%u HTTP/1.%d",
+                                  uri->host, uri->port,
+                                  soup_message_get_http_version (msg));
+       } else {
+               soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                                  "%s %s%s%s HTTP/1.%d",
+                                  msg->method, uri->path,
+                                  uri->query ? "?" : "",
+                                  uri->query ? uri->query : "",
+                                  soup_message_get_http_version (msg));
+       }
+
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                          "Soup-Debug-Timestamp: %lu",
+                          (unsigned long)time (0));
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                          "Soup-Debug: session %u (%p), msg %u (%p), conn %u (%p)%s",
+                          soup_logger_get_id (logger, session), session,
+                          soup_logger_get_id (logger, msg), msg,
+                          soup_logger_get_id (logger, socket), socket,
+                          restarted ? ", restarted" : "");
+
+       if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+               return;
+
+       print_header ("Host", uri->host, logger);
+       soup_message_headers_foreach (msg->request_headers,
+                                     print_header, logger);
+       if (log_level == SOUP_LOGGER_LOG_HEADERS)
+               return;
+
+       if (msg->request_body->length) {
+               SoupBuffer *request;
+
+               request = soup_message_body_flatten (msg->request_body);
+               soup_buffer_free (request);
+
+               if (soup_message_headers_get_expectations (msg->request_headers) != SOUP_EXPECTATION_CONTINUE) {
+                       soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+                                          "\n%s", msg->request_body->data);
+               }
+       }
+}
+
+static void
+print_response (SoupLogger *logger, SoupMessage *msg)
+{
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       SoupLoggerLogLevel log_level;
+
+       if (priv->response_filter) {
+               log_level = priv->response_filter (logger, msg,
+                                                  priv->response_filter_data);
+       } else
+               log_level = priv->level;
+
+       if (log_level == SOUP_LOGGER_LOG_NONE)
+               return;
+
+       priv->direction = '<';
+
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                          "HTTP/1.%d %u %s\n",
+                          soup_message_get_http_version (msg),
+                          msg->status_code, msg->reason_phrase);
+
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                          "Soup-Debug-Timestamp: %lu",
+                          (unsigned long)time (0));
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                          "Soup-Debug: msg %u (%p)",
+                          soup_logger_get_id (logger, msg), msg);
+
+       if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+               return;
+
+       soup_message_headers_foreach (msg->response_headers,
+                                    print_header, logger);
+       if (log_level == SOUP_LOGGER_LOG_HEADERS)
+               return;
+
+       if (msg->response_body->length) {
+               soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+                                  "\n%s", msg->response_body->data);
+       }
+}
+
+static void
+got_informational (SoupMessage *msg, gpointer user_data)
+{
+       SoupLogger *logger = user_data;
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       g_mutex_lock (priv->lock);
+
+       print_response (logger, msg);
+       priv->direction = ' ';
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+
+       if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) {
+               SoupLoggerLogLevel log_level;
+
+               priv->direction = '>';
+               soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+                                  "[Now sending request body...]");
+
+               if (priv->request_filter) {
+                       log_level = priv->request_filter (logger, msg,
+                                                         priv->request_filter_data);
+               } else
+                       log_level = priv->level;
+
+               if (log_level == SOUP_LOGGER_LOG_BODY) {
+                       soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+                                          "%s", msg->request_body->data);
+               }
+
+               priv->direction = ' ';
+               soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+       }
+
+       g_mutex_unlock (priv->lock);
+}
+
+static void
+got_body (SoupMessage *msg, gpointer user_data)
+{
+       SoupLogger *logger = user_data;
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+       g_mutex_lock (priv->lock);
+
+       print_response (logger, msg);
+       priv->direction = ' ';
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+
+       g_mutex_unlock (priv->lock);
+}
+
+static void
+finished_handler (SoupMessage *msg, gpointer user_data)
+{
+       SoupLogger *logger = user_data;
+
+       g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
+       g_signal_handlers_disconnect_by_func (msg, got_body, logger);
+       g_signal_handlers_disconnect_by_func (msg, finished_handler, logger);
+
+       soup_logger_clear_id (logger, msg);
+}
+
+static void
+request_started (SoupSession *session, SoupMessage *msg,
+                SoupSocket *socket, gpointer user_data)
+{
+       SoupLogger *logger = user_data;
+       SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+       gboolean restarted;
+       guint msg_id;
+
+       msg_id = soup_logger_get_id (logger, msg);
+       if (msg_id)
+               restarted = TRUE;
+       else {
+               msg_id = soup_logger_set_id (logger, msg);
+               restarted = FALSE;
+
+               g_signal_connect (msg, "got-informational",
+                                 G_CALLBACK (got_informational),
+                                 logger);
+               g_signal_connect (msg, "got-body",
+                                 G_CALLBACK (got_body),
+                                 logger);
+               g_signal_connect (msg, "finished",
+                                 G_CALLBACK (finished_handler),
+                                 logger);
+       }
+
+       if (!soup_logger_get_id (logger, socket))
+               soup_logger_set_id (logger, socket);
+
+       print_request (logger, msg, session, socket, restarted);
+       priv->direction = ' ';
+       soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+}
diff --git a/libsoup/soup-logger.h b/libsoup/soup-logger.h
new file mode 100644 (file)
index 0000000..dc96bc0
--- /dev/null
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_LOGGER_H
+#define SOUP_LOGGER_H 1
+
+#include <libsoup/soup-types.h>
+
+#define SOUP_TYPE_LOGGER            (soup_logger_get_type ())
+#define SOUP_LOGGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_LOGGER, SoupLogger))
+#define SOUP_LOGGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_LOGGER, SoupLoggerClass))
+#define SOUP_IS_LOGGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_IS_LOGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_LOGGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_LOGGER, SoupLoggerClass))
+
+typedef struct SoupLogger      SoupLogger;
+typedef struct SoupLoggerClass SoupLoggerClass;
+
+typedef enum {
+       SOUP_LOGGER_LOG_NONE,
+       SOUP_LOGGER_LOG_MINIMAL,
+       SOUP_LOGGER_LOG_HEADERS,
+       SOUP_LOGGER_LOG_BODY
+} SoupLoggerLogLevel;
+
+typedef SoupLoggerLogLevel (*SoupLoggerFilter)  (SoupLogger         *logger,
+                                                SoupMessage        *msg,
+                                                gpointer            user_data);
+
+typedef void               (*SoupLoggerPrinter) (SoupLogger         *logger,
+                                                SoupLoggerLogLevel  level,
+                                                char                direction,
+                                                const char         *data,
+                                                gpointer            user_data);
+
+struct SoupLogger {
+       GObject parent;
+
+};
+
+struct SoupLoggerClass {
+       GObjectClass parent_class;
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
+};
+
+GType       soup_logger_get_type    (void);
+
+SoupLogger *soup_logger_new         (SoupLoggerLogLevel  level,
+                                    int                 max_body_size);
+void        soup_logger_attach      (SoupLogger         *logger,
+                                    SoupSession        *session);
+void        soup_logger_detach      (SoupLogger         *logger,
+                                    SoupSession        *session);
+
+void        soup_logger_set_request_filter  (SoupLogger        *logger,
+                                            SoupLoggerFilter   request_filter,
+                                            gpointer           filter_data,
+                                            GDestroyNotify     destroy);
+void        soup_logger_set_response_filter (SoupLogger        *logger,
+                                            SoupLoggerFilter   response_filter,
+                                            gpointer           filter_data,
+                                            GDestroyNotify     destroy);
+
+void        soup_logger_set_printer         (SoupLogger        *logger,
+                                            SoupLoggerPrinter  printer,
+                                            gpointer           printer_data,
+                                            GDestroyNotify     destroy);
+
+#endif /* SOUP_LOGGER_H */
index 9f42926..4e1b2bc 100644 (file)
@@ -1,4 +1,7 @@
-NONE:NONE
+NONE:BOXED
 NONE:INT
+NONE:NONE
 NONE:OBJECT
-NONE:OBJECT,STRING,STRING,POINTER,POINTER
+NONE:OBJECT,OBJECT
+NONE:OBJECT,POINTER
+NONE:OBJECT,OBJECT,BOOLEAN
diff --git a/libsoup/soup-md5-utils.c b/libsoup/soup-md5-utils.c
deleted file mode 100644 (file)
index e3dedfa..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest.  This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * SoupMD5Context structure, pass it to soup_md5_init, call soup_md5_update as
- * needed on buffers full of bytes, and then call soup_md5_Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#include "soup-md5-utils.h"
-#include <string.h>
-
-static void soup_md5_transform (guint32 buf[4], const guint32 in[16]);
-
-/*
- * Note: this code is harmless on little-endian machines.
- */
-static void 
-byte_reverse (guchar *buf, guint32 longs)
-{
-       guint32 t;
-       do {
-               t = (guint32) ((guint32) buf[3] << 8 | buf[2]) << 16 |
-                       ((guint32) buf[1] << 8 | buf[0]);
-               *(guint32 *) buf = t;
-               buf += 4;
-       } while (--longs);
-}
-
-/**
- * soup_md5_init: Initialise an md5 context object
- * @ctx: md5 context 
- * 
- * Initialise an md5 buffer. 
- *
- **/
-void 
-soup_md5_init (SoupMD5Context *ctx)
-{
-       ctx->buf[0] = 0x67452301;
-       ctx->buf[1] = 0xefcdab89;
-       ctx->buf[2] = 0x98badcfe;
-       ctx->buf[3] = 0x10325476;
-       
-       ctx->bits[0] = 0;
-       ctx->bits[1] = 0;
-       
-       ctx->doByteReverse = (G_BYTE_ORDER == G_BIG_ENDIAN);
-}
-
-
-
-/**
- * soup_md5_update: add a buffer to md5 hash computation
- * @ctx: conetxt object used for md5 computaion
- * @buf: buffer to add
- * @len: buffer length
- * 
- * Update context to reflect the concatenation of another buffer full
- * of bytes. Use this to progressively construct an md5 hash.
- **/
-void 
-soup_md5_update (SoupMD5Context *ctx, gconstpointer buf, gsize len)
-{
-       const char *cbuf = buf;
-       guint32 t;
-       
-       /* Update bitcount */
-       
-       t = ctx->bits[0];
-       if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
-               ctx->bits[1]++;         /* Carry from low to high */
-       ctx->bits[1] += len >> 29;
-       
-       t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
-       
-       /* Handle any leading odd-sized chunks */
-       
-       if (t) {
-               guchar *p = (guchar *) ctx->in + t;
-               
-               t = 64 - t;
-               if (len < t) {
-                       memcpy (p, cbuf, len);
-                       return;
-               }
-               memcpy (p, cbuf, t);
-               if (ctx->doByteReverse)
-                       byte_reverse (ctx->in, 16);
-               soup_md5_transform (ctx->buf, (guint32 *) ctx->in);
-               cbuf += t;
-               len -= t;
-       }
-       /* Process data in 64-byte chunks */
-       
-       while (len >= 64) {
-               memcpy (ctx->in, cbuf, 64);
-               if (ctx->doByteReverse)
-                       byte_reverse (ctx->in, 16);
-               soup_md5_transform (ctx->buf, (guint32 *) ctx->in);
-               cbuf += 64;
-               len -= 64;
-       }
-       
-       /* Handle any remaining bytes of data. */
-       
-       memcpy (ctx->in, cbuf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern 
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-/**
- * soup_md5_final: copy the final md5 hash to a bufer
- * @digest: 16 bytes buffer
- * @ctx: context containing the calculated md5
- * 
- * Performs the final md5 transformation on the context, and
- * then copies the resulting md5 hash to a buffer
- **/
-void 
-soup_md5_final (SoupMD5Context *ctx, guchar digest[16])
-{
-       guint32 count;
-       guchar *p;
-       
-       /* Compute number of bytes mod 64 */
-       count = (ctx->bits[0] >> 3) & 0x3F;
-       
-       /* Set the first char of padding to 0x80.  This is safe since there is
-          always at least one byte free */
-       p = ctx->in + count;
-       *p++ = 0x80;
-       
-       /* Bytes of padding needed to make 64 bytes */
-       count = 64 - 1 - count;
-       
-       /* Pad out to 56 mod 64 */
-       if (count < 8) {
-               /* Two lots of padding:  Pad the first block to 64 bytes */
-               memset (p, 0, count);
-               if (ctx->doByteReverse)
-                       byte_reverse (ctx->in, 16);
-               soup_md5_transform (ctx->buf, (guint32 *) ctx->in);
-               
-               /* Now fill the next block with 56 bytes */
-               memset (ctx->in, 0, 56);
-       } else {
-               /* Pad block to 56 bytes */
-               memset (p, 0, count - 8);
-       }
-       if (ctx->doByteReverse)
-               byte_reverse (ctx->in, 14);
-       
-       /* Append length in bits and transform */
-       ((guint32 *) ctx->in)[14] = ctx->bits[0];
-       ((guint32 *) ctx->in)[15] = ctx->bits[1];
-       
-       soup_md5_transform (ctx->buf, (guint32 *) ctx->in);
-       if (ctx->doByteReverse)
-               byte_reverse ((guchar *) ctx->buf, 4);
-       memcpy (digest, ctx->buf, 16);
-}
-
-
-
-/**
- * soup_md5_final_hex: copy the final md5 hash to a bufer
- * @digest: 33 bytes buffer (32 hex digits plus NUL)
- * @ctx: context containing the calculated md5
- * 
- * As soup_md5_final(), but copies the final md5 hash
- * to a buffer as a NUL-terminated hexadecimal string
- **/
-void 
-soup_md5_final_hex (SoupMD5Context *ctx, char hex_digest[33])
-{
-       static const char hexdigits[16] =  {
-               '0', '1', '2', '3', '4', '5', '6', '7',
-               '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-       };
-       guchar digest[16];
-       int p;
-
-       soup_md5_final (ctx, digest);
-
-       hex_digest[32] = 0;
-       for (p = 15; p >= 0; p--) {
-               guchar b = digest[p];
-               hex_digest[p * 2 + 1] = hexdigits[ (b & 0x0F ) ];
-               hex_digest[p * 2] = hexdigits[ (b & 0xF0 ) >> 4 ];
-       }
-}
-
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
-       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  soup_md5_Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void 
-soup_md5_transform (guint32 buf[4], const guint32 in[16])
-{
-       register guint32 a, b, c, d;
-       
-       a = buf[0];
-       b = buf[1];
-       c = buf[2];
-       d = buf[3];
-       
-       MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-       MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-       MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
-       MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-       MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-       MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-       MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
-       MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
-       MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
-       MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-       MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-       MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-       MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
-       MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
-       MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
-       MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
-       
-       MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-       MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
-       MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-       MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-       MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-       MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
-       MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-       MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-       MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-       MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-       MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-       MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-       MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-       MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-       MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-       MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-       
-       MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-       MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
-       MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-       MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-       MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-       MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-       MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-       MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-       MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-       MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-       MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-       MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
-       MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-       MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-       MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-       MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-       
-       MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
-       MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
-       MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-       MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-       MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-       MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-       MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-       MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-       MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-       MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-       MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
-       MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-       MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-       MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-       MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-       MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-       
-       buf[0] += a;
-       buf[1] += b;
-       buf[2] += c;
-       buf[3] += d;
-}
-
diff --git a/libsoup/soup-md5-utils.h b/libsoup/soup-md5-utils.h
deleted file mode 100644 (file)
index 83eb031..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest.  This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to rpmMD5Init, call rpmMD5Update as
- * needed on buffers full of bytes, and then call rpmMD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#ifndef SOUP_MD5_UTILS_H
-#define SOUP_MD5_UTILS_H
-
-#include <glib.h>
-
-typedef struct {
-       /*< private >*/
-       guint32  buf[4];
-       guint32  bits[2];
-       guchar   in[64];
-       gboolean doByteReverse;
-} SoupMD5Context;
-
-void soup_md5_init      (SoupMD5Context *ctx);
-void soup_md5_update    (SoupMD5Context *ctx,
-                        gconstpointer   buf,
-                        gsize           len);
-void soup_md5_final     (SoupMD5Context *ctx,
-                        guchar          digest[16]);
-void soup_md5_final_hex (SoupMD5Context *ctx,
-                        char            digest[33]);
-
-
-#endif /* SOUP_MD5_UTILS_H */
diff --git a/libsoup/soup-message-body.c b/libsoup/soup-message-body.c
new file mode 100644 (file)
index 0000000..effabd7
--- /dev/null
@@ -0,0 +1,429 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-body.c: SoupMessage request/response bodies
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#include <string.h>
+
+#include "soup-message-body.h"
+
+/**
+ * SECTION:soup-message-body
+ * @short_description: HTTP message body
+ * @see_also: #SoupMessage
+ *
+ * #SoupMessageBody represents the request or response body of a
+ * #SoupMessage.
+ *
+ * In addition to #SoupMessageBody, libsoup also defines a "smaller"
+ * data buffer type, #SoupBuffer, which is primarily used as a
+ * component of #SoupMessageBody. In particular, when using chunked
+ * encoding to transmit or receive a message, each chunk is
+ * represented as a #SoupBuffer.
+ **/
+
+/**
+ * SoupMemoryUse:
+ * @SOUP_MEMORY_STATIC: The memory is statically allocated and
+ * constant; libsoup can use the passed-in buffer directly and not
+ * need to worry about it being modified or freed.
+ * @SOUP_MEMORY_TAKE: The caller has allocated the memory for the
+ * #SoupBuffer's use; libsoup will assume ownership of it and free it
+ * (with g_free()) when it is done with it.
+ * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller; the
+ * #SoupBuffer will copy it into new memory, leaving the caller free
+ * to reuse the original memory.
+ * @SOUP_MEMORY_TEMPORARY: The passed-in data belongs to the caller,
+ * but will remain valid for the lifetime of the #SoupBuffer. The
+ * difference between this and @SOUP_MEMORY_STATIC is that if you copy
+ * a @SOUP_MEMORY_TEMPORARY buffer, it will make a copy of the memory
+ * as well, rather than reusing the original memory.
+ *
+ * Describes how #SoupBuffer should use the data passed in by the
+ * caller.
+ **/
+
+/**
+ * SoupBuffer:
+ * @data: the data
+ * @length: length of @data
+ *
+ * A data buffer, generally used to represent a chunk of a
+ * #SoupMessageBody.
+ *
+ * @data is a #char because that's generally convenient; in some
+ * situations you may need to cast it to #guchar or another type.
+ **/
+
+typedef struct {
+       SoupBuffer     buffer;
+       SoupMemoryUse  use;
+       guint          refcount;
+
+       /* @other is used in subbuffers to store a reference to
+        * the parent buffer, or in TEMPORARY buffers to store a
+        * reference to a copy (see soup_buffer_copy()). Either
+        * way, we hold a ref.
+        */
+       SoupBuffer    *other;
+} SoupBufferPrivate;
+
+/**
+ * soup_buffer_new:
+ * @use: how @data is to be used by the buffer
+ * @data: data
+ * @length: length of @data
+ *
+ * Creates a new #SoupBuffer containing @length bytes from @data.
+ *
+ * Return value: the new #SoupBuffer.
+ **/
+SoupBuffer *
+soup_buffer_new (SoupMemoryUse use, gconstpointer data, gsize length)
+{
+       SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
+
+       if (use == SOUP_MEMORY_COPY) {
+               priv->buffer.data = g_memdup (data, length);
+               priv->use = SOUP_MEMORY_TAKE;
+       } else {
+               priv->buffer.data = data;
+               priv->use = use;
+       }
+       priv->buffer.length = length;
+       priv->refcount = 1;
+
+       return (SoupBuffer *)priv;
+}
+
+/**
+ * soup_buffer_new_subbuffer:
+ * @parent: the parent #SoupBuffer
+ * @offset: offset within @parent to start at
+ * @length: number of bytes to copy from @parent
+ *
+ * Creates a new #SoupBuffer containing @length bytes "copied" from
+ * @parent starting at @offset. (Normally this will not actually copy
+ * any data, but will instead simply reference the same data as
+ * @parent does.)
+ *
+ * Return value: the new #SoupBuffer.
+ **/
+SoupBuffer *
+soup_buffer_new_subbuffer (SoupBuffer *parent, gsize offset, gsize length)
+{
+       SoupBufferPrivate *priv;
+
+       priv = g_slice_new0 (SoupBufferPrivate);
+       priv->other = soup_buffer_copy (parent);
+       priv->buffer.data = priv->other->data + offset;
+       priv->buffer.length = length;
+       priv->use = SOUP_MEMORY_STATIC;
+       priv->refcount = 1;
+       return (SoupBuffer *)priv;
+}
+
+/**
+ * soup_buffer_copy:
+ * @buffer: a #SoupBuffer
+ *
+ * Makes a copy of @buffer. In reality, #SoupBuffer is a refcounted
+ * type, and calling soup_buffer_copy() will normally just increment
+ * the refcount on @buffer and return it. However, if @buffer was
+ * created with #SOUP_MEMORY_TEMPORARY memory, then soup_buffer_copy()
+ * will actually return a copy of it, so that the data in the copy
+ * will remain valid after the temporary buffer is freed.
+ *
+ * Return value: the new (or newly-reffed) buffer
+ **/
+SoupBuffer *
+soup_buffer_copy (SoupBuffer *buffer)
+{
+       SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
+
+       /* For non-TEMPORARY buffers, this is just a ref */
+       if (priv->use != SOUP_MEMORY_TEMPORARY) {
+               priv->refcount++;
+               return buffer;
+       }
+
+       /* For TEMPORARY buffers, we need to do a real copy the
+        * first time, and then after that, we just keep returning
+        * the copy. Use priv->other to store the copy.
+        */
+
+       if (!priv->other) {
+               priv->other = soup_buffer_new (SOUP_MEMORY_COPY,
+                                              buffer->data, buffer->length);
+       }
+       return soup_buffer_copy (priv->other);
+}
+
+/**
+ * soup_buffer_free:
+ * @buffer: a #SoupBuffer
+ *
+ * Frees @buffer. (In reality, as described in the documentation for
+ * soup_buffer_copy(), this is actually an "unref" operation, and may
+ * or may not actually free @buffer.)
+ **/
+void
+soup_buffer_free (SoupBuffer *buffer)
+{
+       SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
+
+       if (!--priv->refcount) {
+               if (priv->use == SOUP_MEMORY_TAKE)
+                       g_free ((gpointer)buffer->data);
+               if (priv->other)
+                       soup_buffer_free (priv->other);
+               g_slice_free (SoupBufferPrivate, priv);
+       }
+}
+
+GType
+soup_buffer_get_type (void)
+{
+       static GType type = 0;
+
+       if (type == 0) {
+               type = g_boxed_type_register_static (
+                       g_intern_static_string ("SoupBuffer"),
+                       (GBoxedCopyFunc)soup_buffer_copy,
+                       (GBoxedFreeFunc)soup_buffer_free);
+       }
+       return type;
+}
+
+
+/**
+ * SoupMessageBody:
+ * @data: the data
+ * @length: length of @data
+ *
+ * A #SoupMessage request or response body.
+ *
+ * Note that while @length always reflects the full length of the
+ * message body, @data is normally %NULL, and will only be filled in
+ * after soup_message_body_flatten() is called. For client-side
+ * messages, this automatically happens for the response body after it
+ * has been fully read, unless you set the
+ * %SOUP_MESSAGE_OVERWRITE_CHUNKS flags. Likewise, for server-side
+ * messages, the request body is automatically filled in after being
+ * read.
+ *
+ * As an added bonus, when @data is filled in, it is always terminated
+ * with a '\0' byte (which is not reflected in @length).
+ **/
+
+typedef struct {
+       SoupMessageBody body;
+       GSList *chunks, *last;
+       SoupBuffer *flattened;
+} SoupMessageBodyPrivate;
+
+/**
+ * soup_message_body_new:
+ *
+ * Creates a new #SoupMessageBody. #SoupMessage uses this internally; you
+ * will not normally need to call it yourself.
+ *
+ * Return value: a new #SoupMessageBody.
+ **/
+SoupMessageBody *
+soup_message_body_new (void)
+{
+       return (SoupMessageBody *)g_slice_new0 (SoupMessageBodyPrivate);
+}
+
+static void
+append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+{
+       SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+       if (priv->last) {
+               priv->last = g_slist_append (priv->last, buffer);
+               priv->last = priv->last->next;
+       } else
+               priv->chunks = priv->last = g_slist_append (NULL, buffer);
+
+       if (priv->flattened) {
+               soup_buffer_free (priv->flattened);
+               priv->flattened = NULL;
+               body->data = NULL;
+       }
+       body->length += buffer->length;
+}
+
+/**
+ * soup_message_body_append:
+ * @body: a #SoupMessageBody
+ * @use: how to use @data
+ * @data: data to append
+ * @length: length of @data
+ *
+ * Appends @length bytes from @data to @body according to @use.
+ **/
+void
+soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use,
+                         gconstpointer data, gsize length)
+{
+       if (length > 0)
+               append_buffer (body, soup_buffer_new (use, data, length));
+}
+
+/**
+ * soup_message_body_append_buffer:
+ * @body: a #SoupMessageBody
+ * @buffer: a #SoupBuffer
+ *
+ * Appends the data from @buffer to @body. (#SoupMessageBody uses
+ * #SoupBuffers internally, so this is normally a constant-time
+ * operation that doesn't actually require copying the data in
+ * @buffer.)
+ **/
+void
+soup_message_body_append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+{
+       g_return_if_fail (buffer->length > 0);
+       append_buffer (body, soup_buffer_copy (buffer));
+}
+
+/**
+ * soup_message_body_truncate:
+ * @body: a #SoupMessageBody
+ *
+ * Deletes all of the data in @body.
+ **/
+void
+soup_message_body_truncate (SoupMessageBody *body)
+{
+       SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+       GSList *iter;
+
+       for (iter = priv->chunks; iter; iter = iter->next)
+               soup_buffer_free (iter->data);
+       g_slist_free (priv->chunks);
+       priv->chunks = priv->last = NULL;
+
+       if (priv->flattened) {
+               soup_buffer_free (priv->flattened);
+               priv->flattened = NULL;
+               body->data = NULL;
+       }
+       body->length = 0;
+}
+
+/**
+ * soup_message_body_complete:
+ * @body: a #SoupMessageBody
+ *
+ * Tags @body as being complete; Call this when using chunked encoding
+ * after you have appended the last chunk.
+ **/
+void
+soup_message_body_complete (SoupMessageBody *body)
+{
+       append_buffer (body, soup_buffer_new (SOUP_MEMORY_STATIC, NULL, 0));
+}
+
+/**
+ * soup_message_body_flatten:
+ * @body: a #SoupMessageBody
+ *
+ * Fills in @body's data field with a buffer containing all of the
+ * data in @body (plus an additional '\0' byte not counted by @body's
+ * length field).
+ *
+ * Return value: a #SoupBuffer containing the same data as @body.
+ * (You must free this buffer if you do not want it.)
+ **/
+SoupBuffer *
+soup_message_body_flatten (SoupMessageBody *body)
+{
+       SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+       char *buf, *ptr;
+       GSList *iter;
+       SoupBuffer *chunk;
+
+       if (!priv->flattened) {
+#if GLIB_SIZEOF_SIZE_T < 8
+               g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
+#endif
+
+               buf = ptr = g_malloc (body->length + 1);
+               for (iter = priv->chunks; iter; iter = iter->next) {
+                       chunk = iter->data;
+                       memcpy (ptr, chunk->data, chunk->length);
+                       ptr += chunk->length;
+               }
+               *ptr = '\0';
+
+               priv->flattened = soup_buffer_new (SOUP_MEMORY_TAKE,
+                                                  buf, body->length);
+               body->data = priv->flattened->data;
+       }
+
+       return soup_buffer_copy (priv->flattened);
+}
+
+/**
+ * soup_message_body_get_chunk:
+ * @body: a #SoupMessageBody
+ * @offset: an offset
+ *
+ * Gets a #SoupBuffer containing data from @body starting at @offset.
+ * The size of the returned chunk is unspecified. You can iterate
+ * through the entire body by first calling
+ * soup_message_body_get_chunk() with an offset of 0, and then on each
+ * successive call, increment the offset by the length of the
+ * previously-returned chunk.
+ *
+ * If @offset is greater than or equal to the total length of @body,
+ * then the return value depends on whether or not
+ * soup_message_body_complete() has been called or not; if it has,
+ * then soup_message_body_get_chunk() will return a 0-length chunk
+ * (indicating the end of @body). If it has not, then
+ * soup_message_body_get_chunk() will return %NULL (indicating that
+ * @body may still potentially have more data, but that data is not
+ * currently available).
+ *
+ * Return value: a #SoupBuffer, or %NULL.
+ **/
+SoupBuffer *
+soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
+{
+       SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+       GSList *iter;
+       SoupBuffer *chunk = NULL;
+
+       for (iter = priv->chunks; iter; iter = iter->next) {
+               chunk = iter->data;
+
+               if (offset < chunk->length || offset == 0)
+                       break;
+
+               offset -= chunk->length;
+       }
+
+       if (!iter)
+               return NULL;
+
+       if (offset == 0)
+               return soup_buffer_copy (chunk);
+       else {
+               return soup_buffer_new_subbuffer (chunk, offset,
+                                                 chunk->length - offset);
+       }
+}
+
+void
+soup_message_body_free (SoupMessageBody *body)
+{
+       SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+       soup_message_body_truncate (body);
+       g_slice_free (SoupMessageBodyPrivate, priv);
+}
diff --git a/libsoup/soup-message-body.h b/libsoup/soup-message-body.h
new file mode 100644 (file)
index 0000000..329c773
--- /dev/null
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_BODY_H
+#define SOUP_MESSAGE_BODY_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+       SOUP_MEMORY_STATIC,
+       SOUP_MEMORY_TAKE,
+       SOUP_MEMORY_COPY,
+       SOUP_MEMORY_TEMPORARY,
+} SoupMemoryUse;
+
+typedef struct {
+       const char *data;
+       gsize       length;
+} SoupBuffer;
+
+GType soup_buffer_get_type (void);
+#define SOUP_TYPE_BUFFER (soup_buffer_get_type ())
+
+SoupBuffer *soup_buffer_new           (SoupMemoryUse  use,
+                                      gconstpointer  data,
+                                      gsize          length);
+SoupBuffer *soup_buffer_new_subbuffer (SoupBuffer    *parent,
+                                      gsize          offset,
+                                      gsize          length);
+
+SoupBuffer *soup_buffer_copy          (SoupBuffer    *buffer);
+void        soup_buffer_free          (SoupBuffer    *buffer);
+
+typedef struct {
+       const char *data;
+       goffset     length;
+} SoupMessageBody;
+
+SoupMessageBody *soup_message_body_new           (void);
+
+void             soup_message_body_append        (SoupMessageBody *body,
+                                                 SoupMemoryUse    use,
+                                                 gconstpointer    data,
+                                                 gsize            length);
+void             soup_message_body_append_buffer (SoupMessageBody *body,
+                                                 SoupBuffer      *buffer);
+void             soup_message_body_truncate      (SoupMessageBody *body);
+void             soup_message_body_complete      (SoupMessageBody *body);
+
+SoupBuffer      *soup_message_body_flatten       (SoupMessageBody *body);
+
+SoupBuffer      *soup_message_body_get_chunk     (SoupMessageBody *body,
+                                                 goffset          offset);
+
+void             soup_message_body_free          (SoupMessageBody *body);
+
+G_END_DECLS
+
+#endif /* SOUP_MESSAGE_BODY_H */
index 109d976..5d5b244 100644 (file)
 static guint
 parse_response_headers (SoupMessage *req,
                        char *headers, guint headers_len,
-                       SoupTransferEncoding *encoding,
-                       guint *content_len,
+                       SoupEncoding *encoding,
                        gpointer user_data)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
-       SoupHttpVersion version;
-       GHashTable *resp_hdrs;
+       SoupHTTPVersion version;
 
        g_free((char*)req->reason_phrase);
        req->reason_phrase = NULL;
@@ -37,42 +35,47 @@ parse_response_headers (SoupMessage *req,
                                          (char **) &req->reason_phrase))
                return SOUP_STATUS_MALFORMED;
 
-       if (version < priv->http_version)
-               priv->http_version = version;
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
 
-       resp_hdrs = req->response_headers;
+       if (version < priv->http_version) {
+               priv->http_version = version;
+               g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+       }
 
-       *encoding = soup_message_get_response_encoding (req, content_len);
-       if (*encoding == SOUP_TRANSFER_NONE) {
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-               *content_len = 0;
-       } else if (*encoding == SOUP_TRANSFER_UNKNOWN)
+       if ((req->method == SOUP_METHOD_HEAD ||
+            req->status_code  == SOUP_STATUS_NO_CONTENT ||
+            req->status_code  == SOUP_STATUS_NOT_MODIFIED ||
+            SOUP_STATUS_IS_INFORMATIONAL (req->status_code)) ||
+           (req->method == SOUP_METHOD_CONNECT &&
+            SOUP_STATUS_IS_SUCCESSFUL (req->status_code)))
+               *encoding = SOUP_ENCODING_NONE;
+       else
+               *encoding = soup_message_headers_get_encoding (req->response_headers);
+
+       if (*encoding == SOUP_ENCODING_UNRECOGNIZED)
                return SOUP_STATUS_MALFORMED;
 
        return SOUP_STATUS_OK;
 }
 
 static void 
-add_header (gpointer name, gpointer value, gpointer data)
+add_header (const char *name, const char *value, gpointer data)
 {
        GString *headers = data;
-
-       g_string_append_printf (headers, "%s: %s\r\n",
-                               (char *)name, (char *)value);
+       g_string_append_printf (headers, "%s: %s\r\n", name, value);
 }
 
 static void
 get_request_headers (SoupMessage *req, GString *header,
-                    SoupTransferEncoding *encoding,
-                    gpointer user_data)
+                    SoupEncoding *encoding, gpointer user_data)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
        gboolean proxy = GPOINTER_TO_UINT (user_data);
-       const SoupUri *uri = soup_message_get_uri (req);
-       const char *expect;
+       SoupURI *uri = soup_message_get_uri (req);
        char *uri_string;
 
-       if (!strcmp (req->method, "CONNECT")) {
+       if (req->method == SOUP_METHOD_CONNECT) {
                /* CONNECT URI is hostname:port for tunnel destination */
                uri_string = g_strdup_printf ("%s:%d", uri->host, uri->port);
        } else {
@@ -98,46 +101,20 @@ get_request_headers (SoupMessage *req, GString *header,
        }
        g_free (uri_string);
 
-       if (req->request.length > 0) {
-               if (!soup_message_get_header (req->request_headers,
-                                             "Content-Type")) {
-                       g_string_append (header, "Content-Type: text/xml; "
-                                        "charset=utf-8\r\n");
-               }
-               g_string_append_printf (header, "Content-Length: %d\r\n",
-                                       req->request.length);
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+       *encoding = soup_message_headers_get_encoding (req->request_headers);
+       if (*encoding != SOUP_ENCODING_CHUNKED &&
+           req->request_body->length > 0) {
+               soup_message_headers_set_content_length (req->request_headers,
+                                                        req->request_body->length);
        }
 
-       soup_message_foreach_header (req->request_headers, add_header, header);
+       soup_message_headers_foreach (req->request_headers, add_header, header);
        g_string_append (header, "\r\n");
-
-       expect = soup_message_get_header (req->request_headers, "Expect");
-       if (expect && !strcmp (expect, "100-continue"))
-               priv->msg_flags |= SOUP_MESSAGE_EXPECT_CONTINUE;
 }
 
-/**
- * soup_message_send_request:
- * @req: a #SoupMessage
- * @sock: the #SoupSocket to send @req on
- * @is_via_proxy: %TRUE if @sock is a connection to a proxy server
- * rather than a direct connection to the desired HTTP server
- *
- * Begins the process of sending @msg across @sock. (If @sock is
- * synchronous, then soup_message_send_request() won't return until
- * the response has been received.)
- **/
 void
 soup_message_send_request (SoupMessage *req, SoupSocket *sock,
-                          gboolean is_via_proxy)
-{
-       soup_message_send_request_internal (req, sock, NULL, is_via_proxy);
-}
-
-void
-soup_message_send_request_internal (SoupMessage *req, SoupSocket *sock,
-                                   SoupConnection *conn, gboolean is_via_proxy)
+                          SoupConnection *conn, gboolean is_via_proxy)
 {
        soup_message_cleanup_response (req);
        soup_message_io_client (req, sock, conn,
diff --git a/libsoup/soup-message-filter.c b/libsoup/soup-message-filter.c
deleted file mode 100644 (file)
index 4b516c1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-filter-offset: 8 -*- */
-/*
- * soup-message-filter.c: Interface for arbitrary message manipulation
- *
- * Copyright (C) 2003, Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "soup-message-filter.h"
-
-SOUP_MAKE_INTERFACE (soup_message_filter, SoupMessageFilter, NULL)
-
-/**
- * soup_message_filter_setup_message:
- * @filter: an object that implements the #SoupMessageFilter interface
- * @msg: a #SoupMessage
- *
- * Performs some sort of processing on @msg in preparation for it
- * being sent. This will generally involve some combination of adding
- * headers, adding handlers, and connecting to signals.
- **/
-void
-soup_message_filter_setup_message (SoupMessageFilter *filter,
-                                  SoupMessage       *msg)
-{
-       SOUP_MESSAGE_FILTER_GET_CLASS (filter)->setup_message (filter, msg);
-}
diff --git a/libsoup/soup-message-filter.h b/libsoup/soup-message-filter.h
deleted file mode 100644 (file)
index cebd93e..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifndef SOUP_MESSAGE_FILTER_H
-#define SOUP_MESSAGE_FILTER_H 1
-
-#include <libsoup/soup-types.h>
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_MESSAGE_FILTER            (soup_message_filter_get_type ())
-#define SOUP_MESSAGE_FILTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilter))
-#define SOUP_MESSAGE_FILTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilterClass))
-#define SOUP_IS_MESSAGE_FILTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_MESSAGE_FILTER))
-#define SOUP_IS_MESSAGE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_MESSAGE_FILTER))
-#define SOUP_MESSAGE_FILTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilterClass))
-
-typedef struct {
-       GTypeInterface parent;
-
-       /* methods */
-       void  (*setup_message) (SoupMessageFilter *filter, SoupMessage *msg);
-} SoupMessageFilterClass;
-
-GType soup_message_filter_get_type (void);
-
-void soup_message_filter_setup_message (SoupMessageFilter *filter,
-                                       SoupMessage       *msg);
-
-G_END_DECLS
-
-#endif /* SOUP_MESSAGE_FILTER_H */
diff --git a/libsoup/soup-message-handlers.c b/libsoup/soup-message-handlers.c
deleted file mode 100644 (file)
index 3d59821..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-message-handlers.c: HTTP response handlers
- *
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "soup-message.h"
-#include "soup-message-private.h"
-
-typedef enum {
-       SOUP_HANDLER_HEADER = 1,
-       SOUP_HANDLER_STATUS_CODE,
-       SOUP_HANDLER_STATUS_CLASS
-} SoupHandlerKind;
-
-typedef struct {
-       SoupHandlerPhase         phase;
-       SoupMessageCallbackFn    handler_cb;
-       gpointer                 user_data;
-
-       SoupHandlerKind          kind;
-       union {
-               guint            status_code;
-               SoupStatusClass  status_class;
-               const char      *header;
-       } data;
-} SoupHandlerData;
-
-static inline void
-run_handler (SoupMessage     *msg,
-            SoupHandlerPhase invoke_phase,
-            SoupHandlerData *data)
-{
-       if (data->phase != invoke_phase)
-               return;
-
-       switch (data->kind) {
-       case SOUP_HANDLER_HEADER:
-               if (!soup_message_get_header (msg->response_headers,
-                                             data->data.header))
-                       return;
-               break;
-       case SOUP_HANDLER_STATUS_CODE:
-               if (msg->status_code != data->data.status_code)
-                       return;
-               break;
-       case SOUP_HANDLER_STATUS_CLASS:
-               if (msg->status_code < data->data.status_class * 100 ||
-                   msg->status_code >= (data->data.status_class + 1) * 100)
-                       return;
-               break;
-       default:
-               break;
-       }
-
-       (*data->handler_cb) (msg, data->user_data);
-}
-
-/**
- * soup_message_run_handlers:
- * @msg: a #SoupMessage
- * @phase: which group of handlers to run
- *
- * Run each @phase handler on @msg. If a handler requeues the message,
- * we stop processing at that point.
- */
-void
-soup_message_run_handlers (SoupMessage *msg, SoupHandlerPhase phase)
-{
-       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-       GSList *copy, *list;
-
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-
-       /* Jump through hoops to deal with callbacks that modify the list. */
-       copy = g_slist_copy (priv->content_handlers);
-
-       for (list = copy; list; list = list->next) {
-               if (!g_slist_find (priv->content_handlers, list->data))
-                       continue;
-               run_handler (msg, phase, list->data);
-
-               if (SOUP_MESSAGE_IS_STARTING (msg))
-                       break;
-       }
-
-       g_slist_free (copy);
-}
-
-static void
-add_handler (SoupMessage           *msg,
-            SoupHandlerPhase       phase,
-            SoupMessageCallbackFn  handler_cb,
-            gpointer               user_data,
-            SoupHandlerKind        kind,
-            const char            *header,
-            guint                  status_code,
-            SoupStatusClass        status_class)
-{
-       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-       SoupHandlerData *data;
-
-       data = g_new0 (SoupHandlerData, 1);
-       data->phase = phase;
-       data->handler_cb = handler_cb;
-       data->user_data = user_data;
-       data->kind = kind;
-
-       switch (kind) {
-       case SOUP_HANDLER_HEADER:
-               data->data.header = header;
-               break;
-       case SOUP_HANDLER_STATUS_CODE:
-               data->data.status_code = status_code;
-               break;
-       case SOUP_HANDLER_STATUS_CLASS:
-               data->data.status_class = status_class;
-               break;
-       default:
-               break;
-       }
-
-       priv->content_handlers =
-               g_slist_append (priv->content_handlers, data);
-}
-
-/**
- * soup_message_add_header_handler:
- * @msg: a #SoupMessage
- * @header: HTTP response header to match against
- * @phase: processing phase to run the handler in
- * @handler_cb: the handler
- * @user_data: data to pass to @handler_cb
- *
- * Adds a handler to @msg for messages containing the given response
- * header.
- **/
-void
-soup_message_add_header_handler (SoupMessage           *msg,
-                                const char            *header,
-                                SoupHandlerPhase       phase,
-                                SoupMessageCallbackFn  handler_cb,
-                                gpointer               user_data)
-{
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (header != NULL);
-       g_return_if_fail (handler_cb != NULL);
-
-       add_handler (msg, phase, handler_cb, user_data,
-                    SOUP_HANDLER_HEADER,
-                    header, 0, 0);
-}
-
-/**
- * soup_message_add_status_code_handler:
- * @msg: a #SoupMessage
- * @status_code: HTTP status code to match against
- * @phase: processing phase to run the handler in
- * @handler_cb: the handler
- * @user_data: data to pass to @handler_cb
- *
- * Adds a handler to @msg for messages receiving the given status
- * code.
- **/
-void
-soup_message_add_status_code_handler (SoupMessage           *msg,
-                                     guint                  status_code,
-                                     SoupHandlerPhase       phase,
-                                     SoupMessageCallbackFn  handler_cb,
-                                     gpointer               user_data)
-{
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (status_code != 0);
-       g_return_if_fail (handler_cb != NULL);
-
-       add_handler (msg, phase, handler_cb, user_data,
-                    SOUP_HANDLER_STATUS_CODE,
-                    NULL, status_code, 0);
-}
-
-/**
- * soup_message_add_status_class_handler:
- * @msg: a #SoupMessage
- * @status_class: HTTP status code class to match against
- * @phase: processing phase to run the handler in
- * @handler_cb: the handler
- * @user_data: data to pass to @handler_cb
- *
- * Adds a handler to @msg for messages receiving a status code in
- * the given class.
- **/
-void
-soup_message_add_status_class_handler (SoupMessage           *msg,
-                                      SoupStatusClass        status_class,
-                                      SoupHandlerPhase       phase,
-                                      SoupMessageCallbackFn  handler_cb,
-                                      gpointer               user_data)
-{
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (status_class != 0);
-       g_return_if_fail (handler_cb != NULL);
-
-       add_handler (msg, phase, handler_cb, user_data,
-                    SOUP_HANDLER_STATUS_CLASS,
-                    NULL, 0, status_class);
-}
-
-/**
- * soup_message_add_handler:
- * @msg: a #SoupMessage
- * @phase: processing phase to run the handler in
- * @handler_cb: the handler
- * @user_data: data to pass to @handler_cb
- *
- * Adds a handler to @msg for all messages
- **/
-void
-soup_message_add_handler (SoupMessage           *msg,
-                         SoupHandlerPhase       phase,
-                         SoupMessageCallbackFn  handler_cb,
-                         gpointer               user_data)
-{
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (handler_cb != NULL);
-
-       add_handler (msg, phase, handler_cb, user_data, 0, NULL, 0, 0);
-}
-
-/**
- * soup_message_remove_handler:
- * @msg: a #SoupMessage
- * @phase: processing phase to run the handler in
- * @handler_cb: the handler
- * @user_data: data to pass to @handler_cb
- *
- * Removes all matching handlers from @msg
- **/
-void
-soup_message_remove_handler (SoupMessage           *msg,
-                            SoupHandlerPhase       phase,
-                            SoupMessageCallbackFn  handler_cb,
-                            gpointer               user_data)
-{
-       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-
-       GSList *iter = priv->content_handlers;
-
-       while (iter) {
-               SoupHandlerData *data = iter->data;
-
-               if (data->handler_cb == handler_cb &&
-                   data->user_data == user_data &&
-                   data->phase == phase) {
-                       priv->content_handlers =
-                               g_slist_remove (priv->content_handlers,
-                                               data);
-                       g_free (data);
-                       break;
-               }
-
-               iter = iter->next;
-       }
-}
diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
new file mode 100644 (file)
index 0000000..646aaed
--- /dev/null
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-headers.c: HTTP message header arrays
+ *
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+
+#include "soup-message-headers.h"
+#include "soup-misc.h"
+
+/**
+ * SECTION:soup-message-headers
+ * @short_description: HTTP message headers
+ * @see_also: #SoupMessage
+ *
+ * #SoupMessageHeaders represents the HTTP message headers associated
+ * with a request or response.
+ **/
+
+typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
+static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
+
+typedef struct {
+       const char *name;
+       char *value;
+} SoupHeader;
+
+struct SoupMessageHeaders {
+       GArray *array;
+       GHashTable *concat;
+       SoupMessageHeadersType type;
+
+       SoupEncoding encoding;
+       goffset content_length;
+       SoupExpectation expectations;
+};
+
+/**
+ * soup_message_headers_new:
+ * @type: the type of headers
+ *
+ * Creates a #SoupMessageHeaders. (#SoupMessage does this
+ * automatically for its own headers. You would only need to use this
+ * method if you are manually parsing or generating message headers.)
+ *
+ * Return value: a new #SoupMessageHeaders
+ **/
+SoupMessageHeaders *
+soup_message_headers_new (SoupMessageHeadersType type)
+{
+       SoupMessageHeaders *hdrs;
+
+       hdrs = g_slice_new0 (SoupMessageHeaders);
+       /* FIXME: is "5" a good default? */
+       hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
+       hdrs->type = type;
+       hdrs->encoding = -1;
+
+       return hdrs;
+}
+
+/**
+ * soup_message_headers_free:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Frees @hdrs.
+ **/
+void
+soup_message_headers_free (SoupMessageHeaders *hdrs)
+{
+       soup_message_headers_clear (hdrs);
+       g_array_free (hdrs->array, TRUE);
+       g_slice_free (SoupMessageHeaders, hdrs);
+}
+
+/**
+ * soup_message_headers_clear:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Clears @hdrs.
+ **/
+void
+soup_message_headers_clear (SoupMessageHeaders *hdrs)
+{
+       SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
+       int i;
+
+       for (i = 0; i < hdrs->array->len; i++)
+               g_free (hdr_array[i].value);
+       g_array_set_size (hdrs->array, 0);
+
+       if (hdrs->concat)
+               g_hash_table_destroy (hdrs->concat);
+
+       hdrs->encoding = -1;
+}
+
+/**
+ * soup_message_headers_append:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to add
+ * @value: the new value of @name
+ *
+ * Appends a new header with name @name and value @value to @hdrs.
+ **/
+void
+soup_message_headers_append (SoupMessageHeaders *hdrs,
+                            const char *name, const char *value)
+{
+       SoupHeader header;
+       SoupHeaderSetter setter;
+
+       header.name = intern_header_name (name, &setter);
+       header.value = g_strdup (value);
+       g_array_append_val (hdrs->array, header);
+       if (hdrs->concat)
+               g_hash_table_remove (hdrs->concat, header.name);
+       if (setter)
+               setter (hdrs, header.value);
+}
+
+/**
+ * soup_message_headers_replace:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to replace
+ * @value: the new value of @name
+ *
+ * Replaces the value of the header @name in @hdrs with @value.
+ **/
+void
+soup_message_headers_replace (SoupMessageHeaders *hdrs,
+                             const char *name, const char *value)
+{
+       soup_message_headers_remove (hdrs, name);
+       soup_message_headers_append (hdrs, name, value);
+}
+
+static int
+find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
+{
+       int i;
+
+       for (i = 0; hdr_array[i].name; i++) {
+               if (hdr_array[i].name == interned_name) {
+                       if (nth-- == 0)
+                               return i;
+               }
+       }
+       return -1;
+}
+
+/**
+ * soup_message_headers_remove:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to remove
+ *
+ * Removes @name from @hdrs. If there are multiple values for @name,
+ * they are all removed.
+ **/
+void
+soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
+{
+       SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+       SoupHeaderSetter setter;
+       int index;
+
+       name = intern_header_name (name, &setter);
+       while ((index = find_header (hdr_array, name, 0)) != -1) {
+               g_free (hdr_array[index].value);
+               g_array_remove_index (hdrs->array, index);
+       }
+       if (hdrs->concat)
+               g_hash_table_remove (hdrs->concat, name);
+       if (setter)
+               setter (hdrs, NULL);
+}
+
+/**
+ * soup_message_headers_get:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: header name
+ * 
+ * Gets the value of header @name in @hdrs.
+ *
+ * If @name has multiple values in @hdrs, soup_message_headers_get()
+ * will concatenate all of the values together, separated by commas.
+ * This is sometimes awkward to parse (eg, WWW-Authenticate,
+ * Set-Cookie), but you have to be able to deal with it anyway,
+ * because an upstream proxy could do the same thing.
+ * 
+ * Return value: the header's value or %NULL if not found.
+ **/
+const char *
+soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
+{
+       SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+       GString *concat;
+       char *value;
+       int index, i;
+
+       name = intern_header_name (name, NULL);
+       if (hdrs->concat) {
+               value = g_hash_table_lookup (hdrs->concat, name);
+               if (value)
+                       return value;
+       }
+
+       index = find_header (hdr_array, name, 0);
+       if (index == -1)
+               return NULL;
+       else if (find_header (hdr_array, name, 1) == -1)
+               return hdr_array[index].value;
+
+       concat = g_string_new (NULL);
+       for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
+               if (i != 0)
+                       g_string_append (concat, ", ");
+               g_string_append (concat, hdr_array[index].value);
+       }
+       value = g_string_free (concat, FALSE);
+
+       if (!hdrs->concat)
+               hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+       g_hash_table_insert (hdrs->concat, (gpointer)name, value);
+       return value;
+}
+
+/**
+ * SoupMessageHeadersForeachFunc:
+ * @name: the header name
+ * @value: the header value
+ * @user_data: the data passed to soup_message_headers_foreach()
+ *
+ * The callback passed to soup_message_headers_foreach().
+ **/
+
+/**
+ * soup_message_headers_foreach:
+ * @hdrs: a #SoupMessageHeaders
+ * @func: callback function to run for each header
+ * @user_data: data to pass to @func
+ * 
+ * Calls @func once for each header value in @hdrs.
+ *
+ * Beware that unlike soup_message_headers_get(), this processes the
+ * headers in exactly the way they were added, rather than
+ * concatenating multiple same-named headers into a single value.
+ * (This is intentional; it ensures that if you call
+ * soup_message_headers_append() multiple times with the same name,
+ * then the I/O code will output multiple copies of the header when
+ * sending the message to the remote implementation, which may be
+ * required for interoperability in some cases.)
+ **/
+void
+soup_message_headers_foreach (SoupMessageHeaders *hdrs,
+                             SoupMessageHeadersForeachFunc func,
+                             gpointer            user_data)
+{
+       SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
+       int i;
+
+       for (i = 0; i < hdrs->array->len; i++)
+               func (hdr_array[i].name, hdr_array[i].value, user_data);
+}
+
+
+static GStaticMutex header_pool_mutex = G_STATIC_MUTEX_INIT;
+static GHashTable *header_pool, *header_setters;
+
+static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
+static void content_length_setter (SoupMessageHeaders *, const char *);
+static void expectation_setter (SoupMessageHeaders *, const char *);
+
+static char *
+intern_header_locked (const char *name)
+{
+       char *interned;
+
+       interned = g_hash_table_lookup (header_pool, name);
+       if (!interned) {
+               char *dup = g_strdup (name);
+               g_hash_table_insert (header_pool, dup, dup);
+               interned = dup;
+       }
+       return interned;
+}
+
+static const char *
+intern_header_name (const char *name, SoupHeaderSetter *setter)
+{
+       const char *interned;
+
+       g_static_mutex_lock (&header_pool_mutex);
+
+       if (!header_pool) {
+               header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
+               header_setters = g_hash_table_new (NULL, NULL);
+               g_hash_table_insert (header_setters,
+                                    intern_header_locked ("Transfer-Encoding"),
+                                    transfer_encoding_setter);
+               g_hash_table_insert (header_setters,
+                                    intern_header_locked ("Content-Length"),
+                                    content_length_setter);
+               g_hash_table_insert (header_setters,
+                                    intern_header_locked ("Expect"),
+                                    expectation_setter);
+       }
+
+       interned = intern_header_locked (name);
+       if (setter)
+               *setter = g_hash_table_lookup (header_setters, interned);
+
+       g_static_mutex_unlock (&header_pool_mutex);
+       return interned;
+}
+
+
+/* Specific headers */
+
+static void
+transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+       if (value) {
+               if (g_ascii_strcasecmp (value, "chunked") == 0)
+                       hdrs->encoding = SOUP_ENCODING_CHUNKED;
+               else
+                       hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+       } else
+               hdrs->encoding = -1;
+}
+
+static void
+content_length_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+       /* Transfer-Encoding trumps Content-Length */
+       if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
+               return;
+
+       if (value) {
+               char *end;
+
+               hdrs->content_length = g_ascii_strtoull (value, &end, 10);
+               if (*end)
+                       hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+               else
+                       hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
+       } else
+               hdrs->encoding = -1;
+}
+
+/**
+ * SoupEncoding:
+ * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
+ * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
+ * 0-length body, and only occurs in certain places)
+ * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
+ * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
+ * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
+ * for response)
+ * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
+ * use: NOT CURRENTLY IMPLEMENTED)
+ *
+ * How a message body is encoded for transport
+ **/
+
+/**
+ * soup_message_headers_get_encoding:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the message body encoding that @hdrs declare. This may not
+ * always correspond to the encoding used on the wire; eg, a HEAD
+ * response may declare a Content-Length or Transfer-Encoding, but
+ * it will never actually include a body.
+ *
+ * Return value: the encoding declared by @hdrs.
+ **/
+SoupEncoding
+soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
+{
+       const char *header;
+
+       if (hdrs->encoding != -1)
+               return hdrs->encoding;
+
+       /* If Transfer-Encoding was set, hdrs->encoding would already
+        * be set. So we don't need to check that possibility.
+        */
+       header = soup_message_headers_get (hdrs, "Content-Length");
+       if (header) {
+               content_length_setter (hdrs, header);
+               if (hdrs->encoding != -1)
+                       return hdrs->encoding;
+       }
+
+       hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_REQUEST) ?
+               SOUP_ENCODING_NONE : SOUP_ENCODING_EOF;
+       return hdrs->encoding;
+}
+
+/**
+ * soup_message_headers_set_encoding:
+ * @hdrs: a #SoupMessageHeaders
+ * @encoding: a #SoupEncoding
+ *
+ * Sets the message body encoding that @hdrs will declare. In particular,
+ * you should use this if you are going to send a request or response in
+ * chunked encoding.
+ **/
+void
+soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
+                                  SoupEncoding        encoding)
+{
+       if (encoding == hdrs->encoding)
+               return;
+
+       switch (encoding) {
+       case SOUP_ENCODING_NONE:
+       case SOUP_ENCODING_EOF:
+               soup_message_headers_remove (hdrs, "Transfer-Encoding");
+               soup_message_headers_remove (hdrs, "Content-Length");
+               break;
+
+       case SOUP_ENCODING_CONTENT_LENGTH:
+               soup_message_headers_remove (hdrs, "Transfer-Encoding");
+               break;
+
+       case SOUP_ENCODING_CHUNKED:
+               soup_message_headers_remove (hdrs, "Content-Length");
+               soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+
+       hdrs->encoding = encoding;
+}
+
+/**
+ * soup_message_headers_get_content_length:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the message body length that @hdrs declare. This will only
+ * be non-0 if soup_message_headers_get_encoding() returns
+ * %SOUP_ENCODING_CONTENT_LENGTH.
+ *
+ * Return value: the message body length declared by @hdrs.
+ **/
+goffset
+soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
+{
+       return (hdrs->encoding == SOUP_ENCODING_CONTENT_LENGTH) ?
+               hdrs->content_length : 0;
+}
+
+/**
+ * soup_message_headers_set_content_length:
+ * @hdrs: a #SoupMessageHeaders
+ * @content_length: the message body length
+ *
+ * Sets the message body length that @hdrs will declare, and sets
+ * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
+ *
+ * You do not normally need to call this; if @hdrs is set to use
+ * Content-Length encoding, libsoup will automatically set its
+ * Content-Length header for you immediately before sending the
+ * headers. One situation in which this method is useful is when
+ * generating the response to a HEAD request; Calling
+ * soup_message_headers_set_content_length() allows you to put the
+ * correct content length into the response without needing to waste
+ * memory by filling in a response body which won't actually be sent.
+ **/
+void
+soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
+                                        goffset             content_length)
+{
+       char length[128];
+
+       snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
+                 content_length);
+       soup_message_headers_remove (hdrs, "Transfer-Encoding");
+       soup_message_headers_replace (hdrs, "Content-Length", length);
+}
+
+static void
+expectation_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+       if (value) {
+               if (!g_ascii_strcasecmp (value, "100-continue"))
+                       hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
+               else
+                       hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
+       } else
+               hdrs->expectations = 0;
+}
+
+/**
+ * soup_message_headers_get_expectations:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the expectations declared by @hdrs's "Expect" header.
+ * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
+ * %SOUP_EXPECTATION_UNRECOGNIZED.
+ *
+ * Return value: the contents of @hdrs's "Expect" header
+ **/
+SoupExpectation
+soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
+{
+       return hdrs->expectations;
+}
+
+/**
+ * soup_message_headers_set_expectations:
+ * @hdrs: a #SoupMessageHeaders
+ * @expectations: the expectations to set
+ *
+ * Sets @hdrs's "Expect" header according to @expectations.
+ *
+ * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
+ * value. You should set this value on a request if you are sending a
+ * large message body (eg, via POST or PUT), and want to give the
+ * server a chance to reject the request after seeing just the headers
+ * (eg, because it will require authentication before allowing you to
+ * post). This saves you from having to transmit the large request
+ * body when the server is just going to ignore it anyway.
+ **/
+void
+soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
+                                      SoupExpectation     expectations)
+{
+       g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
+
+       if (expectations & SOUP_EXPECTATION_CONTINUE)
+               soup_message_headers_replace (hdrs, "Expect", "100-continue");
+       else
+               soup_message_headers_remove (hdrs, "Expect");
+}
diff --git a/libsoup/soup-message-headers.h b/libsoup/soup-message-headers.h
new file mode 100644 (file)
index 0000000..b6eb94e
--- /dev/null
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_HEADERS_H
+#define SOUP_MESSAGE_HEADERS_H 1
+
+#include <libsoup/soup-types.h>
+
+typedef struct SoupMessageHeaders SoupMessageHeaders;
+typedef enum {
+       SOUP_MESSAGE_HEADERS_REQUEST,
+       SOUP_MESSAGE_HEADERS_RESPONSE
+} SoupMessageHeadersType;
+
+SoupMessageHeaders *soup_message_headers_new      (SoupMessageHeadersType type);
+
+void                soup_message_headers_free     (SoupMessageHeaders *hdrs);
+
+void                soup_message_headers_append   (SoupMessageHeaders *hdrs,
+                                                  const char         *name,
+                                                  const char         *value);
+void                soup_message_headers_replace  (SoupMessageHeaders *hdrs,
+                                                  const char         *name,
+                                                  const char         *value);
+
+void                soup_message_headers_remove   (SoupMessageHeaders *hdrs,
+                                                  const char         *name);
+void                soup_message_headers_clear    (SoupMessageHeaders *hdrs);
+
+const char         *soup_message_headers_get      (SoupMessageHeaders *hdrs,
+                                                  const char         *name);
+
+typedef void      (*SoupMessageHeadersForeachFunc)(const char         *name,
+                                                  const char         *value,
+                                                  gpointer            user_data);
+
+void                soup_message_headers_foreach  (SoupMessageHeaders *hdrs,
+                                                  SoupMessageHeadersForeachFunc func,
+                                                  gpointer            user_data);
+
+/* Specific headers */
+
+typedef enum {
+       SOUP_ENCODING_UNRECOGNIZED,
+       SOUP_ENCODING_NONE,
+       SOUP_ENCODING_CONTENT_LENGTH,
+       SOUP_ENCODING_EOF,
+       SOUP_ENCODING_CHUNKED,
+       SOUP_ENCODING_BYTERANGES
+} SoupEncoding;
+
+SoupEncoding    soup_message_headers_get_encoding        (SoupMessageHeaders *hdrs);
+void            soup_message_headers_set_encoding        (SoupMessageHeaders *hdrs,
+                                                         SoupEncoding        encoding);
+
+goffset         soup_message_headers_get_content_length  (SoupMessageHeaders *hdrs);
+void            soup_message_headers_set_content_length  (SoupMessageHeaders *hdrs,
+                                                         goffset             content_length);
+
+typedef enum {
+       SOUP_EXPECTATION_UNRECOGNIZED = (1 << 0),
+       SOUP_EXPECTATION_CONTINUE     = (1 << 1)
+} SoupExpectation;
+
+SoupExpectation soup_message_headers_get_expectations    (SoupMessageHeaders *hdrs);
+void            soup_message_headers_set_expectations    (SoupMessageHeaders *hdrs,
+                                                         SoupExpectation     expectations);
+
+#endif /* SOUP_MESSAGE_HEADERS_H */
index 7c089e8..f10d4c3 100644 (file)
@@ -48,16 +48,17 @@ typedef struct {
        SoupMessageIOMode     mode;
 
        SoupMessageIOState    read_state;
-       SoupTransferEncoding  read_encoding;
-       GByteArray           *read_buf;
+       SoupEncoding          read_encoding;
        GByteArray           *read_meta_buf;
-       SoupDataBuffer       *read_body;
+       SoupMessageBody      *read_body;
        guint                 read_length;
 
        SoupMessageIOState    write_state;
-       SoupTransferEncoding  write_encoding;
+       SoupEncoding          write_encoding;
        GString              *write_buf;
-       SoupDataBuffer       *write_body;
+       SoupMessageBody      *write_body;
+       SoupBuffer           *write_chunk;
+       gsize                 write_body_offset;
        guint                 written;
 
        guint read_tag, write_tag, err_tag;
@@ -96,13 +97,13 @@ soup_message_io_cleanup (SoupMessage *msg)
        if (io->conn)
                g_object_unref (io->conn);
 
-       if (io->read_buf)
-               g_byte_array_free (io->read_buf, TRUE);
        g_byte_array_free (io->read_meta_buf, TRUE);
 
        g_string_free (io->write_buf, TRUE);
+       if (io->write_chunk)
+               soup_buffer_free (io->write_chunk);
 
-       g_free (io);
+       g_slice_free (SoupMessageIOData, io);
 }
 
 /**
@@ -168,32 +169,37 @@ soup_message_io_finished (SoupMessage *msg)
 static void io_read (SoupSocket *sock, SoupMessage *msg);
 
 static void
-io_error (SoupSocket *sock, SoupMessage *msg)
+io_error (SoupSocket *sock, SoupMessage *msg, GError *error)
+{
+       if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
+               if (error && error->domain == SOUP_SSL_ERROR) {
+                       soup_message_set_status_full (msg,
+                                                     SOUP_STATUS_SSL_FAILED,
+                                                     error->message);
+               } else
+                       soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
+       }
+       if (error)
+               g_error_free (error);
+
+       soup_message_io_finished (msg);
+}
+
+static void
+io_disconnected (SoupSocket *sock, SoupMessage *msg)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
        SoupMessageIOData *io = priv->io_data;
 
        /* Closing the connection to signify EOF is sometimes ok */
        if (io->read_state == SOUP_MESSAGE_IO_STATE_BODY &&
-           io->read_encoding == SOUP_TRANSFER_EOF) {
+           io->read_encoding == SOUP_ENCODING_EOF) {
                io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
                io_read (sock, msg);
                return;
        }
 
-       if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
-               GError *err = g_object_get_data (G_OBJECT (sock),
-                                                "SoupSocket-last_error");
-
-               if (err && err->domain == SOUP_SSL_ERROR) {
-                       soup_message_set_status_full (msg,
-                                                     SOUP_STATUS_SSL_FAILED,
-                                                     err->message);
-               } else
-                       soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
-       }
-
-       soup_message_io_finished (msg);
+       io_error (sock, msg, NULL);
 }
 
 /* Reads data from io->sock into io->read_meta_buf up until @boundary.
@@ -220,12 +226,13 @@ read_metadata (SoupMessage *msg, const char *boundary)
        guint boundary_len = strlen (boundary);
        gsize nread;
        gboolean done;
+       GError *error = NULL;
 
        do {
                status = soup_socket_read_until (io->sock, read_buf,
                                                 sizeof (read_buf),
                                                 boundary, boundary_len,
-                                                &nread, &done);
+                                                &nread, &done, NULL, &error);
                switch (status) {
                case SOUP_SOCKET_OK:
                        g_byte_array_append (io->read_meta_buf, read_buf, nread);
@@ -233,7 +240,7 @@ read_metadata (SoupMessage *msg, const char *boundary)
 
                case SOUP_SOCKET_ERROR:
                case SOUP_SOCKET_EOF:
-                       io_error (io->sock, msg);
+                       io_error (io->sock, msg, error);
                        return FALSE;
 
                case SOUP_SOCKET_WOULD_BLOCK:
@@ -247,9 +254,8 @@ read_metadata (SoupMessage *msg, const char *boundary)
 /* Reads as much message body data as is available on io->sock (but no
  * further than the end of the current message body or chunk). On a
  * successful read, emits "got_chunk" (possibly multiple times), and
- * if io->read_buf is non-%NULL (meaning that the message doesn't have
- * %SOUP_MESSAGE_OVERWRITE_CHUNKS set), the data will be appended to
- * it.
+ * if %SOUP_MESSAGE_OVERWRITE_CHUNKS wasn't set, appends the chunk
+ * to io->read_body.
  *
  * See the note at read_metadata() for an explanation of the return
  * value.
@@ -262,34 +268,34 @@ read_body_chunk (SoupMessage *msg)
        SoupSocketIOStatus status;
        guchar read_buf[RESPONSE_BLOCK_SIZE];
        guint len = sizeof (read_buf);
-       gboolean read_to_eof = (io->read_encoding == SOUP_TRANSFER_EOF);
+       gboolean read_to_eof = (io->read_encoding == SOUP_ENCODING_EOF);
        gsize nread;
+       GError *error = NULL;
+       SoupBuffer *buffer;
 
        while (read_to_eof || io->read_length > 0) {
                if (!read_to_eof)
                        len = MIN (len, io->read_length);
 
-               status = soup_socket_read (io->sock, read_buf, len, &nread);
+               status = soup_socket_read (io->sock, read_buf, len,
+                                          &nread, NULL, &error);
 
                switch (status) {
                case SOUP_SOCKET_OK:
                        if (!nread)
                                break;
 
-                       if (io->read_buf)
-                               g_byte_array_append (io->read_buf, read_buf, nread);
-                       io->read_length -= nread;
+                       buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
+                                                 read_buf, nread);
+                       if (!(priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS))
+                               soup_message_body_append_buffer (io->read_body, buffer);
 
-                       io->read_body->owner  = SOUP_BUFFER_STATIC;
-                       io->read_body->body   = (char *)read_buf;
-                       io->read_body->length = nread;
+                       io->read_length -= nread;
 
                        SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
-                       soup_message_got_chunk (msg);
-                       if (priv->io_data == io)
-                               memset (io->read_body, 0, sizeof (SoupDataBuffer));
+                       soup_message_got_chunk (msg, buffer);
+                       soup_buffer_free (buffer);
                        SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
-
                        break;
 
                case SOUP_SOCKET_EOF:
@@ -298,7 +304,7 @@ read_body_chunk (SoupMessage *msg)
                        /* else fall through */
 
                case SOUP_SOCKET_ERROR:
-                       io_error (io->sock, msg);
+                       io_error (io->sock, msg, error);
                        return FALSE;
 
                case SOUP_SOCKET_WOULD_BLOCK:
@@ -319,16 +325,17 @@ write_data (SoupMessage *msg, const char *data, guint len)
        SoupMessageIOData *io = priv->io_data;
        SoupSocketIOStatus status;
        gsize nwrote;
+       GError *error = NULL;
 
        while (len > io->written) {
                status = soup_socket_write (io->sock,
                                            data + io->written,
                                            len - io->written,
-                                           &nwrote);
+                                           &nwrote, NULL, &error);
                switch (status) {
                case SOUP_SOCKET_EOF:
                case SOUP_SOCKET_ERROR:
-                       io_error (io->sock, msg);
+                       io_error (io->sock, msg, error);
                        return FALSE;
 
                case SOUP_SOCKET_WOULD_BLOCK:
@@ -345,12 +352,10 @@ write_data (SoupMessage *msg, const char *data, guint len)
 }
 
 static inline SoupMessageIOState
-io_body_state (SoupTransferEncoding encoding)
+io_body_state (SoupEncoding encoding)
 {
-       if (encoding == SOUP_TRANSFER_CHUNKED)
+       if (encoding == SOUP_ENCODING_CHUNKED)
                return SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
-       else if (encoding == SOUP_TRANSFER_NONE)
-               return SOUP_MESSAGE_IO_STATE_FINISHING;
        else
                return SOUP_MESSAGE_IO_STATE_BODY;
 }
@@ -368,14 +373,15 @@ io_body_state (SoupTransferEncoding encoding)
  *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
  *      W:DONE     / R:DONE               R:DONE     / W:DONE
  *     
- * and the "Expect: 100-continue" request/response, in which each
- * writer has to pause and wait for the other at some point:
+ * and the "Expect: 100-continue" request/response, with the client
+ * blocking halfway through its request, and then either continuing or
+ * aborting, depending on the server response:
  *
  *     Client                            Server
  *      W:HEADERS  / R:NOT_STARTED    ->  R:HEADERS  / W:NOT_STARTED
- *      W:BLOCKING / R:HEADERS (100)  <-  R:BLOCKING / W:HEADERS (100)
- *      W:BODY     / R:BLOCKING       ->  R:BODY     / W:BLOCKING
- *      W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS
+ *      W:BLOCKING / R:HEADERS        <-  R:BLOCKING / W:HEADERS
+ *     [W:BODY     / R:BLOCKING       ->  R:BODY     / W:BLOCKING]
+ *     [W:DONE     / R:HEADERS        <-  R:DONE     / W:HEADERS]
  *      W:DONE     / R:BODY           <-  R:DONE     / W:BODY
  *      W:DONE     / R:DONE               R:DONE     / W:DONE
  */
@@ -425,17 +431,27 @@ io_write (SoupSocket *sock, SoupMessage *msg)
                                 */
                        }
                } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
-                          priv->msg_flags & SOUP_MESSAGE_EXPECT_CONTINUE) {
+                          soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
                        /* Need to wait for the Continue response */
                        io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
                        io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
-               } else
+               } else {
                        io->write_state = io_body_state (io->write_encoding);
 
+                       /* If the client was waiting for a Continue
+                        * but we sent something else, then they're
+                        * now done writing.
+                        */
+                       if (io->mode == SOUP_MESSAGE_IO_SERVER &&
+                           io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
+                               io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+               }
+
                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
-               if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code))
+               if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
                        soup_message_wrote_informational (msg);
-               else
+                       soup_message_cleanup_response (msg);
+               } else
                        soup_message_wrote_headers (msg);
                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
                break;
@@ -454,9 +470,13 @@ io_write (SoupSocket *sock, SoupMessage *msg)
 
 
        case SOUP_MESSAGE_IO_STATE_BODY:
-               if (!write_data (msg, io->write_body->body,
-                                io->write_body->length))
+               if (!io->write_chunk)
+                       io->write_chunk = soup_message_body_flatten (io->write_body);
+               if (!write_data (msg, io->write_chunk->data,
+                                io->write_chunk->length))
                        return;
+               soup_buffer_free (io->write_chunk);
+               io->write_chunk = NULL;
 
                io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
 
@@ -467,22 +487,15 @@ io_write (SoupSocket *sock, SoupMessage *msg)
 
 
        case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
-               if (!io->write_buf->len) {
-                       SoupDataBuffer *chunk;
-
-                       SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
-                       chunk = soup_message_pop_chunk (msg);
-                       SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
-
-                       if (!chunk) {
+               if (!io->write_chunk) {
+                       io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
+                       if (!io->write_chunk) {
                                soup_message_io_pause (msg);
                                return;
                        }
-                       memcpy (io->write_body, chunk, sizeof (SoupDataBuffer));
-                       g_free (chunk);
-
-                       g_string_append_printf (io->write_buf, "%x\r\n",
-                                               io->write_body->length);
+                       g_string_append_printf (io->write_buf, "%lx\r\n",
+                                               (unsigned long) io->write_chunk->length);
+                       io->write_body_offset += io->write_chunk->length;
                }
 
                if (!write_data (msg, io->write_buf->str, io->write_buf->len))
@@ -490,7 +503,7 @@ io_write (SoupSocket *sock, SoupMessage *msg)
 
                g_string_truncate (io->write_buf, 0);
 
-               if (io->write_body->length == 0) {
+               if (io->write_chunk->length == 0) {
                        /* The last chunk has no CHUNK_END... */
                        io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
                        break;
@@ -501,15 +514,19 @@ io_write (SoupSocket *sock, SoupMessage *msg)
 
 
        case SOUP_MESSAGE_IO_STATE_CHUNK:
-               if (!write_data (msg, io->write_body->body,
-                                io->write_body->length))
+               if (!write_data (msg, io->write_chunk->data,
+                                io->write_chunk->length))
                        return;
 
+               soup_buffer_free (io->write_chunk);
+               io->write_chunk = NULL;
+
                io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
 
                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
                soup_message_wrote_chunk (msg);
                SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+
                /* fall through */
 
 
@@ -518,10 +535,6 @@ io_write (SoupSocket *sock, SoupMessage *msg)
                                 SOUP_MESSAGE_IO_EOL_LEN))
                        return;
 
-               if (io->write_body->owner == SOUP_BUFFER_SYSTEM_OWNED)
-                       g_free (io->write_body->body);
-               memset (io->write_body, 0, sizeof (SoupDataBuffer));
-
                io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
                break;
 
@@ -584,7 +597,6 @@ io_read (SoupSocket *sock, SoupMessage *msg)
                status = io->parse_headers_cb (msg, (char *)io->read_meta_buf->data,
                                               io->read_meta_buf->len,
                                               &io->read_encoding,
-                                              &io->read_length,
                                               io->user_data);
                g_byte_array_set_size (io->read_meta_buf, 0);
 
@@ -597,12 +609,19 @@ io_read (SoupSocket *sock, SoupMessage *msg)
                         * closed when we're done.
                         */
                        soup_message_set_status (msg, status);
-                       soup_message_add_header (msg->request_headers,
-                                                "Connection", "close");
+                       soup_message_headers_append (msg->request_headers,
+                                                    "Connection", "close");
                        io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
                        break;
                }
 
+               if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
+                       SoupMessageHeaders *hdrs =
+                               (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
+                               msg->response_headers : msg->request_headers;
+                       io->read_length = soup_message_headers_get_content_length (hdrs);
+               }
+
                if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
                    SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
                        if (msg->status_code == SOUP_STATUS_CONTINUE &&
@@ -617,17 +636,28 @@ io_read (SoupSocket *sock, SoupMessage *msg)
                                io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
                        }
                } else if (io->mode == SOUP_MESSAGE_IO_SERVER &&
-                          (priv->msg_flags & SOUP_MESSAGE_EXPECT_CONTINUE)) {
-                       /* The client requested a Continue response. */
+                          soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
+                       /* The client requested a Continue response. The
+                        * got_headers handler may change this to something
+                        * else though.
+                        */
                        soup_message_set_status (msg, SOUP_STATUS_CONTINUE);
-                       
                        io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
                        io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
-               } else
+               } else {
                        io->read_state = io_body_state (io->read_encoding);
 
-               if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code) &&
-                   !(priv->msg_flags & SOUP_MESSAGE_EXPECT_CONTINUE)) {
+                       /* If the client was waiting for a Continue
+                        * but got something else, then it's done
+                        * writing.
+                        */
+                       if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+                           io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
+                               io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+               }
+
+               if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+                   SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
                        SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
                        soup_message_got_informational (msg);
                        soup_message_cleanup_response (msg);
@@ -652,15 +682,6 @@ io_read (SoupSocket *sock, SoupMessage *msg)
                        return;
 
        got_body:
-               if (io->read_buf) {
-                       io->read_body->owner = SOUP_BUFFER_SYSTEM_OWNED;
-                       io->read_body->body = (char *)io->read_buf->data;
-                       io->read_body->length = io->read_buf->len;
-
-                       g_byte_array_free (io->read_buf, FALSE);
-                       io->read_buf = NULL;
-               }
-
                io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
 
                SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
@@ -744,19 +765,14 @@ new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
        SoupMessageIOData *io;
 
-       io = g_new0 (SoupMessageIOData, 1);
+       io = g_slice_new0 (SoupMessageIOData);
        io->sock = g_object_ref (sock);
        io->mode = mode;
        io->get_headers_cb   = get_headers_cb;
        io->parse_headers_cb = parse_headers_cb;
        io->user_data        = user_data;
 
-       io->read_encoding    = SOUP_TRANSFER_UNKNOWN;
-       io->write_encoding   = SOUP_TRANSFER_UNKNOWN;
-
        io->read_meta_buf    = g_byte_array_new ();
-       if (!(priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS))
-               io->read_buf = g_byte_array_new ();
        io->write_buf        = g_string_new (NULL);
 
        io->read_tag  = g_signal_connect (io->sock, "readable",
@@ -764,7 +780,7 @@ new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
        io->write_tag = g_signal_connect (io->sock, "writable",
                                          G_CALLBACK (io_write), msg);
        io->err_tag   = g_signal_connect (io->sock, "disconnected",
-                                         G_CALLBACK (io_error), msg);
+                                         G_CALLBACK (io_disconnected), msg);
 
        io->read_state  = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
        io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
@@ -775,19 +791,6 @@ new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
        return io;
 }
 
-/**
- * soup_message_io_client:
- * @msg: a #SoupMessage
- * @sock: socket to send @msg across
- * @conn: the connection that owns @sock (or %NULL)
- * @get_headers_cb: callback function to generate request headers
- * @parse_headers_cb: callback function to parse response headers
- * @user_data: data to pass to the callbacks
- *
- * Begins the process of sending @msg across @sock.
- *
- * Don't call this. Use soup_message_send_request().
- **/
 void
 soup_message_io_client (SoupMessage *msg, SoupSocket *sock,
                        SoupConnection *conn,
@@ -803,25 +806,13 @@ soup_message_io_client (SoupMessage *msg, SoupSocket *sock,
        if (conn)
                io->conn = g_object_ref (conn);
 
-       io->read_body       = &msg->response;
-       io->write_body      = &msg->request;
+       io->read_body       = msg->response_body;
+       io->write_body      = msg->request_body;
 
        io->write_state     = SOUP_MESSAGE_IO_STATE_HEADERS;
        io_write (sock, msg);
 }
 
-/**
- * soup_message_io_server:
- * @msg: an empty #SoupServerMessage
- * @sock: socket to receive a request on
- * @get_headers_cb: callback function to generate response headers
- * @parse_headers_cb: callback function to parse request headers
- * @user_data: data to pass to the callbacks
- *
- * Begins the process of receiving a request from @sock into @msg.
- *
- * Don't use this. Use soup_message_receive_request() instead.
- **/
 void
 soup_message_io_server (SoupMessage *msg, SoupSocket *sock,
                        SoupMessageGetHeadersFn get_headers_cb,
@@ -833,22 +824,13 @@ soup_message_io_server (SoupMessage *msg, SoupSocket *sock,
        io = new_iostate (msg, sock, SOUP_MESSAGE_IO_SERVER,
                          get_headers_cb, parse_headers_cb, user_data);
 
-       io->read_body       = &msg->request;
-       io->write_body      = &msg->response;
+       io->read_body       = msg->request_body;
+       io->write_body      = msg->response_body;
 
        io->read_state      = SOUP_MESSAGE_IO_STATE_HEADERS;
        io_read (sock, msg);
 }
 
-/**
- * soup_message_io_pause:
- * @msg: a #SoupMessage
- *
- * Pauses I/O on @msg. This can be used in a #SoupServer handler when
- * you don't have the data ready to return yet, or with a client-side
- * message if you are not ready to process any more of the response at
- * this time; call soup_message_io_unpause() to resume I/O.
- **/
 void  
 soup_message_io_pause (SoupMessage *msg)
 {
@@ -896,18 +878,6 @@ io_unpause_internal (gpointer msg)
        return FALSE;
 }
 
-/**
- * soup_message_io_unpause:
- * @msg: a #SoupMessage
- *
- * Resumes I/O on @msg. Use this to resume after calling
- * soup_message_io_pause(), or after adding a new chunk to a chunked
- * response.
- *
- * If @msg is being sent via blocking I/O, this will resume reading or
- * writing immediately. If @msg is using non-blocking I/O, then
- * reading or writing won't resume until you return to the main loop.
- **/
 void
 soup_message_io_unpause (SoupMessage *msg)
 {
index 14f55d2..1a22ac6 100644 (file)
@@ -8,45 +8,51 @@
 
 #include "soup-message.h"
 #include "soup-auth.h"
+#include "soup-connection.h"
+
+typedef enum {
+       SOUP_MESSAGE_IO_STATUS_IDLE,
+       SOUP_MESSAGE_IO_STATUS_QUEUED,
+        SOUP_MESSAGE_IO_STATUS_CONNECTING,
+        SOUP_MESSAGE_IO_STATUS_RUNNING,
+       SOUP_MESSAGE_IO_STATUS_FINISHED
+} SoupMessageIOStatus;
 
 typedef struct {
        gpointer           io_data;
+       SoupMessageIOStatus io_status;
 
        guint              msg_flags;
 
-       GSList            *chunks, *last_chunk;
-
-       GSList            *content_handlers;
+       SoupHTTPVersion    http_version;
 
-       SoupHttpVersion    http_version;
-
-       SoupUri           *uri;
+       SoupURI           *uri;
 
        SoupAuth          *auth, *proxy_auth;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
 
-void             soup_message_run_handlers     (SoupMessage      *msg,
-                                               SoupHandlerPhase  phase);
+#define SOUP_MESSAGE_IS_STARTING(msg) (SOUP_MESSAGE_GET_PRIVATE (msg)->io_status == SOUP_MESSAGE_IO_STATUS_QUEUED || SOUP_MESSAGE_GET_PRIVATE (msg)->io_status == SOUP_MESSAGE_IO_STATUS_CONNECTING)
 
 void             soup_message_cleanup_response (SoupMessage      *req);
 
 
 typedef void     (*SoupMessageGetHeadersFn)  (SoupMessage      *msg,
                                              GString          *headers,
-                                             SoupTransferEncoding *encoding,
+                                             SoupEncoding     *encoding,
                                              gpointer          user_data);
 typedef guint    (*SoupMessageParseHeadersFn)(SoupMessage      *msg,
                                              char             *headers,
                                              guint             header_len,
-                                             SoupTransferEncoding *encoding,
-                                             guint            *content_len,
+                                             SoupEncoding     *encoding,
                                              gpointer          user_data);
 
-void soup_message_send_request_internal (SoupMessage       *req,
-                                        SoupSocket        *sock,
-                                        SoupConnection    *conn,
-                                        gboolean           via_proxy);
+void           soup_message_send_request        (SoupMessage       *req,
+                                                SoupSocket        *sock,
+                                                SoupConnection    *conn,
+                                                gboolean           via_proxy);
+void           soup_message_read_request        (SoupMessage       *req,
+                                                SoupSocket        *sock);
 
 void soup_message_io_client  (SoupMessage               *msg,
                              SoupSocket                *sock,
@@ -69,4 +75,13 @@ void           soup_message_set_proxy_auth (SoupMessage *msg,
                                            SoupAuth    *auth);
 SoupAuth      *soup_message_get_proxy_auth (SoupMessage *msg);
 
+/* I/O */
+void                soup_message_set_io_status  (SoupMessage          *msg,
+                                                SoupMessageIOStatus  status);
+SoupMessageIOStatus soup_message_get_io_status  (SoupMessage          *msg);
+void                soup_message_io_stop        (SoupMessage          *msg);
+void                soup_message_io_pause       (SoupMessage          *msg);
+void                soup_message_io_unpause     (SoupMessage          *msg);
+gboolean            soup_message_io_in_progress (SoupMessage          *msg);
+
 #endif /* SOUP_MESSAGE_PRIVATE_H */
index d730dc3..a73749a 100644 (file)
@@ -30,7 +30,7 @@ soup_message_queue_new (void)
 {
        SoupMessageQueue *queue;
 
-       queue = g_new0 (SoupMessageQueue, 1);
+       queue = g_slice_new0 (SoupMessageQueue);
        queue->mutex = g_mutex_new ();
        return queue;
 }
@@ -49,7 +49,7 @@ soup_message_queue_destroy (SoupMessageQueue *queue)
        g_list_free (queue->head);
        g_list_free (queue->iters);
        g_mutex_free (queue->mutex);
-       g_free (queue);
+       g_slice_free (SoupMessageQueue, queue);
 }
 
 /**
index d3b0185..24e8ddb 100644 (file)
@@ -13,14 +13,7 @@ G_BEGIN_DECLS
 
 typedef struct SoupMessageQueue SoupMessageQueue; 
 
-/**
- * SoupMessageQueueIter:
- *
- * An opaque data structure used to iterate the elements of a
- * #SoupMessageQueue.
- **/
 typedef struct {
-       /*< private >*/
        GList *cur, *next;
 } SoupMessageQueueIter;
 
index 7927319..c4b85cb 100644 (file)
 #include "soup-address.h"
 #include "soup-auth.h"
 #include "soup-headers.h"
-#include "soup-server-message.h"
 #include "soup-server.h"
 #include "soup-socket.h"
 
 static guint
 parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
-                      SoupTransferEncoding *encoding, guint *content_len,
-                      gpointer sock)
+                      SoupEncoding *encoding, gpointer sock)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-       SoupUri *uri;
-       char *req_path = NULL, *url;
-       const char *expect, *req_host;
-       SoupServer *server;
-
-       if (!soup_headers_parse_request (headers, headers_len,
-                                        msg->request_headers,
-                                        (char **) &msg->method,
-                                        &req_path,
-                                        &priv->http_version))
-               return SOUP_STATUS_BAD_REQUEST;
-
-       expect = soup_message_get_header (msg->request_headers, "Expect");
-       if (expect && !strcmp (expect, "100-continue"))
-               priv->msg_flags |= SOUP_MESSAGE_EXPECT_CONTINUE;
+       char *req_method, *req_path, *url;
+       SoupHTTPVersion version;
+       const char *req_host;
+       guint status;
+       SoupURI *uri;
+
+       status = soup_headers_parse_request (headers, headers_len,
+                                            msg->request_headers,
+                                            &req_method,
+                                            &req_path,
+                                            &version);
+       if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+               return status;
+
+       g_object_set (G_OBJECT (msg),
+                     SOUP_MESSAGE_METHOD, req_method,
+                     SOUP_MESSAGE_HTTP_VERSION, version,
+                     NULL);
+       g_free (req_method);
 
        /* Handle request body encoding */
-       *encoding = soup_message_get_request_encoding (msg, content_len);
-       if (*encoding == SOUP_TRANSFER_NONE) {
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-               *content_len = 0;
-       } else if (*encoding == SOUP_TRANSFER_UNKNOWN) {
-               if (soup_message_get_header (msg->request_headers, "Transfer-Encoding"))
+       *encoding = soup_message_headers_get_encoding (msg->request_headers);
+       if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
+               if (soup_message_headers_get (msg->request_headers, "Transfer-Encoding"))
                        return SOUP_STATUS_NOT_IMPLEMENTED;
                else
                        return SOUP_STATUS_BAD_REQUEST;
        }
 
        /* Generate correct context for request */
-       server = soup_server_message_get_server (SOUP_SERVER_MESSAGE (msg));
-       req_host = soup_message_get_header (msg->request_headers, "Host");
+       req_host = soup_message_headers_get (msg->request_headers, "Host");
 
        if (*req_path != '/') {
                /* Check for absolute URI */
-               SoupUri *absolute;
-
-               absolute = soup_uri_new (req_path);
-               if (absolute) {
-                       url = g_strdup (req_path);
-                       soup_uri_free (absolute);
-               } else {
+               uri = soup_uri_new (req_path);
+               if (!uri) {
                        g_free (req_path);
                        return SOUP_STATUS_BAD_REQUEST;
                }
        } else if (req_host) {
                url = g_strdup_printf ("%s://%s%s",
-                                      soup_server_get_protocol (server) == SOUP_PROTOCOL_HTTPS ? "https" : "http",
+                                      soup_socket_is_ssl (sock) ? "https" : "http",
                                       req_host, req_path);
+               uri = soup_uri_new (url);
+               g_free (url);
        } else if (priv->http_version == SOUP_HTTP_1_0) {
                /* No Host header, no AbsoluteUri */
                SoupAddress *addr = soup_socket_get_local_address (sock);
                const char *host = soup_address_get_physical (addr);
 
                url = g_strdup_printf ("%s://%s:%d%s",
-                                      soup_server_get_protocol (server) == SOUP_PROTOCOL_HTTPS ? "https" : "http",
-                                      host, soup_server_get_port (server),
+                                      soup_socket_is_ssl (sock) ? "https" : "http",
+                                      host, soup_address_get_port (addr),
                                       req_path);
-       } else {
-               g_free (req_path);
-               return SOUP_STATUS_BAD_REQUEST;
-       }
+               uri = soup_uri_new (url);
+               g_free (url);
+       } else
+               uri = NULL;
 
-       uri = soup_uri_new (url);
-       g_free (url);
        g_free (req_path);
-
        if (!uri)
                return SOUP_STATUS_BAD_REQUEST;
-
        soup_message_set_uri (msg, uri);
        soup_uri_free (uri);
 
@@ -102,46 +93,42 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
 }
 
 static void
-write_header (gpointer name, gpointer value, gpointer headers)
+write_header (const char *name, const char *value, gpointer headers)
 {
-       g_string_append_printf (headers, "%s: %s\r\n",
-                               (char *)name, (char *)value);
+       g_string_append_printf (headers, "%s: %s\r\n", name, value);
 }
 
 static void
 get_response_headers (SoupMessage *msg, GString *headers,
-                     SoupTransferEncoding *encoding,
-                     gpointer user_data)
+                     SoupEncoding *encoding, gpointer user_data)
 {
-       SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (msg);
-       SoupTransferEncoding claimed_encoding;
+       SoupEncoding claimed_encoding;
 
        g_string_append_printf (headers, "HTTP/1.1 %d %s\r\n",
                                msg->status_code, msg->reason_phrase);
 
-       soup_message_foreach_header (msg->response_headers,
-                                    write_header, headers);
-
-       *encoding = soup_message_get_response_encoding (msg, NULL);
-
-       claimed_encoding = soup_server_message_get_encoding (smsg);
-       if (claimed_encoding == SOUP_TRANSFER_CONTENT_LENGTH &&
-           !soup_message_get_header (msg->response_headers, "Content-Length")) {
-               g_string_append_printf (headers, "Content-Length: %d\r\n",
-                                       msg->response.length);
-       } else if (claimed_encoding == SOUP_TRANSFER_CHUNKED)
-               g_string_append (headers, "Transfer-Encoding: chunked\r\n");
+       claimed_encoding = soup_message_headers_get_encoding (msg->response_headers);
+       if ((msg->method == SOUP_METHOD_HEAD ||
+            msg->status_code  == SOUP_STATUS_NO_CONTENT ||
+            msg->status_code  == SOUP_STATUS_NOT_MODIFIED ||
+            SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
+           (msg->method == SOUP_METHOD_CONNECT &&
+            SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
+               *encoding = SOUP_ENCODING_NONE;
+       else
+               *encoding = claimed_encoding;
+
+       if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH &&
+           !soup_message_headers_get_content_length (msg->response_headers)) {
+               soup_message_headers_set_content_length (msg->response_headers,
+                                                        msg->response_body->length);
+       }
 
+       soup_message_headers_foreach (msg->response_headers,
+                                     write_header, headers);
        g_string_append (headers, "\r\n");
 }
 
-/**
- * soup_message_read_request:
- * @req: an empty #SoupServerMessage
- * @sock: socket to receive the request on
- *
- * Begins the process of receiving a request from @sock into @req.
- **/
 void
 soup_message_read_request (SoupMessage *req, SoupSocket *sock)
 {
index 5a0f8ba..12f62e1 100644 (file)
@@ -9,13 +9,50 @@
 #include <string.h>
 
 #include "soup-auth.h"
+#include "soup-enum-types.h"
 #include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
 #include "soup-misc.h"
-#include "soup-server-message.h"
 #include "soup-uri.h"
 
+/**
+ * SECTION:soup-message
+ * @short_description: An HTTP request and response.
+ * @see_also: #SoupMessageHeaders, #SoupMessageBody
+ *
+ **/
+
+/**
+ * SoupMessage:
+ * @method: the HTTP method
+ * @status_code: the HTTP status code
+ * @reason_phrase: the status phrase associated with @status_code
+ * @request_body: the request body
+ * @request_headers: the request headers
+ * @response_body: the response body
+ * @response_headers: the response headers
+ *
+ * Represents an HTTP message being sent or received.
+ *
+ * As described in the #SoupMessageBody documentation, the
+ * @request_body and @response_body %data fields will not necessarily
+ * be filled in at all times. When they are filled in, they will be
+ * terminated with a '\0' byte (which is not included in the %length),
+ * so you can use them as ordinary C strings (assuming that you know
+ * that the body doesn't have any other '\0' bytes).
+ *
+ * For a client-side #SoupMessage, @request_body's %data is usually
+ * filled in right before libsoup writes the request to the network,
+ * but you should not count on this; use soup_message_body_flatten()
+ * if you want to ensure that %data is filled in. @response_body's
+ * %data will be filled in before #SoupMessage::finished is emitted,
+ * unless you set the %SOUP_MESSAGE_OVERWRITE_CHUNKS flag.
+ *
+ * For a server-side #SoupMessage, @request_body's %data will be
+ * filled in before #SoupMessage::got_body is emitted.
+ **/
+
 G_DEFINE_TYPE (SoupMessage, soup_message, G_TYPE_OBJECT)
 
 enum {
@@ -37,26 +74,40 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-static void wrote_body (SoupMessage *req);
-static void got_headers (SoupMessage *req);
-static void got_chunk (SoupMessage *req);
+enum {
+       PROP_0,
+
+       PROP_METHOD,
+       PROP_URI,
+       PROP_HTTP_VERSION,
+       PROP_FLAGS,
+       PROP_STATUS_CODE,
+       PROP_REASON_PHRASE,
+
+       LAST_PROP
+};
+
 static void got_body (SoupMessage *req);
 static void restarted (SoupMessage *req);
 static void finished (SoupMessage *req);
-static void free_chunks (SoupMessage *msg);
+
+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
 soup_message_init (SoupMessage *msg)
 {
-       msg->status  = SOUP_MESSAGE_STATUS_IDLE;
-
-       msg->request_headers = g_hash_table_new (soup_str_case_hash,
-                                                soup_str_case_equal);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       msg->response_headers = g_hash_table_new (soup_str_case_hash,
-                                                 soup_str_case_equal);
+       priv->io_status = SOUP_MESSAGE_IO_STATUS_IDLE;
+       priv->http_version = SOUP_HTTP_1_1;
 
-       SOUP_MESSAGE_GET_PRIVATE (msg)->http_version = SOUP_HTTP_1_1;
+       msg->request_body = soup_message_body_new ();
+       msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+       msg->response_body = soup_message_body_new ();
+       msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
 }
 
 static void
@@ -75,20 +126,10 @@ finalize (GObject *object)
        if (priv->proxy_auth)
                g_object_unref (priv->proxy_auth);
 
-       if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (msg->request.body);
-       if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (msg->response.body);
-       free_chunks (msg);
-
-       soup_message_clear_headers (msg->request_headers);
-       g_hash_table_destroy (msg->request_headers);
-
-       soup_message_clear_headers (msg->response_headers);
-       g_hash_table_destroy (msg->response_headers);
-
-       g_slist_foreach (priv->content_handlers, (GFunc) g_free, NULL);
-       g_slist_free (priv->content_handlers);
+       soup_message_body_free (msg->request_body);
+       soup_message_headers_free (msg->request_headers);
+       soup_message_body_free (msg->response_body);
+       soup_message_headers_free (msg->response_headers);
 
        g_free ((char *) msg->reason_phrase);
 
@@ -103,15 +144,14 @@ soup_message_class_init (SoupMessageClass *message_class)
        g_type_class_add_private (message_class, sizeof (SoupMessagePrivate));
 
        /* virtual method definition */
-       message_class->wrote_body   = wrote_body;
-       message_class->got_headers  = got_headers;
-       message_class->got_chunk    = got_chunk;
        message_class->got_body     = got_body;
        message_class->restarted    = restarted;
        message_class->finished     = finished;
 
        /* virtual method override */
        object_class->finalize = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
 
        /* signals */
 
@@ -120,7 +160,7 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted immediately after writing a 1xx (Informational)
-        * response for a message.
+        * response for a (server-side) message.
         **/
        signals[WROTE_INFORMATIONAL] =
                g_signal_new ("wrote_informational",
@@ -135,7 +175,10 @@ soup_message_class_init (SoupMessageClass *message_class)
         * SoupMessage::wrote-headers:
         * @msg: the message
         *
-        * Emitted immediately after writing the headers for a message.
+        * Emitted immediately after writing the headers for a
+        * message. (For a client-side message, this is after writing
+        * the request headers; for a server-side message, it is after
+        * writing the response headers.)
         **/
        signals[WROTE_HEADERS] =
                g_signal_new ("wrote_headers",
@@ -165,7 +208,12 @@ soup_message_class_init (SoupMessageClass *message_class)
         * SoupMessage::wrote-body:
         * @msg: the message
         *
-        * Emitted immediately after writing the complete body for a message.
+        * Emitted immediately after writing the complete body for a
+        * message. (For a client-side message, this means that
+        * libsoup is done writing and is now waiting for the response
+        * from the server. For a server-side message, this means that
+        * libsoup has finished writing the response and is nearly
+        * done with the message.)
         **/
        signals[WROTE_BODY] =
                g_signal_new ("wrote_body",
@@ -181,7 +229,14 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted after receiving a 1xx (Informational) response for
-        * a message.
+        * a (client-side) message. The response_headers will be
+        * filled in with the headers associated with the
+        * informational response; however, those header values will
+        * be erased after this signal is done.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
         **/
        signals[GOT_INFORMATIONAL] =
                g_signal_new ("got_informational",
@@ -197,6 +252,23 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted after receiving all message headers for a message.
+        * (For a client-side message, this is after receiving the
+        * Status-Line and response headers; for a server-side
+        * message, it is after receiving the Request-Line and request
+        * headers.)
+        *
+        * See also soup_message_add_header_handler() and
+        * soup_message_add_status_code_handler(), which can be used
+        * to connect to a subset of emissions of this signal.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
+        * (If you need to requeue a message--eg, after handling
+        * authentication or redirection--it is usually better to
+        * requeue it from a #SoupMessage::got_body handler rather
+        * than a #SoupMessage::got_header handler, so that the
+        * existing HTTP connection can be reused.)
         **/
        signals[GOT_HEADERS] =
                g_signal_new ("got_headers",
@@ -210,11 +282,16 @@ soup_message_class_init (SoupMessageClass *message_class)
        /**
         * SoupMessage::got-chunk:
         * @msg: the message
+        * @chunk: the just-read chunk
         *
         * Emitted after receiving a chunk of a message body. Note
         * that "chunk" in this context means any subpiece of the
         * body, not necessarily the specific HTTP 1.1 chunks sent by
         * the other side.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
         **/
        signals[GOT_CHUNK] =
                g_signal_new ("got_chunk",
@@ -222,14 +299,22 @@ soup_message_class_init (SoupMessageClass *message_class)
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
                              NULL, NULL,
-                             soup_marshal_NONE__NONE,
-                             G_TYPE_NONE, 0);
+                             soup_marshal_NONE__BOXED,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_BUFFER);
 
        /**
         * SoupMessage::got-body:
         * @msg: the message
         *
-        * Emitted after receiving the complete message body.
+        * Emitted after receiving the complete message body. (For a
+        * server-side message, this means it has received the request
+        * body. For a client-side message, this means it has received
+        * the response body and is nearly done with the message.)
+        *
+        * See also soup_message_add_header_handler() and
+        * soup_message_add_status_code_handler(), which can be used
+        * to connect to a subset of emissions of this signal.
         **/
        signals[GOT_BODY] =
                g_signal_new ("got_body",
@@ -244,7 +329,10 @@ soup_message_class_init (SoupMessageClass *message_class)
         * SoupMessage::restarted:
         * @msg: the message
         *
-        * Emitted when a message is about to be re-queued.
+        * Emitted when a request that was already sent once is now
+        * being sent again (eg, because the first attempt received a
+        * redirection response, or because we needed to use
+        * authentication).
         **/
        signals[RESTARTED] =
                g_signal_new ("restarted",
@@ -260,8 +348,8 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted when all HTTP processing is finished for a message.
-        * (After #read-body for client-side code, or after
-        * #wrote-body for server-side code.)
+        * (After #SoupMessage::got_body for client-side messages, or
+        * after #SoupMessage::wrote_body for server-side messages.)
         **/
        signals[FINISHED] =
                g_signal_new ("finished",
@@ -271,6 +359,114 @@ soup_message_class_init (SoupMessageClass *message_class)
                              NULL, NULL,
                              soup_marshal_NONE__NONE,
                              G_TYPE_NONE, 0);
+
+       /* properties */
+       g_object_class_install_property (
+               object_class, PROP_METHOD,
+               g_param_spec_string (SOUP_MESSAGE_METHOD,
+                                    "Method",
+                                    "The message's HTTP method",
+                                    SOUP_METHOD_GET,
+                                    G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_URI,
+               g_param_spec_boxed (SOUP_MESSAGE_URI,
+                                   "URI",
+                                   "The message's Request-URI",
+                                   SOUP_TYPE_URI,
+                                   G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_HTTP_VERSION,
+               g_param_spec_enum (SOUP_MESSAGE_HTTP_VERSION,
+                                  "HTTP Version",
+                                  "The HTTP protocol version to use",
+                                  SOUP_TYPE_HTTP_VERSION,
+                                  SOUP_HTTP_1_1,
+                                  G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_FLAGS,
+               g_param_spec_flags (SOUP_MESSAGE_FLAGS,
+                                   "Flags",
+                                   "Various message options",
+                                   SOUP_TYPE_MESSAGE_FLAGS,
+                                   0,
+                                   G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_STATUS_CODE,
+               g_param_spec_uint (SOUP_MESSAGE_STATUS_CODE,
+                                  "Status code",
+                                  "The HTTP response status code",
+                                  0, 599, 0,
+                                  G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_REASON_PHRASE,
+               g_param_spec_string (SOUP_MESSAGE_REASON_PHRASE,
+                                    "Reason phrase",
+                                    "The HTTP response reason phrase",
+                                    NULL,
+                                    G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupMessage *msg = SOUP_MESSAGE (object);
+
+       switch (prop_id) {
+       case PROP_METHOD:
+               msg->method = g_intern_string (g_value_get_string (value));
+               break;
+       case PROP_URI:
+               soup_message_set_uri (msg, g_value_get_boxed (value));
+               break;
+       case PROP_HTTP_VERSION:
+               soup_message_set_http_version (msg, g_value_get_enum (value));
+               break;
+       case PROP_FLAGS:
+               soup_message_set_flags (msg, g_value_get_flags (value));
+               break;
+       case PROP_STATUS_CODE:
+               soup_message_set_status (msg, g_value_get_uint (value));
+               break;
+       case PROP_REASON_PHRASE:
+               soup_message_set_status_full (msg, msg->status_code,
+                                             g_value_get_string (value));
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupMessage *msg = SOUP_MESSAGE (object);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+       switch (prop_id) {
+       case PROP_METHOD:
+               g_value_set_string (value, msg->method);
+               break;
+       case PROP_URI:
+               g_value_set_boxed (value, priv->uri);
+               break;
+       case PROP_HTTP_VERSION:
+               g_value_set_enum (value, priv->http_version);
+               break;
+       case PROP_FLAGS:
+               g_value_set_flags (value, priv->msg_flags);
+               break;
+       case PROP_STATUS_CODE:
+               g_value_set_uint (value, msg->status_code);
+               break;
+       case PROP_REASON_PHRASE:
+               g_value_set_string (value, msg->reason_phrase);
+               break;
+       default:
+               break;
+       }
 }
 
 
@@ -288,99 +484,106 @@ SoupMessage *
 soup_message_new (const char *method, const char *uri_string)
 {
        SoupMessage *msg;
-       SoupUri *uri;
+       SoupURI *uri;
+
+       g_return_val_if_fail (method != NULL, NULL);
+       g_return_val_if_fail (uri_string != NULL, NULL);
 
        uri = soup_uri_new (uri_string);
        if (!uri)
                return NULL;
-
        if (!uri->host) {
                soup_uri_free (uri);
                return NULL;
        }
 
-       msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
-       msg->method = method ? method : SOUP_METHOD_GET;
-       SOUP_MESSAGE_GET_PRIVATE (msg)->uri = uri;
-
+       msg = soup_message_new_from_uri (method, uri);
+       soup_uri_free (uri);
        return msg;
 }
 
 /**
  * soup_message_new_from_uri:
  * @method: the HTTP method for the created request
- * @uri: the destination endpoint (as a #SoupUri)
+ * @uri: the destination endpoint (as a #SoupURI)
  * 
  * Creates a new empty #SoupMessage, which will connect to @uri
  *
  * Return value: the new #SoupMessage
  */
 SoupMessage *
-soup_message_new_from_uri (const char *method, const SoupUri *uri)
+soup_message_new_from_uri (const char *method, SoupURI *uri)
 {
-       SoupMessage *msg;
-
-       msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
-       msg->method = method ? method : SOUP_METHOD_GET;
-       SOUP_MESSAGE_GET_PRIVATE (msg)->uri = soup_uri_copy (uri);
-
-       return msg;
+       return g_object_new (SOUP_TYPE_MESSAGE,
+                            SOUP_MESSAGE_METHOD, method,
+                            SOUP_MESSAGE_URI, uri,
+                            NULL);
 }
 
 /**
  * soup_message_set_request:
  * @msg: the message
  * @content_type: MIME Content-Type of the body
- * @req_owner: the #SoupOwnership of the passed data buffer.
+ * @req_use: a #SoupMemoryUse describing how to handle @req_body
  * @req_body: a data buffer containing the body of the message request.
  * @req_length: the byte length of @req_body.
  * 
- * Convenience function to set the request body of a #SoupMessage
+ * Convenience function to set the request body of a #SoupMessage. If
+ * @content_type is %NULL, the request body must be empty as well.
  */
 void
-soup_message_set_request (SoupMessage   *msg,
-                         const char    *content_type,
-                         SoupOwnership  req_owner,
-                         char          *req_body,
-                         gulong         req_length)
+soup_message_set_request (SoupMessage    *msg,
+                         const char     *content_type,
+                         SoupMemoryUse   req_use,
+                         const char     *req_body,
+                         gsize           req_length)
 {
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (content_type != NULL);
-       g_return_if_fail (req_body != NULL || req_length == 0);
-
-       soup_message_add_header (msg->request_headers,
-                                "Content-Type", content_type);
-       msg->request.owner = req_owner;
-       msg->request.body = req_body;
-       msg->request.length = req_length;
+       g_return_if_fail (content_type != NULL || req_length == 0);
+
+       if (content_type) {
+               soup_message_headers_replace (msg->request_headers,
+                                             "Content-Type", content_type);
+               soup_message_body_append (msg->request_body, req_use,
+                                         req_body, req_length);
+       } else {
+               soup_message_headers_remove (msg->request_headers,
+                                            "Content-Type");
+               soup_message_body_truncate (msg->request_body);
+       }
 }
 
 /**
  * soup_message_set_response:
  * @msg: the message
  * @content_type: MIME Content-Type of the body
- * @resp_owner: the #SoupOwnership of the passed data buffer.
+ * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
  * @resp_body: a data buffer containing the body of the message response.
  * @resp_length: the byte length of @resp_body.
  * 
- * Convenience function to set the response body of a #SoupMessage
+ * Convenience function to set the response body of a #SoupMessage. If
+ * @content_type is %NULL, the response body must be empty as well.
  */
 void
-soup_message_set_response (SoupMessage   *msg,
-                          const char    *content_type,
-                          SoupOwnership  resp_owner,
-                          char          *resp_body,
-                          gulong         resp_length)
+soup_message_set_response (SoupMessage    *msg,
+                          const char     *content_type,
+                          SoupMemoryUse   resp_use,
+                          const char     *resp_body,
+                          gsize           resp_length)
 {
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (content_type != NULL);
-       g_return_if_fail (resp_body != NULL || resp_length == 0);
-
-       soup_message_add_header (msg->response_headers,
-                                "Content-Type", content_type);
-       msg->response.owner = resp_owner;
-       msg->response.body = resp_body;
-       msg->response.length = resp_length;
+       g_return_if_fail (content_type != NULL || resp_length == 0);
+
+       if (content_type) {
+               soup_message_headers_replace (msg->response_headers,
+                                             "Content-Type", content_type);
+               soup_message_body_append (msg->response_body, resp_use,
+                                         resp_body, resp_length);
+       } else {
+               soup_message_headers_remove (msg->response_headers,
+                                            "Content-Type");
+               soup_message_body_truncate (msg->response_body);
+       }
 }
 
 /**
@@ -422,14 +625,6 @@ soup_message_wrote_chunk (SoupMessage *msg)
        g_signal_emit (msg, signals[WROTE_CHUNK], 0);
 }
 
-static void
-wrote_body (SoupMessage *req)
-{
-       g_object_ref (req);
-       soup_message_run_handlers (req, SOUP_HANDLER_POST_REQUEST);
-       g_object_unref (req);
-}
-
 /**
  * soup_message_wrote_body:
  * @msg: a #SoupMessage
@@ -456,16 +651,6 @@ soup_message_got_informational (SoupMessage *msg)
        g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
 }
 
-static void
-got_headers (SoupMessage *req)
-{
-       g_object_ref (req);
-       soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
-       if (SOUP_MESSAGE_IS_STARTING (req))
-               g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
-       g_object_unref (req);
-}
-
 /**
  * soup_message_got_headers:
  * @msg: a #SoupMessage
@@ -479,37 +664,35 @@ soup_message_got_headers (SoupMessage *msg)
        g_signal_emit (msg, signals[GOT_HEADERS], 0);
 }
 
-static void
-got_chunk (SoupMessage *req)
-{
-       g_object_ref (req);
-       soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
-       if (SOUP_MESSAGE_IS_STARTING (req))
-               g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
-       g_object_unref (req);
-}
-
 /**
  * soup_message_got_chunk:
  * @msg: a #SoupMessage
+ * @chunk: the newly-read chunk
  *
  * Emits the %got_chunk signal, indicating that the IO layer finished
  * reading a chunk of @msg's body.
  **/
 void
-soup_message_got_chunk (SoupMessage *msg)
+soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk)
 {
-       g_signal_emit (msg, signals[GOT_CHUNK], 0);
+       g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
 }
 
 static void
 got_body (SoupMessage *req)
 {
-       g_object_ref (req);
-       soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
-       if (SOUP_MESSAGE_IS_STARTING (req))
-               g_signal_stop_emission (req, signals[GOT_BODY], 0);
-       g_object_unref (req);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+
+       if (!(priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS)) {
+               SoupBuffer *buffer;
+
+               /* Figure out *which* body we read, and flatten it. */
+               if (req->status_code == 0)
+                       buffer = soup_message_body_flatten (req->request_body);
+               else
+                       buffer = soup_message_body_flatten (req->response_body);
+               soup_buffer_free (buffer);
+       }
 }
 
 /**
@@ -547,8 +730,10 @@ soup_message_restarted (SoupMessage *msg)
 static void
 finished (SoupMessage *req)
 {
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+
        soup_message_io_stop (req);
-       req->status = SOUP_MESSAGE_STATUS_FINISHED;
+       priv->io_status = SOUP_MESSAGE_IO_STATUS_FINISHED;
 }
 
 /**
@@ -564,170 +749,150 @@ soup_message_finished (SoupMessage *msg)
        g_signal_emit (msg, signals[FINISHED], 0);
 }
 
-static gboolean
-free_header_list (gpointer name, gpointer vals, gpointer user_data)
-{
-       g_free (name);
-       g_slist_foreach (vals, (GFunc) g_free, NULL);
-       g_slist_free (vals);
-
-       return TRUE;
-}
-
-/**
- * soup_message_clear_headers:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- *
- * Clears @hash.
- **/
-void
-soup_message_clear_headers (GHashTable *hash)
+static void
+header_handler_free (gpointer header_name, GClosure *closure)
 {
-       g_return_if_fail (hash != NULL);
-
-       g_hash_table_foreach_remove (hash, free_header_list, NULL);
+       g_free (header_name);
 }
 
-/**
- * soup_message_remove_header:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- * @name: the header name to remove
- *
- * Removes @name from @hash. If there are multiple values for @name,
- * they are all removed.
- **/
-void
-soup_message_remove_header (GHashTable *hash, const char *name)
+static void
+header_handler_metamarshal (GClosure *closure, GValue *return_value,
+                           guint n_param_values, const GValue *param_values,
+                           gpointer invocation_hint, gpointer marshal_data)
 {
-       gpointer old_key, old_vals;
+       SoupMessage *msg = g_value_get_object (&param_values[0]);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       const char *header_name = marshal_data;
+       SoupMessageHeaders *hdrs;
 
-       g_return_if_fail (hash != NULL);
-       g_return_if_fail (name != NULL || name[0] != '\0');
+       if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+               return;
 
-       if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
-               g_hash_table_remove (hash, name);
-               free_header_list (old_key, old_vals, NULL);
+       /* If status_code is SOUP_STATUS_NONE, we're still processing
+        * the request side; if it's not, we're processing the
+        * response side.
+        */
+       hdrs = (msg->status_code == SOUP_STATUS_NONE) ?
+               msg->request_headers : msg->response_headers;
+
+       if (soup_message_headers_get (hdrs, header_name)) {
+               closure->marshal (closure, return_value, n_param_values,
+                                 param_values, invocation_hint,
+                                 ((GCClosure *)closure)->callback);
        }
 }
 
 /**
- * soup_message_add_header:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- * @name: the header name to add
- * @value: the value of the new header
+ * soup_message_add_header_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @header: HTTP response header to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
  *
- * Adds a header with name @name and value @value to @hash. If there
- * was already a header with name @name, this one does not replace it,
- * it is merely added to it.
- **/
-void
-soup_message_add_header (GHashTable *hash, const char *name, const char *value)
-{
-       GSList *old_value;
-
-       g_return_if_fail (hash != NULL);
-       g_return_if_fail (name != NULL || name [0] != '\0');
-       g_return_if_fail (value != NULL);
-
-       old_value = g_hash_table_lookup (hash, name);
-
-       if (old_value)
-               old_value = g_slist_append (old_value, g_strdup (value));
-       else {
-               g_hash_table_insert (hash, g_strdup (name),
-                                    g_slist_append (NULL, g_strdup (value)));
-       }
-}
-
-/**
- * soup_message_get_header:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- * @name: header name.
- * 
- * Finds the first header in @hash with name @name.
- * 
- * Return value: the header's value or %NULL if not found.
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect(), but with two differences: the @callback will
+ * only be run if @msg has a header named @header, and it will only be
+ * run if no earlier handler cancelled or requeued the message.
+ *
+ * If @signal is one of the "got" signals (eg, "got_headers"), or
+ * "finished" or "restarted", then @header is matched against the
+ * incoming message headers (that is, the #request_headers for a
+ * client #SoupMessage, or the #response_headers for a server
+ * #SoupMessage). If @signal is one of the "wrote" signals, then
+ * @header is matched against the outgoing message headers.
+ *
+ * Return value: the handler ID from g_signal_connect()
  **/
-const char *
-soup_message_get_header (GHashTable *hash, const char *name)
+guint
+soup_message_add_header_handler (SoupMessage *msg,
+                                const char  *signal,
+                                const char  *header,
+                                GCallback    callback,
+                                gpointer     user_data)
 {
-       GSList *vals;
+       SoupMessagePrivate *priv;
+       GClosure *closure;
+       char *header_name;
 
-       g_return_val_if_fail (hash != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+       g_return_val_if_fail (signal != NULL, 0);
+       g_return_val_if_fail (header != NULL, 0);
+       g_return_val_if_fail (callback != NULL, 0);
 
-       vals = g_hash_table_lookup (hash, name);
-       if (vals)
-               return vals->data;
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       return NULL;
-}
+       closure = g_cclosure_new (callback, user_data, NULL);
 
-/**
- * soup_message_get_header_list:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- * @name: header name.
- * 
- * Finds all headers in @hash with name @name.
- * 
- * Return value: a (possibly empty) list of values of headers with
- * name @name. The caller should not modify or free this list.
- **/
-const GSList *
-soup_message_get_header_list (GHashTable *hash, const char *name)
-{
-       g_return_val_if_fail (hash != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
+       header_name = g_strdup (header);
+       g_closure_set_meta_marshal (closure, header_name,
+                                   header_handler_metamarshal);
+       g_closure_add_finalize_notifier (closure, header_name,
+                                        header_handler_free);
 
-       return g_hash_table_lookup (hash, name);
+       return g_signal_connect_closure (msg, signal, closure, FALSE);
 }
 
-typedef struct {
-       GHFunc   func;
-       gpointer user_data;
-} SoupMessageForeachHeaderData;
-
 static void
-foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
+status_handler_metamarshal (GClosure *closure, GValue *return_value,
+                           guint n_param_values, const GValue *param_values,
+                           gpointer invocation_hint, gpointer marshal_data)
 {
-       GSList *vals = value;
-       SoupMessageForeachHeaderData *data = user_data;
+       SoupMessage *msg = g_value_get_object (&param_values[0]);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       guint status = GPOINTER_TO_UINT (marshal_data);
+
+       if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+               return;
 
-       while (vals) {
-               (*data->func) (name, vals->data, data->user_data);
-               vals = vals->next;
+       if (msg->status_code == status) {
+               closure->marshal (closure, return_value, n_param_values,
+                                 param_values, invocation_hint,
+                                 ((GCClosure *)closure)->callback);
        }
 }
 
 /**
- * soup_message_foreach_header:
- * @hash: a header table (the %request_headers or %response_headers
- * field of a #SoupMessage)
- * @func: callback function to run for each header
- * @user_data: data to pass to @func
- * 
- * Calls @func once for each header value in @hash. (If there are
- * headers will multiple values, @func will be called once on each
- * value.)
+ * soup_message_add_status_code_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @status_code: status code to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
+ *
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect() but with two differences: the @callback will
+ * only be run if @msg has the status @status_code, and it will only
+ * be run if no earlier handler cancelled or requeued the message.
+ *
+ * @signal must be a signal that will be emitted after @msg's status
+ * is set. For a client #SoupMessage, this means it can't be a "wrote"
+ * signal. For a server #SoupMessage, this means it can't be a "got"
+ * signal.
+ *
+ * Return value: the handler ID from g_signal_connect()
  **/
-void
-soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
+guint
+soup_message_add_status_code_handler (SoupMessage *msg,
+                                     const char  *signal,
+                                     guint        status_code,
+                                     GCallback    callback,
+                                     gpointer     user_data)
 {
-       SoupMessageForeachHeaderData data;
+       GClosure *closure;
 
-       g_return_if_fail (hash != NULL);
-       g_return_if_fail (func != NULL);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+       g_return_val_if_fail (signal != NULL, 0);
+       g_return_val_if_fail (callback != NULL, 0);
+
+       closure = g_cclosure_new (callback, user_data, NULL);
+       g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (status_code),
+                                   status_handler_metamarshal);
 
-       data.func = func;
-       data.user_data = user_data;
-       g_hash_table_foreach (hash, foreach_value_in_list, &data);
+       return g_signal_connect_closure (msg, signal, closure, FALSE);
 }
 
+
 /**
  * soup_message_set_auth:
  * @msg: a #SoupMessage
@@ -751,7 +916,8 @@ soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
 
        if (priv->auth) {
                g_object_unref (priv->auth);
-               soup_message_remove_header (msg->request_headers, "Authorization");
+               soup_message_headers_remove (msg->request_headers,
+                                            "Authorization");
        }
        priv->auth = auth;
        if (!priv->auth)
@@ -759,7 +925,8 @@ soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
 
        g_object_ref (priv->auth);
        token = soup_auth_get_authorization (auth, msg);
-       soup_message_add_header (msg->request_headers, "Authorization", token);
+       soup_message_headers_append (msg->request_headers,
+                                    "Authorization", token);
        g_free (token);
 }
 
@@ -803,8 +970,8 @@ soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
 
        if (priv->proxy_auth) {
                g_object_unref (priv->proxy_auth);
-               soup_message_remove_header (msg->request_headers,
-                                           "Proxy-Authorization");
+               soup_message_headers_remove (msg->request_headers,
+                                            "Proxy-Authorization");
        }
        priv->proxy_auth = auth;
        if (!priv->proxy_auth)
@@ -812,8 +979,8 @@ soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
 
        g_object_ref (priv->proxy_auth);
        token = soup_auth_get_authorization (auth, msg);
-       soup_message_add_header (msg->request_headers,
-                                "Proxy-Authorization", token);
+       soup_message_headers_append (msg->request_headers,
+                                    "Proxy-Authorization", token);
        g_free (token);
 }
 
@@ -845,25 +1012,33 @@ soup_message_get_proxy_auth (SoupMessage *msg)
 void
 soup_message_cleanup_response (SoupMessage *req)
 {
-       if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (req->response.body);
-
-       req->response.owner = 0;
-       req->response.body = NULL;
-       req->response.length = 0;
-
-       free_chunks (req);
-
-       soup_message_clear_headers (req->response_headers);
+       soup_message_body_truncate (req->response_body);
+       soup_message_headers_clear (req->response_headers);
 
        req->status_code = SOUP_STATUS_NONE;
        if (req->reason_phrase) {
                g_free ((char *) req->reason_phrase);
                req->reason_phrase = NULL;
        }
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
 }
 
 /**
+ * SoupMessageFlags:
+ * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
+ * (3xx) responses received by this message.
+ * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Each chunk of the response will be
+ * freed after its corresponding %got_chunk signal is emitted, meaning
+ * %response will still be empty after the message is complete. You
+ * can use this to save memory if you expect the response to be large
+ * and you are able to process it a chunk at a time.
+ *
+ * Various flags that can be set on a #SoupMessage to alter its
+ * behavior.
+ **/
+
+/**
  * soup_message_set_flags:
  * @msg: a #SoupMessage
  * @flags: a set of #SoupMessageFlags values
@@ -871,11 +1046,12 @@ soup_message_cleanup_response (SoupMessage *req)
  * Sets the specified flags on @msg.
  **/
 void
-soup_message_set_flags (SoupMessage *msg, guint flags)
+soup_message_set_flags (SoupMessage *msg, SoupMessageFlags flags)
 {
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
        SOUP_MESSAGE_GET_PRIVATE (msg)->msg_flags = flags;
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FLAGS);
 }
 
 /**
@@ -886,7 +1062,7 @@ soup_message_set_flags (SoupMessage *msg, guint flags)
  *
  * Return value: the flags
  **/
-guint
+SoupMessageFlags
 soup_message_get_flags (SoupMessage *msg)
 {
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
@@ -895,6 +1071,14 @@ soup_message_get_flags (SoupMessage *msg)
 }
 
 /**
+ * SoupHTTPVersion:
+ * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
+ * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
+ *
+ * Indicates the HTTP protocol version being used.
+ **/
+
+/**
  * soup_message_set_http_version:
  * @msg: a #SoupMessage
  * @version: the HTTP version
@@ -904,11 +1088,12 @@ soup_message_get_flags (SoupMessage *msg)
  * functionality from being used.
  **/
 void
-soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
+soup_message_set_http_version (SoupMessage *msg, SoupHTTPVersion version)
 {
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
        SOUP_MESSAGE_GET_PRIVATE (msg)->http_version = version;
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
 }
 
 /**
@@ -920,7 +1105,7 @@ soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
  *
  * Return value: the HTTP version
  **/
-SoupHttpVersion
+SoupHTTPVersion
 soup_message_get_http_version (SoupMessage *msg)
 {
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
@@ -933,7 +1118,8 @@ soup_message_get_http_version (SoupMessage *msg)
  * @msg: a #SoupMessage
  *
  * Determines whether or not @msg's connection can be kept alive for
- * further requests after processing @msg.
+ * further requests after processing @msg, based on the HTTP version,
+ * Connection header, etc.
  *
  * Return value: %TRUE or %FALSE.
  **/
@@ -942,11 +1128,11 @@ soup_message_is_keepalive (SoupMessage *msg)
 {
        const char *c_conn, *s_conn;
 
-       c_conn = soup_message_get_header (msg->request_headers, "Connection");
-       s_conn = soup_message_get_header (msg->response_headers, "Connection");
+       c_conn = soup_message_headers_get (msg->request_headers, "Connection");
+       s_conn = soup_message_headers_get (msg->response_headers, "Connection");
 
        if (msg->status_code == SOUP_STATUS_OK &&
-           soup_method_get_id (msg->method) == SOUP_METHOD_ID_CONNECT)
+           msg->method == SOUP_METHOD_CONNECT)
                return TRUE;
 
        if (SOUP_MESSAGE_GET_PRIVATE (msg)->http_version == SOUP_HTTP_1_0) {
@@ -956,20 +1142,20 @@ soup_message_is_keepalive (SoupMessage *msg)
 
                if (!c_conn || !s_conn)
                        return FALSE;
-               if (g_ascii_strcasecmp (c_conn, "Keep-Alive") != 0 ||
-                   g_ascii_strcasecmp (s_conn, "Keep-Alive") != 0)
+               if (soup_header_contains (c_conn, "Keep-Alive") ||
+                   soup_header_contains (s_conn, "Keep-Alive"))
                        return FALSE;
 
                return TRUE;
        } else {
                /* Normally persistent unless either side requested otherwise */
-               if (c_conn && g_ascii_strcasecmp (c_conn, "close") == 0)
+               if (c_conn && soup_header_contains (c_conn, "close"))
                        return FALSE;
-               if (s_conn && g_ascii_strcasecmp (s_conn, "close") == 0)
+               if (s_conn && soup_header_contains (s_conn, "close"))
                        return FALSE;
 
                /* But not if the server sent a terminate-by-EOF response */
-               if (soup_message_get_response_encoding (msg, NULL) == SOUP_TRANSFER_EOF)
+               if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
                        return FALSE;
 
                return TRUE;
@@ -979,14 +1165,14 @@ soup_message_is_keepalive (SoupMessage *msg)
 /**
  * soup_message_set_uri:
  * @msg: a #SoupMessage
- * @uri: the new #SoupUri
+ * @uri: the new #SoupURI
  *
  * Sets @msg's URI to @uri. If @msg has already been sent and you want
  * to re-send it with the new URI, you need to call
  * soup_session_requeue_message().
  **/
 void
-soup_message_set_uri (SoupMessage *msg, const SoupUri *uri)
+soup_message_set_uri (SoupMessage *msg, SoupURI *uri)
 {
        SoupMessagePrivate *priv;
 
@@ -996,6 +1182,8 @@ soup_message_set_uri (SoupMessage *msg, const SoupUri *uri)
        if (priv->uri)
                soup_uri_free (priv->uri);
        priv->uri = soup_uri_copy (uri);
+
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_URI);
 }
 
 /**
@@ -1006,7 +1194,7 @@ soup_message_set_uri (SoupMessage *msg, const SoupUri *uri)
  *
  * Return value: the URI @msg is targeted for.
  **/
-const SoupUri *
+SoupURI *
 soup_message_get_uri (SoupMessage *msg)
 {
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
@@ -1015,129 +1203,6 @@ soup_message_get_uri (SoupMessage *msg)
 }
 
 /**
- * soup_message_get_request_encoding:
- * @msg: a #SoupMessage
- * @content_length: a pointer to store the Content-Length in (or
- * %NULL).
- *
- * Gets @msg's request encoding. For an outgoing (client) request,
- * this is only valid after the message has been fully set up (from
- * the library's perspective, that means not until the message has
- * been queued). For an incoming (server) request, this is valid after
- * the request headers have been read and @msg->request_headers filled
- * in.
- *
- * Return value: the request encoding (which cannot be
- * %SOUP_TRANSFER_UNKNOWN or %SOUP_TRANSFER_EOF). If it is
- * %SOUP_TRANSFER_CONTENT_LENGTH, *@content_length will be set to the
- * request body's length.
- **/
-SoupTransferEncoding
-soup_message_get_request_encoding  (SoupMessage *msg, guint *content_length)
-{
-       if (SOUP_IS_SERVER_MESSAGE (msg)) {
-               const char *enc, *len;
-
-               enc = soup_message_get_header (msg->request_headers,
-                                              "Transfer-Encoding");
-               len = soup_message_get_header (msg->request_headers,
-                                              "Content-Length");
-               if (enc) {
-                       if (g_ascii_strcasecmp (enc, "chunked") == 0)
-                               return SOUP_TRANSFER_CHUNKED;
-                       else
-                               return SOUP_TRANSFER_UNKNOWN;
-               } else if (len) {
-                       int lval = atoi (len);
-
-                       if (lval < 0)
-                               return SOUP_TRANSFER_UNKNOWN;
-                       else {
-                               if (content_length)
-                                       *content_length = lval;
-                               return SOUP_TRANSFER_CONTENT_LENGTH;
-                       }
-               } else
-                       return SOUP_TRANSFER_NONE;
-       } else {
-               if (msg->request.length) {
-                       if (content_length)
-                               *content_length = msg->request.length;
-                       return SOUP_TRANSFER_CONTENT_LENGTH;
-               } else
-                       return SOUP_TRANSFER_NONE;
-       }
-}
-
-/**
- * soup_message_get_response_encoding:
- * @msg: a #SoupMessage
- * @content_length: a pointer to store the Content-Length in (or
- * %NULL).
- *
- * Gets @msg's response encoding. For an outgoing (client) request,
- * this is only valid after the response headers have been read and
- * @msg->response_headers filled in. For an incoming (server) request,
- * this is valid after the server handler has run.
- *
- * Note that the returned value is the encoding actually used on the
- * wire; this will not agree with the response headers in some cases
- * (eg, a HEAD response may have a Content-Length header, but will
- * still be considered %SOUP_TRANSFER_NONE by this function).
- *
- * Return value: the response encoding (which will not be
- * %SOUP_TRANSFER_UNKNOWN). If it is %SOUP_TRANSFER_CONTENT_LENGTH,
- * *@content_length will be set to the response body's length.
- **/
-SoupTransferEncoding
-soup_message_get_response_encoding (SoupMessage *msg, guint *content_length)
-{
-       SoupMethodId method = soup_method_get_id (msg->method);
-
-       if (method == SOUP_METHOD_ID_HEAD ||
-           msg->status_code  == SOUP_STATUS_NO_CONTENT ||
-           msg->status_code  == SOUP_STATUS_NOT_MODIFIED ||
-           SOUP_STATUS_IS_INFORMATIONAL (msg->status_code))
-               return SOUP_TRANSFER_NONE;
-
-       if (SOUP_IS_SERVER_MESSAGE (msg)) {
-               SoupTransferEncoding enc =
-                       soup_server_message_get_encoding ((SoupServerMessage *)msg);
-               if (enc == SOUP_TRANSFER_UNKNOWN)
-                       enc = SOUP_TRANSFER_CONTENT_LENGTH;
-               if (enc == SOUP_TRANSFER_CONTENT_LENGTH && content_length)
-                       *content_length = msg->response.length;
-               return enc;
-       } else {
-               const char *enc, *len;
-
-               enc = soup_message_get_header (msg->response_headers,
-                                              "Transfer-Encoding");
-               len = soup_message_get_header (msg->response_headers,
-                                              "Content-Length");
-               if (enc) {
-                       if (g_ascii_strcasecmp (enc, "chunked") == 0)
-                               return SOUP_TRANSFER_CHUNKED;
-                       else
-                               return SOUP_TRANSFER_UNKNOWN;
-               } else if (len) {
-                       int lval = atoi (len);
-
-                       if (lval < 0)
-                               return SOUP_TRANSFER_UNKNOWN;
-                       else {
-                               if (content_length)
-                                       *content_length = lval;
-                               return SOUP_TRANSFER_CONTENT_LENGTH;
-                       }
-               } else if (method == SOUP_METHOD_ID_CONNECT)
-                       return SOUP_TRANSFER_NONE;
-               else
-                       return SOUP_TRANSFER_EOF;
-       }
-}
-
-/**
  * soup_message_set_status:
  * @msg: a #SoupMessage
  * @status_code: an HTTP status code
@@ -1155,6 +1220,8 @@ soup_message_set_status (SoupMessage *msg, guint status_code)
 
        msg->status_code = status_code;
        msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
 }
 
 /**
@@ -1178,110 +1245,23 @@ soup_message_set_status_full (SoupMessage *msg,
 
        msg->status_code = status_code;
        msg->reason_phrase = g_strdup (reason_phrase);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
 }
 
-
-/**
- * soup_message_add_chunk:
- * @msg: a #SoupMessage
- * @owner: the ownership of @body
- * @body: body data
- * @length: length of @body
- *
- * Adds a chunk of response data to @body. (Note that currently
- * there is no way to send a request using chunked encoding.)
- **/
-void
-soup_message_add_chunk (SoupMessage   *msg,
-                       SoupOwnership  owner,
-                       const char    *body,
-                       guint          length)
-{
-       SoupMessagePrivate *priv;
-       SoupDataBuffer *chunk;
-
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-       g_return_if_fail (body != NULL || length == 0);
-
-       chunk = g_new0 (SoupDataBuffer, 1);
-       if (owner == SOUP_BUFFER_USER_OWNED) {
-               chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
-               chunk->body = g_memdup (body, length);
-       } else {
-               chunk->owner = owner;
-               chunk->body = (char *)body;
-       }
-       chunk->length = length;
-
-       if (priv->chunks) {
-               priv->last_chunk = g_slist_append (priv->last_chunk, chunk);
-               priv->last_chunk = priv->last_chunk->next;
-       } else {
-               priv->chunks = priv->last_chunk =
-                       g_slist_append (NULL, chunk);
-       }
-}
-
-/**
- * soup_message_add_final_chunk:
- * @msg: a #SoupMessage
- *
- * Adds a final, empty chunk of response data to @body. This must
- * be called after adding the last real chunk, to indicate that
- * there is no more data.
- **/
 void
-soup_message_add_final_chunk (SoupMessage *msg)
-{
-       soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
-}
-
-/**
- * soup_message_pop_chunk:
- * @msg: a #SoupMessage
- *
- * Pops a chunk of response data from @msg's chunk list. The caller
- * must free @chunk itself, and must handle the data in @chunk
- * according to its %ownership.
- *
- * Return value: the chunk, or %NULL if there are no chunks left.
- **/
-SoupDataBuffer *
-soup_message_pop_chunk (SoupMessage *msg)
+soup_message_set_io_status (SoupMessage          *msg,
+                           SoupMessageIOStatus   status)
 {
-       SoupMessagePrivate *priv;
-       SoupDataBuffer *chunk;
-
-       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
-       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-
-       if (!priv->chunks)
-               return NULL;
-
-       chunk = priv->chunks->data;
-       priv->chunks = g_slist_remove (priv->chunks, chunk);
-       if (!priv->chunks)
-               priv->last_chunk = NULL;
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       return chunk;
+       priv->io_status = status;
 }
 
-static void
-free_chunks (SoupMessage *msg)
+SoupMessageIOStatus
+soup_message_get_io_status (SoupMessage *msg)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
-       SoupDataBuffer *chunk;
-       GSList *ch;
-
-       for (ch = priv->chunks; ch; ch = ch->next) {
-               chunk = ch->data;
-
-               if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
-                       g_free (chunk->body);
-               g_free (chunk);
-       }
 
-       g_slist_free (priv->chunks);
-       priv->chunks = priv->last_chunk = NULL;
+       return priv->io_status;
 }
index e048a4a..6f38a31 100644 (file)
@@ -7,6 +7,8 @@
 #define SOUP_MESSAGE_H 1
 
 #include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+#include <libsoup/soup-message-headers.h>
 #include <libsoup/soup-method.h>
 
 G_BEGIN_DECLS
@@ -18,106 +20,6 @@ G_BEGIN_DECLS
 #define SOUP_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_MESSAGE))
 #define SOUP_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_MESSAGE, SoupMessageClass))
 
-/**
- * SoupMessageStatus:
- * @SOUP_MESSAGE_STATUS_IDLE: The message has not yet been queued.
- * @SOUP_MESSAGE_STATUS_QUEUED: The message has been queued, but is
- * waiting for a connection to be available.
- * @SOUP_MESSAGE_STATUS_CONNECTING: The message is waiting for a
- * specific connection to finish connecting.
- * @SOUP_MESSAGE_STATUS_RUNNING: The message is being processed.
- * @SOUP_MESSAGE_STATUS_FINISHED: The message is complete (request and
- * response both processed).
- *
- * Enum indicating the lifecycle of a #SoupMessage.
- **/
-typedef enum {
-       SOUP_MESSAGE_STATUS_IDLE,
-       SOUP_MESSAGE_STATUS_QUEUED,
-        SOUP_MESSAGE_STATUS_CONNECTING,
-        SOUP_MESSAGE_STATUS_RUNNING,
-       SOUP_MESSAGE_STATUS_FINISHED
-} SoupMessageStatus;
-
-/**
- * SOUP_MESSAGE_IS_STARTING:
- * @msg: a #SoupMessage
- *
- * Tests if @msg is in a "starting" state, waiting to be sent. (More
- * commonly used to test if a message has been requeued after its
- * first attempt.)
- *
- * Return value: %TRUE if @msg is waiting to be sent.
- **/
-#define SOUP_MESSAGE_IS_STARTING(msg) (msg->status == SOUP_MESSAGE_STATUS_QUEUED || msg->status == SOUP_MESSAGE_STATUS_CONNECTING)
-
-/**
- * SoupTransferEncoding:
- * @SOUP_TRANSFER_UNKNOWN: unknown / error
- * @SOUP_TRANSFER_CHUNKED: chunked encoding (currently only supported
- * for response)
- * @SOUP_TRANSFER_CONTENT_LENGTH: Content-Length encoding
- * @SOUP_TRANSFER_BYTERANGES: multipart/byteranges (Reserved for future
- * use: NOT CURRENTLY IMPLEMENTED)
- * @SOUP_TRANSFER_NONE: no body is present (which is not the same as a
- * 0-length body, and only occurs in certain places)
- * @SOUP_TRANSFER_EOF: Response body ends when the connection is closed
- *
- * How the length of a request or response is to be encoded.
- **/
-typedef enum {
-       SOUP_TRANSFER_UNKNOWN = 0,
-       SOUP_TRANSFER_CHUNKED,
-       SOUP_TRANSFER_CONTENT_LENGTH,
-       SOUP_TRANSFER_BYTERANGES,
-       SOUP_TRANSFER_NONE,
-       SOUP_TRANSFER_EOF
-} SoupTransferEncoding;
-
-/**
- * SoupOwnership:
- * @SOUP_BUFFER_SYSTEM_OWNED: The data is owned by soup and it can
- * free it when it is done with it.
- * @SOUP_BUFFER_USER_OWNED: The data is owned by the user, who is
- * responsible for freeing it at the right point
- * @SOUP_BUFFER_STATIC: The data should not be freed.
- *
- * Used by #SoupDataBuffer (and several functions) to indicate the
- * ownership of a buffer.
- **/
-typedef enum {
-       SOUP_BUFFER_SYSTEM_OWNED = 0,
-       SOUP_BUFFER_USER_OWNED,
-       SOUP_BUFFER_STATIC
-} SoupOwnership;
-
-/**
- * SoupDataBuffer:
- * @owner: the ownership of the data
- * @body: the data itself
- * @length: length of @body
- *
- * A data buffer used in several places.
- **/
-typedef struct {
-       SoupOwnership  owner;
-       char          *body;
-       guint          length;
-} SoupDataBuffer;
-
-/**
- * SoupMessage:
- * @method: the HTTP method
- * @status_code: the HTTP status code
- * @reason_phrase: the status phrase associated with @status_code
- * @request: the request buffer
- * @request_headers: the request headers
- * @response: the response buffer
- * @response_headers: the response headers
- * @status: the processing status of the message
- *
- * Represents an HTTP message being sent or received.
- **/
 struct SoupMessage {
        GObject parent;
 
@@ -127,13 +29,11 @@ struct SoupMessage {
        guint               status_code;
        const char         *reason_phrase;
 
-       SoupDataBuffer      request;
-       GHashTable         *request_headers;
+       SoupMessageBody    *request_body;
+       SoupMessageHeaders *request_headers;
 
-       SoupDataBuffer      response;
-       GHashTable         *response_headers;
-
-       SoupMessageStatus   status;
+       SoupMessageBody    *response_body;
+       SoupMessageHeaders *response_headers;
 };
 
 typedef struct {
@@ -146,167 +46,80 @@ typedef struct {
        void     (*wrote_body)          (SoupMessage *msg);
        void     (*got_informational)   (SoupMessage *msg);
        void     (*got_headers)         (SoupMessage *msg);
-       void     (*got_chunk)           (SoupMessage *msg);
+       void     (*got_chunk)           (SoupMessage *msg, SoupBuffer *chunk);
        void     (*got_body)            (SoupMessage *msg);
        void     (*restarted)           (SoupMessage *msg);
        void     (*finished)            (SoupMessage *msg);
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupMessageClass;
 
 GType soup_message_get_type (void);
 
-/**
- * SoupMessageCallbackFn:
- * @req: the #SoupMessage in question
- * @user_data: user data
- *
- * A callback function used by many #SoupMessage methods.
- **/
-typedef void (*SoupMessageCallbackFn) (SoupMessage *req, gpointer user_data);
+#define SOUP_MESSAGE_METHOD        "method"
+#define SOUP_MESSAGE_URI           "uri"
+#define SOUP_MESSAGE_HTTP_VERSION  "http-version"
+#define SOUP_MESSAGE_FLAGS         "flags"
+#define SOUP_MESSAGE_STATUS_CODE   "status-code"
+#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
 
 SoupMessage   *soup_message_new                 (const char        *method,
                                                 const char        *uri_string);
 SoupMessage   *soup_message_new_from_uri        (const char        *method,
-                                                const SoupUri     *uri);
+                                                SoupURI           *uri);
 
 void           soup_message_set_request         (SoupMessage       *msg,
                                                 const char        *content_type,
-                                                SoupOwnership      req_owner,
-                                                char              *req_body,
-                                                gulong             req_length);
-
+                                                SoupMemoryUse      req_use,
+                                                const char        *req_body,
+                                                gsize              req_length);
 void           soup_message_set_response        (SoupMessage       *msg,
                                                 const char        *content_type,
-                                                SoupOwnership      resp_owner,
-                                                char              *resp_body,
-                                                gulong             resp_length);
-
-void           soup_message_add_header          (GHashTable        *hash,
-                                                const char        *name,
-                                                const char        *value);
-
-const char    *soup_message_get_header          (GHashTable        *hash,
-                                                const char        *name);
-
-const GSList  *soup_message_get_header_list     (GHashTable        *hash,
-                                                const char        *name);
-
-void           soup_message_foreach_header      (GHashTable        *hash,
-                                                GHFunc             func,
-                                                gpointer           user_data);
-
-void           soup_message_remove_header       (GHashTable        *hash,
-                                                const char        *name);
+                                                SoupMemoryUse      resp_use,
+                                                const char        *resp_body,
+                                                gsize              resp_length);
 
-void           soup_message_clear_headers       (GHashTable        *hash);
-
-/**
- * SoupHttpVersion:
- * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
- * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
- *
- * Indicates the HTTP protocol version being used.
- **/
 typedef enum {
        SOUP_HTTP_1_0 = 0,
        SOUP_HTTP_1_1 = 1
-} SoupHttpVersion;
+} SoupHTTPVersion;
 
 void             soup_message_set_http_version    (SoupMessage       *msg,
-                                                  SoupHttpVersion    version);
-SoupHttpVersion  soup_message_get_http_version    (SoupMessage       *msg);
+                                                  SoupHTTPVersion    version);
+SoupHTTPVersion  soup_message_get_http_version    (SoupMessage       *msg);
 
 gboolean         soup_message_is_keepalive        (SoupMessage       *msg);
 
-const SoupUri   *soup_message_get_uri             (SoupMessage       *msg);
+SoupURI         *soup_message_get_uri             (SoupMessage       *msg);
 void             soup_message_set_uri             (SoupMessage       *msg,
-                                                  const SoupUri     *uri);
-
-SoupTransferEncoding soup_message_get_request_encoding  (SoupMessage *msg,
-                                                        guint       *content_length);
-SoupTransferEncoding soup_message_get_response_encoding (SoupMessage *msg,
-                                                        guint       *content_length);
+                                                  SoupURI           *uri);
 
-/**
- * SoupMessageFlags:
- * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
- * (3xx) responses received by this message.
- * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Rather than building up the
- * response body in %response, each new chunk should overwrite the
- * previous one. (This can be used if you are connecting to the
- * %got_chunk signal or have installed a %SOUP_MESSAGE_BODY_CHUNK
- * handler.
- * @SOUP_MESSAGE_EXPECT_CONTINUE: This will cause an "Expect:
- * 100-continue" header to be added to the outgoing request, giving
- * the server the opportunity to reject the message (eg, with a 401
- * Unauthorized) before the full request body is sent.
- *
- * Various flags that can be set on a #SoupMessage to alter its
- * behavior.
- **/
 typedef enum {
        SOUP_MESSAGE_NO_REDIRECT      = (1 << 1),
        SOUP_MESSAGE_OVERWRITE_CHUNKS = (1 << 3),
-       SOUP_MESSAGE_EXPECT_CONTINUE  = (1 << 4)
 } SoupMessageFlags;
 
 void           soup_message_set_flags           (SoupMessage        *msg,
-                                                guint               flags);
-
-guint          soup_message_get_flags           (SoupMessage        *msg);
+                                                SoupMessageFlags    flags);
 
-/*
- * Handler Registration 
- */
-
-/**
- * SoupHandlerPhase:
- * @SOUP_HANDLER_POST_REQUEST: The handler should run immediately
- * after sending the request body
- * @SOUP_HANDLER_PRE_BODY: The handler should run before reading the
- * response body (after reading the headers).
- * @SOUP_HANDLER_BODY_CHUNK: The handler should run after every body
- * chunk is read. (See also %SOUP_MESSAGE_OVERWRITE_CHUNKS.)
- * @SOUP_HANDLER_POST_BODY: The handler should run after the entire
- * message body has been read.
- *
- * Indicates when a handler added with soup_message_add_handler() or
- * the like will be run.
- **/
-typedef enum {
-       SOUP_HANDLER_POST_REQUEST = 1,
-       SOUP_HANDLER_PRE_BODY,
-       SOUP_HANDLER_BODY_CHUNK,
-       SOUP_HANDLER_POST_BODY
-} SoupHandlerPhase;
-
-void           soup_message_add_handler         (SoupMessage       *msg,
-                                                SoupHandlerPhase   phase,
-                                                SoupMessageCallbackFn     handler_cb,
-                                                gpointer           user_data);
+SoupMessageFlags soup_message_get_flags         (SoupMessage        *msg);
 
-void           soup_message_add_header_handler  (SoupMessage       *msg,
+/* Specialized signal handlers */
+guint          soup_message_add_header_handler  (SoupMessage       *msg,
+                                                const char        *signal,
                                                 const char        *header,
-                                                SoupHandlerPhase   phase,
-                                                SoupMessageCallbackFn handler_cb,
+                                                GCallback          callback,
                                                 gpointer           user_data);
 
-void           soup_message_add_status_code_handler (
+guint          soup_message_add_status_code_handler (
                                                 SoupMessage       *msg,
+                                                const char        *signal,
                                                 guint              status_code,
-                                                SoupHandlerPhase   phase,
-                                                SoupMessageCallbackFn handler_cb,
-                                                gpointer           user_data);
-
-void           soup_message_add_status_class_handler (
-                                                SoupMessage       *msg,
-                                                SoupStatusClass    status_class,
-                                                SoupHandlerPhase   phase,
-                                                SoupMessageCallbackFn handler_cb,
-                                                gpointer           user_data);
-
-void           soup_message_remove_handler      (SoupMessage       *msg, 
-                                                SoupHandlerPhase   phase,
-                                                SoupMessageCallbackFn handler_cb,
+                                                GCallback          callback,
                                                 gpointer           user_data);
 
 /*
@@ -320,34 +133,13 @@ void           soup_message_set_status_full     (SoupMessage       *msg,
                                                 const char        *reason_phrase);
 
 
-/* Chunked encoding */
-void           soup_message_add_chunk           (SoupMessage       *msg,
-                                                SoupOwnership      owner,
-                                                const char        *body,
-                                                guint              length);
-void           soup_message_add_final_chunk     (SoupMessage       *msg);
-
-SoupDataBuffer*soup_message_pop_chunk           (SoupMessage       *msg);
-
-
-/* I/O */
-void           soup_message_send_request        (SoupMessage       *req,
-                                                SoupSocket        *sock,
-                                                gboolean           is_via_proxy);
-void           soup_message_read_request        (SoupMessage       *req,
-                                                SoupSocket        *sock);
-void           soup_message_io_stop             (SoupMessage       *msg);
-void           soup_message_io_pause            (SoupMessage       *msg);
-void           soup_message_io_unpause          (SoupMessage       *msg);
-gboolean       soup_message_io_in_progress      (SoupMessage       *msg);
-
 void soup_message_wrote_informational (SoupMessage *msg);
 void soup_message_wrote_headers       (SoupMessage *msg);
 void soup_message_wrote_chunk         (SoupMessage *msg);
 void soup_message_wrote_body          (SoupMessage *msg);
 void soup_message_got_informational   (SoupMessage *msg);
 void soup_message_got_headers         (SoupMessage *msg);
-void soup_message_got_chunk           (SoupMessage *msg);
+void soup_message_got_chunk           (SoupMessage *msg, SoupBuffer *chunk);
 void soup_message_got_body            (SoupMessage *msg);
 void soup_message_restarted           (SoupMessage *msg);
 void soup_message_finished            (SoupMessage *msg);
diff --git a/libsoup/soup-method.c b/libsoup/soup-method.c
deleted file mode 100644 (file)
index 26f5c24..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-method.c: HTTP Method related processing.
- *
- * Copyright (C) 2001-2002, Ximian, Inc.
- */
-
-#include <glib.h>
-
-#include "soup-method.h"
-
-/**
- * soup_method_get_id:
- * @method: an HTTP method
- *
- * Converts @method into a corresponding #SoupMethodId (possibly
- * %SOUP_METHOD_ID_UNKNOWN).
- *
- * Return value: the #SoupMethodId
- **/
-SoupMethodId
-soup_method_get_id (const char *method)
-{
-       g_return_val_if_fail (method != NULL, SOUP_METHOD_ID_UNKNOWN);
-
-       switch (*method) {
-        case 'H':
-               if (g_ascii_strcasecmp (method, "HEAD") == 0)
-                       return SOUP_METHOD_ID_HEAD;
-               break;
-        case 'G':
-               if (g_ascii_strcasecmp (method, "GET") == 0)
-                       return SOUP_METHOD_ID_GET;
-               break;
-        case 'P':
-               if (g_ascii_strcasecmp (method, "POST") == 0)
-                       return SOUP_METHOD_ID_POST;
-               if (g_ascii_strcasecmp (method, "PUT") == 0)
-                       return SOUP_METHOD_ID_PUT;
-               if (g_ascii_strcasecmp (method, "PATCH") == 0)
-                       return SOUP_METHOD_ID_PATCH;
-               if (g_ascii_strcasecmp (method, "PROPFIND") == 0)
-                       return SOUP_METHOD_ID_PROPFIND;
-               if (g_ascii_strcasecmp (method, "PROPPATCH") == 0)
-                       return SOUP_METHOD_ID_PROPPATCH;
-               break;
-        case 'D':
-               if (g_ascii_strcasecmp (method, "DELETE") == 0)
-                       return SOUP_METHOD_ID_DELETE;
-               break;
-        case 'C':
-               if (g_ascii_strcasecmp (method, "CONNECT") == 0)
-                       return SOUP_METHOD_ID_CONNECT;
-               if (g_ascii_strcasecmp (method, "COPY") == 0)
-                       return SOUP_METHOD_ID_COPY;
-               break;
-        case 'M':
-               if (g_ascii_strcasecmp (method, "MKCOL") == 0)
-                       return SOUP_METHOD_ID_MKCOL;
-               if (g_ascii_strcasecmp (method, "MOVE") == 0)
-                       return SOUP_METHOD_ID_MOVE;
-               break;
-        case 'O':
-               if (g_ascii_strcasecmp (method, "OPTIONS") == 0)
-                       return SOUP_METHOD_ID_OPTIONS;
-               break;
-        case 'T':
-               if (g_ascii_strcasecmp (method, "TRACE") == 0)
-                       return SOUP_METHOD_ID_TRACE;
-               break;
-        case 'L':
-               if (g_ascii_strcasecmp (method, "LOCK") == 0)
-                       return SOUP_METHOD_ID_LOCK;
-               break;
-        case 'U':
-               if (g_ascii_strcasecmp (method, "UNLOCK") == 0)
-                       return SOUP_METHOD_ID_UNLOCK;
-               break;
-       }
-
-       return SOUP_METHOD_ID_UNKNOWN;
-}
-
index 4ef69bf..861f7ef 100644 (file)
@@ -6,48 +6,47 @@
 #ifndef SOUP_METHOD_H
 #define SOUP_METHOD_H 1
 
-#include <glib/gmacros.h>
-
 G_BEGIN_DECLS
 
-#define SOUP_METHOD_POST      "POST"
-#define SOUP_METHOD_GET       "GET"
-#define SOUP_METHOD_HEAD      "HEAD"
-#define SOUP_METHOD_OPTIONS   "OPTIONS"
-#define SOUP_METHOD_PUT       "PUT"
-#define SOUP_METHOD_MOVE      "MOVE"
-#define SOUP_METHOD_COPY      "COPY"
-#define SOUP_METHOD_DELETE    "DELETE"
-#define SOUP_METHOD_TRACE     "TRACE"
-#define SOUP_METHOD_CONNECT   "CONNECT"
-#define SOUP_METHOD_MKCOL     "MKCOL"
-#define SOUP_METHOD_PROPPATCH "PROPPATCH"
-#define SOUP_METHOD_PROPFIND  "PROPFIND"
-#define SOUP_METHOD_PATCH     "PATCH"
-#define SOUP_METHOD_LOCK      "LOCK"
-#define SOUP_METHOD_UNLOCK    "UNLOCK"
-
-typedef enum {
-       SOUP_METHOD_ID_UNKNOWN = 0,
-       SOUP_METHOD_ID_POST,
-       SOUP_METHOD_ID_GET,
-       SOUP_METHOD_ID_HEAD,
-       SOUP_METHOD_ID_OPTIONS,
-       SOUP_METHOD_ID_PUT,
-       SOUP_METHOD_ID_MOVE,
-       SOUP_METHOD_ID_COPY,
-       SOUP_METHOD_ID_DELETE,
-       SOUP_METHOD_ID_TRACE,
-       SOUP_METHOD_ID_CONNECT,
-       SOUP_METHOD_ID_MKCOL,
-       SOUP_METHOD_ID_PROPPATCH,
-       SOUP_METHOD_ID_PROPFIND,
-       SOUP_METHOD_ID_PATCH,
-       SOUP_METHOD_ID_LOCK,
-       SOUP_METHOD_ID_UNLOCK
-} SoupMethodId;
-
-SoupMethodId soup_method_get_id (const char *method);
+/**
+ * SECTION:soup-method
+ * @short_description: HTTP method definitions
+ *
+ * soup-method.h contains a number of defines for standard HTTP and
+ * WebDAV headers. You do not need to use these defines; you can pass
+ * arbitrary strings to soup_message_new() if you prefer.
+ * 
+ * The thing that these defines <emphasis>are</emphasis> useful for is
+ * performing quick comparisons against #SoupMessage's %method field;
+ * because that field always contains an interned string, and these
+ * macros return interned strings, you can compare %method directly
+ * against these macros rather than needing to use strcmp(). This is
+ * most useful in SoupServer handlers. Eg:
+ * 
+ * <informalexample><programlisting>
+ *     if (msg->method != SOUP_METHOD_GET &amp;&amp; msg->method != SOUP_METHOD_HEAD) {
+ *             soup_message_set_status (msg, SOUP_METHOD_NOT_IMPLEMENTED);
+ *             return;
+ *     }
+ * </programlisting></informalexample>
+ **/
+
+#define SOUP_METHOD_POST      (g_intern_static_string ("POST"))
+#define SOUP_METHOD_GET       (g_intern_static_string ("GET"))
+#define SOUP_METHOD_HEAD      (g_intern_static_string ("HEAD"))
+#define SOUP_METHOD_OPTIONS   (g_intern_static_string ("OPTIONS"))
+#define SOUP_METHOD_PUT       (g_intern_static_string ("PUT"))
+#define SOUP_METHOD_MOVE      (g_intern_static_string ("MOVE"))
+#define SOUP_METHOD_COPY      (g_intern_static_string ("COPY"))
+#define SOUP_METHOD_DELETE    (g_intern_static_string ("DELETE"))
+#define SOUP_METHOD_TRACE     (g_intern_static_string ("TRACE"))
+#define SOUP_METHOD_CONNECT   (g_intern_static_string ("CONNECT"))
+#define SOUP_METHOD_MKCOL     (g_intern_static_string ("MKCOL"))
+#define SOUP_METHOD_PROPPATCH (g_intern_static_string ("PROPPATCH"))
+#define SOUP_METHOD_PROPFIND  (g_intern_static_string ("PROPFIND"))
+#define SOUP_METHOD_PATCH     (g_intern_static_string ("PATCH"))
+#define SOUP_METHOD_LOCK      (g_intern_static_string ("LOCK"))
+#define SOUP_METHOD_UNLOCK    (g_intern_static_string ("UNLOCK"))
 
 G_END_DECLS
 
index 252596d..79a7399 100644 (file)
 #include "soup-misc.h"
 
 /**
+ * SECTION:soup-misc
+ * @short_description: Miscellaneous functions
+ *
+ **/
+
+/**
  * soup_str_case_hash:
  * @key: ASCII string to hash
  *
@@ -50,68 +56,6 @@ soup_str_case_equal (gconstpointer v1,
        return g_ascii_strcasecmp (string1, string2) == 0;
 }
 
-int
-soup_base64_encode_close (const guchar  *in, 
-                         int            inlen, 
-                         gboolean       break_lines, 
-                         guchar        *out, 
-                         int           *state, 
-                         int           *save)
-{
-       if (inlen > 0) {
-               out += soup_base64_encode_step (in, 
-                                               inlen, 
-                                               break_lines, 
-                                               out, 
-                                               state, 
-                                               save);
-       }
-
-       return (int)g_base64_encode_close (break_lines, (char *) out,
-                                          state, save);
-}
-
-int
-soup_base64_encode_step (const guchar  *in, 
-                        int            len, 
-                        gboolean       break_lines, 
-                        guchar        *out, 
-                        int           *state, 
-                        int           *save)
-{
-       return (int)g_base64_encode_step (in, len, break_lines,
-                                         (char *)out, state, save);
-}
-
-char *
-soup_base64_encode (const char *text, int len)
-{
-       return g_base64_encode ((const guchar *)text, len);
-}
-
-int
-soup_base64_decode_step (const guchar  *in, 
-                        int            len, 
-                        guchar        *out, 
-                        int           *state, 
-                        guint         *save)
-{
-       return (int) g_base64_decode_step ((const char *)in, len,
-                                          out, state, save);
-}
-
-char *
-soup_base64_decode (const char   *text,
-                   int          *out_len)
-{
-       char *ret;
-       gsize out_len_tmp;
-
-       ret = (char *) g_base64_decode (text, &out_len_tmp);
-       *out_len = out_len_tmp;
-       return ret;
-}
-
 typedef struct {
        gpointer instance;
        guint    signal_id;
@@ -120,7 +64,7 @@ typedef struct {
 static void
 signal_once_object_destroyed (gpointer ssod, GObject *ex_object)
 {
-       g_free (ssod);
+       g_slice_free (SoupSignalOnceData, ssod);
 }
 
 static void
@@ -137,7 +81,7 @@ signal_once_metamarshal (GClosure *closure, GValue *return_value,
        if (g_signal_handler_is_connected (ssod->instance, ssod->signal_id))
                g_signal_handler_disconnect (ssod->instance, ssod->signal_id);
        g_object_weak_unref (G_OBJECT (ssod->instance), signal_once_object_destroyed, ssod);
-       g_free (ssod);
+       g_slice_free (SoupSignalOnceData, ssod);
 }
 
 /**
@@ -164,7 +108,7 @@ soup_signal_connect_once (gpointer instance, const char *detailed_signal,
        g_return_val_if_fail (detailed_signal != NULL, 0);
        g_return_val_if_fail (c_handler != NULL, 0);
 
-       ssod = g_new0 (SoupSignalOnceData, 1);
+       ssod = g_slice_new0 (SoupSignalOnceData);
        ssod->instance = instance;
        g_object_weak_ref (G_OBJECT (instance), signal_once_object_destroyed, ssod);
 
@@ -252,21 +196,3 @@ soup_add_timeout (GMainContext *async_context,
        g_source_unref (source);
        return source;
 }
-
-/**
- * soup_xml_real_node:
- * @node: an %xmlNodePtr
- *
- * Finds the first "real" node (ie, not a comment or whitespace) at or
- * after @node at its level in the tree.
- *
- * Return: a node, or %NULL
- **/
-xmlNode *
-soup_xml_real_node (xmlNode *node)
-{
-       while (node && (node->type == XML_COMMENT_NODE ||
-                       xmlIsBlankNode (node)))
-               node = node->next;
-       return node;
-}
index d88edae..3699606 100644 (file)
@@ -7,40 +7,9 @@
 #define SOUP_MISC_H 1
 
 #include <glib-object.h>
-#include <libxml/tree.h>
 
 G_BEGIN_DECLS
 
-#ifndef LIBSOUP_DISABLE_DEPRECATED
-/* Base64 encoding/decoding. DEPRECATED: use <glib/base64.h> */
-
-char              *soup_base64_encode        (const char   *text,
-                                             int           len);
-
-int                soup_base64_encode_close  (const guchar *in, 
-                                             int           inlen, 
-                                             gboolean      break_lines, 
-                                             guchar       *out, 
-                                             int          *state, 
-                                             int          *save);
-
-int                soup_base64_encode_step   (const guchar *in, 
-                                             int           len, 
-                                             gboolean      break_lines, 
-                                             guchar       *out, 
-                                             int          *state, 
-                                             int          *save);
-
-char              *soup_base64_decode        (const gchar  *text,
-                                             int          *out_len);
-
-int                soup_base64_decode_step   (const guchar *in, 
-                                             int           len, 
-                                             guchar       *out, 
-                                             int          *state, 
-                                             guint        *save);
-#endif /* LIBSOUP_DISABLE_DEPRECATED */
-
 /* Non-default-GMainContext operations */
 GSource           *soup_add_io_watch         (GMainContext *async_context,
                                              GIOChannel   *chan,
@@ -66,15 +35,18 @@ guint              soup_str_case_hash        (gconstpointer key);
 gboolean           soup_str_case_equal       (gconstpointer v1,
                                              gconstpointer v2);
 
-xmlNode           *soup_xml_real_node        (xmlNode      *node);
-
-/**
- * soup_ssl_supported:
- *
- * Can be used to test if libsoup was compiled with ssl support.
- **/
 extern gboolean soup_ssl_supported;
 
+#define SOUP_SSL_ERROR soup_ssl_error_quark()
+
+GQuark soup_ssl_error_quark (void);
+
+typedef enum {
+       SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ,
+       SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE,
+       SOUP_SSL_ERROR_CERTIFICATE,
+} SoupSSLError;
+
 G_END_DECLS
 
 #endif /* SOUP_MISC_H */
diff --git a/libsoup/soup-path-map.c b/libsoup/soup-path-map.c
new file mode 100644 (file)
index 0000000..60191b4
--- /dev/null
@@ -0,0 +1,186 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-path-map.c: URI path prefix-matcher
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#include "soup-path-map.h"
+#include <string.h>
+
+/* This could be replaced with something more clever, like a Patricia
+ * trie, but it's probably not worth it since the total number of
+ * mappings is likely to always be small. So we keep an array of
+ * paths, sorted by decreasing length. (The first prefix match will
+ * therefore be the longest.)
+ */
+
+typedef struct {
+       char     *path;
+       int       len;
+       gpointer  data;
+} SoupPathMapping;
+
+struct SoupPathMap {
+       GArray *mappings;
+       GDestroyNotify free_func;
+};
+
+/**
+ * soup_path_map_new:
+ * @data_free_func: function to use to free data added with
+ * soup_path_map_add().
+ *
+ * Creates a new %SoupPathMap.
+ *
+ * Return value: the new %SoupPathMap
+ **/
+SoupPathMap *
+soup_path_map_new (GDestroyNotify data_free_func)
+{
+       SoupPathMap *map;
+
+       map = g_slice_new0 (SoupPathMap);
+       map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
+       map->free_func = data_free_func;
+
+       return map;
+}
+
+/**
+ * soup_path_map_free:
+ * @map: a %SoupPathMap
+ *
+ * Frees @map and all data stored in it.
+ **/
+void
+soup_path_map_free (SoupPathMap *map)
+{
+       SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+       int i;
+
+       for (i = 0; i < map->mappings->len; i++) {
+               g_free (mappings[i].path);
+               if (map->free_func)
+                       map->free_func (mappings[i].data);
+       }
+       g_array_free (map->mappings, TRUE);
+       
+       g_slice_free (SoupPathMap, map);
+}
+
+/* Scan @map looking for @path or one of its ancestors.
+ * Sets *@match to the index of a match, or -1 if no match is found.
+ * Sets *@insert to the index to insert @path at if a new mapping is
+ * desired. Returns %TRUE if *@match is an exact match.
+ */
+static gboolean
+mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
+{
+       SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+       int i, path_len;
+       gboolean exact = FALSE;
+
+       *match = -1;
+
+       path_len = strcspn (path, "?");
+       for (i = 0; i < map->mappings->len; i++) {
+               if (mappings[i].len > path_len)
+                       continue;
+
+               if (insert && mappings[i].len < path_len) {
+                       *insert = i;
+                       /* Clear insert so we don't try to set it again */
+                       insert = NULL;
+               }
+
+               if (!strncmp (mappings[i].path, path, mappings[i].len)) {
+                       *match = i;
+                       if (path_len == mappings[i].len)
+                               exact = TRUE;
+                       if (!insert)
+                               return exact;
+               }
+       }
+
+       if (insert)
+               *insert = i;
+       return exact;
+}      
+
+/**
+ * soup_path_map_add:
+ * @map: a %SoupPathMap
+ * @path: the path
+ * @data: the data
+ *
+ * Adds @data to @map at @path. If there was already data at @path it
+ * will be freed.
+ **/
+void
+soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
+{
+       SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+       int match, insert;
+
+       if (mapping_lookup (map, path, &match, &insert)) {
+               if (map->free_func)
+                       map->free_func (mappings[match].data);
+               mappings[match].data = data;
+       } else {
+               SoupPathMapping mapping;
+
+               mapping.path = g_strdup (path);
+               mapping.len = strlen (path);
+               mapping.data = data;
+               g_array_insert_val (map->mappings, insert, mapping);
+       }
+}
+
+/**
+ * soup_path_map_remove:
+ * @map: a %SoupPathMap
+ * @path: the path
+ *
+ * Removes @data from @map at @path. (This must be called with the same
+ * path the data was originally added with, not a subdirectory.)
+ **/
+void
+soup_path_map_remove (SoupPathMap *map, const char *path)
+{
+       SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+       int match;
+
+       if (!mapping_lookup (map, path, &match, NULL))
+               return;
+
+       if (map->free_func)
+               map->free_func (mappings[match].data);
+       g_free (mappings[match].path);
+       g_array_remove_index (map->mappings, match);
+}
+
+/**
+ * soup_path_map_lookup:
+ * @map: a %SoupPathMap
+ * @path: the path
+ *
+ * Finds the data associated with @path in @map. If there is no data
+ * specifically associated with @path, it will return the data for the
+ * closest parent directory of @path that has data associated with it.
+ *
+ * Return value: the data set with soup_path_map_add(), or %NULL if no
+ * data could be found for @path or any of its ancestors.
+ **/
+gpointer
+soup_path_map_lookup (SoupPathMap *map, const char *path)
+{
+       SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+       int match;
+
+       mapping_lookup (map, path, &match, NULL);
+       if (match == -1)
+               return NULL;
+       else
+               return mappings[match].data;
+}
diff --git a/libsoup/soup-path-map.h b/libsoup/soup-path-map.h
new file mode 100644 (file)
index 0000000..37b5fb9
--- /dev/null
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_PATH_MAP_H
+#define SOUP_PATH_MAP_H 1
+
+#include <soup-types.h>
+
+typedef struct SoupPathMap SoupPathMap;
+
+SoupPathMap *soup_path_map_new    (GDestroyNotify  data_free_func);
+void         soup_path_map_free   (SoupPathMap    *map);
+
+void         soup_path_map_add    (SoupPathMap    *map,
+                                  const char     *path,
+                                  gpointer        data);
+void         soup_path_map_remove (SoupPathMap    *map,
+                                  const char     *path);
+
+gpointer     soup_path_map_lookup (SoupPathMap    *map,
+                                  const char     *path);
+
+
+#endif /* SOUP_PATH_MAP_H */
diff --git a/libsoup/soup-server-auth.c b/libsoup/soup-server-auth.c
deleted file mode 100644 (file)
index e2c5138..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-server-auth.c: Server-side authentication handling
- *
- * Copyright (C) 2001-2003, Ximian, Inc.
- */
-
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "soup-server-auth.h"
-
-#include "soup-headers.h"
-#include "soup-md5-utils.h"
-#include "soup-misc.h"
-#include "soup-uri.h"
-
-typedef struct {
-       const gchar   *scheme;
-       SoupAuthType   type;
-       gint           strength;
-} AuthScheme; 
-
-static AuthScheme known_auth_schemes [] = {
-       { "Basic",  SOUP_AUTH_TYPE_BASIC,  0 },
-       { "Digest", SOUP_AUTH_TYPE_DIGEST, 3 },
-       { NULL }
-};
-
-static SoupAuthType
-soup_auth_get_strongest_header (guint          auth_types,
-                               const GSList  *vals, 
-                               gchar        **out_hdr)
-{
-       gchar *header = NULL;
-       AuthScheme *scheme = NULL, *iter;
-
-       g_return_val_if_fail (vals != NULL, 0);
-
-       if (!auth_types) 
-               return 0;
-
-       while (vals) {
-               for (iter = known_auth_schemes; iter->scheme; iter++) {
-                       gchar *tryheader = vals->data;
-
-                       if ((iter->type & auth_types) &&
-                           !g_ascii_strncasecmp (tryheader, 
-                                                 iter->scheme, 
-                                                 strlen (iter->scheme))) {
-                               if (!scheme || 
-                                   scheme->strength < iter->strength) {
-                                       header = tryheader;
-                                       scheme = iter;
-                               }
-                               break;
-                       }
-               }
-
-               vals = vals->next;
-       }
-
-       if (!scheme) 
-               return 0;
-
-       *out_hdr = header + strlen (scheme->scheme) + 1;
-       return scheme->type;
-}
-
-static gboolean 
-check_digest_passwd (SoupServerAuthDigest *digest,
-                    gchar                *passwd)
-{
-       SoupMD5Context ctx;
-       guchar d[16];
-       char hex_a1 [33], hex_a2[33], o[33];
-       char *tmp;
-
-       /* compute A1 */
-       soup_md5_init (&ctx);
-       soup_md5_update (&ctx, digest->user, strlen (digest->user));
-       soup_md5_update (&ctx, ":", 1);
-       soup_md5_update (&ctx, digest->realm, strlen (digest->realm));
-       soup_md5_update (&ctx, ":", 1);
-
-       if (passwd)
-               soup_md5_update (&ctx, passwd, strlen (passwd));
-
-       if (digest->algorithm == SOUP_ALGORITHM_MD5_SESS) {
-               soup_md5_final (&ctx, d);
-
-               soup_md5_init (&ctx);
-               soup_md5_update (&ctx, d, 16);
-               soup_md5_update (&ctx, ":", 1);
-               soup_md5_update (&ctx, digest->nonce, strlen (digest->nonce));
-               soup_md5_update (&ctx, ":", 1);
-               soup_md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
-       }
-
-       /* hexify A1 */
-       soup_md5_final_hex (&ctx, hex_a1);
-
-       /* compute A2 */
-       soup_md5_init (&ctx);
-       soup_md5_update (&ctx, 
-                   digest->request_method, 
-                   strlen (digest->request_method));
-       soup_md5_update (&ctx, ":", 1);
-       soup_md5_update (&ctx, digest->digest_uri, strlen (digest->digest_uri));
-
-       if (digest->integrity) {
-               /* FIXME: Actually implement. Ugh. */
-               soup_md5_update (&ctx, ":", 1);
-               soup_md5_update (&ctx, "00000000000000000000000000000000", 32);
-       }
-
-       /* hexify A2 */
-       soup_md5_final_hex (&ctx, hex_a2);
-
-       /* compute KD */
-       soup_md5_init (&ctx);
-       soup_md5_update (&ctx, hex_a1, 32);
-       soup_md5_update (&ctx, ":", 1);
-       soup_md5_update (&ctx, digest->nonce, strlen (digest->nonce));
-       soup_md5_update (&ctx, ":", 1);
-
-       tmp = g_strdup_printf ("%.8x", digest->nonce_count);
-       soup_md5_update (&ctx, tmp, strlen (tmp));
-       g_free (tmp);
-
-       soup_md5_update (&ctx, ":", 1);
-       soup_md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
-       soup_md5_update (&ctx, ":", 1);
-
-       if (digest->integrity)
-               tmp = "auth-int";
-       else 
-               tmp = "auth";
-
-       soup_md5_update (&ctx, tmp, strlen (tmp));
-       soup_md5_update (&ctx, ":", 1);
-
-       soup_md5_update (&ctx, hex_a2, 32);
-       soup_md5_final_hex (&ctx, o);
-
-       return strcmp (o, digest->digest_response) == 0;
-}
-
-gboolean 
-soup_server_auth_check_passwd (SoupServerAuth *auth,
-                              gchar          *passwd)
-{
-       g_return_val_if_fail (auth != NULL, TRUE);
-
-       switch (auth->type) {
-       case SOUP_AUTH_TYPE_BASIC:
-               if (passwd && auth->basic.passwd)
-                       return strcmp (auth->basic.passwd, passwd) == 0;
-               else
-                       return passwd == auth->basic.passwd;
-       case SOUP_AUTH_TYPE_DIGEST:
-               return check_digest_passwd (&auth->digest, passwd);
-       }
-
-       return FALSE;
-}
-
-const gchar *
-soup_server_auth_get_user (SoupServerAuth *auth)
-{
-       g_return_val_if_fail (auth != NULL, NULL);
-
-       switch (auth->type) {
-       case SOUP_AUTH_TYPE_BASIC:
-               return auth->basic.user;
-       case SOUP_AUTH_TYPE_DIGEST:
-               return auth->digest.user;
-       }
-
-       return NULL;
-}
-
-static gboolean
-parse_digest (SoupServerAuthContext *auth_ctx, 
-             gchar                 *header,
-             SoupMessage           *msg,
-             SoupServerAuth        *out_auth)
-{
-       GHashTable *tokens;
-       gchar *user, *realm, *uri, *response;
-       gchar *nonce, *cnonce;
-       gint nonce_count;
-       gboolean integrity;
-
-       user = realm = uri = response = NULL;
-       nonce = cnonce = NULL;
-       nonce_count = 0;
-       integrity = FALSE;
-
-       tokens = soup_header_param_parse_list (header);
-       if (!tokens) 
-               goto DIGEST_AUTH_FAIL;
-
-       /* Check uri */
-       {
-               SoupUri *dig_uri;
-               const SoupUri *req_uri;
-
-               uri = soup_header_param_copy_token (tokens, "uri");
-               if (!uri)
-                       goto DIGEST_AUTH_FAIL;
-
-               req_uri = soup_message_get_uri (msg);
-
-               dig_uri = soup_uri_new (uri);
-               if (dig_uri) {
-                       if (!soup_uri_equal (dig_uri, req_uri)) {
-                               soup_uri_free (dig_uri);
-                               goto DIGEST_AUTH_FAIL;
-                       }
-                       soup_uri_free (dig_uri);
-               } else {        
-                       char *req_path;
-
-                       req_path = soup_uri_to_string (req_uri, TRUE);
-                       if (strcmp (uri, req_path) != 0) {
-                               g_free (req_path);
-                               goto DIGEST_AUTH_FAIL;
-                       }
-                       g_free (req_path);
-               }
-       }
-
-       /* Check qop */
-       {
-               gchar *qop;
-               qop = soup_header_param_copy_token (tokens, "qop");
-               if (!qop)
-                       goto DIGEST_AUTH_FAIL;
-
-               if (!strcmp (qop, "auth-int")) {
-                       g_free (qop);
-                       integrity = TRUE;
-               } else if (auth_ctx->digest_info.force_integrity) {
-                       g_free (qop);
-                       goto DIGEST_AUTH_FAIL;
-               }
-       }                       
-
-       /* Check realm */
-       realm = soup_header_param_copy_token (tokens, "realm");
-       if (!realm && auth_ctx->digest_info.realm)
-               goto DIGEST_AUTH_FAIL;
-       else if (realm && 
-                auth_ctx->digest_info.realm &&
-                strcmp (realm, auth_ctx->digest_info.realm) != 0)
-               goto DIGEST_AUTH_FAIL;
-
-       /* Check username */
-       user = soup_header_param_copy_token (tokens, "username");
-       if (!user)
-               goto DIGEST_AUTH_FAIL;
-
-       /* Check nonce */
-       nonce = soup_header_param_copy_token (tokens, "nonce");
-       if (!nonce)
-               goto DIGEST_AUTH_FAIL;
-
-       /* Check nonce count */
-       {
-               gchar *nc;
-               nc = soup_header_param_copy_token (tokens, "nc");
-               if (!nc)
-                       goto DIGEST_AUTH_FAIL;
-
-               nonce_count = atoi (nc);
-               if (nonce_count <= 0) {
-                       g_free (nc);
-                       goto DIGEST_AUTH_FAIL;
-               }
-               g_free (nc);
-       }
-
-       cnonce = soup_header_param_copy_token (tokens, "cnonce");
-       if (!cnonce)
-               goto DIGEST_AUTH_FAIL;
-
-       response = soup_header_param_copy_token (tokens, "response");
-       if (!response)
-               goto DIGEST_AUTH_FAIL;
-
-       out_auth->digest.type            = SOUP_AUTH_TYPE_DIGEST;
-       out_auth->digest.digest_uri      = uri;
-       out_auth->digest.integrity       = integrity;
-       out_auth->digest.realm           = realm;
-       out_auth->digest.user            = user;
-       out_auth->digest.nonce           = nonce;
-       out_auth->digest.nonce_count     = nonce_count;
-       out_auth->digest.cnonce          = cnonce;
-       out_auth->digest.digest_response = response;
-       out_auth->digest.request_method  = msg->method;
-
-       soup_header_param_destroy_hash (tokens);
-
-       return TRUE;
-
- DIGEST_AUTH_FAIL:
-       if (tokens)
-               soup_header_param_destroy_hash (tokens);
-
-       g_free (user);
-       g_free (realm);
-       g_free (nonce);
-       g_free (response);
-       g_free (cnonce);
-       g_free (uri);
-
-       return FALSE;
-}
-
-SoupServerAuth * 
-soup_server_auth_new (SoupServerAuthContext *auth_ctx, 
-                     const GSList          *auth_hdrs, 
-                     SoupMessage           *msg)
-{
-       SoupServerAuth *ret;
-       SoupAuthType type;
-       gchar *header = NULL;
-
-       g_return_val_if_fail (auth_ctx != NULL, NULL);
-       g_return_val_if_fail (msg != NULL, NULL);
-
-       if (!auth_hdrs && auth_ctx->types) {
-               soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
-               return NULL;
-       }
-
-       type = soup_auth_get_strongest_header (auth_ctx->types,
-                                              auth_hdrs, 
-                                              &header);
-
-       if (!type && auth_ctx->types) {
-               soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
-               return NULL;
-       }
-
-       ret = g_new0 (SoupServerAuth, 1);
-
-       switch (type) {
-       case SOUP_AUTH_TYPE_BASIC:
-               {
-                       guchar *userpass, *colon;
-                       gsize len;
-
-                       userpass = g_base64_decode (header, &len);
-                       if (!userpass)
-                               break;
-
-                       colon = memchr (userpass, ':', len);
-                       if (!colon) {
-                               g_free (userpass);
-                               break;
-                       }
-
-                       ret->basic.type = SOUP_AUTH_TYPE_BASIC;
-                       ret->basic.user = g_strndup ((char *)userpass, 
-                                                    colon - userpass);
-                       ret->basic.passwd = g_strndup ((char *)colon + 1,
-                                                      len - (colon + 1 - userpass));
-
-                       g_free (userpass);
-
-                       return ret;
-               }
-       case SOUP_AUTH_TYPE_DIGEST:
-               if (parse_digest (auth_ctx, header, msg, ret))
-                       return ret;
-               break;
-       }
-
-       g_free (ret);
-
-       soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
-       return NULL;
-}
-
-void
-soup_server_auth_free (SoupServerAuth *auth)
-{
-       g_return_if_fail (auth != NULL);
-
-       switch (auth->type) {
-       case SOUP_AUTH_TYPE_BASIC:
-               g_free ((gchar *) auth->basic.user);
-               g_free ((gchar *) auth->basic.passwd);
-               break;
-       case SOUP_AUTH_TYPE_DIGEST:
-               g_free ((gchar *) auth->digest.realm);
-               g_free ((gchar *) auth->digest.user);
-               g_free ((gchar *) auth->digest.nonce);
-               g_free ((gchar *) auth->digest.cnonce);
-               g_free ((gchar *) auth->digest.digest_uri);
-               g_free ((gchar *) auth->digest.digest_response);
-               break;
-       }
-
-       g_free (auth);
-}
-
-void
-soup_server_auth_context_challenge (SoupServerAuthContext *auth_ctx,
-                                   SoupMessage           *msg,
-                                   gchar                 *header_name)
-{
-       if (auth_ctx->types & SOUP_AUTH_TYPE_BASIC) {
-               gchar *hdr;
-
-               hdr = g_strdup_printf ("Basic realm=\"%s\"", 
-                                      auth_ctx->basic_info.realm);
-               soup_message_add_header (msg->response_headers,
-                                        header_name,
-                                        hdr);
-               g_free (hdr);
-       }
-
-       if (auth_ctx->types & SOUP_AUTH_TYPE_DIGEST) {
-               GString *str;
-
-               str = g_string_new ("Digest ");
-
-               if (auth_ctx->digest_info.realm)
-                       g_string_sprintfa (str, 
-                                          "realm=\"%s\", ", 
-                                          auth_ctx->digest_info.realm);
-
-               g_string_sprintfa (str, 
-                                  "nonce=\"%lu%lu\", ", 
-                                  (unsigned long) msg,
-                                  (unsigned long) time (0));
-
-               if (auth_ctx->digest_info.force_integrity) 
-                       g_string_sprintfa (str, "qop=\"auth-int\", ");
-               else
-                       g_string_sprintfa (str, "qop=\"auth,auth-int\", ");
-
-               if (auth_ctx->digest_info.allow_algorithms & SOUP_ALGORITHM_MD5_SESS)
-                       g_string_sprintfa (str, "algorithm=\"MD5-sess\"");
-               else
-                       g_string_sprintfa (str, "algorithm=\"MD5\"");
-
-               soup_message_add_header (msg->response_headers,
-                                        header_name,
-                                        str->str);
-               g_string_free (str, TRUE);
-       }
-}
diff --git a/libsoup/soup-server-auth.h b/libsoup/soup-server-auth.h
deleted file mode 100644 (file)
index f537e37..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-server-auth.h: Server-side authentication handling
- *
- * Copyright (C) 2001-2003, Ximian, Inc.
- */
-
-#ifndef SOUP_SERVER_AUTH_H
-#define SOUP_SERVER_AUTH_H 1
-
-#include <libsoup/soup-types.h>
-
-G_BEGIN_DECLS
-
-typedef gboolean (*SoupServerAuthCallbackFn) (SoupServerAuthContext *auth_ctx,
-                                             SoupServerAuth        *auth,
-                                             SoupMessage           *msg, 
-                                             gpointer               data);
-
-struct SoupServerAuthContext {
-       guint                     types;
-       SoupServerAuthCallbackFn  callback;
-       gpointer                  user_data;
-
-       struct {
-               const gchar *realm;
-       } basic_info;
-
-       struct {
-               const gchar *realm;
-               guint        allow_algorithms;
-               gboolean     force_integrity;
-       } digest_info;
-};
-
-void soup_server_auth_context_challenge (SoupServerAuthContext *auth_ctx,
-                                        SoupMessage           *msg,
-                                        gchar                 *header_name);
-
-
-typedef enum {
-       SOUP_AUTH_TYPE_BASIC = 1,
-       SOUP_AUTH_TYPE_DIGEST
-} SoupAuthType;
-
-typedef struct {
-       SoupAuthType  type;
-       const gchar  *user;
-       const gchar  *passwd;
-} SoupServerAuthBasic;
-
-typedef enum {
-       SOUP_ALGORITHM_MD5      = 1 << 0,
-       SOUP_ALGORITHM_MD5_SESS = 1 << 1
-} SoupDigestAlgorithm;
-
-typedef struct {
-       SoupAuthType          type;
-       SoupDigestAlgorithm   algorithm;
-       gboolean              integrity;
-       const gchar          *realm;
-       const gchar          *user;
-       const gchar          *nonce;
-       gint                  nonce_count;
-       const gchar          *cnonce;
-       const gchar          *digest_uri;
-       const gchar          *digest_response;
-       const gchar          *request_method;
-} SoupServerAuthDigest;
-
-union SoupServerAuth {
-       SoupAuthType          type;
-       SoupServerAuthBasic   basic;
-       SoupServerAuthDigest  digest;
-};
-
-SoupServerAuth *soup_server_auth_new          (SoupServerAuthContext *auth_ctx, 
-                                              const GSList          *auth_hdrs,
-                                              SoupMessage           *msg);
-
-void            soup_server_auth_free         (SoupServerAuth        *auth);
-
-const gchar    *soup_server_auth_get_user     (SoupServerAuth        *auth);
-
-gboolean        soup_server_auth_check_passwd (SoupServerAuth        *auth,
-                                              gchar                 *passwd);
-
-G_END_DECLS
-
-#endif /* SOUP_SERVER_AUTH_H */
diff --git a/libsoup/soup-server-message.c b/libsoup/soup-server-message.c
deleted file mode 100644 (file)
index ca4cc1e..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-server-message.c: Server-side messages
- *
- * Copyright (C) 2001-2003, Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "soup-server-message.h"
-#include "soup-server.h"
-
-typedef struct {
-       SoupServer *server;
-
-       SoupTransferEncoding encoding;
-
-       gboolean  started;
-       gboolean  finished;
-} SoupServerMessagePrivate;
-#define SOUP_SERVER_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER_MESSAGE, SoupServerMessagePrivate))
-
-G_DEFINE_TYPE (SoupServerMessage, soup_server_message, SOUP_TYPE_MESSAGE)
-
-static void
-soup_server_message_init (SoupServerMessage *smsg)
-{
-       SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-}
-
-static void
-finalize (GObject *object)
-{
-       SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (object);
-
-       /* FIXME */
-       g_free ((char *) ((SoupMessage *)smsg)->method);
-
-       G_OBJECT_CLASS (soup_server_message_parent_class)->finalize (object);
-}
-
-static void
-soup_server_message_class_init (SoupServerMessageClass *soup_server_message_class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (soup_server_message_class);
-
-       g_type_class_add_private (soup_server_message_class, sizeof (SoupServerMessagePrivate));
-
-       /* virtual method override */
-       object_class->finalize = finalize;
-}
-
-
-SoupServerMessage *
-soup_server_message_new (SoupServer *server)
-{
-       SoupServerMessage *smsg;
-
-       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
-
-       smsg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
-       SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->server = server;
-
-       return smsg;
-}
-
-SoupServer *
-soup_server_message_get_server (SoupServerMessage *smsg)
-{
-       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (smsg), NULL);
-
-       return SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->server;
-}
-
-void
-soup_server_message_set_encoding (SoupServerMessage *smsg,
-                                 SoupTransferEncoding encoding)
-{
-       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (smsg));
-
-       if (encoding < SOUP_TRANSFER_UNKNOWN ||
-           encoding > SOUP_TRANSFER_CONTENT_LENGTH)
-               return;
-
-       SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->encoding = encoding;
-}
-
-SoupTransferEncoding
-soup_server_message_get_encoding (SoupServerMessage *smsg)
-{
-       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (smsg), SOUP_TRANSFER_UNKNOWN);
-
-       return SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->encoding;
-}
-
-void
-soup_server_message_start (SoupServerMessage *smsg)
-{
-       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (smsg));
-
-       SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->started = TRUE;
-
-       soup_message_io_unpause (SOUP_MESSAGE (smsg));
-}
-
-gboolean
-soup_server_message_is_started (SoupServerMessage *smsg)
-{
-       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (smsg), TRUE);
-
-       return SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->started;
-}
-
-void
-soup_server_message_finish  (SoupServerMessage *smsg)
-{
-       SoupServerMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (smsg));
-       priv = SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg);
-
-       priv->started = TRUE;
-       priv->finished = TRUE;
-
-       soup_message_io_unpause (SOUP_MESSAGE (smsg));
-}
-
-gboolean
-soup_server_message_is_finished (SoupServerMessage *smsg)
-{
-       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (smsg), TRUE);
-
-       return SOUP_SERVER_MESSAGE_GET_PRIVATE (smsg)->finished;
-}
diff --git a/libsoup/soup-server-message.h b/libsoup/soup-server-message.h
deleted file mode 100644 (file)
index cae2774..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifndef SOUP_SERVER_MESSAGE_H
-#define SOUP_SERVER_MESSAGE_H 1
-
-#include <libsoup/soup-message.h>
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_SERVER_MESSAGE            (soup_server_message_get_type ())
-#define SOUP_SERVER_MESSAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SERVER_MESSAGE, SoupServerMessage))
-#define SOUP_SERVER_MESSAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SERVER_MESSAGE, SoupServerMessageClass))
-#define SOUP_IS_SERVER_MESSAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SERVER_MESSAGE))
-#define SOUP_IS_SERVER_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SERVER_MESSAGE))
-#define SOUP_SERVER_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SERVER_MESSAGE, SoupServerMessageClass))
-
-struct SoupServerMessage {
-       SoupMessage parent;
-
-};
-
-typedef struct {
-       SoupMessageClass parent_class;
-
-} SoupServerMessageClass;
-
-GType soup_server_message_get_type (void);
-
-
-SoupServerMessage    *soup_server_message_new          (SoupServer           *server);
-
-SoupServer           *soup_server_message_get_server   (SoupServerMessage    *smsg);
-
-void                  soup_server_message_set_encoding (SoupServerMessage    *smsg,
-                                                       SoupTransferEncoding  encoding);
-SoupTransferEncoding  soup_server_message_get_encoding (SoupServerMessage    *smsg);
-
-void                  soup_server_message_start        (SoupServerMessage    *smsg);
-gboolean              soup_server_message_is_started   (SoupServerMessage    *smsg);
-
-void                  soup_server_message_finish       (SoupServerMessage    *smsg);
-gboolean              soup_server_message_is_finished  (SoupServerMessage    *smsg);
-
-G_END_DECLS
-
-#endif /* SOUP_SERVER_H */
index 051effe..933c80f 100644 (file)
@@ -5,10 +5,6 @@
  * Copyright (C) 2001-2003, Ximian, Inc.
  */
 
-/*
- * FIXME: Split into SoupServerTCP and SoupServerCGI subclasses
- */
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include "soup-server.h"
 #include "soup-address.h"
+#include "soup-auth-domain.h"
+#include "soup-date.h"
+#include "soup-form.h"
 #include "soup-headers.h"
-#include "soup-server-auth.h"
-#include "soup-server-message.h"
+#include "soup-message-private.h"
+#include "soup-marshal.h"
+#include "soup-path-map.h" 
 #include "soup-socket.h"
 #include "soup-ssl.h"
 
+/**
+ * SECTION:soup-server
+ * @short_description: HTTP server
+ * @see_also: #SoupAuthDomain
+ *
+ * #SoupServer implements a simple HTTP server.
+ * 
+ * To begin, create a server using soup_server_new(). Add at least one
+ * handler by calling soup_server_add_handler(); the handler will be
+ * called to process any requests underneath the path passed to
+ * soup_server_add_handler(). (If you want all requests to go to the
+ * same handler, just pass "/" (or %NULL) for the path.) Any request
+ * that does not match any handler will automatically be returned to
+ * the client with a 404 (Not Found) status.
+ * 
+ * To add authentication to some or all paths, create an appropriate
+ * #SoupAuthDomain (qv), and add it to the server via
+ * soup_server_add_auth_domain.
+ * 
+ * Additional processing options are available via #SoupServer's
+ * signals; Connect to #SoupServer::request-started to be notified
+ * every time a new request is being processed. (This gives you a
+ * chance to connect to the #SoupMessage "got-" signals in case you
+ * want to do processing before the body has been fully read.)
+ * 
+ * Once the server is set up, start it processing connections by
+ * calling soup_server_run_async() or soup_server_run(). #SoupServer
+ * runs via the glib main loop; if you need to have a server that runs
+ * in another thread (or merely isn't bound to the default main loop),
+ * create a #GMainContext for it to use, and set that via the
+ * #SOUP_SERVER_ASYNC_CONTEXT property.
+ **/
+
 G_DEFINE_TYPE (SoupServer, soup_server, G_TYPE_OBJECT)
 
+enum {
+       REQUEST_STARTED,
+       REQUEST_READ,
+       REQUEST_FINISHED,
+       REQUEST_ABORTED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct SoupClientContext {
+       SoupServer     *server;
+       SoupSocket     *sock;
+       SoupAuthDomain *auth_domain;
+       char           *auth_user;
+};
+
+typedef struct {
+       char                   *path;
+
+       SoupServerCallback      callback;
+       GDestroyNotify          destroy;
+       gpointer                user_data;
+} SoupServerHandler;
+
 typedef struct {
        SoupAddress       *interface;
        guint              port;
@@ -40,9 +98,12 @@ typedef struct {
        SoupSocket        *listen_sock;
        GSList            *client_socks;
 
-       GHashTable        *handlers; /* KEY: path, VALUE: SoupServerHandler */
+       gboolean           raw_paths;
+       SoupPathMap       *handlers;
        SoupServerHandler *default_handler;
        
+       GSList            *auth_domains;
+
        GMainContext      *async_context;
 } SoupServerPrivate;
 #define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate))
@@ -55,43 +116,32 @@ enum {
        PROP_SSL_CERT_FILE,
        PROP_SSL_KEY_FILE,
        PROP_ASYNC_CONTEXT,
+       PROP_RAW_PATHS,
 
        LAST_PROP
 };
 
+static GObject *constructor (GType                  type,
+                            guint                  n_construct_properties,
+                            GObjectConstructParam *construct_properties);
 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
-soup_server_init (SoupServer *server)
+free_handler (SoupServerHandler *hand)
 {
-       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
-
-       priv->handlers = g_hash_table_new (g_str_hash, g_str_equal);
-}
-
-static void
-free_handler (SoupServer *server, SoupServerHandler *hand)
-{
-       if (hand->unregister)
-               (*hand->unregister) (server, hand, hand->user_data);
-
-       if (hand->auth_ctx) {
-               g_free ((char *) hand->auth_ctx->basic_info.realm);
-               g_free ((char *) hand->auth_ctx->digest_info.realm);
-               g_free (hand->auth_ctx);
-       }
-
        g_free (hand->path);
-       g_free (hand);
+       g_slice_free (SoupServerHandler, hand);
 }
 
 static void
-free_handler_foreach (gpointer key, gpointer hand, gpointer server)
+soup_server_init (SoupServer *server)
 {
-       free_handler (server, hand);
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler);
 }
 
 static void
@@ -99,6 +149,7 @@ finalize (GObject *object)
 {
        SoupServer *server = SOUP_SERVER (object);
        SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+       GSList *iter;
 
        if (priv->interface)
                g_object_unref (priv->interface);
@@ -120,10 +171,12 @@ finalize (GObject *object)
        }
 
        if (priv->default_handler)
-               free_handler (server, priv->default_handler);
+               free_handler (priv->default_handler);
+       soup_path_map_free (priv->handlers);
 
-       g_hash_table_foreach (priv->handlers, free_handler_foreach, server);
-       g_hash_table_destroy (priv->handlers);
+       for (iter = priv->auth_domains; iter; iter = iter->next)
+               g_object_unref (iter->data);
+       g_slist_free (priv->auth_domains);
 
        if (priv->loop)
                g_main_loop_unref (priv->loop);
@@ -141,10 +194,116 @@ soup_server_class_init (SoupServerClass *server_class)
        g_type_class_add_private (server_class, sizeof (SoupServerPrivate));
 
        /* virtual method override */
+       object_class->constructor = constructor;
        object_class->finalize = finalize;
        object_class->set_property = set_property;
        object_class->get_property = get_property;
 
+       /* signals */
+
+       /**
+        * SoupServer::request-started
+        * @server: the server
+        * @message: the new message
+        * @client: the client context
+        *
+        * Emitted when the server has started reading a new request.
+        * @message will be completely blank; not even the
+        * Request-Line will have been read yet. About the only thing
+        * you can usefully do with it is connect to its signals.
+        *
+        * If the request is read successfully, this will eventually
+        * be followed by a #SoupServer::request_read signal. If a
+        * response is then sent, the request processing will end with
+        * a #SoupServer::request_finished signal. If a network error
+        * occurs, the processing will instead end with
+        * #SoupServer::request_aborted.
+        **/
+       signals[REQUEST_STARTED] =
+               g_signal_new ("request-started",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupServerClass, request_started),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT_POINTER,
+                             G_TYPE_NONE, 2, 
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_CLIENT_CONTEXT);
+
+       /**
+        * SoupServer::request-read
+        * @server: the server
+        * @message: the message
+        * @client: the client context
+        *
+        * Emitted when the server has successfully read a request.
+        * @message will have all of its request-side information
+        * filled in, and if the message was authenticated, @client
+        * will have information about that. This signal is emitted
+        * before any handlers are called for the message, and if it
+        * sets the message's #status_code, then normal handler
+        * processing will be skipped.
+        **/
+       signals[REQUEST_READ] =
+               g_signal_new ("request-read",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupServerClass, request_read),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT_POINTER,
+                             G_TYPE_NONE, 2,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_CLIENT_CONTEXT);
+
+       /**
+        * SoupServer::request-finished
+        * @server: the server
+        * @message: the message
+        * @client: the client context
+        *
+        * Emitted when the server has finished writing a response to
+        * a request.
+        **/
+       signals[REQUEST_FINISHED] =
+               g_signal_new ("request-finished",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupServerClass, request_finished),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT_POINTER,
+                             G_TYPE_NONE, 2,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_CLIENT_CONTEXT);
+
+       /**
+        * SoupServer::request-aborted
+        * @server: the server
+        * @message: the message
+        * @client: the client context
+        *
+        * Emitted when processing has failed for a message; this
+        * could mean either that it could not be read (if
+        * #SoupServer::request_read has not been emitted for it yet),
+        * or that the response could not be written back (if
+        * #SoupServer::request_read has been emitted but
+        * #SoupServer::request_finished has not been).
+        *
+        * @message is in an undefined state when this signal is
+        * emitted; the signal exists primarily to allow the server to
+        * free any state that it may have allocated in
+        * #SoupServer::request_started.
+        **/
+       signals[REQUEST_ABORTED] =
+               g_signal_new ("request-aborted",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupServerClass, request_aborted),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT_POINTER,
+                             G_TYPE_NONE, 2,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_CLIENT_CONTEXT);
+
        /* properties */
        g_object_class_install_property (
                object_class, PROP_PORT,
@@ -180,8 +339,65 @@ soup_server_class_init (SoupServerClass *server_class)
                                      "Async GMainContext",
                                      "The GMainContext to dispatch async I/O in",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_RAW_PATHS,
+               g_param_spec_boolean (SOUP_SERVER_RAW_PATHS,
+                                     "Raw paths",
+                                     "If %TRUE, percent-encoding in the Request-URI path will not be automatically decoded.",
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
+static GObject *
+constructor (GType                  type,
+            guint                  n_construct_properties,
+            GObjectConstructParam *construct_properties)
+{
+       GObject *server;
+       SoupServerPrivate *priv;
+
+       server = G_OBJECT_CLASS (soup_server_parent_class)->constructor (
+               type, n_construct_properties, construct_properties);
+       if (!server)
+               return NULL;
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       if (!priv->interface) {
+               priv->interface =
+                       soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
+                                             priv->port);
+       }
+
+       if (priv->ssl_cert_file && priv->ssl_key_file) {
+               priv->ssl_creds = soup_ssl_get_server_credentials (
+                       priv->ssl_cert_file,
+                       priv->ssl_key_file);
+               if (!priv->ssl_creds) {
+                       g_object_unref (server);
+                       return NULL;
+               }
+       }
+
+       priv->listen_sock =
+               soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->interface,
+                                SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+                                SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+                                NULL);
+       if (!soup_socket_listen (priv->listen_sock)) {
+               g_object_unref (server);
+               return NULL;
+       }
+
+       /* Re-resolve the interface address, in particular in case
+        * the passed-in address had SOUP_ADDRESS_ANY_PORT.
+        */
+       g_object_unref (priv->interface);
+       priv->interface = soup_socket_get_local_address (priv->listen_sock);
+       g_object_ref (priv->interface);
+       priv->port = soup_address_get_port (priv->interface);
+
+       return server;
+}
 
 static void
 set_property (GObject *object, guint prop_id,
@@ -213,6 +429,9 @@ set_property (GObject *object, guint prop_id,
                if (priv->async_context)
                        g_main_context_ref (priv->async_context);
                break;
+       case PROP_RAW_PATHS:
+               priv->raw_paths = g_value_get_boolean (value);
+               break;
        default:
                break;
        }
@@ -240,16 +459,25 @@ get_property (GObject *object, guint prop_id,
        case PROP_ASYNC_CONTEXT:
                g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
                break;
+       case PROP_RAW_PATHS:
+               g_value_set_boolean (value, priv->raw_paths);
+               break;
        default:
                break;
        }
 }
 
+/**
+ * soup_server_new:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates a new #SoupServer.
+ **/
 SoupServer *
 soup_server_new (const char *optname1, ...)
 {
        SoupServer *server;
-       SoupServerPrivate *priv;
        va_list ap;
 
        va_start (ap, optname1);
@@ -257,46 +485,19 @@ soup_server_new (const char *optname1, ...)
                                                    optname1, ap);
        va_end (ap);
 
-       if (!server)
-               return NULL;
-       priv = SOUP_SERVER_GET_PRIVATE (server);
-
-       if (!priv->interface) {
-               priv->interface =
-                       soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
-                                             priv->port);
-       }
-
-       if (priv->ssl_cert_file && priv->ssl_key_file) {
-               priv->ssl_creds = soup_ssl_get_server_credentials (
-                       priv->ssl_cert_file,
-                       priv->ssl_key_file);
-               if (!priv->ssl_creds) {
-                       g_object_unref (server);
-                       return NULL;
-               }
-       }
-
-       priv->listen_sock =
-               soup_socket_new (SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
-                                SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
-                                NULL);
-       if (!soup_socket_listen (priv->listen_sock, priv->interface)) {
-               g_object_unref (server);
-               return NULL;
-       }
-
-       /* Re-resolve the interface address, in particular in case
-        * the passed-in address had SOUP_ADDRESS_ANY_PORT.
-        */
-       g_object_unref (priv->interface);
-       priv->interface = soup_socket_get_local_address (priv->listen_sock);
-       g_object_ref (priv->interface);
-       priv->port = soup_address_get_port (priv->interface);
-
        return server;
 }
 
+/**
+ * soup_server_get_port:
+ * @server: a #SoupServer
+ *
+ * Gets the TCP port that @server is listening on. This is most useful
+ * when you did not request a specific port (or explicitly requested
+ * %SOUP_ADDRESS_ANY_PORT).
+ *
+ * Return value: the port @server is listening on.
+ **/
 guint
 soup_server_get_port (SoupServer *server)
 {
@@ -305,20 +506,39 @@ soup_server_get_port (SoupServer *server)
        return SOUP_SERVER_GET_PRIVATE (server)->port;
 }
 
-SoupProtocol
-soup_server_get_protocol (SoupServer *server)
+/**
+ * soup_server_is_https:
+ * @server: a #SoupServer
+ *
+ * Checks whether @server is running plain http or https.
+ *
+ * In order for a server to run https, you must set the
+ * %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties
+ * to provide it with an SSL certificate to use.
+ *
+ * Return value: %TRUE if @server is serving https.
+ **/
+gboolean
+soup_server_is_https (SoupServer *server)
 {
        SoupServerPrivate *priv;
 
        g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       if (priv->ssl_cert_file && priv->ssl_key_file)
-               return SOUP_PROTOCOL_HTTPS;
-       else
-               return SOUP_PROTOCOL_HTTP;
+       return (priv->ssl_cert_file && priv->ssl_key_file);
 }
 
+/**
+ * soup_server_get_listener:
+ * @server: a #SoupServer
+ *
+ * Gets @server's listening socket. You should treat this as
+ * read-only; writing to it or modifiying it may cause @server to
+ * malfunction.
+ *
+ * Return value: the listening socket.
+ **/
 SoupSocket *
 soup_server_get_listener (SoupServer *server)
 {
@@ -330,125 +550,178 @@ soup_server_get_listener (SoupServer *server)
        return priv->listen_sock;
 }
 
-static void start_request (SoupServer *, SoupSocket *);
+static void start_request (SoupServer *, SoupClientContext *);
+
+static void
+soup_client_context_cleanup (SoupClientContext *client)
+{
+       if (client->auth_domain) {
+               g_object_unref (client->auth_domain);
+               client->auth_domain = NULL;
+       }
+       if (client->auth_user) {
+               g_free (client->auth_user);
+               client->auth_user = NULL;
+       }
+}
 
 static void
-request_finished (SoupMessage *msg, gpointer sock)
+request_finished (SoupMessage *msg, SoupClientContext *client)
 {
-       SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (msg);
+       SoupServer *server = client->server;
+       SoupSocket *sock = client->sock;
+
+       g_signal_emit (server,
+                      msg->status_code == SOUP_STATUS_IO_ERROR ?
+                      signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED],
+                      0, msg, client);
 
+       soup_client_context_cleanup (client);
        if (soup_socket_is_connected (sock) && soup_message_is_keepalive (msg)) {
                /* Start a new request */
-               start_request (soup_server_message_get_server (smsg), sock);
-       } else
+               start_request (server, client);
+       } else {
                soup_socket_disconnect (sock);
+               g_slice_free (SoupClientContext, client);
+       }
        g_object_unref (msg);
        g_object_unref (sock);
 }
 
-static inline void
-set_response_error (SoupMessage *req, guint code, char *phrase, char *body)
+static SoupServerHandler *
+soup_server_get_handler (SoupServer *server, const char *path)
 {
-       if (phrase)
-               soup_message_set_status_full (req, code, phrase);
-       else
-               soup_message_set_status (req, code);
+       SoupServerPrivate *priv;
+       SoupServerHandler *hand;
 
-       req->response.owner = SOUP_BUFFER_STATIC;
-       req->response.body = body;
-       req->response.length = body ? strlen (req->response.body) : 0;
-}
+       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+       priv = SOUP_SERVER_GET_PRIVATE (server);
 
+       if (path) {
+               hand = soup_path_map_lookup (priv->handlers, path);
+               if (hand)
+                       return hand;
+       }
+       return priv->default_handler;
+}
 
 static void
-call_handler (SoupMessage *req, SoupSocket *sock)
+got_headers (SoupMessage *req, SoupClientContext *client)
 {
-       SoupServer *server;
-       SoupServerHandler *hand;
-       SoupServerAuth *auth = NULL;
-       const char *handler_path;
-
-       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (req));
+       SoupServer *server = client->server;
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+       SoupURI *uri;
+       SoupDate *date;
+       char *date_string;
+       SoupAuthDomain *domain;
+       GSList *iter;
+       gboolean rejected = FALSE;
+       char *auth_user;
+
+       if (!priv->raw_paths) {
+               char *decoded_path;
+
+               uri = soup_message_get_uri (req);
+               decoded_path = soup_uri_decode (uri->path);
+               soup_uri_set_path (uri, decoded_path);
+       }
 
-       server = soup_server_message_get_server (SOUP_SERVER_MESSAGE (req));
-       handler_path = soup_message_get_uri (req)->path;
+       /* Add required response headers */
+       date = soup_date_new_from_now (0);
+       date_string = soup_date_to_string (date, SOUP_DATE_HTTP);
+       soup_message_headers_replace (req->response_headers, "Date",
+                                     date_string);
+       g_free (date_string);
+       soup_date_free (date);
+       
+       /* Now handle authentication. (We do this here so that if
+        * the request uses "Expect: 100-continue", we can reject it
+        * immediately rather than waiting for the request body to
+        * be sent.
+        */
+       for (iter = priv->auth_domains; iter; iter = iter->next) {
+               domain = iter->data;
+
+               if (soup_auth_domain_covers (domain, req)) {
+                       auth_user = soup_auth_domain_accepts (domain, req);
+                       if (auth_user) {
+                               client->auth_domain = g_object_ref (domain);
+                               client->auth_user = auth_user;
+                               return;
+                       }
 
-       hand = soup_server_get_handler (server, handler_path);
-       if (!hand) {
-               set_response_error (req, SOUP_STATUS_NOT_FOUND, NULL, NULL);
-               return;
+                       rejected = TRUE;
+               }
        }
 
-       if (hand->auth_ctx) {
-               SoupServerAuthContext *auth_ctx = hand->auth_ctx;
-               const GSList *auth_hdrs;
+       /* If no auth domain rejected it, then it's ok. */
+       if (!rejected)
+               return;
 
-               auth_hdrs = soup_message_get_header_list (req->request_headers,
-                                                         "Authorization");
-               auth = soup_server_auth_new (auth_ctx, auth_hdrs, req);
+       for (iter = priv->auth_domains; iter; iter = iter->next) {
+               domain = iter->data;
 
-               if (auth_ctx->callback) {
-                       gboolean ret = FALSE;
+               if (soup_auth_domain_covers (domain, req))
+                       soup_auth_domain_challenge (domain, req);
+       }
+}
 
-                       ret = (*auth_ctx->callback) (auth_ctx,
-                                                    auth,
-                                                    req,
-                                                    auth_ctx->user_data);
-                       if (!ret) {
-                               soup_server_auth_context_challenge (
-                                       auth_ctx,
-                                       req,
-                                       "WWW-Authenticate");
+static void
+call_handler (SoupMessage *req, SoupClientContext *client)
+{
+       SoupServer *server = client->server;
+       SoupServerHandler *hand;
+       SoupURI *uri;
 
-                               if (!req->status_code)
-                                       soup_message_set_status (
-                                               req,
-                                               SOUP_STATUS_UNAUTHORIZED);
+       if (req->status_code != 0)
+               return;
 
-                               return;
-                       }
-               } else if (req->status_code) {
-                       soup_server_auth_context_challenge (
-                               auth_ctx,
-                               req,
-                               "WWW-Authenticate");
-                       return;
-               }
+       uri = soup_message_get_uri (req);
+       hand = soup_server_get_handler (server, uri->path);
+       if (!hand) {
+               soup_message_set_status (req, SOUP_STATUS_NOT_FOUND);
+               return;
        }
 
        if (hand->callback) {
-               const SoupUri *uri = soup_message_get_uri (req);
-               SoupServerContext ctx;
+               GHashTable *form_data_set;
 
-               ctx.msg       = req;
-               ctx.path      = uri->path;
-               ctx.method_id = soup_method_get_id (req->method);
-               ctx.auth      = auth;
-               ctx.server    = server;
-               ctx.handler   = hand;
-               ctx.sock      = sock;
+               if (uri->query)
+                       form_data_set = soup_form_decode_urlencoded (uri->query);
+               else
+                       form_data_set = NULL;
 
                /* Call method handler */
-               (*hand->callback) (&ctx, req, hand->user_data);
-       }
+               (*hand->callback) (server, req,
+                                  uri->path, form_data_set,
+                                  client, hand->user_data);
 
-       if (auth)
-               soup_server_auth_free (auth);
+               if (form_data_set)
+                       g_hash_table_destroy (form_data_set);
+       }
 }
 
 static void
-start_request (SoupServer *server, SoupSocket *server_sock)
+start_request (SoupServer *server, SoupClientContext *client)
 {
        SoupMessage *msg;
 
+       soup_client_context_cleanup (client);
+
        /* Listen for another request on this connection */
-       msg = (SoupMessage *)soup_server_message_new (server);
+       msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
+        soup_message_headers_set_encoding (msg->response_headers,
+                                           SOUP_ENCODING_CONTENT_LENGTH);
 
-       g_signal_connect (msg, "got_body", G_CALLBACK (call_handler), server_sock);
-       g_signal_connect (msg, "finished", G_CALLBACK (request_finished), server_sock);
+       g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), client);
+       g_signal_connect (msg, "got_body", G_CALLBACK (call_handler), client);
+       g_signal_connect (msg, "finished", G_CALLBACK (request_finished), client);
 
-       g_object_ref (server_sock);
-       soup_message_read_request (msg, server_sock);
+       g_signal_emit (server, signals[REQUEST_STARTED], 0,
+                      msg, client);
+
+       g_object_ref (client->sock);
+       soup_message_read_request (msg, client->sock);
 }
 
 static void
@@ -466,14 +739,29 @@ new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
 {
        SoupServer *server = user_data;
        SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+       SoupClientContext *client = g_slice_new0 (SoupClientContext);
 
-       g_object_ref (sock);
+       client->server = server;
+       client->sock = g_object_ref (sock);
        priv->client_socks = g_slist_prepend (priv->client_socks, sock);
        g_signal_connect (sock, "disconnected",
                          G_CALLBACK (socket_disconnected), server);
-       start_request (server, sock);
+       start_request (server, client);
 }
 
+/**
+ * soup_server_run_async:
+ * @server: a #SoupServer
+ *
+ * Starts @server, causing it to listen for and process incoming
+ * connections.
+ *
+ * The server actually runs in @server's #GMainContext. It will not
+ * actually perform any processing unless the appropriate main loop is
+ * running. In the simple case where you did not set the server's
+ * %SOUP_SERVER_ASYNC_CONTEXT property, this means the server will run
+ * whenever the glib main loop is running.
+ **/
 void
 soup_server_run_async (SoupServer *server)
 {
@@ -492,12 +780,20 @@ soup_server_run_async (SoupServer *server)
 
        g_signal_connect (priv->listen_sock, "new_connection",
                          G_CALLBACK (new_connection), server);
-       g_object_ref (server);
 
        return;
 
 }
 
+/**
+ * soup_server_run:
+ * @server: a #SoupServer
+ *
+ * Starts @server, causing it to listen for and process incoming
+ * connections. Unlike soup_server_run_async(), this creates a
+ * #GMainLoop and runs it, and it will not return until someone calls
+ * soup_server_quit() to stop the server.
+ **/
 void
 soup_server_run (SoupServer *server)
 {
@@ -515,6 +811,16 @@ soup_server_run (SoupServer *server)
                g_main_loop_run (priv->loop);
 }
 
+/**
+ * soup_server_quit:
+ * @server: a #SoupServer
+ *
+ * Stops processing for @server. Call this to clean up after
+ * soup_server_run_async(), or to terminate a call to soup_server_run().
+ *
+ * @server is still in a working state after this call; you can start
+ * and stop a server as many times as you want.
+ **/
 void
 soup_server_quit (SoupServer *server)
 {
@@ -528,8 +834,6 @@ soup_server_quit (SoupServer *server)
                                              server);
        if (priv->loop)
                g_main_loop_quit (priv->loop);
-
-       g_object_unref (server);
 }
 
 /**
@@ -553,140 +857,242 @@ soup_server_get_async_context (SoupServer *server)
        return priv->async_context;
 }
 
-static void
-append_handler (gpointer key, gpointer value, gpointer user_data)
+/**
+ * SoupClientContext:
+ *
+ * A #SoupClientContext provides additional information about the
+ * client making a particular request. In particular, you can use
+ * soup_client_context_get_auth_domain() and
+ * soup_client_context_get_auth_user() to determine if HTTP
+ * authentication was used successfully.
+ *
+ * soup_client_context_get_address() and/or
+ * soup_client_context_get_host() can be used to get information for
+ * logging or debugging purposes. soup_client_context_get_socket() may
+ * also be of use in some situations (eg, tracking when multiple
+ * requests are made on the same connection).
+ **/
+GType
+soup_client_context_get_type (void)
 {
-       GSList **ret = user_data;
+       static GType type = 0;
 
-       *ret = g_slist_prepend (*ret, value);
+       if (type == 0)
+               type = g_pointer_type_register_static ("SoupClientContext");
+       return type;
 }
 
-GSList *
-soup_server_list_handlers (SoupServer *server)
-{
-       SoupServerPrivate *priv;
-       GSList *ret = NULL;
-
-       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
-       priv = SOUP_SERVER_GET_PRIVATE (server);
-
-       g_hash_table_foreach (priv->handlers, append_handler, &ret);
-
-       return ret;
-}
-
-SoupServerHandler *
-soup_server_get_handler (SoupServer *server, const char *path)
+/**
+ * soup_client_context_get_socket:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the #SoupSocket that @client is associated with.
+ *
+ * If you are using this method to observe when multiple requests are
+ * made on the same persistent HTTP connection (eg, as the ntlm-test
+ * test program does), you will need to pay attention to socket
+ * destruction as well (either by using weak references, or by
+ * connecting to the #SoupSocket::disconnected signal), so that you do
+ * not get fooled when the allocator reuses the memory address of a
+ * previously-destroyed socket to represent a new socket.
+ *
+ * Return value: the #SoupSocket that @client is associated with.
+ **/
+SoupSocket *
+soup_client_context_get_socket (SoupClientContext *client)
 {
-       SoupServerPrivate *priv;
-       char *mypath, *dir;
-       SoupServerHandler *hand = NULL;
-
-       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
-       priv = SOUP_SERVER_GET_PRIVATE (server);
-
-       if (!path || !priv->handlers)
-               return priv->default_handler;
-
-       mypath = g_strdup (path);
-
-       dir = strchr (mypath, '?');
-       if (dir) *dir = '\0';
-
-       dir = mypath;
+       g_return_val_if_fail (client != NULL, NULL);
 
-       do {
-               hand = g_hash_table_lookup (priv->handlers, mypath);
-               if (hand) {
-                       g_free (mypath);
-                       return hand;
-               }
-
-               dir = strrchr (mypath, '/');
-               if (dir) *dir = '\0';
-       } while (dir);
-
-       g_free (mypath);
-
-       return priv->default_handler;
+       return client->sock;
 }
 
+/**
+ * soup_client_context_get_address:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the #SoupAddress associated with the remote end
+ * of a connection.
+ *
+ * Return value: the #SoupAddress associated with the remote end of a
+ * connection.
+ **/
 SoupAddress *
-soup_server_context_get_client_address (SoupServerContext *context)
+soup_client_context_get_address (SoupClientContext *client)
 {
-       g_return_val_if_fail (context != NULL, NULL);
+       g_return_val_if_fail (client != NULL, NULL);
 
-       return soup_socket_get_remote_address (context->sock);
+       return soup_socket_get_remote_address (client->sock);
 }
 
+/**
+ * soup_client_context_get_host:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the IP address associated with the remote end of a
+ * connection. (If you want the actual hostname, you'll have to call
+ * soup_client_context_get_address() and then call the appropriate
+ * #SoupAddress method to resolve it.)
+ *
+ * Return value: the IP address associated with the remote end of a
+ * connection.
+ **/
 const char *
-soup_server_context_get_client_host (SoupServerContext *context)
+soup_client_context_get_host (SoupClientContext *client)
 {
        SoupAddress *address;
 
-       address = soup_server_context_get_client_address (context);
+       address = soup_client_context_get_address (client);
        return soup_address_get_physical (address);
 }
 
-static SoupServerAuthContext *
-auth_context_copy (SoupServerAuthContext *auth_ctx)
+/**
+ * soup_client_context_get_auth_domain:
+ * @client: a #SoupClientContext
+ *
+ * Checks whether the request associated with @client has been
+ * authenticated, and if so returns the #SoupAuthDomain that
+ * authenticated it.
+ *
+ * Return value: a #SoupAuthDomain, or %NULL if the request was not
+ * authenticated.
+ **/
+SoupAuthDomain *
+soup_client_context_get_auth_domain (SoupClientContext *client)
 {
-       SoupServerAuthContext *new_auth_ctx = NULL;
-
-       new_auth_ctx = g_new0 (SoupServerAuthContext, 1);
-
-       new_auth_ctx->types = auth_ctx->types;
-       new_auth_ctx->callback = auth_ctx->callback;
-       new_auth_ctx->user_data = auth_ctx->user_data;
+       g_return_val_if_fail (client != NULL, NULL);
 
-       new_auth_ctx->basic_info.realm =
-               g_strdup (auth_ctx->basic_info.realm);
+       return client->auth_domain;
+}
 
-       new_auth_ctx->digest_info.realm =
-               g_strdup (auth_ctx->digest_info.realm);
-       new_auth_ctx->digest_info.allow_algorithms =
-               auth_ctx->digest_info.allow_algorithms;
-       new_auth_ctx->digest_info.force_integrity =
-               auth_ctx->digest_info.force_integrity;
+/**
+ * soup_client_context_get_auth_user:
+ * @client: a #SoupClientContext
+ *
+ * Checks whether the request associated with @client has been
+ * authenticated, and if so returns the username that the client
+ * authenticated as.
+ *
+ * Return value: the authenticated-as user, or %NULL if the request
+ * was not authenticated.
+ **/
+const char *
+soup_client_context_get_auth_user (SoupClientContext *client)
+{
+       g_return_val_if_fail (client != NULL, NULL);
 
-       return new_auth_ctx;
+       return client->auth_user;
 }
 
+/**
+ * SoupServerCallback:
+ * @server: the #SoupServer
+ * @msg: the message being processed
+ * @path: the path component of @msg's Request-URI
+ * @query: the parsed query component of @msg's Request-URI
+ * @client: additional contextual information about the client
+ * @user_data: the data passed to @soup_server_add_handler
+ *
+ * A callback used to handle requests to a #SoupServer. The callback
+ * will be invoked after receiving the request body; @msg's %method,
+ * %request_headers, and %request_body fields will be filled in.
+ *
+ * @path and @query contain the likewise-named components of the
+ * Request-URI, subject to certain assumptions. By default,
+ * #SoupServer decodes all percent-encoding in the URI path, such that
+ * "/foo%<!-- -->2Fbar" is treated the same as "/foo/bar". If your
+ * server is serving resources in some non-POSIX-filesystem namespace,
+ * you may want to distinguish those as two distinct paths. In that
+ * case, you can set the %SOUP_SERVER_RAW_PATHS property when creating
+ * the #SoupServer, and it will leave those characters undecoded. (You
+ * may want to call soup_uri_normalize() to decode any percent-encoded
+ * characters that you aren't handling specially.)
+ *
+ * @query contains the query component of the Request-URI parsed
+ * according to the rules for HTML form handling. Although this is the
+ * only commonly-used query string format in HTTP, there is nothing
+ * that actually requires that HTTP URIs use that format; if your
+ * server needs to use some other format, you can just ignore @query,
+ * and call soup_message_get_uri() and parse the URI's query field
+ * yourself.
+ *
+ * After determining what to do with the request, the callback must at
+ * a minimum call soup_message_set_status() (or
+ * soup_message_set_status_full()) on @msg to set the response status
+ * code. Additionally, it may set response headers and/or fill in the
+ * response body.
+ *
+ * If the callback cannot fully fill in the response before returning
+ * (eg, if it needs to wait for information from a database, or
+ * another network server), it should call soup_server_pause_message()
+ * to tell #SoupServer to not send the response right away. When the
+ * response is ready, call soup_server_unpause_message() to cause it
+ * to be sent.
+ *
+ * To send the response body a bit at a time using "chunked" encoding,
+ * first call soup_message_headers_set_encoding() to set
+ * %SOUP_ENCODING_CHUNKED on the %response_headers. Then call
+ * soup_message_body_append() (or soup_message_body_append_buffer())
+ * to append each chunk as it becomes ready, and
+ * soup_server_unpause_message() to make sure it's running. (The
+ * server will automatically pause the message if it is using chunked
+ * encoding but no more chunks are available.) When you are done, call
+ * soup_message_body_complete() to indicate that no more chunks are
+ * coming.
+ **/
+
+/**
+ * soup_server_add_handler:
+ * @server: a #SoupServer
+ * @path: the toplevel path for the handler
+ * @callback: callback to invoke for requests under @path
+ * @user_data: data for @callback
+ * @destroy: destroy notifier to free @user_data
+ *
+ * Adds a handler to @server for requests under @path. See the
+ * documentation for #SoupServerCallback for information about
+ * how callbacks should behave.
+ **/
 void
 soup_server_add_handler (SoupServer            *server,
                         const char            *path,
-                        SoupServerAuthContext *auth_ctx,
-                        SoupServerCallbackFn   callback,
-                        SoupServerUnregisterFn unregister,
-                        gpointer               user_data)
+                        SoupServerCallback     callback,
+                        gpointer               user_data,
+                        GDestroyNotify         destroy)
 {
        SoupServerPrivate *priv;
        SoupServerHandler *hand;
-       SoupServerAuthContext *new_auth_ctx = NULL;
 
        g_return_if_fail (SOUP_IS_SERVER (server));
        g_return_if_fail (callback != NULL);
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       if (auth_ctx)
-               new_auth_ctx = auth_context_copy (auth_ctx);
-
-       hand = g_new0 (SoupServerHandler, 1);
+       hand = g_slice_new0 (SoupServerHandler);
        hand->path       = g_strdup (path);
-       hand->auth_ctx   = new_auth_ctx;
        hand->callback   = callback;
-       hand->unregister = unregister;
+       hand->destroy    = destroy;
        hand->user_data  = user_data;
 
-       if (path) {
-               soup_server_remove_handler (server, path);
-               g_hash_table_insert (priv->handlers, hand->path, hand);
-       } else {
-               soup_server_remove_handler (server, NULL);
+       soup_server_remove_handler (server, path);
+       if (path)
+               soup_path_map_add (priv->handlers, path, hand);
+       else
                priv->default_handler = hand;
-       }
 }
 
+static void
+unregister_handler (SoupServerHandler *handler)
+{
+       if (handler->destroy)
+               handler->destroy (handler->user_data);
+}
+
+/**
+ * soup_server_remove_handler:
+ * @server: a #SoupServer
+ * @path: the toplevel path for the handler
+ *
+ * Removes the handler registered at @path.
+ **/
 void
 soup_server_remove_handler (SoupServer *server, const char *path)
 {
@@ -698,15 +1104,104 @@ soup_server_remove_handler (SoupServer *server, const char *path)
 
        if (!path) {
                if (priv->default_handler) {
-                       free_handler (server, priv->default_handler);
+                       unregister_handler (priv->default_handler);
+                       free_handler (priv->default_handler);
                        priv->default_handler = NULL;
                }
                return;
        }
 
-       hand = g_hash_table_lookup (priv->handlers, path);
-       if (hand) {
-               g_hash_table_remove (priv->handlers, path);
-               free_handler (server, hand);
+       hand = soup_path_map_lookup (priv->handlers, path);
+       if (hand && !strcmp (path, hand->path)) {
+               unregister_handler (hand);
+               soup_path_map_remove (priv->handlers, path);
        }
 }
+
+/**
+ * soup_server_add_auth_domain:
+ * @server: a #SoupServer
+ * @auth_domain: a #SoupAuthDomain
+ *
+ * Adds an authentication domain to @server. Each auth domain will
+ * have the chance to require authentication for each request that
+ * comes in; normally auth domains will require authentication for
+ * requests on certain paths that they have been set up to watch, or
+ * that meet other criteria set by the caller. If an auth domain
+ * determines that a request requires authentication (and the request
+ * doesn't contain authentication), @server will automatically reject
+ * the request with an appropriate status (401 Unauthorized or 407
+ * Proxy Authentication Required). If the request used the
+ * "100-continue" Expectation, @server will reject it before the
+ * request body is sent.
+ **/
+void
+soup_server_add_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
+{
+       SoupServerPrivate *priv;
+
+       g_return_if_fail (SOUP_IS_SERVER (server));
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       priv->auth_domains = g_slist_prepend (priv->auth_domains, auth_domain);
+}
+
+/**
+ * soup_server_remove_auth_domain:
+ * @server: a #SoupServer
+ * @auth_domain: a #SoupAuthDomain
+ *
+ * Removes @auth_domain from @server.
+ **/
+void
+soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
+{
+       SoupServerPrivate *priv;
+
+       g_return_if_fail (SOUP_IS_SERVER (server));
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       priv->auth_domains = g_slist_remove (priv->auth_domains, auth_domain);
+       g_object_unref (auth_domain);
+}
+
+/**
+ * soup_server_pause_message:
+ * @server: a #SoupServer
+ * @msg: a #SoupMessage associated with @server.
+ *
+ * Pauses I/O on @msg. This can be used when you need to return from
+ * the server handler without having the full response ready yet. Use
+ * soup_server_unpause_message() to resume I/O.
+ **/
+void
+soup_server_pause_message (SoupServer *server,
+                          SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SERVER (server));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       soup_message_io_unpause (msg);
+}
+
+/**
+ * soup_server_unpause_message:
+ * @server: a #SoupServer
+ * @msg: a #SoupMessage associated with @server.
+ *
+ * Resumes I/O on @msg. Use this to resume after calling
+ * soup_server_pause_message(), or after adding a new chunk to a
+ * chunked response.
+ *
+ * I/O won't actually resume until you return to the main loop.
+ **/
+void
+soup_server_unpause_message (SoupServer *server,
+                            SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SERVER (server));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       soup_message_io_unpause (msg);
+}
+
index aab2769..bfa0f2e 100644 (file)
@@ -7,7 +7,6 @@
 #define SOUP_SERVER_H 1
 
 #include <libsoup/soup-types.h>
-#include <libsoup/soup-method.h>
 #include <libsoup/soup-uri.h>
 
 G_BEGIN_DECLS
@@ -19,6 +18,10 @@ G_BEGIN_DECLS
 #define SOUP_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SERVER))
 #define SOUP_SERVER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SERVER, SoupServerClass))
 
+typedef struct SoupClientContext SoupClientContext;
+GType soup_client_context_get_type (void);
+#define SOUP_TYPE_CLIENT_CONTEXT (soup_client_context_get_type ())
+
 struct SoupServer {
        GObject parent;
 
@@ -27,80 +30,78 @@ struct SoupServer {
 typedef struct {
        GObjectClass parent_class;
 
+       /* signals */
+       void (*request_started)  (SoupServer *, SoupMessage *, SoupClientContext *);
+       void (*request_read)     (SoupServer *, SoupMessage *, SoupClientContext *);
+       void (*request_finished) (SoupServer *, SoupMessage *, SoupClientContext *);
+       void (*request_aborted)  (SoupServer *, SoupMessage *, SoupClientContext *);
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupServerClass;
 
 GType soup_server_get_type (void);
 
-
-typedef struct SoupServerHandler SoupServerHandler;
-
-typedef struct {
-       SoupMessage       *msg;
-       char              *path;
-       SoupMethodId       method_id;
-       SoupServerAuth    *auth;
-       SoupServer        *server;
-       SoupServerHandler *handler;
-       SoupSocket        *sock;
-} SoupServerContext;
-
-typedef void (*SoupServerCallbackFn) (SoupServerContext    *context,
-                                     SoupMessage          *msg, 
-                                     gpointer              user_data);
-
-typedef void (*SoupServerUnregisterFn) (SoupServer        *server,
-                                       SoupServerHandler *handler,
-                                       gpointer           user_data);
-
-struct SoupServerHandler {
-       char                   *path;
-
-       SoupServerAuthContext  *auth_ctx;
-
-       SoupServerCallbackFn    callback;
-       SoupServerUnregisterFn  unregister;
-       gpointer                user_data;
-};
+typedef void (*SoupServerCallback) (SoupServer        *server,
+                                   SoupMessage       *msg, 
+                                   const char        *path,
+                                   GHashTable        *query,
+                                   SoupClientContext *client,
+                                   gpointer           user_data);
 
 #define SOUP_SERVER_PORT          "port"
 #define SOUP_SERVER_INTERFACE     "interface"
 #define SOUP_SERVER_SSL_CERT_FILE "ssl-cert-file"
 #define SOUP_SERVER_SSL_KEY_FILE  "ssl-key-file"
 #define SOUP_SERVER_ASYNC_CONTEXT "async-context"
+#define SOUP_SERVER_RAW_PATHS     "raw-paths"
 
 SoupServer        *soup_server_new            (const char            *optname1,
                                               ...) G_GNUC_NULL_TERMINATED;
 
-SoupProtocol       soup_server_get_protocol   (SoupServer            *serv);
-guint              soup_server_get_port       (SoupServer            *serv);
+gboolean           soup_server_is_https       (SoupServer            *server);
+guint              soup_server_get_port       (SoupServer            *server);
 
-SoupSocket        *soup_server_get_listener   (SoupServer            *serv);
+SoupSocket        *soup_server_get_listener   (SoupServer            *server);
 
-void               soup_server_run            (SoupServer            *serv);
-void               soup_server_run_async      (SoupServer            *serv);
-void               soup_server_quit           (SoupServer            *serv);
+void               soup_server_run            (SoupServer            *server);
+void               soup_server_run_async      (SoupServer            *server);
+void               soup_server_quit           (SoupServer            *server);
 
-GMainContext      *soup_server_get_async_context (SoupServer         *serv);
+GMainContext      *soup_server_get_async_context (SoupServer         *server);
 
-/* Handlers */
+/* Handlers and auth */
 
-void               soup_server_add_handler    (SoupServer            *serv,
+void               soup_server_add_handler    (SoupServer            *server,
                                               const char            *path,
-                                              SoupServerAuthContext *auth_ctx,
-                                              SoupServerCallbackFn   callback,
-                                              SoupServerUnregisterFn unreg,
-                                              gpointer               data);
-void               soup_server_remove_handler (SoupServer            *serv,
-                                              const char            *path);
-SoupServerHandler *soup_server_get_handler    (SoupServer            *serv,
+                                              SoupServerCallback     callback,
+                                              gpointer               user_data,
+                                              GDestroyNotify         destroy);
+void               soup_server_remove_handler (SoupServer            *server,
                                               const char            *path);
-GSList            *soup_server_list_handlers  (SoupServer            *serv);
 
+void               soup_server_add_auth_domain    (SoupServer     *server,
+                                                  SoupAuthDomain *auth_domain);
+void               soup_server_remove_auth_domain (SoupServer     *server,
+                                                  SoupAuthDomain *auth_domain);
+
+/* I/O */
+
+void               soup_server_pause_message   (SoupServer           *server,
+                                               SoupMessage          *msg);
+void               soup_server_unpause_message (SoupServer           *server,
+                                               SoupMessage          *msg);
 
-/* Functions for accessing information about the specific connection */
+/* Client context */
 
-SoupAddress *soup_server_context_get_client_address (SoupServerContext *ctx);
-const char  *soup_server_context_get_client_host    (SoupServerContext *ctx);
+SoupSocket     *soup_client_context_get_socket      (SoupClientContext *client);
+SoupAddress    *soup_client_context_get_address     (SoupClientContext *client);
+const char     *soup_client_context_get_host        (SoupClientContext *client);
+SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client);
+const char     *soup_client_context_get_auth_user   (SoupClientContext *client);
 
 G_END_DECLS
 
index a5634f2..a383900 100644 (file)
 #endif
 
 #include "soup-session-async.h"
-#include "soup-connection.h"
+#include "soup-session-private.h"
+#include "soup-message-private.h"
 #include "soup-misc.h"
 
+/**
+ * SECTION:soup-session-async
+ * @short_description: Soup session for asynchronous (main-loop-based) I/O.
+ *
+ * #SoupSessionAsync is an implementation of #SoupSession that uses
+ * non-blocking I/O via the glib main loop. It is intended for use in
+ * single-threaded programs.
+ **/
+
 static gboolean run_queue (SoupSessionAsync *sa, gboolean try_pruning);
 
 static void  queue_message   (SoupSession *session, SoupMessage *req,
-                             SoupMessageCallbackFn callback,
-                             gpointer user_data);
+                             SoupSessionCallback callback, gpointer user_data);
 static guint send_message    (SoupSession *session, SoupMessage *req);
 
 G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
@@ -123,6 +132,7 @@ static gboolean
 run_queue (SoupSessionAsync *sa, gboolean try_pruning)
 {
        SoupSession *session = SOUP_SESSION (sa);
+       SoupMessageQueue *queue = soup_session_get_queue (session);
        SoupMessageQueueIter iter;
        SoupMessage *msg;
        SoupConnection *conn;
@@ -131,7 +141,9 @@ run_queue (SoupSessionAsync *sa, gboolean try_pruning)
        /* FIXME: prefer CONNECTING messages */
 
  try_again:
-       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
+       for (msg = soup_message_queue_first (queue, &iter);
+            msg;
+            msg = soup_message_queue_next (queue, &iter)) {
 
                if (!SOUP_MESSAGE_IS_STARTING (msg) ||
                    soup_message_io_in_progress (msg))
@@ -172,14 +184,27 @@ request_restarted (SoupMessage *req, gpointer sa)
        run_queue (sa, FALSE);
 }
 
+typedef struct {
+       SoupSessionAsync *sa;
+       SoupSessionCallback callback;
+       gpointer callback_data;
+} SoupSessionAsyncQueueData;
+
 static void
 final_finished (SoupMessage *req, gpointer user_data)
 {
-       SoupSessionAsync *sa = user_data;
+       SoupSessionAsyncQueueData *saqd = user_data;
+       SoupSessionAsync *sa = saqd->sa;
 
        if (!SOUP_MESSAGE_IS_STARTING (req)) {
-               g_signal_handlers_disconnect_by_func (req, final_finished, sa);
+               g_signal_handlers_disconnect_by_func (req, final_finished, saqd);
+               if (saqd->callback) {
+                       saqd->callback ((SoupSession *)sa, req,
+                                       saqd->callback_data);
+               }
+
                g_object_unref (req);
+               g_slice_free (SoupSessionAsyncQueueData, saqd);
        }
 
        run_queue (sa, FALSE);
@@ -202,19 +227,20 @@ idle_run_queue (gpointer user_data)
 
 static void
 queue_message (SoupSession *session, SoupMessage *req,
-              SoupMessageCallbackFn callback, gpointer user_data)
+              SoupSessionCallback callback, gpointer user_data)
 {
        SoupSessionAsync *sa = SOUP_SESSION_ASYNC (session);
+       SoupSessionAsyncQueueData *saqd;
 
        g_signal_connect (req, "restarted",
                          G_CALLBACK (request_restarted), sa);
 
-       if (callback) {
-               g_signal_connect (req, "finished",
-                                 G_CALLBACK (callback), user_data);
-       }
+       saqd = g_slice_new (SoupSessionAsyncQueueData);
+       saqd->sa = sa;
+       saqd->callback = callback;
+       saqd->callback_data = user_data;
        g_signal_connect_after (req, "finished",
-                               G_CALLBACK (final_finished), sa);
+                               G_CALLBACK (final_finished), saqd);
 
        SOUP_SESSION_CLASS (soup_session_async_parent_class)->queue_message (session, req, callback, user_data);
 
@@ -234,7 +260,7 @@ send_message (SoupSession *session, SoupMessage *req)
 
        queue_message (session, req, NULL, NULL);
 
-       while (req->status != SOUP_MESSAGE_STATUS_FINISHED &&
+       while (soup_message_get_io_status (req) != SOUP_MESSAGE_IO_STATUS_FINISHED &&
               !SOUP_STATUS_IS_TRANSPORT_ERROR (req->status_code))
                g_main_context_iteration (async_context, TRUE);
 
index 67e72f4..15fb22d 100644 (file)
@@ -26,6 +26,11 @@ struct SoupSessionAsync {
 typedef struct {
        SoupSessionClass parent_class;
 
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupSessionAsyncClass;
 
 GType soup_session_async_get_type (void);
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
new file mode 100644 (file)
index 0000000..5d3313a
--- /dev/null
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_PRIVATE_H
+#define SOUP_SESSION_PRIVATE_H 1
+
+#include "soup-session.h"
+#include "soup-connection.h"
+#include "soup-message-queue.h"
+
+G_BEGIN_DECLS
+
+/* internal methods */
+void              soup_session_emit_authenticate    (SoupSession *session,
+                                                    SoupMessage *msg,
+                                                    SoupAuth    *auth,
+                                                    gboolean     retrying);
+
+/* "protected" methods for subclasses */
+SoupMessageQueue *soup_session_get_queue            (SoupSession *session);
+
+SoupConnection   *soup_session_get_connection       (SoupSession *session,
+                                                    SoupMessage *msg,
+                                                    gboolean    *try_pruning,
+                                                    gboolean    *is_new);
+gboolean          soup_session_try_prune_connection (SoupSession *session);
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_PRIVATE_H */
index b45408e..2db8435 100644 (file)
 #endif
 
 #include "soup-session-sync.h"
-#include "soup-connection.h"
+#include "soup-session-private.h"
+#include "soup-message-private.h"
 #include "soup-misc.h"
 
+/**
+ * SECTION:soup-session-sync
+ * @short_description: Soup session for blocking I/O in multithreaded programs.
+ *
+ * #SoupSessionSync is an implementation of #SoupSession that uses
+ * synchronous I/O, intended for use in multi-threaded programs.
+ **/
+
 typedef struct {
        GMutex *lock;
        GCond *cond;
@@ -20,10 +29,10 @@ typedef struct {
 #define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
 
 static void  queue_message  (SoupSession *session, SoupMessage *msg,
-                            SoupMessageCallbackFn callback,
-                            gpointer user_data);
+                            SoupSessionCallback callback, gpointer user_data);
 static guint send_message   (SoupSession *session, SoupMessage *msg);
-static void  cancel_message (SoupSession *session, SoupMessage *msg);
+static void  cancel_message (SoupSession *session, SoupMessage *msg,
+                            guint status_code);
 
 G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
 
@@ -102,7 +111,7 @@ soup_session_sync_new_with_options (const char *optname1, ...)
 typedef struct {
        SoupSession *session;
        SoupMessage *msg;
-       SoupMessageCallbackFn callback;
+       SoupSessionCallback callback;
        gpointer user_data;
 } SoupSessionSyncAsyncData;
 
@@ -111,7 +120,7 @@ async_data_free (SoupSessionSyncAsyncData *sad)
 {
        g_object_unref (sad->session);
        g_object_unref (sad->msg);
-       g_free (sad);
+       g_slice_free (SoupSessionSyncAsyncData, sad);
 }
 
 static gboolean
@@ -119,7 +128,7 @@ queue_message_callback (gpointer data)
 {
        SoupSessionSyncAsyncData *sad = data;
 
-       sad->callback (sad->msg, sad->user_data);
+       sad->callback (sad->session, sad->msg, sad->user_data);
        async_data_free (sad);
        return FALSE;
 }
@@ -141,11 +150,11 @@ queue_message_thread (gpointer data)
 
 static void
 queue_message (SoupSession *session, SoupMessage *msg,
-              SoupMessageCallbackFn callback, gpointer user_data)
+              SoupSessionCallback callback, gpointer user_data)
 {
        SoupSessionSyncAsyncData *sad;
 
-       sad = g_new (SoupSessionSyncAsyncData, 1);
+       sad = g_slice_new (SoupSessionSyncAsyncData);
        sad->session = g_object_ref (session);
        sad->msg = g_object_ref (msg);
        sad->callback = callback;
@@ -181,7 +190,7 @@ wait_for_connection (SoupSession *session, SoupMessage *msg)
                                goto try_again;
                        else if (!SOUP_STATUS_IS_SUCCESSFUL (status))
                                conn = NULL;
-                       else if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
+                       else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
                                /* Message was cancelled while we were
                                 * connecting.
                                 */
@@ -201,7 +210,7 @@ wait_for_connection (SoupSession *session, SoupMessage *msg)
        g_cond_wait (priv->cond, priv->lock);
 
        /* See if something bad happened */
-       if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
+       if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
                g_mutex_unlock (priv->lock);
                return NULL;
        }
@@ -225,17 +234,17 @@ send_message (SoupSession *session, SoupMessage *msg)
 
                soup_connection_send_request (conn, msg);
                g_cond_broadcast (priv->cond);
-       } while (msg->status != SOUP_MESSAGE_STATUS_FINISHED);
+       } while (soup_message_get_io_status (msg) != SOUP_MESSAGE_IO_STATUS_FINISHED);
 
        return msg->status_code;
 }
 
 static void
-cancel_message (SoupSession *session, SoupMessage *msg)
+cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
 {
        SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
 
-       SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg);
+       SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
        g_cond_broadcast (priv->cond);
 }
 
index cf478d4..3edbb16 100644 (file)
@@ -26,6 +26,11 @@ struct SoupSessionSync {
 typedef struct {
        SoupSessionClass parent_class;
 
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupSessionSyncClass;
 
 GType soup_session_sync_get_type (void);
index 37c1900..e6e5426 100644 (file)
 #include <stdlib.h>
 
 #include "soup-auth.h"
-#include "soup-session.h"
+#include "soup-auth-basic.h"
+#include "soup-auth-digest.h"
+#include "soup-auth-manager.h"
 #include "soup-connection.h"
 #include "soup-connection-ntlm.h"
 #include "soup-marshal.h"
-#include "soup-message-filter.h"
 #include "soup-message-private.h"
 #include "soup-message-queue.h"
+#include "soup-session.h"
+#include "soup-session-private.h"
+#include "soup-socket.h"
 #include "soup-ssl.h"
 #include "soup-uri.h"
 
+/**
+ * SECTION:soup-session
+ * @short_description: Soup session state object
+ *
+ **/
+
 typedef struct {
-       SoupUri    *root_uri;
+       SoupURI    *root_uri;
 
        GSList     *connections;      /* CONTAINS: SoupConnection */
        guint       num_conns;
@@ -35,21 +45,23 @@ typedef struct {
 } SoupSessionHost;
 
 typedef struct {
-       SoupUri *proxy_uri;
+       SoupURI *proxy_uri;
+       SoupAuth *proxy_auth;
+
        guint max_conns, max_conns_per_host;
        gboolean use_ntlm;
 
        char *ssl_ca_file;
        SoupSSLCredentials *ssl_creds;
 
-       GSList *filters;
+       SoupMessageQueue *queue;
 
-       GHashTable *hosts; /* SoupUri -> SoupSessionHost */
+       SoupAuthManager *auth_manager;
+
+       GHashTable *hosts; /* SoupURI -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
 
-       SoupSessionHost *proxy_host;
-
        /* Must hold the host_lock before potentially creating a
         * new SoupSessionHost, or adding/removing a connection.
         * Must not emit signals or destroy objects while holding it.
@@ -65,30 +77,28 @@ typedef struct {
 } SoupSessionPrivate;
 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
 
-static guint    host_uri_hash  (gconstpointer key);
-static gboolean host_uri_equal (gconstpointer v1, gconstpointer v2);
 static void     free_host      (SoupSessionHost *host);
 
-static void setup_message   (SoupMessageFilter *filter, SoupMessage *msg);
-
 static void queue_message   (SoupSession *session, SoupMessage *msg,
-                            SoupMessageCallbackFn callback,
-                            gpointer user_data);
+                            SoupSessionCallback callback, gpointer user_data);
 static void requeue_message (SoupSession *session, SoupMessage *msg);
-static void cancel_message  (SoupSession *session, SoupMessage *msg);
+static void cancel_message  (SoupSession *session, SoupMessage *msg,
+                            guint status_code);
+
+/* temporary until we fix this to index hosts by SoupAddress */
+extern guint     soup_uri_host_hash  (gconstpointer  key);
+extern gboolean  soup_uri_host_equal (gconstpointer  v1,
+                                     gconstpointer  v2);
+extern SoupURI  *soup_uri_copy_root  (SoupURI *uri);
 
 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 4
 
-static void filter_iface_init (SoupMessageFilterClass *filter_class);
-
-G_DEFINE_TYPE_EXTENDED (SoupSession, soup_session, G_TYPE_OBJECT, 0,
-                       G_IMPLEMENT_INTERFACE (SOUP_TYPE_MESSAGE_FILTER,
-                                              filter_iface_init))
+G_DEFINE_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
 
 enum {
+       REQUEST_STARTED,
        AUTHENTICATE,
-       REAUTHENTICATE,
        LAST_SIGNAL
 };
 
@@ -118,16 +128,21 @@ soup_session_init (SoupSession *session)
 {
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       session->queue = soup_message_queue_new ();
+       priv->queue = soup_message_queue_new ();
 
        priv->host_lock = g_mutex_new ();
-       priv->hosts = g_hash_table_new (host_uri_hash, host_uri_equal);
+       priv->hosts = g_hash_table_new (soup_uri_host_hash,
+                                       soup_uri_host_equal);
        priv->conns = g_hash_table_new (NULL, NULL);
 
        priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
        priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
 
        priv->timeout = 0;
+
+       priv->auth_manager = soup_auth_manager_new (session);
+       soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_BASIC);
+       soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_DIGEST);
 }
 
 static gboolean
@@ -144,7 +159,8 @@ cleanup_hosts (SoupSessionPrivate *priv)
 
        g_mutex_lock (priv->host_lock);
        old_hosts = priv->hosts;
-       priv->hosts = g_hash_table_new (host_uri_hash, host_uri_equal);
+       priv->hosts = g_hash_table_new (soup_uri_host_hash,
+                                       soup_uri_host_equal);
        g_mutex_unlock (priv->host_lock);
 
        g_hash_table_foreach_remove (old_hosts, foreach_free_host, NULL);
@@ -156,18 +172,10 @@ dispose (GObject *object)
 {
        SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GSList *f;
 
        soup_session_abort (session);
        cleanup_hosts (priv);
 
-       if (priv->filters) {
-               for (f = priv->filters; f; f = f->next)
-                       g_object_unref (f->data);
-               g_slist_free (priv->filters);
-               priv->filters = NULL;
-       }
-
        G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
 }
 
@@ -177,16 +185,16 @@ finalize (GObject *object)
        SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       soup_message_queue_destroy (session->queue);
+       soup_message_queue_destroy (priv->queue);
 
        g_mutex_free (priv->host_lock);
        g_hash_table_destroy (priv->hosts);
        g_hash_table_destroy (priv->conns);
 
+       soup_auth_manager_free (priv->auth_manager);
+
        if (priv->proxy_uri)
                soup_uri_free (priv->proxy_uri);
-       if (priv->proxy_host)
-               free_host (priv->proxy_host);
 
        if (priv->ssl_creds)
                soup_ssl_free_client_credentials (priv->ssl_creds);
@@ -218,92 +226,58 @@ soup_session_class_init (SoupSessionClass *session_class)
        /* signals */
 
        /**
-        * SoupSession::authenticate:
+        * SoupSession::request-started:
         * @session: the session
-        * @msg: the #SoupMessage being sent
-        * @auth_type: the authentication type
-        * @auth_realm: the realm being authenticated to
-        * @username: the signal handler should set this to point to
-        * the provided username
-        * @password: the signal handler should set this to point to
-        * the provided password
+        * @msg: the request being sent
+        * @socket: the socket the request is being sent on
         *
-        * Emitted when the session requires authentication. The
-        * credentials may come from the user, or from cached
-        * information. If no credentials are available, leave
-        * @username and @password unchanged.
-        *
-        * If the provided credentials fail, the #reauthenticate
-        * signal will be emitted.
+        * Emitted just before a request is sent.
         **/
-       signals[AUTHENTICATE] =
-               g_signal_new ("authenticate",
+       signals[REQUEST_STARTED] =
+               g_signal_new ("request-started",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupSessionClass, authenticate),
+                             G_STRUCT_OFFSET (SoupSessionClass, request_started),
                              NULL, NULL,
-                             soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
-                             G_TYPE_NONE, 5,
+                             soup_marshal_NONE__OBJECT_OBJECT,
+                             G_TYPE_NONE, 2,
                              SOUP_TYPE_MESSAGE,
-                             G_TYPE_STRING,
-                             G_TYPE_STRING,
-                             G_TYPE_POINTER,
-                             G_TYPE_POINTER);
+                             SOUP_TYPE_SOCKET);
 
        /**
-        * SoupSession::reauthenticate:
+        * SoupSession::authenticate:
         * @session: the session
         * @msg: the #SoupMessage being sent
-        * @auth_type: the authentication type
-        * @auth_realm: the realm being authenticated to
-        * @username: the signal handler should set this to point to
-        * the provided username
-        * @password: the signal handler should set this to point to
-        * the provided password
-        *
-        * Emitted when the credentials provided by the application to
-        * the #authenticate signal have failed. This gives the
-        * application a second chance to provide authentication
-        * credentials. If the new credentials also fail, #SoupSession
-        * will emit #reauthenticate again, and will continue doing so
-        * until the provided credentials work, or a #reauthenticate
-        * signal emission "fails" (because the handler left @username
-        * and @password unchanged). At that point, the 401 or 407
-        * error status will be returned to the caller.
+        * @auth: the #SoupAuth to authenticate
+        * @retrying: %TRUE if this is the second (or later) attempt
         *
-        * If your application only uses cached passwords, it should
-        * only connect to #authenticate, and not #reauthenticate.
-        *
-        * If your application always prompts the user for a password,
-        * and never uses cached information, then you can connect the
-        * same handler to #authenticate and #reauthenticate.
-        *
-        * To get standard web-browser behavior, return either cached
-        * information or a user-provided password (whichever is
-        * available) from the #authenticate handler, but return only
-        * user-provided information from the #reauthenticate handler.
+        * Emitted when the session requires authentication. If
+        * credentials are available call soup_auth_authenticate() on
+        * @auth. If these credentials fail, the signal will be
+        * emitted again, with @retrying set to %TRUE, which will
+        * continue until you return without calling
+        * soup_auth_authenticate() on @auth.
         **/
-       signals[REAUTHENTICATE] =
-               g_signal_new ("reauthenticate",
+       signals[AUTHENTICATE] =
+               g_signal_new ("authenticate",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupSessionClass, reauthenticate),
+                             G_STRUCT_OFFSET (SoupSessionClass, authenticate),
                              NULL, NULL,
-                             soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
-                             G_TYPE_NONE, 5,
+                             soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+                             G_TYPE_NONE, 3,
                              SOUP_TYPE_MESSAGE,
-                             G_TYPE_STRING,
-                             G_TYPE_STRING,
-                             G_TYPE_POINTER,
-                             G_TYPE_POINTER);
+                             SOUP_TYPE_AUTH,
+                             G_TYPE_BOOLEAN);
 
        /* 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_spec_boxed (SOUP_SESSION_PROXY_URI,
+                                   "Proxy URI",
+                                   "The HTTP Proxy to use for this session",
+                                   SOUP_TYPE_URI,
+                                   G_PARAM_READWRITE));
        g_object_class_install_property (
                object_class, PROP_MAX_CONNS,
                g_param_spec_int (SOUP_SESSION_MAX_CONNS,
@@ -320,7 +294,7 @@ soup_session_class_init (SoupSessionClass *session_class)
                                  "The maximum number of connections that the session can open at once to a given host",
                                  1,
                                  G_MAXINT,
-                                 4,
+                                 2,
                                  G_PARAM_READWRITE));
        g_object_class_install_property (
                object_class, PROP_USE_NTLM,
@@ -351,16 +325,8 @@ soup_session_class_init (SoupSessionClass *session_class)
                                   G_PARAM_READWRITE));
 }
 
-static void
-filter_iface_init (SoupMessageFilterClass *filter_class)
-{
-       /* interface implementation */
-       filter_class->setup_message = setup_message;
-}
-
-
 static gboolean
-safe_uri_equal (const SoupUri *a, const SoupUri *b)
+safe_uri_equal (SoupURI *a, SoupURI *b)
 {
        if (!a && !b)
                return TRUE;
@@ -389,22 +355,22 @@ set_property (GObject *object, guint prop_id,
 {
        SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       gpointer pval;
+       SoupURI *uri;
        gboolean need_abort = FALSE;
        gboolean ca_file_changed = FALSE;
        const char *new_ca_file;
 
        switch (prop_id) {
        case PROP_PROXY_URI:
-               pval = g_value_get_pointer (value);
+               uri = g_value_get_boxed (value);
 
-               if (!safe_uri_equal (priv->proxy_uri, pval))
+               if (!safe_uri_equal (priv->proxy_uri, uri))
                        need_abort = TRUE;
 
                if (priv->proxy_uri)
                        soup_uri_free (priv->proxy_uri);
 
-               priv->proxy_uri = pval ? soup_uri_copy (pval) : NULL;
+               priv->proxy_uri = uri ? soup_uri_copy (uri) : NULL;
 
                if (need_abort) {
                        soup_session_abort (session);
@@ -462,9 +428,7 @@ get_property (GObject *object, guint prop_id,
 
        switch (prop_id) {
        case PROP_PROXY_URI:
-               g_value_set_pointer (value, priv->proxy_uri ?
-                                    soup_uri_copy (priv->proxy_uri) :
-                                    NULL);
+               g_value_set_boxed (value, priv->proxy_uri);
                break;
        case PROP_MAX_CONNS:
                g_value_set_int (value, priv->max_conns);
@@ -491,47 +455,6 @@ get_property (GObject *object, guint prop_id,
 
 
 /**
- * soup_session_add_filter:
- * @session: a #SoupSession
- * @filter: an object implementing the #SoupMessageFilter interface
- *
- * Adds @filter to @session's list of message filters to be applied to
- * all messages.
- **/
-void
-soup_session_add_filter (SoupSession *session, SoupMessageFilter *filter)
-{
-       SoupSessionPrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       g_object_ref (filter);
-       priv->filters = g_slist_prepend (priv->filters, filter);
-}
-
-/**
- * soup_session_remove_filter:
- * @session: a #SoupSession
- * @filter: an object implementing the #SoupMessageFilter interface
- *
- * Removes @filter from @session's list of message filters
- **/
-void
-soup_session_remove_filter (SoupSession *session, SoupMessageFilter *filter)
-{
-       SoupSessionPrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       priv->filters = g_slist_remove (priv->filters, filter);
-       g_object_unref (filter);
-}
-
-/**
  * soup_session_get_async_context:
  * @session: a #SoupSession
  *
@@ -553,38 +476,17 @@ soup_session_get_async_context (SoupSession *session)
 }
 
 /* Hosts */
-static guint
-host_uri_hash (gconstpointer key)
-{
-       const SoupUri *uri = key;
-
-       return (uri->protocol << 16) + uri->port + g_str_hash (uri->host);
-}
-
-static gboolean
-host_uri_equal (gconstpointer v1, gconstpointer v2)
-{
-       const SoupUri *one = v1;
-       const SoupUri *two = v2;
-
-       if (one->protocol != two->protocol)
-               return FALSE;
-       if (one->port != two->port)
-               return FALSE;
-
-       return strcmp (one->host, two->host) == 0;
-}
 
 static SoupSessionHost *
-soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
+soup_session_host_new (SoupSession *session, SoupURI *source_uri)
 {
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
        SoupSessionHost *host;
 
-       host = g_new0 (SoupSessionHost, 1);
+       host = g_slice_new0 (SoupSessionHost);
        host->root_uri = soup_uri_copy_root (source_uri);
 
-       if (host->root_uri->protocol == SOUP_PROTOCOL_HTTPS &&
+       if (host->root_uri->scheme == SOUP_URI_SCHEME_HTTPS &&
            !priv->ssl_creds) {
                priv->ssl_creds =
                        soup_ssl_get_client_credentials (priv->ssl_ca_file);
@@ -602,7 +504,7 @@ get_host_for_message (SoupSession *session, SoupMessage *msg)
 {
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
        SoupSessionHost *host;
-       const SoupUri *source = soup_message_get_uri (msg);
+       SoupURI *source = soup_message_get_uri (msg);
 
        host = g_hash_table_lookup (priv->hosts, source);
        if (host)
@@ -614,36 +516,6 @@ get_host_for_message (SoupSession *session, SoupMessage *msg)
        return host;
 }
 
-/* Note: get_proxy_host doesn't lock the host_lock. The caller must do
- * it itself if there's a chance the host doesn't already exist.
- */
-static SoupSessionHost *
-get_proxy_host (SoupSession *session)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       if (priv->proxy_host || !priv->proxy_uri)
-               return priv->proxy_host;
-
-       priv->proxy_host =
-               soup_session_host_new (session, priv->proxy_uri);
-       return priv->proxy_host;
-}
-
-static void
-free_realm (gpointer path, gpointer scheme_realm, gpointer data)
-{
-       g_free (path);
-       g_free (scheme_realm);
-}
-
-static void
-free_auth (gpointer scheme_realm, gpointer auth, gpointer data)
-{
-       g_free (scheme_realm);
-       g_object_unref (auth);
-}
-
 static void
 free_host (SoupSessionHost *host)
 {
@@ -654,257 +526,22 @@ free_host (SoupSessionHost *host)
                soup_connection_disconnect (conn);
        }
 
-       if (host->auth_realms) {
-               g_hash_table_foreach (host->auth_realms, free_realm, NULL);
-               g_hash_table_destroy (host->auth_realms);
-       }
-       if (host->auths) {
-               g_hash_table_foreach (host->auths, free_auth, NULL);
-               g_hash_table_destroy (host->auths);
-       }
-
        soup_uri_free (host->root_uri);
-       g_free (host);
+       g_slice_free (SoupSessionHost, host);
 }      
 
-/* Authentication */
-
-static SoupAuth *
-lookup_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
-{
-       SoupSessionHost *host;
-       char *path, *dir;
-       const char *realm, *const_path;
-
-       if (proxy) {
-               host = get_proxy_host (session);
-               const_path = "/";
-       } else {
-               host = get_host_for_message (session, msg);
-               const_path = soup_message_get_uri (msg)->path;
-
-               if (!const_path)
-                       const_path = "/";
-       }
-       g_return_val_if_fail (host != NULL, NULL);
-
-       if (!host->auth_realms)
-               return NULL;
-
-       path = g_strdup (const_path);
-       dir = path;
-        do {
-                realm = g_hash_table_lookup (host->auth_realms, path);
-                if (realm)
-                       break;
-
-                dir = strrchr (path, '/');
-                if (dir) {
-                       if (dir[1])
-                               dir[1] = '\0';
-                       else
-                               *dir = '\0';
-               }
-        } while (dir);
-
-       g_free (path);
-       if (realm)
-               return g_hash_table_lookup (host->auths, realm);
-       else
-               return NULL;
-}
-
-static void
-invalidate_auth (SoupSessionHost *host, SoupAuth *auth)
-{
-       char *info;
-       gpointer key, value;
-
-       info = soup_auth_get_info (auth);
-       if (g_hash_table_lookup_extended (host->auths, info, &key, &value) &&
-           auth == (SoupAuth *)value) {
-               g_hash_table_remove (host->auths, info);
-               g_free (key);
-               g_object_unref (auth);
-       }
-       g_free (info);
-}
-
-static gboolean
-authenticate_auth (SoupSession *session, SoupAuth *auth,
-                  SoupMessage *msg, gboolean prior_auth_failed,
-                  gboolean proxy)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       const SoupUri *uri;
-       char *username = NULL, *password = NULL;
-
-       if (proxy)
-               uri = priv->proxy_uri;
-       else
-               uri = soup_message_get_uri (msg);
-
-       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,
-                      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 gboolean
-update_auth_internal (SoupSession *session, SoupMessage *msg,
-                     const GSList *headers, gboolean proxy)
-{
-       SoupSessionHost *host;
-       SoupAuth *new_auth, *prior_auth, *old_auth;
-       gpointer old_path, old_auth_info;
-       const SoupUri *msg_uri;
-       const char *path;
-       char *auth_info;
-       GSList *pspace, *p;
-       gboolean prior_auth_failed = FALSE;
-
-       if (proxy)
-               host = get_proxy_host (session);
-       else
-               host = get_host_for_message (session, msg);
-
-       g_return_val_if_fail (host != NULL, FALSE);
-
-       /* Try to construct a new auth from the headers; if we can't,
-        * there's no way we'll be able to authenticate.
-        */
-       msg_uri = soup_message_get_uri (msg);
-       new_auth = soup_auth_new_from_header_list (headers);
-       if (!new_auth)
-               return FALSE;
-
-       auth_info = soup_auth_get_info (new_auth);
-
-       /* See if this auth is the same auth we used last time */
-       prior_auth = proxy ? soup_message_get_proxy_auth (msg) : soup_message_get_auth (msg);
-       if (prior_auth) {
-               char *old_auth_info = soup_auth_get_info (prior_auth);
-
-               if (!strcmp (old_auth_info, auth_info)) {
-                       /* The server didn't like the username/password we
-                        * provided before. Invalidate it and note this fact.
-                        */
-                       invalidate_auth (host, prior_auth);
-                       prior_auth_failed = TRUE;
-               }
-               g_free (old_auth_info);
-       }
-
-       if (!host->auth_realms) {
-               host->auth_realms = g_hash_table_new (g_str_hash, g_str_equal);
-               host->auths = g_hash_table_new (g_str_hash, g_str_equal);
-       }
-
-       /* Record where this auth realm is used. RFC 2617 is somewhat
-        * unclear about the scope of protection spaces with regard to
-        * proxies. The only mention of it is as an aside in section
-        * 3.2.1, where it is defining the fields of a Digest
-        * challenge and says that the protection space is always the
-        * entire proxy. Is this the case for all authentication
-        * schemes or just Digest? Who knows, but we're assuming all.
-        */
-       if (proxy)
-               pspace = g_slist_prepend (NULL, g_strdup (""));
-       else
-               pspace = soup_auth_get_protection_space (new_auth, msg_uri);
-
-       for (p = pspace; p; p = p->next) {
-               path = p->data;
-               if (g_hash_table_lookup_extended (host->auth_realms, path,
-                                                 &old_path, &old_auth_info)) {
-                       g_hash_table_remove (host->auth_realms, old_path);
-                       g_free (old_path);
-                       g_free (old_auth_info);
-               }
-
-               g_hash_table_insert (host->auth_realms,
-                                    g_strdup (path), g_strdup (auth_info));
-       }
-       soup_auth_free_protection_space (new_auth, pspace);
-
-       /* Now, make sure the auth is recorded. (If there's a
-        * pre-existing auth, we keep that rather than the new one,
-        * since the old one might already be authenticated.)
-        */
-       old_auth = g_hash_table_lookup (host->auths, auth_info);
-       if (old_auth) {
-               g_free (auth_info);
-               g_object_unref (new_auth);
-               new_auth = old_auth;
-       } else 
-               g_hash_table_insert (host->auths, auth_info, new_auth);
-
-       /* If we need to authenticate, try to do it. */
-       if (!soup_auth_is_authenticated (new_auth)) {
-               return authenticate_auth (session, new_auth,
-                                         msg, prior_auth_failed, proxy);
-       }
-
-       /* Otherwise we're good. */
-       return TRUE;
-}
-
-static void
-connection_authenticate (SoupConnection *conn, SoupMessage *msg,
-                        const char *auth_type, const char *auth_realm,
-                        char **username, char **password, gpointer session)
+void
+soup_session_emit_authenticate (SoupSession *session, SoupMessage *msg,
+                               SoupAuth *auth, gboolean retrying)
 {
-       g_signal_emit (session, signals[AUTHENTICATE], 0,
-                      msg, auth_type, auth_realm, username, password);
+       g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
 }
 
 static void
-connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
-                          const char *auth_type, const char *auth_realm,
-                          char **username, char **password,
-                          gpointer user_data)
+reemit_authenticate (SoupConnection *conn, SoupMessage *msg,
+                    SoupAuth *auth, gboolean retrying, gpointer session)
 {
-       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;
-       const GSList *headers;
-       gboolean proxy;
-
-       if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
-               headers = soup_message_get_header_list (msg->response_headers,
-                                                       "Proxy-Authenticate");
-               proxy = TRUE;
-       } else {
-               headers = soup_message_get_header_list (msg->response_headers,
-                                                       "WWW-Authenticate");
-               proxy = FALSE;
-       }
-       if (!headers)
-               return;
-
-       if (update_auth_internal (session, msg, headers, proxy))
-               soup_session_requeue_message (session, msg);
+       soup_session_emit_authenticate (session, msg, auth, retrying);
 }
 
 static void
@@ -912,9 +549,12 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
 {
        SoupSession *session = user_data;
        const char *new_loc;
-       SoupUri *new_uri;
+       SoupURI *new_uri;
+
+       if (!SOUP_STATUS_IS_REDIRECTION (msg->status_code))
+               return;
 
-       new_loc = soup_message_get_header (msg->response_headers, "Location");
+       new_loc = soup_message_headers_get (msg->response_headers, "Location");
        if (!new_loc)
                return;
 
@@ -936,47 +576,13 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
 }
 
 static void
-add_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
-{
-       SoupAuth *auth;
-
-       auth = lookup_auth (session, msg, proxy);
-       if (auth && !soup_auth_is_authenticated (auth)) {
-               if (!authenticate_auth (session, auth, msg, FALSE, proxy))
-                       auth = NULL;
-       }
-
-       if (proxy)
-               soup_message_set_proxy_auth (msg, auth);
-       else
-               soup_message_set_auth (msg, auth);
-}
-
-static void
-setup_message (SoupMessageFilter *filter, SoupMessage *msg)
+connection_started_request (SoupConnection *conn, SoupMessage *msg,
+                           gpointer data)
 {
-       SoupSession *session = SOUP_SESSION (filter);
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GSList *f;
+       SoupSession *session = data;
 
-       for (f = priv->filters; f; f = f->next) {
-               filter = f->data;
-               soup_message_filter_setup_message (filter, msg);
-       }
-
-       add_auth (session, msg, FALSE);
-       soup_message_add_status_code_handler (
-               msg, SOUP_STATUS_UNAUTHORIZED,
-               SOUP_HANDLER_POST_BODY,
-               authorize_handler, session);
-
-       if (priv->proxy_uri) {
-               add_auth (session, msg, TRUE);
-               soup_message_add_status_code_handler  (
-                       msg, SOUP_STATUS_PROXY_UNAUTHORIZED,
-                       SOUP_HANDLER_POST_BODY,
-                       authorize_handler, session);
-       }
+       g_signal_emit (session, signals[REQUEST_STARTED], 0,
+                      msg, soup_connection_get_socket (conn));
 }
 
 static void
@@ -1099,14 +705,14 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
         * any messages waiting for this host, since they're out
         * of luck.
         */
-       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
+       for (msg = soup_message_queue_first (priv->queue, &iter); msg; msg = soup_message_queue_next (priv->queue, &iter)) {
                if (get_host_for_message (session, msg) == host) {
                        if (status == SOUP_STATUS_TRY_AGAIN) {
-                               if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING)
-                                       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
+                               if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING)
+                                       soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
                        } else {
-                               soup_message_set_status (msg, status);
-                               soup_session_cancel_message (session, msg);
+                               soup_session_cancel_message (session, msg,
+                                                            status);
                        }
                }
        }
@@ -1171,7 +777,7 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
                }
        }
 
-       if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING) {
+       if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING) {
                /* We already started a connection for this
                 * message, so don't start another one.
                 */
@@ -1190,19 +796,12 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
                return NULL;
        }
 
-       /* Make sure priv->proxy_host gets set now while
-        * we have the host_lock.
-        */
-       if (priv->proxy_uri)
-               get_proxy_host (session);
-
        conn = g_object_new (
                (priv->use_ntlm ?
                 SOUP_TYPE_CONNECTION_NTLM : SOUP_TYPE_CONNECTION),
                SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
                SOUP_CONNECTION_PROXY_URI, priv->proxy_uri,
                SOUP_CONNECTION_SSL_CREDENTIALS, priv->ssl_creds,
-               SOUP_CONNECTION_MESSAGE_FILTER, session,
                SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
                SOUP_CONNECTION_TIMEOUT, priv->timeout,
                NULL);
@@ -1212,11 +811,11 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
        g_signal_connect (conn, "disconnected",
                          G_CALLBACK (connection_disconnected),
                          session);
-       g_signal_connect (conn, "authenticate",
-                         G_CALLBACK (connection_authenticate),
+       g_signal_connect (conn, "request_started",
+                         G_CALLBACK (connection_started_request),
                          session);
-       g_signal_connect (conn, "reauthenticate",
-                         G_CALLBACK (connection_reauthenticate),
+       g_signal_connect (conn, "authenticate",
+                         G_CALLBACK (reemit_authenticate),
                          session);
 
        g_hash_table_insert (priv->conns, conn, host);
@@ -1231,47 +830,57 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
        /* Mark the request as connecting, so we don't try to open
         * another new connection for it while waiting for this one.
         */
-       msg->status = SOUP_MESSAGE_STATUS_CONNECTING;
+       soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_CONNECTING);
 
        g_mutex_unlock (priv->host_lock);
        *is_new = TRUE;
        return conn;
 }
 
+SoupMessageQueue *
+soup_session_get_queue (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       return priv->queue;
+}
+
 static void
 message_finished (SoupMessage *msg, gpointer user_data)
 {
        SoupSession *session = user_data;
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 
        if (!SOUP_MESSAGE_IS_STARTING (msg)) {
-               soup_message_queue_remove_message (session->queue, msg);
+               soup_message_queue_remove_message (priv->queue, msg);
                g_signal_handlers_disconnect_by_func (msg, message_finished, session);
        }
 }
 
 static void
 queue_message (SoupSession *session, SoupMessage *msg,
-              SoupMessageCallbackFn callback, gpointer user_data)
+              SoupSessionCallback callback, gpointer user_data)
 {
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
        g_signal_connect_after (msg, "finished",
                                G_CALLBACK (message_finished), session);
 
        if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
-               soup_message_add_status_class_handler (
-                       msg, SOUP_STATUS_CLASS_REDIRECT,
-                       SOUP_HANDLER_POST_BODY,
-                       redirect_handler, session);
+               soup_message_add_header_handler (
+                       msg, "got_body", "Location",
+                       G_CALLBACK (redirect_handler), session);
        }
 
-       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
-       soup_message_queue_append (session->queue, msg);
+       soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
+       soup_message_queue_append (priv->queue, msg);
 }
 
 /**
  * soup_session_queue_message:
  * @session: a #SoupSession
  * @msg: the message to queue
- * @callback: a #SoupMessageCallbackFn which will be called after the
+ * @callback: a #SoupSessionCallback which will be called after the
  * message completes or when an unrecoverable error occurs.
  * @user_data: a pointer passed to @callback.
  * 
@@ -1286,7 +895,7 @@ queue_message (SoupSession *session, SoupMessage *msg,
  */
 void
 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
-                           SoupMessageCallbackFn callback, gpointer user_data)
+                           SoupSessionCallback callback, gpointer user_data)
 {
        g_return_if_fail (SOUP_IS_SESSION (session));
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
@@ -1298,7 +907,7 @@ soup_session_queue_message (SoupSession *session, SoupMessage *msg,
 static void
 requeue_message (SoupSession *session, SoupMessage *msg)
 {
-       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
+       soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
 }
 
 /**
@@ -1342,10 +951,55 @@ soup_session_send_message (SoupSession *session, SoupMessage *msg)
 }
 
 
+/**
+ * soup_session_pause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
+ * resume I/O.
+ **/
+void
+soup_session_pause_message (SoupSession *session,
+                           SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       soup_message_io_pause (msg);
+}
+
+/**
+ * soup_session_unpause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Resumes HTTP I/O on @msg. Use this to resume after calling
+ * soup_sessino_pause_message().
+ *
+ * If @msg is being sent via blocking I/O, this will resume reading or
+ * writing immediately. If @msg is using non-blocking I/O, then
+ * reading or writing won't resume until you return to the main loop.
+ **/
+void
+soup_session_unpause_message (SoupSession *session,
+                             SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       soup_message_io_unpause (msg);
+}
+
+
 static void
-cancel_message (SoupSession *session, SoupMessage *msg)
+cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
 {
-       soup_message_queue_remove_message (session->queue, msg);
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       soup_message_queue_remove_message (priv->queue, msg);
+       soup_message_io_stop (msg);
+       soup_message_set_status (msg, status_code);
        soup_message_finished (msg);
 }
 
@@ -1353,18 +1007,21 @@ cancel_message (SoupSession *session, SoupMessage *msg)
  * soup_session_cancel_message:
  * @session: a #SoupSession
  * @msg: the message to cancel
+ * @status_code: status code to set on @msg (generally
+ * %SOUP_STATUS_CANCELLED)
  *
- * Causes @session to immediately finish processing @msg. You should
- * set a status code on @msg with soup_message_set_status() before
- * calling this function.
+ * Causes @session to immediately finish processing @msg, with a final
+ * status_code of @status_code. Depending on when you cancel it, the
+ * response state may be incomplete or inconsistent.
  **/
 void
-soup_session_cancel_message (SoupSession *session, SoupMessage *msg)
+soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
+                            guint status_code)
 {
        g_return_if_fail (SOUP_IS_SESSION (session));
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg);
+       SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
 }
 
 static void
@@ -1393,9 +1050,11 @@ soup_session_abort (SoupSession *session)
        g_return_if_fail (SOUP_IS_SESSION (session));
        priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
-               soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
-               soup_session_cancel_message (session, msg);
+       for (msg = soup_message_queue_first (priv->queue, &iter);
+            msg;
+            msg = soup_message_queue_next (priv->queue, &iter)) {
+               soup_session_cancel_message (session, msg,
+                                            SOUP_STATUS_CANCELLED);
        }
 
        /* Close all connections */
index 38ce382..cc63dcf 100644 (file)
@@ -8,7 +8,6 @@
 
 #include <libsoup/soup-types.h>
 #include <libsoup/soup-message.h>
-#include <libsoup/soup-message-queue.h>
 
 G_BEGIN_DECLS
 
@@ -19,33 +18,38 @@ G_BEGIN_DECLS
 #define SOUP_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION))
 #define SOUP_SESSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION, SoupSessionClass))
 
+typedef void (*SoupSessionCallback) (SoupSession           *session,
+                                    SoupMessage           *msg,
+                                    gpointer               user_data);
+
 struct SoupSession {
        GObject parent;
 
-       /* protected */
-       SoupMessageQueue *queue;
 };
 
 typedef struct {
        GObjectClass parent_class;
 
        /* signals */
-       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);
+       void (*request_started) (SoupSession *, SoupMessage *, SoupSocket *);
+       void (*authenticate)    (SoupSession *, SoupMessage *,
+                                SoupAuth *, gboolean);
 
        /* methods */
        void  (*queue_message)   (SoupSession *session, SoupMessage *msg,
-                                 SoupMessageCallbackFn callback,
+                                 SoupSessionCallback callback,
                                  gpointer user_data);
        void  (*requeue_message) (SoupSession *session, SoupMessage *msg);
        guint (*send_message)    (SoupSession *session, SoupMessage *msg);
 
-       void  (*cancel_message)  (SoupSession *session, SoupMessage *msg);
+       void  (*cancel_message)  (SoupSession *session, SoupMessage *msg,
+                                 guint status_code);
 
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupSessionClass;
 
 GType soup_session_get_type (void);
@@ -58,16 +62,11 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_ASYNC_CONTEXT      "async-context"
 #define SOUP_SESSION_TIMEOUT           "timeout"
 
-void            soup_session_add_filter       (SoupSession           *session,
-                                              SoupMessageFilter     *filter);
-void            soup_session_remove_filter    (SoupSession           *session,
-                                              SoupMessageFilter     *filter);
-
 GMainContext   *soup_session_get_async_context(SoupSession           *session);
 
 void            soup_session_queue_message    (SoupSession           *session,
                                               SoupMessage           *msg,
-                                              SoupMessageCallbackFn  callback,
+                                              SoupSessionCallback    callback,
                                               gpointer               user_data);
 void            soup_session_requeue_message  (SoupSession           *session,
                                               SoupMessage           *msg);
@@ -75,17 +74,15 @@ void            soup_session_requeue_message  (SoupSession           *session,
 guint           soup_session_send_message     (SoupSession           *session,
                                               SoupMessage           *msg);
 
-void            soup_session_cancel_message   (SoupSession           *session,
+void            soup_session_pause_message    (SoupSession           *session,
+                                              SoupMessage           *msg);
+void            soup_session_unpause_message  (SoupSession           *session,
                                               SoupMessage           *msg);
-void            soup_session_abort            (SoupSession           *session);
-
 
-/* Protected methods */
-SoupConnection *soup_session_get_connection       (SoupSession    *session,
-                                                  SoupMessage    *msg,
-                                                  gboolean       *try_pruning,
-                                                  gboolean       *is_new);
-gboolean        soup_session_try_prune_connection (SoupSession    *session);
+void            soup_session_cancel_message   (SoupSession           *session,
+                                              SoupMessage           *msg,
+                                              guint                  status_code);
+void            soup_session_abort            (SoupSession           *session);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-soap-message.c b/libsoup/soup-soap-message.c
deleted file mode 100644 (file)
index 0c50c72..0000000
+++ /dev/null
@@ -1,827 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2003, Novell, Inc.
- */
-
-#include <string.h>
-#include "soup-misc.h"
-#include "soup-soap-message.h"
-#include "soup-uri.h"
-
-G_DEFINE_TYPE (SoupSoapMessage, soup_soap_message, SOUP_TYPE_MESSAGE)
-
-typedef struct {
-       /* Serialization fields */
-       xmlDocPtr doc;
-       xmlNodePtr last_node;
-       xmlNsPtr soap_ns;
-       xmlNsPtr xsi_ns;
-       xmlChar *env_prefix;
-       xmlChar *env_uri;
-       gboolean body_started;
-       gchar *action;
-} SoupSoapMessagePrivate;
-#define SOUP_SOAP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOAP_MESSAGE, SoupSoapMessagePrivate))
-
-static void
-finalize (GObject *object)
-{
-       SoupSoapMessagePrivate *priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (object);
-
-       if (priv->doc) 
-               xmlFreeDoc (priv->doc);
-       if (priv->action)
-               g_free (priv->action);
-       if (priv->env_uri)
-                xmlFree (priv->env_uri);
-       if (priv->env_prefix)
-                xmlFree (priv->env_prefix);
-
-       G_OBJECT_CLASS (soup_soap_message_parent_class)->finalize (object);
-}
-
-static void
-soup_soap_message_class_init (SoupSoapMessageClass *soup_soap_message_class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (soup_soap_message_class);
-
-       g_type_class_add_private (soup_soap_message_class, sizeof (SoupSoapMessagePrivate));
-
-       object_class->finalize = finalize;
-}
-
-static void
-soup_soap_message_init (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       /* initialize XML structures */
-       priv->doc = xmlNewDoc ((const xmlChar *)"1.0");
-       priv->doc->standalone = FALSE;
-       priv->doc->encoding = xmlCharStrdup ("UTF-8");
-}
-
-
-static xmlNsPtr
-fetch_ns (SoupSoapMessage *msg, const char *prefix, const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-        xmlNsPtr ns = NULL;
-                                                                                
-        if (prefix && ns_uri)
-                ns = xmlNewNs (priv->last_node, (const xmlChar *)ns_uri, (const xmlChar *)prefix);
-        else if (prefix && !ns_uri) {
-                ns = xmlSearchNs (priv->doc, priv->last_node, (const xmlChar *)prefix);
-                if (!ns)
-                       ns = xmlNewNs (priv->last_node, (const xmlChar *)"", (const xmlChar *)prefix);
-        }
-                                                                                
-        return ns;
-}
-
-/**
- * soup_soap_message_new:
- * @method: the HTTP method for the created request.
- * @uri_string: the destination endpoint (as a string).
- * @standalone: ??? FIXME
- * @xml_encoding: ??? FIXME
- * @env_prefix: ??? FIXME
- * @env_uri: ??? FIXME
- *
- * Creates a new empty #SoupSoapMessage, which will connect to @uri_string.
- *
- * Returns: the new #SoupSoapMessage (or %NULL if @uri_string could not be
- * parsed).
- */
-SoupSoapMessage *
-soup_soap_message_new (const char *method, const char *uri_string,
-                      gboolean standalone, const char *xml_encoding,
-                      const char *env_prefix, const char *env_uri)
-{
-       SoupSoapMessage *msg;
-       SoupUri *uri;
-
-       uri = soup_uri_new (uri_string);
-       if (!uri)
-               return NULL;
-
-       msg = soup_soap_message_new_from_uri (method, uri, standalone,
-                                             xml_encoding, env_prefix, env_uri);
-
-       soup_uri_free (uri);
-
-       return msg;
-}
-
-/**
- * soup_soap_message_new_from_uri:
- * @method: the HTTP method for the created request.
- * @uri: the destination endpoint (as a #SoupUri).
- * @standalone: ??? FIXME
- * @xml_encoding: ??? FIXME
- * @env_prefix: ??? FIXME
- * @env_uri: ??? FIXME
- *
- * Creates a new empty #SoupSoapMessage, which will connect to @uri
- *
- * Returns: the new #SoupSoapMessage
- */
-SoupSoapMessage *
-soup_soap_message_new_from_uri (const char *method, const SoupUri *uri,
-                               gboolean standalone, const char *xml_encoding,
-                               const char *env_prefix, const char *env_uri)
-{
-       SoupSoapMessage *msg;
-       SoupSoapMessagePrivate *priv;
-
-       msg = g_object_new (SOUP_TYPE_SOAP_MESSAGE, NULL);
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-       SOUP_MESSAGE (msg)->method = method ? method : SOUP_METHOD_GET;
-       soup_message_set_uri (SOUP_MESSAGE (msg), (const SoupUri *) uri);
-
-       priv->doc->standalone = standalone;
-
-       if (xml_encoding) {
-               xmlFree ((xmlChar *)priv->doc->encoding);
-               priv->doc->encoding = xmlCharStrdup (xml_encoding);
-       }
-
-       if (env_prefix)
-               priv->env_prefix = xmlCharStrdup (env_prefix);
-       if (env_uri)
-               priv->env_uri = xmlCharStrdup (env_uri);
-
-       return msg;
-}
-
-/**
- * soup_soap_message_start_envelope:
- * @msg: the %SoupSoapMessage.
- *
- * Starts the top level SOAP Envelope element.
- */
-void
-soup_soap_message_start_envelope (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = priv->doc->xmlRootNode =
-               xmlNewDocNode (priv->doc, NULL, (const xmlChar *)"Envelope", NULL);
-
-       priv->soap_ns = xmlNewNs (priv->doc->xmlRootNode,
-                                 priv->env_uri ? priv->env_uri :
-                                 (const xmlChar *)"http://schemas.xmlsoap.org/soap/envelope/",
-                                 priv->env_prefix ? priv->env_prefix : (const xmlChar *)"SOAP-ENV");
-       if (priv->env_uri) {
-               xmlFree (priv->env_uri);
-               priv->env_uri = NULL;
-       }
-       if (priv->env_prefix) {
-               xmlFree (priv->env_prefix);
-               priv->env_prefix = NULL;
-       }
-
-       xmlSetNs (priv->doc->xmlRootNode, priv->soap_ns);
-
-       xmlNewNs (priv->doc->xmlRootNode,
-                 (const xmlChar *)"http://schemas.xmlsoap.org/soap/encoding/",
-                  (const xmlChar *)"SOAP-ENC");
-       xmlNewNs (priv->doc->xmlRootNode,
-                  (const xmlChar *)"http://www.w3.org/1999/XMLSchema",
-                  (const xmlChar *)"xsd");
-       xmlNewNs (priv->doc->xmlRootNode,
-                 (const xmlChar *)"http://schemas.xmlsoap.org/soap/envelope/",
-                 (const xmlChar *)"SOAP-ENV");
-       priv->xsi_ns = xmlNewNs (priv->doc->xmlRootNode,
-                                (const xmlChar *)"http://www.w3.org/1999/XMLSchema-instance",
-                                (const xmlChar *)"xsi");
-}
-
-/**
- * soup_soap_message_end_envelope:
- * @msg: the %SoupSoapMessage.
- *
- * Closes the top level SOAP Envelope element.
- */
-void
-soup_soap_message_end_envelope (SoupSoapMessage *msg)
-{
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_start_body:
- * @msg: the %SoupSoapMessage.
- *
- * Starts the SOAP Body element.
- */
-void
-soup_soap_message_start_body (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       if (priv->body_started)
-               return;
-
-       priv->last_node = xmlNewChild (priv->last_node,
-                                      priv->soap_ns,
-                                      (const xmlChar *)"Body", NULL);
-
-       priv->body_started = TRUE;
-}
-
-/**
- * soup_soap_message_end_body:
- * @msg: the %SoupSoapMessage.
- *
- * Closes the SOAP Body element.
- */
-void
-soup_soap_message_end_body (SoupSoapMessage *msg)
-{
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_start_element:
- * @msg: the #SoupSoapMessage.
- * @name: the element name.
- * @prefix: the namespace prefix
- * @ns_uri: the namespace URI
- *
- * Starts a new arbitrary message element, with @name as the element
- * name, @prefix as the XML Namespace prefix, and @ns_uri as the XML
- * Namespace uri for * the created element.
- *
- * Passing @prefix with no @ns_uri will cause a recursive search for
- * an existing namespace with the same prefix. Failing that a new ns
- * will be created with an empty uri.
- *
- * Passing both @prefix and @ns_uri always causes new namespace
- * attribute creation.
- *
- * Passing NULL for both @prefix and @ns_uri causes no prefix to be
- * used, and the element will be in the default namespace.
- */
-void
-soup_soap_message_start_element (SoupSoapMessage *msg,
-                                const char *name,
-                                const char *prefix,
-                                const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)name, NULL);
-
-       xmlSetNs (priv->last_node, fetch_ns (msg, prefix, ns_uri));
-
-       if (priv->body_started && !priv->action)
-               priv->action = g_strconcat (ns_uri ? ns_uri : "",
-                                           "#", name, NULL);
-}
-
-/**
- * soup_soap_message_end_element:
- * @msg: the #SoupSoapMessage.
- *
- * Closes the current message element.
- */
-void
-soup_soap_message_end_element (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = priv->last_node->parent;
-}
-
-/**
- * soup_soap_message_start_fault:
- * @msg: the #SoupSoapMessage.
- * @faultcode: faultcode element value
- * @faultstring: faultstring element value
- * @faultfactor: faultfactor element value
- *
- * Starts a new SOAP Fault element, creating faultcode, faultstring,
- * and faultfactor child elements.
- *
- * If you wish to add the faultdetail element, use
- * soup_soap_message_start_fault_detail(), and then
- * soup_soap_message_start_element() to add arbitrary sub-elements.
- */
-void
-soup_soap_message_start_fault (SoupSoapMessage *msg,
-                              const char *faultcode,
-                              const char *faultstring,
-                              const char *faultfactor)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node,
-                                      priv->soap_ns,
-                                      (const xmlChar *)"Fault", NULL);
-       xmlNewChild (priv->last_node, priv->soap_ns, (const xmlChar *)"faultcode", (const xmlChar *)faultcode);
-       xmlNewChild (priv->last_node, priv->soap_ns, (const xmlChar *)"faultstring", (const xmlChar *)faultstring);
-
-       priv->last_node = xmlNewChild (priv->last_node, priv->soap_ns,
-                                      (const xmlChar *)"faultfactor", (const xmlChar *)faultfactor);
-       if (!faultfactor)
-               soup_soap_message_set_null (msg);
-
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_end_fault:
- * @msg: the #SoupSoapMessage.
- *
- * Closes the current SOAP Fault element.
- */
-void
-soup_soap_message_end_fault (SoupSoapMessage *msg)
-{
-        soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_start_fault_detail:
- * @msg: the #SoupSoapMessage.
- *
- * Start the faultdetail child element of the current SOAP Fault
- * element. The faultdetail element allows arbitrary data to be sent
- * in a returned fault.
- **/
-void
-soup_soap_message_start_fault_detail (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-        g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-                                                                                
-        priv->last_node = xmlNewChild (priv->last_node,
-                                      priv->soap_ns,
-                                      (const xmlChar *)"detail",
-                                      NULL);
-}
-
-/**
- * soup_soap_message_end_fault_detail:
- * @msg: the #SoupSoapMessage.
- *
- * Closes the current SOAP faultdetail element.
- */
-void
-soup_soap_message_end_fault_detail (SoupSoapMessage *msg)
-{
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_start_header:
- * @msg: the #SoupSoapMessage.
- *
- * Creates a new SOAP Header element. You can call
- * soup_soap_message_start_header_element() after this to add a new
- * header child element. SOAP Header elements allow out-of-band data
- * to be transferred while not interfering with the message body.
- *
- * This should be called after soup_soap_message_start_envelope() and
- * before soup_soap_message_start_body().
- */
-void
-soup_soap_message_start_header (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, priv->soap_ns,
-                                      (const xmlChar *)"Header", NULL);
-}
-
-/**
- * soup_soap_message_end_header:
- * @msg: the #SoupSoapMessage.
- *
- * Closes the current SOAP Header element.
- */
-void
-soup_soap_message_end_header (SoupSoapMessage *msg)
-{
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_start_header_element:
- * @msg: the #SoupSoapMessage.
- * @name: name of the header element
- * @must_understand: whether the recipient must understand the header in order
- * to proceed with processing the message
- * @actor_uri: the URI which represents the destination actor for this header.
- * @prefix: the namespace prefix
- * @ns_uri: the namespace URI
- *
- * Starts a new SOAP arbitrary header element.
- */
-void
-soup_soap_message_start_header_element (SoupSoapMessage *msg,
-                                       const char *name,
-                                       gboolean must_understand,
-                                       const char *actor_uri,
-                                       const char *prefix,
-                                       const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       soup_soap_message_start_element (msg, name, prefix, ns_uri);
-       if (actor_uri)
-               xmlNewNsProp (priv->last_node, priv->soap_ns, (const xmlChar *)"actorUri", (const xmlChar *)actor_uri);
-       if (must_understand)
-               xmlNewNsProp (priv->last_node, priv->soap_ns, (const xmlChar *)"mustUnderstand", (const xmlChar *)"1");
-}
-
-/**
- * soup_soap_message_end_header_element:
- * @msg: the #SoupSoapMessage.
- *
- * Closes the current SOAP header element.
- */
-void
-soup_soap_message_end_header_element (SoupSoapMessage *msg)
-{
-       soup_soap_message_end_element (msg);
-}
-
-/**
- * soup_soap_message_write_int:
- * @msg: the #SoupSoapMessage.
- * @i: the integer value to write.
- *
- * Writes the stringified value of @i as the current element's content.
- */
-void
-soup_soap_message_write_int (SoupSoapMessage *msg, glong i)
-{
-       char *str = g_strdup_printf ("%ld", i);
-       soup_soap_message_write_string (msg, str);
-       g_free (str);
-}
-
-/**
- * soup_soap_message_write_double:
- * @msg: the #SoupSoapMessage.
- * @d: the double value to write.
- *
- * Writes the stringified value of @d as the current element's content.
- */
-void
-soup_soap_message_write_double (SoupSoapMessage *msg, double d)
-{
-       char *str = g_strdup_printf ("%f", d);
-       soup_soap_message_write_string (msg, str);
-       g_free (str);
-}
-
-/**
- * soup_soap_message_write_base64:
- * @msg: the #SoupSoapMessage
- * @string: the binary data buffer to encode
- * @len: the length of data to encode
- *
- * Writes the Base-64 encoded value of @string as the current
- * element's content.
- **/
-void
-soup_soap_message_write_base64 (SoupSoapMessage *msg, const char *string, int len)
-{
-        gchar *str = g_base64_encode ((const guchar *)string, len);
-        soup_soap_message_write_string (msg, str);
-        g_free (str);
-}
-
-/**
- * soup_soap_message_write_time:
- * @msg: the #SoupSoapMessage.
- * @timeval: pointer to a time_t to encode
- *
- * Writes the stringified value of @timeval as the current element's
- * content.
- **/
-void
-soup_soap_message_write_time (SoupSoapMessage *msg, const time_t *timeval)
-{
-        gchar *str = g_strchomp (ctime (timeval));
-        soup_soap_message_write_string (msg, str);
-}
-
-/**
- * soup_soap_message_write_string:
- * @msg: the #SoupSoapMessage.
- * @string: string to write.
- *
- * Writes the @string as the current element's content.
- */
-void
-soup_soap_message_write_string (SoupSoapMessage *msg, const char *string)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNodeAddContent (priv->last_node, (const xmlChar *)string);
-}
-
-/**
- * soup_soap_message_write_buffer:
- * @msg: the #SoupSoapMessage.
- * @buffer: the string data buffer to write.
- * @len: length of @buffer.
- *
- * Writes the string buffer pointed to by @buffer as the current
- * element's content.
- */
-void
-soup_soap_message_write_buffer (SoupSoapMessage *msg, const char *buffer, int len)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNodeAddContentLen (priv->last_node, (const xmlChar *)buffer, len);
-}
-
-/**
- * soup_soap_message_set_element_type:
- * @msg: the #SoupSoapMessage.
- * @xsi_type: the type name for the element.
- *
- * Sets the current element's XML schema xsi:type attribute, which
- * specifies the element's type name.
- */
-void
-soup_soap_message_set_element_type (SoupSoapMessage *msg, const char *xsi_type)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNewNsProp (priv->last_node, priv->xsi_ns, (const xmlChar *)"type", (const xmlChar *)xsi_type);
-}
-
-/**
- * soup_soap_message_set_null:
- * @msg: the #SoupSoapMessage.
- *
- * Sets the current element's XML Schema xsi:null attribute.
- */
-void
-soup_soap_message_set_null (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNewNsProp (priv->last_node, priv->xsi_ns, (const xmlChar *)"null", (const xmlChar *)"1");
-}
-
-/**
- * soup_soap_message_add_attribute:
- * @msg: the #SoupSoapMessage.
- * @name: name of the attribute
- * @value: value of the attribute
- * @prefix: the namespace prefix
- * @ns_uri: the namespace URI
- *
- * Adds an XML attribute to the current element.
- */
-void
-soup_soap_message_add_attribute (SoupSoapMessage *msg,
-                                const char *name,
-                                const char *value,
-                                const char *prefix,
-                                const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNewNsProp (priv->last_node,
-                     fetch_ns (msg, prefix, ns_uri),
-                     (const xmlChar *)name, (const xmlChar *)value);
-}
-
-/**
- * soup_soap_message_add_namespace:
- * @msg: the #SoupSoapMessage.
- * @prefix: the namespace prefix
- * @ns_uri: the namespace URI, or NULL for empty namespace
- *
- * Adds a new XML namespace to the current element.
- */
-void
-soup_soap_message_add_namespace (SoupSoapMessage *msg, const char *prefix, const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNewNs (priv->last_node, (const xmlChar *)(ns_uri ? ns_uri : ""), (const xmlChar *)prefix);
-}
-
-/**
- * soup_soap_message_set_default_namespace:
- * @msg: the #SoupSoapMessage.
- * @ns_uri: the namespace URI.
- *
- * Sets the default namespace to the URI specified in @ns_uri. The
- * default namespace becomes the namespace all non-explicitly
- * namespaced child elements fall into.
- */
-void
-soup_soap_message_set_default_namespace (SoupSoapMessage *msg, const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       soup_soap_message_add_namespace (msg, NULL, ns_uri);
-}
-
-/**
- * soup_soap_message_set_encoding_style:
- * @msg: the #SoupSoapMessage.
- * @enc_style: the new encodingStyle value
- *
- * Sets the encodingStyle attribute on the current element to the
- * value of @enc_style.
- */
-void
-soup_soap_message_set_encoding_style (SoupSoapMessage *msg, const char *enc_style)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlNewNsProp (priv->last_node, priv->soap_ns, (const xmlChar *)"encodingStyle", (const xmlChar *)enc_style);
-}
-
-/**
- * soup_soap_message_reset:
- * @msg: the #SoupSoapMessage.
- *
- * Resets the internal XML representation of the SOAP message.
- */
-void
-soup_soap_message_reset (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlFreeDoc (priv->doc);
-       priv->doc = xmlNewDoc ((const xmlChar *)"1.0");
-       priv->last_node = NULL;
-
-       g_free (priv->action);
-       priv->action = NULL;
-       priv->body_started = FALSE;
-
-       if (priv->env_uri)
-               xmlFree (priv->env_uri);
-       priv->env_uri = NULL;
-
-       if (priv->env_prefix)
-               xmlFree (priv->env_prefix);
-       priv->env_prefix = NULL;
-}
-
-/**
- * soup_soap_message_persist:
- * @msg: the #SoupSoapMessage.
- *
- * Writes the serialized XML tree to the #SoupMessage's buffer.
- */
-void
-soup_soap_message_persist (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-       xmlChar *body;
-       int len;
-
-       g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlDocDumpMemory (priv->doc, &body, &len);
-
-       /* serialize to SoupMessage class */
-       soup_message_set_request (SOUP_MESSAGE (msg), "text/xml",
-                                 SOUP_BUFFER_SYSTEM_OWNED, (char *)body, len);
-}
-
-/**
- * soup_soap_message_get_namespace_prefix:
- * @msg: the #SoupSoapMessage.
- * @ns_uri: the namespace URI.
- *
- * Returns the namespace prefix for @ns_uri (or an empty string if
- * @ns_uri is set to the default namespace)
- *
- * Return value: The namespace prefix, or %NULL if no namespace exists
- * for the URI given.
- */
-const char *
-soup_soap_message_get_namespace_prefix (SoupSoapMessage *msg, const char *ns_uri)
-{
-       SoupSoapMessagePrivate *priv;
-       xmlNsPtr ns = NULL;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_MESSAGE (msg), NULL);
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-       g_return_val_if_fail (ns_uri != NULL, NULL);
-
-       ns = xmlSearchNsByHref (priv->doc, priv->last_node, (const xmlChar *)ns_uri);
-       if (ns) {
-               if (ns->prefix)
-                       return (const char *)ns->prefix;
-               else
-                       return "";
-       }
-
-       return NULL;
-}
-
-/**
- * soup_soap_message_get_xml_doc:
- * @msg: the #SoupSoapMessage.
- *
- * Returns the internal XML representation tree of the
- * #SoupSoapMessage pointed to by @msg.
- *
- * Return value: the #xmlDocPtr representing the SOAP message.
- */
-xmlDocPtr
-soup_soap_message_get_xml_doc (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_MESSAGE (msg), NULL);
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       return priv->doc;
-}
-
-/**
- * soup_soap_message_parse_response:
- * @msg: the #SoupSoapMessage.
- *
- * Parses the response returned by the server.
- *
- * Return value: a #SoupSoapResponse representing the response from
- * the server, or %NULL if there was an error.
- */
-SoupSoapResponse *
-soup_soap_message_parse_response (SoupSoapMessage *msg)
-{
-       SoupSoapMessagePrivate *priv;
-       char *xmlstr;
-       SoupSoapResponse *soap_response;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_MESSAGE (msg), NULL);
-       priv = SOUP_SOAP_MESSAGE_GET_PRIVATE (msg);
-
-       xmlstr = g_malloc0 (SOUP_MESSAGE (msg)->response.length + 1);
-       strncpy (xmlstr, SOUP_MESSAGE (msg)->response.body, SOUP_MESSAGE (msg)->response.length);
-
-       soap_response = soup_soap_response_new_from_string (xmlstr);
-       g_free (xmlstr);
-
-       return soap_response;
-}
diff --git a/libsoup/soup-soap-message.h b/libsoup/soup-soap-message.h
deleted file mode 100644 (file)
index 9048d62..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2003, Novell, Inc.
- */
-
-#ifndef SOUP_SOAP_MESSAGE_H
-#define SOUP_SOAP_MESSAGE_H 1
-
-#include <time.h>
-#include <libxml/tree.h>
-#include <libsoup/soup-message.h>
-#include <libsoup/soup-soap-response.h>
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_SOAP_MESSAGE            (soup_soap_message_get_type ())
-#define SOUP_SOAP_MESSAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SOAP_MESSAGE, SoupSoapMessage))
-#define SOUP_SOAP_MESSAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SOAP_MESSAGE, SoupSoapMessageClass))
-#define SOUP_IS_SOAP_MESSAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SOAP_MESSAGE))
-#define SOUP_IS_SOAP_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_SOAP_MESSAGE))
-#define SOUP_SOAP_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SOAP_MESSAGE, SoupSoapMessageClass))
-
-typedef struct {
-       SoupMessage parent;
-
-} SoupSoapMessage;
-
-typedef struct {
-       SoupMessageClass parent_class;
-} SoupSoapMessageClass;
-
-GType             soup_soap_message_get_type (void);
-
-SoupSoapMessage  *soup_soap_message_new (const char *method, const char *uri_string,
-                                        gboolean standalone, const char *xml_encoding,
-                                        const char *env_prefix, const char *env_uri);
-SoupSoapMessage  *soup_soap_message_new_from_uri (const char *method, const SoupUri *uri,
-                                                 gboolean standalone, const char *xml_encoding,
-                                                 const char *env_prefix, const char *env_uri);
-
-void              soup_soap_message_start_envelope (SoupSoapMessage *msg);
-void              soup_soap_message_end_envelope (SoupSoapMessage *msg);
-void              soup_soap_message_start_body (SoupSoapMessage *msg);
-void              soup_soap_message_end_body (SoupSoapMessage *msg);
-void              soup_soap_message_start_element (SoupSoapMessage *msg,
-                                                  const char *name,
-                                                  const char *prefix,
-                                                  const char *ns_uri);
-void              soup_soap_message_end_element (SoupSoapMessage *msg);
-void              soup_soap_message_start_fault (SoupSoapMessage *msg,
-                                                const char *faultcode,
-                                                const char *faultstring,
-                                                const char *faultfactor);
-void              soup_soap_message_end_fault (SoupSoapMessage *msg);
-void              soup_soap_message_start_fault_detail (SoupSoapMessage *msg);
-void              soup_soap_message_end_fault_detail (SoupSoapMessage *msg);
-void              soup_soap_message_start_header (SoupSoapMessage *msg);
-void              soup_soap_message_end_header (SoupSoapMessage *msg);
-void              soup_soap_message_start_header_element (SoupSoapMessage *msg,
-                                                         const char *name,
-                                                         gboolean must_understand,
-                                                         const char *actor_uri,
-                                                         const char *prefix,
-                                                         const char *ns_uri);
-void              soup_soap_message_end_header_element (SoupSoapMessage *msg);
-void              soup_soap_message_write_int (SoupSoapMessage *msg, glong i);
-void              soup_soap_message_write_double (SoupSoapMessage *msg, double d);
-void              soup_soap_message_write_base64 (SoupSoapMessage *msg, const char *string, int len);
-void              soup_soap_message_write_time (SoupSoapMessage *msg, const time_t *timeval);
-void              soup_soap_message_write_string (SoupSoapMessage *msg, const char *string);
-void              soup_soap_message_write_buffer (SoupSoapMessage *msg, const char *buffer, int len);
-void              soup_soap_message_set_element_type (SoupSoapMessage *msg, const char *xsi_type);
-void              soup_soap_message_set_null (SoupSoapMessage *msg);
-void              soup_soap_message_add_attribute (SoupSoapMessage *msg,
-                                                  const char *name,
-                                                  const char *value,
-                                                  const char *prefix,
-                                                  const char *ns_uri);
-void              soup_soap_message_add_namespace (SoupSoapMessage *msg,
-                                                  const char *prefix,
-                                                  const char *ns_uri);
-void              soup_soap_message_set_default_namespace (SoupSoapMessage *msg,
-                                                          const char *ns_uri);
-void              soup_soap_message_set_encoding_style (SoupSoapMessage *msg, const char *enc_style);
-void              soup_soap_message_reset (SoupSoapMessage *msg);
-void              soup_soap_message_persist (SoupSoapMessage *msg);
-
-const char       *soup_soap_message_get_namespace_prefix (SoupSoapMessage *msg, const char *ns_uri);
-
-xmlDocPtr         soup_soap_message_get_xml_doc (SoupSoapMessage *msg);
-
-SoupSoapResponse *soup_soap_message_parse_response (SoupSoapMessage *msg);
-
-G_END_DECLS
-
-#endif
diff --git a/libsoup/soup-soap-response.c b/libsoup/soup-soap-response.c
deleted file mode 100644 (file)
index 65b77aa..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2003, Novell, Inc.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <libxml/tree.h>
-#include "soup-misc.h"
-#include "soup-soap-response.h"
-#include "soup-types.h"
-
-G_DEFINE_TYPE (SoupSoapResponse, soup_soap_response, G_TYPE_OBJECT)
-
-typedef struct {
-       /* the XML document */
-       xmlDocPtr xmldoc;
-       xmlNodePtr xml_root;
-       xmlNodePtr xml_body;
-       xmlNodePtr xml_method;
-       xmlNodePtr soap_fault;
-       GList *parameters;
-} SoupSoapResponsePrivate;
-#define SOUP_SOAP_RESPONSE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOAP_RESPONSE, SoupSoapResponsePrivate))
-
-static void
-finalize (GObject *object)
-{
-       SoupSoapResponsePrivate *priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (object);
-
-       if (priv->xmldoc)
-               xmlFreeDoc (priv->xmldoc);
-       if (priv->parameters != NULL)
-               g_list_free (priv->parameters);
-
-       G_OBJECT_CLASS (soup_soap_response_parent_class)->finalize (object);
-}
-
-static void
-soup_soap_response_class_init (SoupSoapResponseClass *soup_soap_response_class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (soup_soap_response_class);
-
-       g_type_class_add_private (soup_soap_response_class, sizeof (SoupSoapResponsePrivate));
-
-       object_class->finalize = finalize;
-}
-
-static void
-soup_soap_response_init (SoupSoapResponse *response)
-{
-       SoupSoapResponsePrivate *priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-
-       priv->xmldoc = xmlNewDoc ((const xmlChar *)"1.0");
-}
-
-
-/**
- * soup_soap_response_new:
- *
- * Create a new empty #SoupSoapResponse object, which can be modified
- * with the accessor functions provided with this class.
- *
- * Return value: the new #SoupSoapResponse (or %NULL if there was an
- * error).
- */
-SoupSoapResponse *
-soup_soap_response_new (void)
-{
-       SoupSoapResponse *response;
-
-       response = g_object_new (SOUP_TYPE_SOAP_RESPONSE, NULL);
-       return response;
-}
-
-/**
- * soup_soap_response_new_from_string:
- * @xmlstr: the XML string to parse.
- *
- * Create a new #SoupSoapResponse object from the XML string contained
- * in @xmlstr.
- *
- * Return value: the new #SoupSoapResponse (or %NULL if there was an
- * error).
- */
-SoupSoapResponse *
-soup_soap_response_new_from_string (const char *xmlstr)
-{
-       SoupSoapResponse *response;
-
-       g_return_val_if_fail (xmlstr != NULL, NULL);
-
-       response = g_object_new (SOUP_TYPE_SOAP_RESPONSE, NULL);
-       if (!soup_soap_response_from_string (response, xmlstr)) {
-               g_object_unref (response);
-               return NULL;
-       }
-
-       return response;
-}
-
-static void
-parse_parameters (SoupSoapResponsePrivate *priv, xmlNodePtr xml_method)
-{
-       xmlNodePtr tmp;
-
-       for (tmp = soup_xml_real_node (xml_method->children);
-            tmp != NULL;
-            tmp = soup_xml_real_node (tmp->next)) {
-               if (!strcmp ((const char *)tmp->name, "Fault")) {
-                       priv->soap_fault = tmp;
-                       continue;
-               } else {
-                       /* regular parameters */
-                       priv->parameters = g_list_append (priv->parameters, tmp);
-               }
-       }
-}
-
-/**
- * soup_soap_response_from_string:
- * @response: the #SoupSoapResponse object.
- * @xmlstr: XML string to parse.
- *
- * Parses the string contained in @xmlstr and sets all properties from
- * it in the @response object.
- *
- * Return value: %TRUE if successful, %FALSE otherwise.
- */
-gboolean
-soup_soap_response_from_string (SoupSoapResponse *response, const char *xmlstr)
-{
-       SoupSoapResponsePrivate *priv;
-       xmlDocPtr old_doc = NULL;
-       xmlNodePtr xml_root, xml_body, xml_method = NULL;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), FALSE);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-       g_return_val_if_fail (xmlstr != NULL, FALSE);
-
-       /* clear the previous contents */
-       if (priv->xmldoc)
-               old_doc = priv->xmldoc;
-
-       /* parse the string */
-       priv->xmldoc = xmlParseMemory (xmlstr, strlen (xmlstr));
-       if (!priv->xmldoc) {
-               priv->xmldoc = old_doc;
-               return FALSE;
-       }
-
-       xml_root = xmlDocGetRootElement (priv->xmldoc);
-       if (!xml_root) {
-               xmlFreeDoc (priv->xmldoc);
-               priv->xmldoc = old_doc;
-               return FALSE;
-       }
-
-       if (strcmp ((const char *)xml_root->name, "Envelope") != 0) {
-               xmlFreeDoc (priv->xmldoc);
-               priv->xmldoc = old_doc;
-               return FALSE;
-       }
-
-       xml_body = soup_xml_real_node (xml_root->children);
-       if (xml_body != NULL) {
-               if (strcmp ((const char *)xml_body->name, "Header") == 0)
-                       xml_body = soup_xml_real_node (xml_body->next);
-               if (strcmp ((const char *)xml_body->name, "Body") != 0) {
-                       xmlFreeDoc (priv->xmldoc);
-                       priv->xmldoc = old_doc;
-                       return FALSE;
-               }
-
-               xml_method = soup_xml_real_node (xml_body->children);
-
-               /* read all parameters */
-               if (xml_method)
-                       parse_parameters (priv, xml_method);
-       }
-
-       xmlFreeDoc (old_doc);
-
-       priv->xml_root = xml_root;
-       priv->xml_body = xml_body;
-       priv->xml_method = xml_method;
-
-       return TRUE;
-}
-
-/**
- * soup_soap_response_get_method_name:
- * @response: the #SoupSoapResponse object.
- *
- * Gets the method name from the SOAP response.
- *
- * Return value: the method name.
- */
-const char *
-soup_soap_response_get_method_name (SoupSoapResponse *response)
-{
-       SoupSoapResponsePrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-       g_return_val_if_fail (priv->xml_method != NULL, NULL);
-
-       return (const char *) priv->xml_method->name;
-}
-
-/**
- * soup_soap_response_set_method_name:
- * @response: the #SoupSoapResponse object.
- * @method_name: the method name to set.
- *
- * Sets the method name on the given #SoupSoapResponse.
- */
-void
-soup_soap_response_set_method_name (SoupSoapResponse *response, const char *method_name)
-{
-       SoupSoapResponsePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SOAP_RESPONSE (response));
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-       g_return_if_fail (priv->xml_method != NULL);
-       g_return_if_fail (method_name != NULL);
-
-       xmlNodeSetName (priv->xml_method, (const xmlChar *)method_name);
-}
-
-/**
- * soup_soap_parameter_get_name:
- * @param: the parameter
- *
- * Returns the parameter name.
- *
- * Return value: the parameter name.
- */
-const char *
-soup_soap_parameter_get_name (SoupSoapParameter *param)
-{
-       g_return_val_if_fail (param != NULL, NULL);
-
-       return (const char *) param->name;
-}
-
-/**
- * soup_soap_parameter_get_int_value:
- * @param: the parameter
- *
- * Returns the parameter's (integer) value.
- *
- * Return value: the parameter value as an integer
- */
-int
-soup_soap_parameter_get_int_value (SoupSoapParameter *param)
-{
-       int i;
-       xmlChar *s;
-       g_return_val_if_fail (param != NULL, -1);
-
-       s = xmlNodeGetContent (param);
-       if (s) {
-               i = atoi ((char *)s);
-               xmlFree (s);
-
-               return i;
-       }
-
-       return -1;
-}
-
-/**
- * soup_soap_parameter_get_string_value:
- * @param: the parameter
- *
- * Returns the parameter's value.
- *
- * Return value: the parameter value as a string, which must be freed
- * by the caller.
- */
-char *
-soup_soap_parameter_get_string_value (SoupSoapParameter *param)
-{
-       xmlChar *xml_s;
-       char *s;
-       g_return_val_if_fail (param != NULL, NULL);
-
-       xml_s = xmlNodeGetContent (param);
-       s = g_strdup ((char *)xml_s);
-       xmlFree (xml_s);
-
-       return s;
-}
-
-/**
- * soup_soap_parameter_get_first_child:
- * @param: A #SoupSoapParameter.
- *
- * Gets the first child of the given #SoupSoapParameter. This is used
- * for compound data types, which can contain several parameters
- * themselves.
- *
- * Return value: the first child or %NULL if there are no children.
- */
-SoupSoapParameter *
-soup_soap_parameter_get_first_child (SoupSoapParameter *param)
-{
-       g_return_val_if_fail (param != NULL, NULL);
-
-       return soup_xml_real_node (param->children);
-}
-
-/**
- * soup_soap_parameter_get_first_child_by_name:
- * @param: A #SoupSoapParameter.
- * @name: The name of the child parameter to look for.
- *
- * Gets the first child of the given #SoupSoapParameter whose name is
- * @name.
- *
- * Return value: the first child with the given name or %NULL if there
- * are no children.
- */
-SoupSoapParameter *
-soup_soap_parameter_get_first_child_by_name (SoupSoapParameter *param, const char *name)
-{
-       SoupSoapParameter *tmp;
-
-       g_return_val_if_fail (param != NULL, NULL);
-       g_return_val_if_fail (name != NULL, NULL);
-
-       for (tmp = soup_soap_parameter_get_first_child (param);
-            tmp != NULL;
-            tmp = soup_soap_parameter_get_next_child (tmp)) {
-               if (!strcmp (name, (const char *)tmp->name))
-                       return tmp;
-       }
-
-       return NULL;
-}
-
-/**
- * soup_soap_parameter_get_next_child:
- * @param: A #SoupSoapParameter.
- *
- * Gets the next sibling of the given #SoupSoapParameter. This is used
- * for compound data types, which can contain several parameters
- * themselves.
- *
- * FIXME: the name of this method is wrong
- *
- * Return value: the next sibling, or %NULL if there are no more
- * siblings.
- */
-SoupSoapParameter *
-soup_soap_parameter_get_next_child (SoupSoapParameter *param)
-{
-       g_return_val_if_fail (param != NULL, NULL);
-
-       return soup_xml_real_node (param->next);
-}
-
-/**
- * soup_soap_parameter_get_next_child_by_name:
- * @param: A #SoupSoapParameter.
- * @name: The name of the sibling parameter to look for.
- *
- * Gets the next sibling of the given #SoupSoapParameter whose name is
- * @name.
- *
- * FIXME: the name of this method is wrong
- *
- * Return value: the next sibling with the given name, or %NULL
- */
-SoupSoapParameter *
-soup_soap_parameter_get_next_child_by_name (SoupSoapParameter *param,
-                                           const char *name)
-{
-       SoupSoapParameter *tmp;
-
-       g_return_val_if_fail (param != NULL, NULL);
-       g_return_val_if_fail (name != NULL, NULL);
-
-       for (tmp = soup_soap_parameter_get_next_child (param);
-            tmp != NULL;
-            tmp = soup_soap_parameter_get_next_child (tmp)) {
-               if (!strcmp (name, (const char *)tmp->name))
-                       return tmp;
-       }
-
-       return NULL;
-}
-
-/**
- * soup_soap_parameter_get_property:
- * @param: the parameter
- * @prop_name: Name of the property to retrieve.
- *
- * Returns the named property of @param.
- *
- * Return value: the property, which must be freed by the caller.
- */
-char *
-soup_soap_parameter_get_property (SoupSoapParameter *param, const char *prop_name)
-{
-       xmlChar *xml_s;
-       char *s;
-
-       g_return_val_if_fail (param != NULL, NULL);
-       g_return_val_if_fail (prop_name != NULL, NULL);
-
-       xml_s = xmlGetProp (param, (const xmlChar *)prop_name);
-       s = g_strdup ((char *)xml_s);
-       xmlFree (xml_s);
-
-       return s;
-}
-
-/**
- * soup_soap_response_get_parameters:
- * @response: the #SoupSoapResponse object.
- *
- * Returns the list of parameters received in the SOAP response.
- *
- * Return value: a list of #SoupSoapParameter
- */
-const GList *
-soup_soap_response_get_parameters (SoupSoapResponse *response)
-{
-       SoupSoapResponsePrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-
-       return (const GList *) priv->parameters;
-}
-
-/**
- * soup_soap_response_get_first_parameter:
- * @response: the #SoupSoapResponse object.
- *
- * Retrieves the first parameter contained in the SOAP response.
- *
- * Return value: a #SoupSoapParameter representing the first
- * parameter, or %NULL if there are no parameters.
- */
-SoupSoapParameter *
-soup_soap_response_get_first_parameter (SoupSoapResponse *response)
-{
-       SoupSoapResponsePrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-
-       return priv->parameters ? priv->parameters->data : NULL;
-}
-
-/**
- * soup_soap_response_get_first_parameter_by_name:
- * @response: the #SoupSoapResponse object.
- * @name: the name of the parameter to look for.
- *
- * Retrieves the first parameter contained in the SOAP response whose
- * name is @name.
- *
- * Return value: a #SoupSoapParameter representing the first parameter
- * with the given name, or %NULL.
- */
-SoupSoapParameter *
-soup_soap_response_get_first_parameter_by_name (SoupSoapResponse *response,
-                                               const char *name)
-{
-       SoupSoapResponsePrivate *priv;
-       GList *l;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-       g_return_val_if_fail (name != NULL, NULL);
-
-       for (l = priv->parameters; l != NULL; l = l->next) {
-               SoupSoapParameter *param = (SoupSoapParameter *) l->data;
-
-               if (!strcmp (name, (const char *)param->name))
-                       return param;
-       }
-
-       return NULL;
-}
-
-/**
- * soup_soap_response_get_next_parameter:
- * @response: the #SoupSoapResponse object.
- * @from: the parameter to start from.
- *
- * Retrieves the parameter following @from in the #SoupSoapResponse
- * object.
- *
- * Return value: a #SoupSoapParameter representing the parameter.
- */
-SoupSoapParameter *
-soup_soap_response_get_next_parameter (SoupSoapResponse *response,
-                                      SoupSoapParameter *from)
-{
-       SoupSoapResponsePrivate *priv;
-       GList *l;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       priv = SOUP_SOAP_RESPONSE_GET_PRIVATE (response);
-       g_return_val_if_fail (from != NULL, NULL);
-
-       l = g_list_find (priv->parameters, (gconstpointer) from);
-       if (!l)
-               return NULL;
-
-       return l->next ? (SoupSoapParameter *) l->next->data : NULL;
-}
-
-/**
- * soup_soap_response_get_next_parameter_by_name:
- * @response: the #SoupSoapResponse object.
- * @from: the parameter to start from.
- * @name: the name of the parameter to look for.
- *
- * Retrieves the first parameter following @from in the
- * #SoupSoapResponse object whose name matches @name.
- *
- * Return value: a #SoupSoapParameter representing the parameter.
- */
-SoupSoapParameter *
-soup_soap_response_get_next_parameter_by_name (SoupSoapResponse *response,
-                                              SoupSoapParameter *from,
-                                              const char *name)
-{
-       SoupSoapParameter *param;
-
-       g_return_val_if_fail (SOUP_IS_SOAP_RESPONSE (response), NULL);
-       g_return_val_if_fail (from != NULL, NULL);
-       g_return_val_if_fail (name != NULL, NULL);
-
-       param = soup_soap_response_get_next_parameter (response, from);
-       while (param) {
-               const char *param_name = soup_soap_parameter_get_name (param);
-
-               if (param_name) {
-                       if (!strcmp (name, param_name))
-                               return param;
-               }
-
-               param = soup_soap_response_get_next_parameter (response, param);
-       }
-
-       return NULL;
-}
diff --git a/libsoup/soup-soap-response.h b/libsoup/soup-soap-response.h
deleted file mode 100644 (file)
index 32427e2..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2003, Novell, Inc.
- */
-
-#ifndef SOUP_SOAP_RESPONSE_H
-#define SOUP_SOAP_RESPONSE_H
-
-#include <glib-object.h>
-#include <libxml/tree.h>
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_SOAP_RESPONSE            (soup_soap_response_get_type ())
-#define SOUP_SOAP_RESPONSE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SOAP_RESPONSE, SoupSoapResponse))
-#define SOUP_SOAP_RESPONSE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SOAP_RESPONSE, SoupSoapResponseClass))
-#define SOUP_IS_SOAP_RESPONSE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SOAP_RESPONSE))
-#define SOUP_IS_SOAP_RESPONSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_SOAP_RESPONSE))
-#define SOUP_SOAP_RESPONSE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SOAP_RESPONSE, SoupSoapResponseClass))
-
-typedef struct {
-       GObject parent;
-
-} SoupSoapResponse;
-
-typedef struct {
-       GObjectClass parent_class;
-} SoupSoapResponseClass;
-
-GType             soup_soap_response_get_type (void);
-
-SoupSoapResponse *soup_soap_response_new (void);
-SoupSoapResponse *soup_soap_response_new_from_string (const char *xmlstr);
-
-gboolean          soup_soap_response_from_string (SoupSoapResponse *response, const char *xmlstr);
-
-const char       *soup_soap_response_get_method_name (SoupSoapResponse *response);
-void              soup_soap_response_set_method_name (SoupSoapResponse *response,
-                                                     const char *method_name);
-
-typedef xmlNode SoupSoapParameter;
-
-const char        *soup_soap_parameter_get_name (SoupSoapParameter *param);
-int                soup_soap_parameter_get_int_value (SoupSoapParameter *param);
-char              *soup_soap_parameter_get_string_value (SoupSoapParameter *param);
-SoupSoapParameter *soup_soap_parameter_get_first_child (SoupSoapParameter *param);
-SoupSoapParameter *soup_soap_parameter_get_first_child_by_name (SoupSoapParameter *param,
-                                                               const char *name);
-SoupSoapParameter *soup_soap_parameter_get_next_child (SoupSoapParameter *param);
-SoupSoapParameter *soup_soap_parameter_get_next_child_by_name (SoupSoapParameter *param,
-                                                              const char *name);
-char              *soup_soap_parameter_get_property (SoupSoapParameter *param, const char *prop_name);
-
-const GList       *soup_soap_response_get_parameters (SoupSoapResponse *response);
-SoupSoapParameter *soup_soap_response_get_first_parameter (SoupSoapResponse *response);
-SoupSoapParameter *soup_soap_response_get_first_parameter_by_name (SoupSoapResponse *response,
-                                                                  const char *name);
-SoupSoapParameter *soup_soap_response_get_next_parameter (SoupSoapResponse *response,
-                                                         SoupSoapParameter *from);
-SoupSoapParameter *soup_soap_response_get_next_parameter_by_name (SoupSoapResponse *response,
-                                                                 SoupSoapParameter *from,
-                                                                 const char *name);
-
-G_END_DECLS
-
-#endif
index eeafa9c..953c6f8 100644 (file)
 #include <sys/time.h>
 #include <sys/types.h>
 
+/**
+ * SECTION:soup-socket
+ * @short_description: A network socket
+ *
+ **/
+
 G_DEFINE_TYPE (SoupSocket, soup_socket, G_TYPE_OBJECT)
 
 enum {
-       CONNECT_RESULT,
        READABLE,
        WRITABLE,
        DISCONNECTED,
@@ -40,10 +45,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
 enum {
        PROP_0,
 
+       PROP_LOCAL_ADDRESS,
+       PROP_REMOTE_ADDRESS,
        PROP_NON_BLOCKING,
-       PROP_NODELAY,
-       PROP_REUSEADDR,
-       PROP_CLOEXEC,
        PROP_IS_SERVER,
        PROP_SSL_CREDENTIALS,
        PROP_ASYNC_CONTEXT,
@@ -58,9 +62,6 @@ typedef struct {
        GIOChannel *iochannel;
 
        guint non_blocking:1;
-       guint nodelay:1;
-       guint reuseaddr:1;
-       guint cloexec:1;
        guint is_server:1;
        gpointer ssl_creds;
 
@@ -86,12 +87,10 @@ static void get_property (GObject *object, guint prop_id,
                          GValue *value, GParamSpec *pspec);
 
 #ifdef G_OS_WIN32
-#define SOUP_CLOSE_SOCKET(socket) closesocket (socket)
 #define SOUP_IS_SOCKET_ERROR(status) ((status) == SOCKET_ERROR)
 #define SOUP_IS_INVALID_SOCKET(socket) ((socket) == INVALID_SOCKET)
 #define SOUP_IS_CONNECT_STATUS_INPROGRESS() (WSAGetLastError () == WSAEWOULDBLOCK)
 #else
-#define SOUP_CLOSE_SOCKET(socket) close (socket)
 #define SOUP_IS_SOCKET_ERROR(status) ((status) == -1)
 #define SOUP_IS_INVALID_SOCKET(socket) ((socket) < 0)
 #define SOUP_IS_CONNECT_STATUS_INPROGRESS() (errno == EINPROGRESS)
@@ -103,9 +102,7 @@ soup_socket_init (SoupSocket *sock)
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
        priv->sockfd = -1;
-       priv->non_blocking = priv->nodelay = TRUE;
-       priv->reuseaddr = TRUE;
-       priv->cloexec = FALSE;
+       priv->non_blocking = TRUE;
        priv->addrlock = g_mutex_new ();
        priv->iolock = g_mutex_new ();
        priv->timeout = 0;
@@ -170,24 +167,6 @@ soup_socket_class_init (SoupSocketClass *socket_class)
        /* signals */
 
        /**
-        * SoupSocket::connect-result:
-        * @sock: the socket
-        * @status: the status
-        *
-        * Emitted when a connection attempt succeeds or fails. This
-        * is used internally by soup_socket_client_new_async().
-        **/
-       signals[CONNECT_RESULT] =
-               g_signal_new ("connect_result",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupSocketClass, connect_result),
-                             NULL, NULL,
-                             soup_marshal_NONE__INT,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
-
-       /**
         * SoupSocket::readable:
         * @sock: the socket
         *
@@ -241,10 +220,10 @@ soup_socket_class_init (SoupSocketClass *socket_class)
         * @new: the new socket
         *
         * Emitted when a listening socket (set up with
-        * soup_socket_listen() or soup_socket_server_new()) receives a
-        * new connection. If you want to keep the connection, do not forget 
-        * to add a reference to it. Otherwise the @new socket will be closed
-        * after this signal.
+        * soup_socket_listen()) receives a new connection.
+        *
+        * You must ref the @new if you want to keep it; otherwise it
+        * will be destroyed after the signal is emitted.
         **/
        signals[NEW_CONNECTION] =
                g_signal_new ("new_connection",
@@ -258,6 +237,20 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 
        /* properties */
        g_object_class_install_property (
+               object_class, PROP_LOCAL_ADDRESS,
+               g_param_spec_object (SOUP_SOCKET_LOCAL_ADDRESS,
+                                    "Local address",
+                                    "Address of local end of socket",
+                                    SOUP_TYPE_ADDRESS,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_REMOTE_ADDRESS,
+               g_param_spec_object (SOUP_SOCKET_REMOTE_ADDRESS,
+                                    "Remote address",
+                                    "Address of remote end of socket",
+                                    SOUP_TYPE_ADDRESS,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
                object_class, PROP_NON_BLOCKING,
                g_param_spec_boolean (SOUP_SOCKET_FLAG_NONBLOCKING,
                                      "Non-blocking",
@@ -265,27 +258,6 @@ soup_socket_class_init (SoupSocketClass *socket_class)
                                      TRUE,
                                      G_PARAM_READWRITE));
        g_object_class_install_property (
-               object_class, PROP_NODELAY,
-               g_param_spec_boolean (SOUP_SOCKET_FLAG_NODELAY,
-                                     "NODELAY",
-                                     "Whether or not the socket uses TCP NODELAY",
-                                     TRUE,
-                                     G_PARAM_READWRITE));
-       g_object_class_install_property (
-               object_class, PROP_REUSEADDR,
-               g_param_spec_boolean (SOUP_SOCKET_FLAG_REUSEADDR,
-                                     "REUSEADDR",
-                                     "Whether or not the socket uses the TCP REUSEADDR flag",
-                                     TRUE,
-                                     G_PARAM_READWRITE));
-       g_object_class_install_property (
-               object_class, PROP_CLOEXEC,
-               g_param_spec_boolean (SOUP_SOCKET_FLAG_CLOEXEC,
-                                     "CLOEXEC",
-                                     "Whether or not the socket will be closed automatically on exec()",
-                                     FALSE,
-                                     G_PARAM_READWRITE));
-       g_object_class_install_property (
                object_class, PROP_IS_SERVER,
                g_param_spec_boolean (SOUP_SOCKET_IS_SERVER,
                                      "Server",
@@ -321,12 +293,12 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 
 
 static void
-update_fdflags (SoupSocketPrivate *priv)
+set_nonblocking (SoupSocketPrivate *priv)
 {
-       int opt;
-       struct timeval timeout;
 #ifndef G_OS_WIN32
        int flags;
+#else
+       u_log val;
 #endif
 
        if (priv->sockfd == -1)
@@ -341,28 +313,39 @@ update_fdflags (SoupSocketPrivate *priv)
                        flags &= ~O_NONBLOCK;
                fcntl (priv->sockfd, F_SETFL, flags);
        }
-       flags = fcntl (priv->sockfd, F_GETFD, 0);
-       if (flags != -1) {
-               if (priv->cloexec)
-                       flags |= FD_CLOEXEC;
-               else
-                       flags &= ~FD_CLOEXEC;
-               fcntl (priv->sockfd, F_SETFD, flags);
-        }
-
 #else
-       if (priv->non_blocking) {
-               u_long val = 1;
-               ioctlsocket (priv->sockfd, FIONBIO, &val);
-       } else {
-               u_long val = 0;
-               ioctlsocket (priv->sockfd, FIONBIO, &val);
-       }               
+       val = priv->non_blocking ? 1 : 0;
+       ioctlsocket (priv->sockfd, FIONBIO, &val);
+#endif
+}
+
+static void
+set_fdflags (SoupSocketPrivate *priv)
+{
+       int opt;
+       struct timeval timeout;
+#ifndef G_OS_WIN32
+       int flags;
+#endif
+
+       if (priv->sockfd == -1)
+               return;
+
+       set_nonblocking (priv);
+
+#ifndef G_OS_WIN32
+       flags = fcntl (priv->sockfd, F_GETFD, 0);
+       if (flags != -1) {
+               flags |= FD_CLOEXEC;
+               fcntl (priv->sockfd, F_SETFD, flags);
+       }
 #endif
 
-       opt = (priv->nodelay != 0);
+       opt = 1;
        setsockopt (priv->sockfd, IPPROTO_TCP,
                    TCP_NODELAY, (void *) &opt, sizeof (opt));
+       setsockopt (priv->sockfd, SOL_SOCKET,
+                   SO_REUSEADDR, (void *) &opt, sizeof (opt));
 
        timeout.tv_sec = priv->timeout;
        timeout.tv_usec = 0;
@@ -374,9 +357,16 @@ update_fdflags (SoupSocketPrivate *priv)
        setsockopt (priv->sockfd, SOL_SOCKET,
                    SO_SNDTIMEO, (void *) &timeout, sizeof (timeout));
 
-       opt = (priv->reuseaddr != 0);
-       setsockopt (priv->sockfd, SOL_SOCKET,
-                   SO_REUSEADDR, (void *) &opt, sizeof (opt));
+#ifndef G_OS_WIN32
+       priv->iochannel =
+               g_io_channel_unix_new (priv->sockfd);
+#else
+       priv->iochannel =
+               g_io_channel_win32_new_socket (priv->sockfd);
+#endif
+       g_io_channel_set_close_on_unref (priv->iochannel, TRUE);
+       g_io_channel_set_encoding (priv->iochannel, NULL, NULL);
+       g_io_channel_set_buffered (priv->iochannel, FALSE);
 }
 
 static void
@@ -386,21 +376,15 @@ set_property (GObject *object, guint prop_id,
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
 
        switch (prop_id) {
-       case PROP_NON_BLOCKING:
-               priv->non_blocking = g_value_get_boolean (value);
-               update_fdflags (priv);
+       case PROP_LOCAL_ADDRESS:
+               priv->local_addr = (SoupAddress *)g_value_dup_object (value);
                break;
-       case PROP_NODELAY:
-               priv->nodelay = g_value_get_boolean (value);
-               update_fdflags (priv);
+       case PROP_REMOTE_ADDRESS:
+               priv->remote_addr = (SoupAddress *)g_value_dup_object (value);
                break;
-       case PROP_REUSEADDR:
-               priv->reuseaddr = g_value_get_boolean (value);
-               update_fdflags (priv);
-               break;
-       case PROP_CLOEXEC:
-               priv->cloexec = g_value_get_boolean (value);
-               update_fdflags (priv);
+       case PROP_NON_BLOCKING:
+               priv->non_blocking = g_value_get_boolean (value);
+               set_nonblocking (priv);
                break;
        case PROP_SSL_CREDENTIALS:
                priv->ssl_creds = g_value_get_pointer (value);
@@ -425,18 +409,15 @@ get_property (GObject *object, guint prop_id,
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
 
        switch (prop_id) {
-       case PROP_NON_BLOCKING:
-               g_value_set_boolean (value, priv->non_blocking);
+       case PROP_LOCAL_ADDRESS:
+               g_value_set_object (value, soup_socket_get_local_address (SOUP_SOCKET (object)));
                break;
-       case PROP_NODELAY:
-               g_value_set_boolean (value, priv->nodelay);
+       case PROP_REMOTE_ADDRESS:
+               g_value_set_object (value, soup_socket_get_remote_address (SOUP_SOCKET (object)));
                break;
-       case PROP_REUSEADDR:
-               g_value_set_boolean (value, priv->reuseaddr);
+       case PROP_NON_BLOCKING:
+               g_value_set_boolean (value, priv->non_blocking);
                break;
-       case PROP_CLOEXEC:
-               g_value_set_boolean (value, priv->cloexec);
-                break;
        case PROP_IS_SERVER:
                g_value_set_boolean (value, priv->is_server);
                break;
@@ -478,44 +459,43 @@ soup_socket_new (const char *optname1, ...)
        return sock;
 }
 
-static GIOChannel *
-get_iochannel (SoupSocketPrivate *priv)
-{
-       g_mutex_lock (priv->iolock);
-       if (!priv->iochannel) {
-#ifndef G_OS_WIN32
-               priv->iochannel =
-                       g_io_channel_unix_new (priv->sockfd);
-#else
-               priv->iochannel =
-                       g_io_channel_win32_new_socket (priv->sockfd);
-#endif
-               g_io_channel_set_close_on_unref (priv->iochannel, TRUE);
-               g_io_channel_set_encoding (priv->iochannel, NULL, NULL);
-               g_io_channel_set_buffered (priv->iochannel, FALSE);
-       }
-       g_mutex_unlock (priv->iolock);
-       return priv->iochannel;
-}
+typedef struct {
+       SoupSocket *sock;
+       GCancellable *cancellable;
+       guint cancel_id;
+       SoupSocketCallback callback;
+       gpointer user_data;
+} SoupSocketAsyncConnectData;
 
 static gboolean
 idle_connect_result (gpointer user_data)
 {
-       SoupSocket *sock = user_data;
-       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       SoupSocketAsyncConnectData *sacd = user_data;
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+       guint status;
 
        priv->watch_src = NULL;
+       if (sacd->cancel_id)
+               g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id);
+
+       if (priv->sockfd == -1) {
+               if (g_cancellable_is_cancelled (sacd->cancellable))
+                       status = SOUP_STATUS_CANCELLED;
+               else
+                       status = SOUP_STATUS_CANT_CONNECT;
+       } else
+               status = SOUP_STATUS_OK;
 
-       g_signal_emit (sock, signals[CONNECT_RESULT], 0,
-                      priv->sockfd != -1 ? SOUP_STATUS_OK : SOUP_STATUS_CANT_CONNECT);
+       sacd->callback (sacd->sock, status, sacd->user_data);
+       g_slice_free (SoupSocketAsyncConnectData, sacd);
        return FALSE;
 }
 
 static gboolean
 connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
 {
-       SoupSocket *sock = data;
-       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       SoupSocketAsyncConnectData *sacd = data;
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
        int error = 0;
        int len = sizeof (error);
 
@@ -523,123 +503,201 @@ connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
        g_source_destroy (priv->watch_src);
        priv->watch_src = NULL;
 
-       if (condition & ~(G_IO_IN | G_IO_OUT))
-               goto cant_connect;
+       if ((condition & ~(G_IO_IN | G_IO_OUT)) ||
+           (getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR,
+                        (void *)&error, (void *)&len) != 0) ||
+           error)
+               disconnect_internal (priv);
 
-       if (getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR,
-                       (void *)&error, (void *)&len) != 0)
-               goto cant_connect;
-       if (error)
-               goto cant_connect;
+       return idle_connect_result (sacd);
+}
 
-       return idle_connect_result (sock);
+static void
+got_address (SoupAddress *addr, guint status, gpointer user_data)
+{
+       SoupSocketAsyncConnectData *sacd = user_data;
 
- cant_connect:
-       g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_STATUS_CANT_CONNECT);
-       return FALSE;
+       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+               sacd->callback (sacd->sock, status, sacd->user_data);
+               g_slice_free (SoupSocketAsyncConnectData, sacd);
+               return;
+       }
+
+       soup_socket_connect_async (sacd->sock, sacd->cancellable,
+                                  sacd->callback, sacd->user_data);
+       g_slice_free (SoupSocketAsyncConnectData, sacd);
 }
 
 static void
-got_address (SoupAddress *addr, guint status, gpointer user_data)
+async_cancel (GCancellable *cancellable, gpointer user_data)
+{
+       SoupSocketAsyncConnectData *sacd = user_data;
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+
+       if (priv->watch_src)
+               g_source_destroy (priv->watch_src);
+       disconnect_internal (priv);
+       priv->watch_src = soup_add_idle (priv->async_context,
+                                        idle_connect_result, sacd);
+}
+
+static guint
+socket_connect_internal (SoupSocket *sock)
 {
-       SoupSocket *sock = user_data;
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       struct sockaddr *sa;
+       int len, status;
 
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               g_signal_emit (sock, signals[CONNECT_RESULT], 0, status);
-               g_object_unref (sock);
+       sa = soup_address_get_sockaddr (priv->remote_addr, &len);
+       if (!sa)
+               return SOUP_STATUS_CANT_RESOLVE;
+
+       priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
+       if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
+               return SOUP_STATUS_CANT_CONNECT;
+       set_fdflags (priv);
+
+       status = connect (priv->sockfd, sa, len);
+
+       if (SOUP_IS_SOCKET_ERROR (status)) {
+               if (SOUP_IS_CONNECT_STATUS_INPROGRESS ())
+                       return SOUP_STATUS_CONTINUE;
+
+               disconnect_internal (priv);
+               return SOUP_STATUS_CANT_CONNECT;
+       } else
+               return SOUP_STATUS_OK;
+}
+
+/**
+ * SoupSocketCallback:
+ * @sock: the #SoupSocket
+ * @status: an HTTP status code indicating success or failure
+ * @user_data: the data passed to soup_socket_connect_async()
+ *
+ * The callback function passed to soup_socket_connect_async().
+ **/
+
+/**
+ * soup_socket_connect_async:
+ * @sock: a client #SoupSocket (which must not already be connected)
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to call after connecting
+ * @user_data: data to pass to @callback
+ *
+ * Begins asynchronously connecting to @sock's remote address. The
+ * socket will call @callback when it succeeds or fails (but not
+ * before returning from this function).
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * connection. @callback will still be invoked in this case, with a
+ * status of %SOUP_STATUS_CANCELLED.
+ **/
+void
+soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
+                          SoupSocketCallback callback, gpointer user_data)
+{
+       SoupSocketPrivate *priv;
+       SoupSocketAsyncConnectData *sacd;
+       guint status;
+
+       g_return_if_fail (SOUP_IS_SOCKET (sock));
+       priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       g_return_if_fail (priv->remote_addr != NULL);
+
+       sacd = g_slice_new0 (SoupSocketAsyncConnectData);
+       sacd->sock = sock;
+       sacd->cancellable = cancellable;
+       sacd->callback = callback;
+       sacd->user_data = user_data;
+
+       if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
+               soup_address_resolve_async (priv->remote_addr,
+                                           priv->async_context,
+                                           cancellable,
+                                           got_address, sacd);
                return;
        }
 
-       soup_socket_connect (sock, priv->remote_addr);
-       /* soup_socket_connect re-reffed addr */
-       g_object_unref (addr);
+       status = socket_connect_internal (sock);
+       if (status == SOUP_STATUS_CONTINUE) {
+               /* Wait for connect to succeed or fail */
+               priv->watch_src =
+                       soup_add_io_watch (priv->async_context,
+                                          priv->iochannel,
+                                          G_IO_IN | G_IO_OUT |
+                                          G_IO_PRI | G_IO_ERR |
+                                          G_IO_HUP | G_IO_NVAL,
+                                          connect_watch, sacd);
+               if (cancellable) {
+                       sacd->cancel_id =
+                               g_signal_connect (cancellable, "cancelled",
+                                                 G_CALLBACK (async_cancel),
+                                                 sacd);
+               }
+       } else {
+               priv->watch_src = soup_add_idle (priv->async_context,
+                                                idle_connect_result, sacd);
+       }
+}
 
-       g_object_unref (sock);
+static void
+sync_cancel (GCancellable *cancellable, gpointer sock)
+{
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+       shutdown (priv->sockfd, SHUT_RDWR);
 }
 
 /**
- * soup_socket_connect:
+ * soup_socket_connect_sync:
  * @sock: a client #SoupSocket (which must not already be connected)
- * @remote_addr: address to connect to
+ * @cancellable: a #GCancellable, or %NULL
  *
- * If %SOUP_SOCKET_FLAG_NONBLOCKING has been set on the socket, this
- * begins asynchronously connecting to the given address. The socket
- * will emit %connect_result when it succeeds or fails (but not before
- * returning from this function).
+ * Attempt to synchronously connect @sock to its remote address.
  *
- * If %SOUP_SOCKET_FLAG_NONBLOCKING has not been set, this will
- * attempt to synchronously connect.
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * connection, in which case soup_socket_connect_sync() will return
+ * %SOUP_STATUS_CANCELLED.
  *
- * Return value: %SOUP_STATUS_CONTINUE if connecting asynchronously,
- * otherwise a success or failure code.
+ * Return value: a success or failure code.
  **/
 guint
-soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr)
+soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
 {
        SoupSocketPrivate *priv;
-       struct sockaddr *sa;
-       int len, status;
+       guint status, cancel_id;
 
        g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
        priv = SOUP_SOCKET_GET_PRIVATE (sock);
        g_return_val_if_fail (!priv->is_server, SOUP_STATUS_MALFORMED);
        g_return_val_if_fail (priv->sockfd == -1, SOUP_STATUS_MALFORMED);
-       g_return_val_if_fail (SOUP_IS_ADDRESS (remote_addr), SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
 
-       priv->remote_addr = g_object_ref (remote_addr);
-       if (!priv->non_blocking) {
-               status = soup_address_resolve_sync (remote_addr);
+       if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
+               status = soup_address_resolve_sync (priv->remote_addr,
+                                                   cancellable);
                if (!SOUP_STATUS_IS_SUCCESSFUL (status))
                        return status;
        }
 
-       sa = soup_address_get_sockaddr (priv->remote_addr, &len);
-       if (!sa) {
-               if (!priv->non_blocking)
-                       return SOUP_STATUS_CANT_RESOLVE;
-
-               g_object_ref (sock);
-               soup_address_resolve_async_full (remote_addr, priv->async_context,
-                                                got_address, sock);
-               return SOUP_STATUS_CONTINUE;
-       }
-
-       priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
-       if (SOUP_IS_INVALID_SOCKET (priv->sockfd)) {
-               goto done;
+       if (cancellable) {
+               cancel_id = g_signal_connect (cancellable, "cancelled",
+                                             G_CALLBACK (sync_cancel), sock);
        }
-       update_fdflags (priv);
 
-       status = connect (priv->sockfd, sa, len);
+       status = socket_connect_internal (sock);
 
-       if (SOUP_IS_SOCKET_ERROR (status)) {
-               if (SOUP_IS_CONNECT_STATUS_INPROGRESS ()) {
-                       /* Wait for connect to succeed or fail */
-                       priv->watch_src =
-                               soup_add_io_watch (priv->async_context,
-                                                  get_iochannel (priv),
-                                                  G_IO_IN | G_IO_OUT |
-                                                  G_IO_PRI | G_IO_ERR |
-                                                  G_IO_HUP | G_IO_NVAL,
-                                                  connect_watch, sock);
-                       return SOUP_STATUS_CONTINUE;
-               } else {
-                       SOUP_CLOSE_SOCKET (priv->sockfd);
-                       priv->sockfd = -1;
+       if (cancellable) {
+               if (status != SOUP_STATUS_OK &&
+                   g_cancellable_is_cancelled (cancellable)) {
+                       status = SOUP_STATUS_CANCELLED;
+                       disconnect_internal (priv);
                }
-       } else
-               get_iochannel (priv);
+               g_signal_handler_disconnect (cancellable, cancel_id);
+       }
 
- done:
-       if (priv->non_blocking) {
-               priv->watch_src = soup_add_idle (priv->async_context,
-                                                idle_connect_result, sock);
-               return SOUP_STATUS_CONTINUE;
-       } else if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
-               return SOUP_STATUS_CANT_CONNECT;
-       else
-               return SOUP_STATUS_OK;
+       return status;
 }
 
 static gboolean
@@ -667,20 +725,18 @@ listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
        if (priv->async_context)
                new_priv->async_context = g_main_context_ref (priv->async_context);
        new_priv->non_blocking = priv->non_blocking;
-       new_priv->nodelay = priv->nodelay;
        new_priv->is_server = TRUE;
        new_priv->ssl_creds = priv->ssl_creds;
-       update_fdflags (new_priv);
+       set_fdflags (new_priv);
 
        new_priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
 
        if (new_priv->ssl_creds) {
-               if (!soup_socket_start_ssl (new)) {
+               if (!soup_socket_start_ssl (new, NULL)) {
                        g_object_unref (new);
                        return TRUE;
                }
-       } else
-               get_iochannel (new_priv);
+       }
 
        g_signal_emit (sock, signals[NEW_CONNECTION], 0, new);
        g_object_unref (new);
@@ -692,15 +748,15 @@ listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
  * soup_socket_listen:
  * @sock: a server #SoupSocket (which must not already be connected or
  * listening)
- * @local_addr: Local address to bind to.
  *
- * Makes @sock start listening on the given interface and port. When
- * connections come in, @sock will emit %new_connection.
+ * Makes @sock start listening on its local address. When connections
+ * come in, @sock will emit %new_connection.
  *
  * Return value: whether or not @sock is now listening.
  **/
 gboolean
-soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
+soup_socket_listen (SoupSocket *sock)
+
 {
        SoupSocketPrivate *priv;
        struct sockaddr *sa;
@@ -709,7 +765,7 @@ soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
        g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
        priv = SOUP_SOCKET_GET_PRIVATE (sock);
        g_return_val_if_fail (priv->sockfd == -1, FALSE);
-       g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), FALSE);
+       g_return_val_if_fail (priv->local_addr != NULL, FALSE);
 
        priv->is_server = TRUE;
 
@@ -719,33 +775,34 @@ soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
         * have to make a new addr by calling getsockname(), which
         * will have the right port number.
         */
-       sa = soup_address_get_sockaddr (local_addr, &sa_len);
+       sa = soup_address_get_sockaddr (priv->local_addr, &sa_len);
        g_return_val_if_fail (sa != NULL, FALSE);
 
        priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
        if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
                goto cant_listen;
-       update_fdflags (priv);
+       set_fdflags (priv);
 
        /* Bind */
        if (bind (priv->sockfd, sa, sa_len) != 0)
                goto cant_listen;
+       /* Force local_addr to be re-resolved now */
+       g_object_unref (priv->local_addr);
+       priv->local_addr = NULL;
 
        /* Listen */
        if (listen (priv->sockfd, 10) != 0)
                goto cant_listen;
 
        priv->watch_src = soup_add_io_watch (priv->async_context,
-                                            get_iochannel (priv),
+                                            priv->iochannel,
                                             G_IO_IN | G_IO_ERR | G_IO_HUP,
                                             listen_watch, sock);
        return TRUE;
 
  cant_listen:
-       if (priv->sockfd != -1) {
-               SOUP_CLOSE_SOCKET (priv->sockfd);
-               priv->sockfd = -1;
-       }
+       if (priv->iochannel)
+               disconnect_internal (priv);
 
        return FALSE;
 }
@@ -753,23 +810,25 @@ soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
 /**
  * soup_socket_start_ssl:
  * @sock: the socket
+ * @cancellable: a #GCancellable
  *
  * Starts using SSL on @socket.
  *
  * Return value: success or failure
  **/
 gboolean
-soup_socket_start_ssl (SoupSocket *sock)
+soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
 {
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
-       return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr));
+       return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr), cancellable);
 }
        
 /**
  * soup_socket_start_proxy_ssl:
  * @sock: the socket
  * @ssl_host: hostname of the SSL server
+ * @cancellable: a #GCancellable
  *
  * Starts using SSL on @socket, expecting to find a host named
  * @ssl_host.
@@ -777,13 +836,14 @@ soup_socket_start_ssl (SoupSocket *sock)
  * Return value: success or failure
  **/
 gboolean
-soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host)
+soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
+                            GCancellable *cancellable)
 {
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
        GIOChannel *ssl_chan;
        GIOChannel *real_chan;
 
-       real_chan = get_iochannel (priv);
+       real_chan = priv->iochannel;
        ssl_chan = soup_ssl_wrap_iochannel (
                real_chan, priv->is_server ?
                SOUP_SSL_TYPE_SERVER : SOUP_SSL_TYPE_CLIENT,
@@ -798,134 +858,14 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host)
        return TRUE;
 }
        
-
-/**
- * soup_socket_client_new_async:
- * @hostname: remote machine to connect to
- * @port: remote port to connect to
- * @ssl_creds: SSL credentials structure, or %NULL if not SSL
- * @callback: callback to call when the socket is connected
- * @user_data: data for @callback
- *
- * Creates a connection to @hostname and @port. @callback will be
- * called when the connection completes (or fails).
- *
- * Uses the default #GMainContext. If you need to use an alternate
- * context, use soup_socket_new() and soup_socket_connect() directly.
- *
- * Return value: the new socket (not yet ready for use).
- **/
-SoupSocket *
-soup_socket_client_new_async (const char *hostname, guint port,
-                             gpointer ssl_creds,
-                             SoupSocketCallback callback, gpointer user_data)
-{
-       SoupSocket *sock;
-       SoupAddress *addr;
-
-       g_return_val_if_fail (hostname != NULL, NULL);
-
-       sock = g_object_new (SOUP_TYPE_SOCKET,
-                            SOUP_SOCKET_SSL_CREDENTIALS, ssl_creds,
-                            NULL);
-       addr = soup_address_new (hostname, port);
-       soup_socket_connect (sock, addr);
-       g_object_unref (addr);
-
-       if (callback) {
-               soup_signal_connect_once (sock, "connect_result",
-                                         G_CALLBACK (callback), user_data);
-       }
-       return sock;
-}
-
-/**
- * soup_socket_client_new_sync:
- * @hostname: remote machine to connect to
- * @port: remote port to connect to
- * @ssl_creds: SSL credentials structure, or %NULL if not SSL
- * @status_ret: pointer to return the soup status in
- *
- * Creates a connection to @hostname and @port. If @status_ret is not
- * %NULL, it will contain a status code on return.
- *
- * Return value: the new socket, or %NULL if it could not connect.
- **/
-SoupSocket *
-soup_socket_client_new_sync (const char *hostname, guint port,
-                            gpointer ssl_creds, guint *status_ret)
-{
-       SoupSocket *sock;
-       SoupSocketPrivate *priv;
-       SoupAddress *addr;
-       guint status;
-
-       g_return_val_if_fail (hostname != NULL, NULL);
-
-       sock = g_object_new (SOUP_TYPE_SOCKET,
-                            SOUP_SOCKET_SSL_CREDENTIALS, ssl_creds,
-                            NULL);
-       priv = SOUP_SOCKET_GET_PRIVATE (sock);
-       priv->non_blocking = FALSE;
-       addr = soup_address_new (hostname, port);
-       status = soup_socket_connect (sock, addr);
-       g_object_unref (addr);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               g_object_unref (sock);
-               sock = NULL;
-       }
-
-       if (status_ret)
-               *status_ret = status;
-       return sock;
-}
-
-/**
- * soup_socket_server_new:
- * @local_addr: Local address to bind to. (Use soup_address_any_new() to
- * accept connections on any local address)
- * @ssl_creds: SSL credentials, or %NULL if this is not an SSL server
- * @callback: Callback to call when a client connects
- * @user_data: data to pass to @callback.
- *
- * Create and open a new #SoupSocket listening on the specified
- * address. @callback will be called each time a client connects,
- * with a new #SoupSocket.
- *
- * Uses the default #GMainContext. If you need to use an alternate
- * context, use soup_socket_new() and soup_socket_listen() directly.
- *
- * Returns: a new #SoupSocket, or NULL if there was a failure.
- **/
-SoupSocket *
-soup_socket_server_new (SoupAddress *local_addr, gpointer ssl_creds,
-                       SoupSocketListenerCallback callback,
-                       gpointer user_data)
+gboolean
+soup_socket_is_ssl (SoupSocket *sock)
 {
-       SoupSocket *sock;
-       SoupSocketPrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), NULL);
-
-       sock = g_object_new (SOUP_TYPE_SOCKET,
-                            SOUP_SOCKET_SSL_CREDENTIALS, ssl_creds,
-                            NULL);
-       priv = SOUP_SOCKET_GET_PRIVATE (sock);
-       if (!soup_socket_listen (sock, local_addr)) {
-               g_object_unref (sock);
-               return NULL;
-       }
-
-       if (callback) {
-               g_signal_connect (sock, "new_connection",
-                                 G_CALLBACK (callback), user_data);
-       }
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
-       return sock;
+       return priv->ssl_creds != NULL;
 }
 
-
 /**
  * soup_socket_disconnect:
  * @sock: a #SoupSocket
@@ -952,19 +892,18 @@ soup_socket_disconnect (SoupSocket *sock)
                int sockfd;
 
                /* Another thread is currently doing IO, so
-                * we can't close the iochannel. So just kick
-                * the file descriptor out from under it.
+                * we can't close the iochannel. So just shutdown
+                * the file descriptor to force the I/O to fail.
+                * (It will actually be closed when the socket is
+                * destroyed.)
                 */
-
                sockfd = priv->sockfd;
                priv->sockfd = -1;
+
                if (sockfd == -1)
                        already_disconnected = TRUE;
-               else {
-                       g_io_channel_set_close_on_unref (priv->iochannel,
-                                                        FALSE);
-                       SOUP_CLOSE_SOCKET (sockfd);
-               }
+               else
+                       shutdown (sockfd, SHUT_RDWR);
        }
 
        if (already_disconnected)
@@ -1078,41 +1017,38 @@ socket_read_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
 }
 
 static SoupSocketIOStatus
-read_from_network (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+read_from_network (SoupSocket *sock, gpointer buffer, gsize len,
+                  gsize *nread, GError **error)
 {
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
        GIOStatus status;
        GIOCondition cond = G_IO_IN;
-       GError *err = NULL;
+       GError *my_err = NULL;
 
        if (!priv->iochannel) 
                return SOUP_SOCKET_EOF;
 
        status = g_io_channel_read_chars (priv->iochannel,
-                                         buffer, len, nread, &err);
-       if (err) {
-               if (err->domain == SOUP_SSL_ERROR &&
-                   err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE)
+                                         buffer, len, nread, &my_err);
+       if (my_err) {
+               if (my_err->domain == SOUP_SSL_ERROR &&
+                   my_err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE)
                        cond = G_IO_OUT;
-               g_object_set_data_full (G_OBJECT (sock),
-                                       "SoupSocket-last_error",
-                                       err, (GDestroyNotify)g_error_free);
-       } else {
-               g_object_set_data (G_OBJECT (sock),
-                                  "SoupSocket-last_error",
-                                  NULL);
+               g_propagate_error (error, my_err);
        }
 
        switch (status) {
        case G_IO_STATUS_NORMAL:
        case G_IO_STATUS_AGAIN:
-               if (*nread > 0)
+               if (*nread > 0) {
+                       g_clear_error (error);
                        return SOUP_SOCKET_OK;
+               }
 
-               /* If the connection/session is sync and  we get
-                  EAGAIN or EWOULDBLOCK, then it will be socket timeout
-                  and should be treated as an error condition.
-               */
+               /* If the socket is sync and we get EAGAIN, then it is
+                * a socket timeout and should be treated as an error
+                * condition.
+                */
                if (!priv->non_blocking)
                        return SOUP_SOCKET_ERROR;
 
@@ -1123,9 +1059,11 @@ read_from_network (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
                                                   cond | G_IO_HUP | G_IO_ERR,
                                                   socket_read_watch, sock);
                }
+               g_clear_error (error);
                return SOUP_SOCKET_WOULD_BLOCK;
 
        case G_IO_STATUS_EOF:
+               g_clear_error (error);
                return SOUP_SOCKET_EOF;
 
        default:
@@ -1155,11 +1093,23 @@ read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
 }
 
 /**
+ * SoupSocketIOStatus:
+ * @SOUP_SOCKET_OK: Success
+ * @SOUP_SOCKET_WOULD_BLOCK: Cannot read/write any more at this time
+ * @SOUP_SOCKET_EOF: End of file
+ * @SOUP_SOCKET_ERROR: Other error
+ *
+ * Return value from the #SoupSocket IO methods.
+ **/
+
+/**
  * soup_socket_read:
  * @sock: the socket
  * @buffer: buffer to read into
  * @len: size of @buffer in bytes
  * @nread: on return, the number of bytes read into @buffer
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
  *
  * Attempts to read up to @len bytes from @sock into @buffer. If some
  * data is successfully read, soup_socket_read() will return
@@ -1175,10 +1125,12 @@ read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
  *
  * Return value: a #SoupSocketIOStatus, as described above (or
  * %SOUP_SOCKET_EOF if the socket is no longer connected, or
- * %SOUP_SOCKET_ERROR on any other error).
+ * %SOUP_SOCKET_ERROR on any other error, in which case @error will
+ * also be set).
  **/
 SoupSocketIOStatus
-soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len,
+                 gsize *nread, GCancellable *cancellable, GError **error)
 {
        SoupSocketPrivate *priv;
        SoupSocketIOStatus status;
@@ -1190,7 +1142,7 @@ soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
        if (priv->read_buf)
                status = read_from_buf (sock, buffer, len, nread);
        else
-               status = read_from_network (sock, buffer, len, nread);
+               status = read_from_network (sock, buffer, len, nread, error);
        g_mutex_unlock (priv->iolock);
 
        return status;
@@ -1206,6 +1158,8 @@ soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
  * @nread: on return, the number of bytes read into @buffer
  * @got_boundary: on return, whether or not the data in @buffer
  * ends with the boundary string
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
  *
  * Like soup_socket_read(), but reads no further than the first
  * occurrence of @boundary. (If the boundary is found, it will be
@@ -1217,7 +1171,8 @@ soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
 SoupSocketIOStatus
 soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
                        gconstpointer boundary, gsize boundary_len,
-                       gsize *nread, gboolean *got_boundary)
+                       gsize *nread, gboolean *got_boundary,
+                       GCancellable *cancellable, GError **error)
 {
        SoupSocketPrivate *priv;
        SoupSocketIOStatus status;
@@ -1242,7 +1197,7 @@ soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
                g_byte_array_set_size (read_buf, len);
                status = read_from_network (sock,
                                            read_buf->data + prev_len,
-                                           len - prev_len, nread);
+                                           len - prev_len, nread, error);
                read_buf->len = prev_len + *nread;
 
                if (status != SOUP_SOCKET_OK) {
@@ -1294,6 +1249,8 @@ socket_write_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
  * @buffer: data to write
  * @len: size of @buffer, in bytes
  * @nwrote: on return, number of bytes written
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
  *
  * Attempts to write @len bytes from @buffer to @sock. If some data is
  * successfully written, the resturn status will be
@@ -1307,11 +1264,13 @@ socket_write_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
  * %SOUP_SOCKET_WOULD_BLOCK.)
  *
  * Return value: a #SoupSocketIOStatus, as described above (or
- * %SOUP_SOCKET_EOF or %SOUP_SOCKET_ERROR).
+ * %SOUP_SOCKET_EOF or %SOUP_SOCKET_ERROR. @error will be set if the
+ * return value is %SOUP_SOCKET_ERROR.)
  **/
 SoupSocketIOStatus
 soup_socket_write (SoupSocket *sock, gconstpointer buffer,
-                  gsize len, gsize *nwrote)
+                  gsize len, gsize *nwrote,
+                  GCancellable *cancellable, GError **error)
 {
        SoupSocketPrivate *priv;
        GIOStatus status;
@@ -1319,7 +1278,7 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
        gpointer pipe_handler;
 #endif
        GIOCondition cond = G_IO_OUT;
-       GError *err = NULL;
+       GError *my_err = NULL;
 
        g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
        priv = SOUP_SOCKET_GET_PRIVATE (sock);
@@ -1339,27 +1298,20 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
        pipe_handler = signal (SIGPIPE, SIG_IGN);
 #endif
        status = g_io_channel_write_chars (priv->iochannel,
-                                          buffer, len, nwrote, &err);
+                                          buffer, len, nwrote, &my_err);
 #ifdef SIGPIPE
        signal (SIGPIPE, pipe_handler);
 #endif
-       if (err) {
-               if (err->domain == SOUP_SSL_ERROR &&
-                   err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ)
+       if (my_err) {
+               if (my_err->domain == SOUP_SSL_ERROR &&
+                   my_err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ)
                        cond = G_IO_IN;
-               g_object_set_data_full (G_OBJECT (sock),
-                                       "SoupSocket-last_error",
-                                       err, (GDestroyNotify)g_error_free);
-       } else {
-               g_object_set_data (G_OBJECT (sock),
-                                  "SoupSocket-last_error",
-                                  NULL);
+               g_propagate_error (error, my_err);
        }
 
-       /* If the connection/session is sync and  we get
-          EAGAIN or EWOULDBLOCK, then it will be socket timeout
-          and should be treated as an error condition.
-       */
+       /* If the socket is sync and we get EAGAIN, then it is a
+        * socket timeout and should be treated as an error condition.
+        */
        if (!priv->non_blocking && status == G_IO_STATUS_AGAIN) {
                g_mutex_unlock (priv->iolock);
                return SOUP_SOCKET_ERROR;
@@ -1370,6 +1322,8 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
                return SOUP_SOCKET_ERROR;
        }
 
+       g_clear_error (error);
+
        if (*nwrote) {
                g_mutex_unlock (priv->iolock);
                return SOUP_SOCKET_OK;
index 5d60080..0cdac17 100644 (file)
@@ -7,6 +7,7 @@
 #define SOUP_SOCKET_H 1
 
 #include <libsoup/soup-types.h>
+#include <gio/gio.h>
 
 G_BEGIN_DECLS
 
@@ -26,91 +27,59 @@ typedef struct {
        GObjectClass parent_class;
 
        /* signals */
-       void (*connect_result) (SoupSocket *, guint);
        void (*readable)       (SoupSocket *);
        void (*writable)       (SoupSocket *);
        void (*disconnected)   (SoupSocket *);
 
        void (*new_connection) (SoupSocket *, SoupSocket *);
+
+       /* Padding for future expansion */
+       void (*_libsoup_reserved1) (void);
+       void (*_libsoup_reserved2) (void);
+       void (*_libsoup_reserved3) (void);
+       void (*_libsoup_reserved4) (void);
 } SoupSocketClass;
 
+#define SOUP_SOCKET_LOCAL_ADDRESS    "local-address"
+#define SOUP_SOCKET_REMOTE_ADDRESS   "remote-address"
 #define SOUP_SOCKET_FLAG_NONBLOCKING "non-blocking"
-#define SOUP_SOCKET_FLAG_NODELAY     "nodelay"
-#define SOUP_SOCKET_FLAG_REUSEADDR   "reuseaddr"
-#define SOUP_SOCKET_FLAG_CLOEXEC     "cloexec"
 #define SOUP_SOCKET_IS_SERVER        "is-server"
 #define SOUP_SOCKET_SSL_CREDENTIALS  "ssl-creds"
 #define SOUP_SOCKET_ASYNC_CONTEXT    "async-context"
 #define SOUP_SOCKET_TIMEOUT         "timeout"
 
-/**
- * SoupSocketCallback:
- * @sock: the #SoupSocket
- * @status: an HTTP status code indicating success or failure
- * @user_data: the data passed to soup_socket_client_new_async()
- *
- * The callback function passed to soup_socket_client_new_async().
- **/
 typedef void (*SoupSocketCallback)            (SoupSocket         *sock,
                                               guint               status,
                                               gpointer            user_data);
 
-/**
- * SoupSocketListenerCallback:
- * @listener: the listening #SoupSocket
- * @sock: the newly-received #SoupSocket
- * @user_data: the data passed to soup_socket_server_new().
- *
- * The callback function passed to soup_socket_server_new(), which
- * receives new connections.
- **/
-typedef void (*SoupSocketListenerCallback)    (SoupSocket         *listener,
-                                              SoupSocket         *sock,
-                                              gpointer            user_data);
-
 GType soup_socket_get_type (void);
 
 SoupSocket    *soup_socket_new                (const char         *optname1,
                                               ...) G_GNUC_NULL_TERMINATED;
 
-guint          soup_socket_connect            (SoupSocket         *sock,
-                                              SoupAddress        *remote_addr);
-gboolean       soup_socket_listen             (SoupSocket         *sock,
-                                              SoupAddress        *local_addr);
-gboolean       soup_socket_start_ssl          (SoupSocket         *sock);
+void           soup_socket_connect_async      (SoupSocket         *sock,
+                                              GCancellable       *cancellable,
+                                              SoupSocketCallback  callback,
+                                              gpointer            user_data);
+guint          soup_socket_connect_sync       (SoupSocket         *sock,
+                                              GCancellable       *cancellable);
+
+gboolean       soup_socket_listen             (SoupSocket         *sock);
+
+gboolean       soup_socket_start_ssl          (SoupSocket         *sock,
+                                              GCancellable       *cancellable);
 gboolean       soup_socket_start_proxy_ssl    (SoupSocket         *sock,
-                                              const char         *ssl_host);
+                                              const char         *ssl_host,
+                                              GCancellable       *cancellable);
+gboolean       soup_socket_is_ssl             (SoupSocket         *sock);
 
 void           soup_socket_disconnect         (SoupSocket         *sock);
 gboolean       soup_socket_is_connected       (SoupSocket         *sock);
 
-SoupSocket    *soup_socket_client_new_async   (const char         *hostname,
-                                              guint               port,
-                                              gpointer            ssl_creds,
-                                              SoupSocketCallback  callback,
-                                              gpointer            user_data);
-SoupSocket    *soup_socket_client_new_sync    (const char         *hostname,
-                                              guint               port,
-                                              gpointer            ssl_creds,
-                                              guint              *status_ret);
-SoupSocket    *soup_socket_server_new         (SoupAddress        *local_addr,
-                                              gpointer            ssl_creds,
-                                              SoupSocketListenerCallback callback,
-                                              gpointer            user_data);
-
 SoupAddress   *soup_socket_get_local_address  (SoupSocket         *sock);
 SoupAddress   *soup_socket_get_remote_address (SoupSocket         *sock);
 
 
-/**
- * SoupSocketIOStatus:
- * @SOUP_SOCKET_OK: Success
- * @SOUP_SOCKET_WOULD_BLOCK: Cannot read/write any more at this time
- * @SOUP_SOCKET_EOF: End of file
- * @SOUP_SOCKET_ERROR: Other error
- *
- * Return value from the #SoupSocket IO methods.
- **/
 typedef enum {
        SOUP_SOCKET_OK,
        SOUP_SOCKET_WOULD_BLOCK,
@@ -121,19 +90,25 @@ typedef enum {
 SoupSocketIOStatus  soup_socket_read       (SoupSocket         *sock,
                                            gpointer            buffer,
                                            gsize               len,
-                                           gsize              *nread);
+                                           gsize              *nread,
+                                           GCancellable       *cancellable,
+                                           GError            **error);
 SoupSocketIOStatus  soup_socket_read_until (SoupSocket         *sock,
                                            gpointer            buffer,
                                            gsize               len,
                                            gconstpointer       boundary,
                                            gsize               boundary_len,
                                            gsize              *nread,
-                                           gboolean           *got_boundary);
+                                           gboolean           *got_boundary,
+                                           GCancellable       *cancellable,
+                                           GError            **error);
 
 SoupSocketIOStatus  soup_socket_write      (SoupSocket         *sock,
                                            gconstpointer       buffer,
                                            gsize               len,
-                                           gsize              *nwrote);
+                                           gsize              *nwrote,
+                                           GCancellable       *cancellable,
+                                           GError            **error);
 
 G_END_DECLS
 
index e0b2b1c..37f6e41 100644 (file)
@@ -8,13 +8,6 @@
 
 #include <glib.h>
 
-/**
- * SoupSSLType:
- * @SOUP_SSL_TYPE_CLIENT: the client side of an SSL connection
- * @SOUP_SSL_TYPE_SERVER: the server side of an SSL connection
- *
- * What kind of SSL connection this is.
- **/
 typedef enum {
        SOUP_SSL_TYPE_CLIENT = 0,
        SOUP_SSL_TYPE_SERVER
@@ -34,14 +27,4 @@ GIOChannel         *soup_ssl_wrap_iochannel          (GIOChannel         *sock,
                                                      const char         *remote_host,
                                                      SoupSSLCredentials *creds);
 
-#define SOUP_SSL_ERROR soup_ssl_error_quark()
-
-GQuark soup_ssl_error_quark (void);
-
-typedef enum {
-       SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ,
-       SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE,
-       SOUP_SSL_ERROR_CERTIFICATE,
-} SoupSocketError;
-
 #endif /* SOUP_SSL_H */
index 923e68e..923b72a 100644 (file)
@@ -9,6 +9,145 @@
 
 #include "soup-status.h"
 
+/**
+ * SECTION:soup-status
+ * @short_description: HTTP (and libsoup) status codes
+ *
+ **/
+
+/**
+ * SOUP_STATUS_IS_TRANSPORT_ERROR:
+ * @status: a status code
+ *
+ * Tests if @status is a libsoup transport error.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_INFORMATIONAL:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is an Informational (1xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_SUCCESSFUL:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Successful (2xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_REDIRECTION:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Redirection (3xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_CLIENT_ERROR:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Client Error (4xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_SERVER_ERROR:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Server Error (5xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+
+/**
+ * SoupKnownStatusCode:
+ * @SOUP_STATUS_NONE: No status available. (Eg, the message has not
+ * been sent yet)
+ * @SOUP_STATUS_CANCELLED: Message was cancelled locally
+ * @SOUP_STATUS_CANT_RESOLVE: Unable to resolve destination host name
+ * @SOUP_STATUS_CANT_RESOLVE_PROXY: Unable to resolve proxy host name
+ * @SOUP_STATUS_CANT_CONNECT: Unable to connect to remote host
+ * @SOUP_STATUS_CANT_CONNECT_PROXY: Unable to connect to proxy
+ * @SOUP_STATUS_SSL_FAILED: SSL negotiation failed
+ * @SOUP_STATUS_IO_ERROR: A network error occurred, or the other end
+ * closed the connection unexpectedly
+ * @SOUP_STATUS_MALFORMED: Malformed data (usually a programmer error)
+ * @SOUP_STATUS_TRY_AGAIN: Try again. (Only returned in certain
+ * specifically documented cases)
+ * @SOUP_STATUS_CONTINUE: 100 Continue (HTTP)
+ * @SOUP_STATUS_SWITCHING_PROTOCOLS: 101 Switching Protocols (HTTP)
+ * @SOUP_STATUS_PROCESSING: 102 Processing (WebDAV)
+ * @SOUP_STATUS_OK: 200 Success (HTTP). Also used by many lower-level
+ * soup routines to indicate success.
+ * @SOUP_STATUS_CREATED: 201 Created (HTTP)
+ * @SOUP_STATUS_ACCEPTED: 202 Accepted (HTTP)
+ * @SOUP_STATUS_NON_AUTHORITATIVE: 203 Non-Authoritative Information
+ * (HTTP)
+ * @SOUP_STATUS_NO_CONTENT: 204 No Content (HTTP)
+ * @SOUP_STATUS_RESET_CONTENT: 205 Reset Content (HTTP)
+ * @SOUP_STATUS_PARTIAL_CONTENT: 206 Partial Content (HTTP)
+ * @SOUP_STATUS_MULTI_STATUS: 207 Multi-Status (WebDAV)
+ * @SOUP_STATUS_MULTIPLE_CHOICES: 300 Multiple Choices (HTTP)
+ * @SOUP_STATUS_MOVED_PERMANENTLY: 301 Moved Permanently (HTTP)
+ * @SOUP_STATUS_FOUND: 302 Found (HTTP)
+ * @SOUP_STATUS_MOVED_TEMPORARILY: 302 Moved Temporarily (old name,
+ * RFC 2068)
+ * @SOUP_STATUS_SEE_OTHER: 303 See Other (HTTP)
+ * @SOUP_STATUS_NOT_MODIFIED: 304 Not Modified (HTTP)
+ * @SOUP_STATUS_USE_PROXY: 305 Use Proxy (HTTP)
+ * @SOUP_STATUS_NOT_APPEARING_IN_THIS_PROTOCOL: 306 [Unused] (HTTP)
+ * @SOUP_STATUS_TEMPORARY_REDIRECT: 307 Temporary Redirect (HTTP)
+ * @SOUP_STATUS_BAD_REQUEST: 400 Bad Request (HTTP)
+ * @SOUP_STATUS_UNAUTHORIZED: 401 Unauthorized (HTTP)
+ * @SOUP_STATUS_PAYMENT_REQUIRED: 402 Payment Required (HTTP)
+ * @SOUP_STATUS_FORBIDDEN: 403 Forbidden (HTTP)
+ * @SOUP_STATUS_NOT_FOUND: 404 Not Found (HTTP)
+ * @SOUP_STATUS_METHOD_NOT_ALLOWED: 405 Method Not Allowed (HTTP)
+ * @SOUP_STATUS_NOT_ACCEPTABLE: 406 Not Acceptable (HTTP)
+ * @SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407 Proxy Authentication
+ * Required (HTTP)
+ * @SOUP_STATUS_PROXY_UNAUTHORIZED: shorter alias for
+ * %SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED
+ * @SOUP_STATUS_REQUEST_TIMEOUT: 408 Request Timeout (HTTP)
+ * @SOUP_STATUS_CONFLICT: 409 Conflict (HTTP)
+ * @SOUP_STATUS_GONE: 410 Gone (HTTP)
+ * @SOUP_STATUS_LENGTH_REQUIRED: 411 Length Required (HTTP)
+ * @SOUP_STATUS_PRECONDITION_FAILED: 412 Precondition Failed (HTTP)
+ * @SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE: 413 Request Entity Too Large
+ * (HTTP)
+ * @SOUP_STATUS_REQUEST_URI_TOO_LONG: 414 Request-URI Too Long (HTTP)
+ * @SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415 Unsupported Media Type
+ * (HTTP)
+ * @SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: 416 Requested Range
+ * Not Satisfiable (HTTP)
+ * @SOUP_STATUS_INVALID_RANGE: shorter alias for
+ * %SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
+ * @SOUP_STATUS_EXPECTATION_FAILED: 417 Expectation Failed (HTTP)
+ * @SOUP_STATUS_UNPROCESSABLE_ENTITY: 422 Unprocessable Entity
+ * (WebDAV)
+ * @SOUP_STATUS_LOCKED: 423 Locked (WebDAV)
+ * @SOUP_STATUS_FAILED_DEPENDENCY: 424 Failed Dependency (WebDAV)
+ * @SOUP_STATUS_INTERNAL_SERVER_ERROR: 500 Internal Server Error
+ * (HTTP)
+ * @SOUP_STATUS_NOT_IMPLEMENTED: 501 Not Implemented (HTTP)
+ * @SOUP_STATUS_BAD_GATEWAY: 502 Bad Gateway (HTTP)
+ * @SOUP_STATUS_SERVICE_UNAVAILABLE: 503 Service Unavailable (HTTP)
+ * @SOUP_STATUS_GATEWAY_TIMEOUT: 504 Gateway Timeout (HTTP)
+ * @SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505 HTTP Version Not
+ * Supported (HTTP)
+ * @SOUP_STATUS_INSUFFICIENT_STORAGE: 507 Insufficient Storage
+ * (WebDAV)
+ * @SOUP_STATUS_NOT_EXTENDED: 510 Not Extended (RFC 2774)
+ * 
+ * These represent the known HTTP status code values, plus various
+ * network and internal errors.
+ **/
+
 static struct {
        guint code;
        const char *phrase;
@@ -90,6 +229,10 @@ static struct {
  *
  * Looks up the stock HTTP description of @status_code.
  *
+ * You should not need to use this; if you are interested in the
+ * textual description for the %status_code of a given #SoupMessage,
+ * just look at the message's %reason_phrase.
+ *
  * Return value: the (English) description of @status_code
  **/
 const char *
@@ -104,3 +247,13 @@ soup_status_get_phrase (guint status_code)
 
        return "Unknown Error";
 }
+
+
+GQuark
+soup_http_error_quark (void)
+{
+       static GQuark error;
+       if (!error)
+               error = g_quark_from_static_string ("soup_http_error_quark");
+       return error;
+}
index 507d690..983250b 100644 (file)
 
 G_BEGIN_DECLS
 
-/**
- * SoupStatusClass:
- * @SOUP_STATUS_CLASS_TRANSPORT_ERROR: Network or Soup-level error
- * @SOUP_STATUS_CLASS_INFORMATIONAL: HTTP 1xx response providing
- * partial information about the state of a request
- * @SOUP_STATUS_CLASS_SUCCESS: HTTP 2xx successful response
- * @SOUP_STATUS_CLASS_REDIRECT: HTTP 3xx redirection response
- * @SOUP_STATUS_CLASS_CLIENT_ERROR: HTTP 4xx client error response
- * @SOUP_STATUS_CLASS_SERVER_ERROR: HTTP 5xx server error response
- *
- * The classes of HTTP and Soup status codes
- **/
-typedef enum {
-       SOUP_STATUS_CLASS_TRANSPORT_ERROR = 0,
-       SOUP_STATUS_CLASS_INFORMATIONAL,
-       SOUP_STATUS_CLASS_SUCCESS,
-       SOUP_STATUS_CLASS_REDIRECT,
-       SOUP_STATUS_CLASS_CLIENT_ERROR,
-       SOUP_STATUS_CLASS_SERVER_ERROR
-} SoupStatusClass;
-
-/**
- * SOUP_STATUS_IS_TRANSPORT_ERROR:
- * @status: a status code
- *
- * Tests if @status is a libsoup transport error.
- *
- * Return value: %TRUE or %FALSE
- **/
-/**
- * SOUP_STATUS_IS_INFORMATIONAL:
- * @status: an HTTP status code
- *
- * Tests if @status is an Informational (1xx) response.
- *
- * Return value: %TRUE or %FALSE
- **/
-/**
- * SOUP_STATUS_IS_SUCCESSFUL:
- * @status: an HTTP status code
- *
- * Tests if @status is a Successful (2xx) response.
- *
- * Return value: %TRUE or %FALSE
- **/
-/**
- * SOUP_STATUS_IS_REDIRECTION:
- * @status: an HTTP status code
- *
- * Tests if @status is a Redirection (3xx) response.
- *
- * Return value: %TRUE or %FALSE
- **/
-/**
- * SOUP_STATUS_IS_CLIENT_ERROR:
- * @status: an HTTP status code
- *
- * Tests if @status is a Client Error (4xx) response.
- *
- * Return value: %TRUE or %FALSE
- **/
-/**
- * SOUP_STATUS_IS_SERVER_ERROR:
- * @status: an HTTP status code
- *
- * Tests if @status is a Server Error (5xx) response.
- *
- * Return value: %TRUE or %FALSE
- **/
-
 #define SOUP_STATUS_IS_TRANSPORT_ERROR(status) ((status) >  0   && (status) < 100)
 #define SOUP_STATUS_IS_INFORMATIONAL(status)   ((status) >= 100 && (status) < 200)
 #define SOUP_STATUS_IS_SUCCESSFUL(status)      ((status) >= 200 && (status) < 300)
@@ -89,89 +19,6 @@ typedef enum {
 #define SOUP_STATUS_IS_CLIENT_ERROR(status)    ((status) >= 400 && (status) < 500)
 #define SOUP_STATUS_IS_SERVER_ERROR(status)    ((status) >= 500 && (status) < 600)
 
-/**
- * SoupKnownStatusCode:
- * @SOUP_STATUS_NONE: No status available. (Eg, the message has not
- * been sent yet)
- * @SOUP_STATUS_CANCELLED: Message was cancelled locally
- * @SOUP_STATUS_CANT_RESOLVE: Unable to resolve destination host name
- * @SOUP_STATUS_CANT_RESOLVE_PROXY: Unable to resolve proxy host name
- * @SOUP_STATUS_CANT_CONNECT: Unable to connect to remote host
- * @SOUP_STATUS_CANT_CONNECT_PROXY: Unable to connect to proxy
- * @SOUP_STATUS_SSL_FAILED: SSL negotiation failed
- * @SOUP_STATUS_IO_ERROR: A network error occurred, or the other end
- * closed the connection unexpectedly
- * @SOUP_STATUS_MALFORMED: Malformed data (usually a programmer error)
- * @SOUP_STATUS_TRY_AGAIN: Try again. (Only returned in certain
- * specifically documented cases)
- * @SOUP_STATUS_CONTINUE: 100 Continue (HTTP)
- * @SOUP_STATUS_SWITCHING_PROTOCOLS: 101 Switching Protocols (HTTP)
- * @SOUP_STATUS_PROCESSING: 102 Processing (WebDAV)
- * @SOUP_STATUS_OK: 200 Success (HTTP). Also used by many lower-level
- * soup routines to indicate success.
- * @SOUP_STATUS_CREATED: 201 Created (HTTP)
- * @SOUP_STATUS_ACCEPTED: 202 Accepted (HTTP)
- * @SOUP_STATUS_NON_AUTHORITATIVE: 203 Non-Authoritative Information
- * (HTTP)
- * @SOUP_STATUS_NO_CONTENT: 204 No Content (HTTP)
- * @SOUP_STATUS_RESET_CONTENT: 205 Reset Content (HTTP)
- * @SOUP_STATUS_PARTIAL_CONTENT: 206 Partial Content (HTTP)
- * @SOUP_STATUS_MULTI_STATUS: 207 Multi-Status (WebDAV)
- * @SOUP_STATUS_MULTIPLE_CHOICES: 300 Multiple Choices (HTTP)
- * @SOUP_STATUS_MOVED_PERMANENTLY: 301 Moved Permanently (HTTP)
- * @SOUP_STATUS_FOUND: 302 Found (HTTP)
- * @SOUP_STATUS_MOVED_TEMPORARILY: 302 Moved Temporarily (old name,
- * RFC 2068)
- * @SOUP_STATUS_SEE_OTHER: 303 See Other (HTTP)
- * @SOUP_STATUS_NOT_MODIFIED: 304 Not Modified (HTTP)
- * @SOUP_STATUS_USE_PROXY: 305 Use Proxy (HTTP)
- * @SOUP_STATUS_NOT_APPEARING_IN_THIS_PROTOCOL: 306 [Unused] (HTTP)
- * @SOUP_STATUS_TEMPORARY_REDIRECT: 307 Temporary Redirect (HTTP)
- * @SOUP_STATUS_BAD_REQUEST: 400 Bad Request (HTTP)
- * @SOUP_STATUS_UNAUTHORIZED: 401 Unauthorized (HTTP)
- * @SOUP_STATUS_PAYMENT_REQUIRED: 402 Payment Required (HTTP)
- * @SOUP_STATUS_FORBIDDEN: 403 Forbidden (HTTP)
- * @SOUP_STATUS_NOT_FOUND: 404 Not Found (HTTP)
- * @SOUP_STATUS_METHOD_NOT_ALLOWED: 405 Method Not Allowed (HTTP)
- * @SOUP_STATUS_NOT_ACCEPTABLE: 406 Not Acceptable (HTTP)
- * @SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407 Proxy Authentication
- * Required (HTTP)
- * @SOUP_STATUS_PROXY_UNAUTHORIZED: shorter alias for
- * %SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED
- * @SOUP_STATUS_REQUEST_TIMEOUT: 408 Request Timeout (HTTP)
- * @SOUP_STATUS_CONFLICT: 409 Conflict (HTTP)
- * @SOUP_STATUS_GONE: 410 Gone (HTTP)
- * @SOUP_STATUS_LENGTH_REQUIRED: 411 Length Required (HTTP)
- * @SOUP_STATUS_PRECONDITION_FAILED: 412 Precondition Failed (HTTP)
- * @SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE: 413 Request Entity Too Large
- * (HTTP)
- * @SOUP_STATUS_REQUEST_URI_TOO_LONG: 414 Request-URI Too Long (HTTP)
- * @SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415 Unsupported Media Type
- * (HTTP)
- * @SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: 416 Requested Range
- * Not Satisfiable (HTTP)
- * @SOUP_STATUS_INVALID_RANGE: shorter alias for
- * %SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
- * @SOUP_STATUS_EXPECTATION_FAILED: 417 Expectation Failed (HTTP)
- * @SOUP_STATUS_UNPROCESSABLE_ENTITY: 422 Unprocessable Entity
- * (WebDAV)
- * @SOUP_STATUS_LOCKED: 423 Locked (WebDAV)
- * @SOUP_STATUS_FAILED_DEPENDENCY: 424 Failed Dependency (WebDAV)
- * @SOUP_STATUS_INTERNAL_SERVER_ERROR: 500 Internal Server Error
- * (HTTP)
- * @SOUP_STATUS_NOT_IMPLEMENTED: 501 Not Implemented (HTTP)
- * @SOUP_STATUS_BAD_GATEWAY: 502 Bad Gateway (HTTP)
- * @SOUP_STATUS_SERVICE_UNAVAILABLE: 503 Service Unavailable (HTTP)
- * @SOUP_STATUS_GATEWAY_TIMEOUT: 504 Gateway Timeout (HTTP)
- * @SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505 HTTP Version Not
- * Supported (HTTP)
- * @SOUP_STATUS_INSUFFICIENT_STORAGE: 507 Insufficient Storage
- * (WebDAV)
- * @SOUP_STATUS_NOT_EXTENDED: 510 Not Extended (RFC 2774)
- * 
- * These represent the known HTTP status code values, plus various
- * network and internal errors.
- **/
 typedef enum {
        SOUP_STATUS_NONE,
 
@@ -246,6 +93,9 @@ typedef enum {
 
 const char *soup_status_get_phrase (guint status_code);
 
+#define SOUP_HTTP_ERROR soup_http_error_quark()
+GQuark soup_http_error_quark (void);
+
 G_END_DECLS
 
 #endif /* SOUP_STATUS_H */
index fa5a400..951dd96 100644 (file)
 G_BEGIN_DECLS
 
 typedef struct SoupAddress           SoupAddress;
-typedef struct SoupConnection        SoupConnection;
+typedef struct SoupAuth              SoupAuth;
+typedef struct SoupAuthDomain        SoupAuthDomain;
 typedef struct SoupMessage           SoupMessage;
-typedef struct SoupMessageFilter     SoupMessageFilter;
 typedef struct SoupServer            SoupServer;
-typedef union  SoupServerAuth        SoupServerAuth;
-typedef struct SoupServerAuthContext SoupServerAuthContext;
-typedef struct SoupServerMessage     SoupServerMessage;
 typedef struct SoupSession           SoupSession;
 typedef struct SoupSessionAsync      SoupSessionAsync;
 typedef struct SoupSessionSync       SoupSessionSync;
 typedef struct SoupSocket            SoupSocket;
-typedef struct SoupUri               SoupUri;
-
-#define SOUP_MAKE_INTERFACE(type_name,TypeName,base_init) \
-GType type_name##_get_type(void)\
-{\
-       static GType type = 0;                          \
-       if (!type){                                     \
-               static GTypeInfo const object_info = {  \
-                       sizeof (TypeName##Class),       \
-                                                       \
-                       (GBaseInitFunc) base_init,      \
-                       (GBaseFinalizeFunc) NULL,       \
-                                                       \
-                       (GClassInitFunc) NULL,          \
-                       (GClassFinalizeFunc) NULL,      \
-                       NULL,   /* class_data */        \
-                                                       \
-                       0,                              \
-                       0,      /* n_preallocs */       \
-                       (GInstanceInitFunc) NULL,       \
-               };                                      \
-               type = g_type_register_static (G_TYPE_INTERFACE, #TypeName, &object_info, 0); \
-       }                                               \
-       return type;                                    \
-}
+typedef struct SoupURI               SoupURI;
 
 G_END_DECLS
 
index ecbf6a9..b9569c7 100644 (file)
 #include <stdlib.h>
 
 #include "soup-uri.h"
+#include "soup-form.h"
+#include "soup-misc.h"
 
-static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
+/**
+ * SECTION:soup-uri
+ * @short_description: URIs
+ *
+ * A #SoupURI represents a (parsed) URI.
+ *
+ * Many applications will not need to use #SoupURI directly at all; on
+ * the client side, soup_message_new() takes a stringified URI, and on
+ * the server side, the path and query components are provided for you
+ * in the server callback.
+ **/
 
-static inline SoupProtocol
-soup_uri_get_protocol (const char *proto, int len)
-{
-       char proto_buf[128];
+/**
+ * SoupURI:
+ * @scheme: the URI scheme (eg, "http")
+ * @user: a username, or %NULL
+ * @password: a password, or %NULL
+ * @host: the hostname or IP address
+ * @port: the port number on @host
+ * @path: the path on @host
+ * @query: a query for @path, or %NULL
+ * @fragment: a fragment identifier within @path, or %NULL
+ *
+ * A #SoupURI represents a (parsed) URI. #SoupURI supports RFC 3986
+ * (URI Generic Syntax), and can parse any valid URI. However, libsoup
+ * only uses "http" and "https" URIs internally.
+ *
+ * @scheme will always be set in any URI. It is an interned string and
+ * is always all lowercase. (If you parse a URI with a non-lowercase
+ * scheme, it will be converted to lowercase.) The macros
+ * %SOUP_URI_SCHEME_HTTP and %SOUP_URI_SCHEME_HTTPS provide the
+ * interned values for "http" and "https" and can be compared against
+ * URI @scheme values.
+ *
+ * @user and @password are parsed as defined in the older URI specs
+ * (ie, separated by a colon; RFC 3986 only talks about a single
+ * "userinfo" field). Note that @password is not included in the
+ * output of soup_uri_to_string(). libsoup does not normally use these
+ * fields; authentication is handled via #SoupSession signals.
+ *
+ * @host contains the hostname, and @port the port specified in the
+ * URI. If the URI doesn't contain a hostname, @host will be %NULL,
+ * and if it doesn't specify a port, @port may be 0. However, for
+ * "http" and "https" URIs, @host is guaranteed to be non-%NULL
+ * (trying to parse an http URI with no @host will return %NULL), and
+ * @port will always be non-0 (because libsoup knows the default value
+ * to use when it is not specified in the URI).
+ *
+ * @path is always non-%NULL. For http/https URIs, @path will never be
+ * an empty string either; if the input URI has no path, the parsed
+ * #SoupURI will have a @path of "/".
+ *
+ * @query and @fragment are optional for all URI types.
+ * soup_form_decode_urlencoded() may be useful for parsing @query.
+ *
+ * Note that @path, @query, and @fragment may contain
+ * %<!-- -->-encoded characters. soup_uri_new() calls
+ * soup_uri_normalize() on them, but not soup_uri_decode(). This is
+ * necessary to ensure that soup_uri_to_string() will generate a URI
+ * that has exactly the same meaning as the original. (In theory,
+ * #SoupURI should leave @user, @password, and @host partially-encoded
+ * as well, but this would be more annoying than useful.)
+ **/
 
-       g_return_val_if_fail (len < sizeof (proto_buf) - 1, 0);
+static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
+static char *uri_decoded_copy (const char *str, int length);
+static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra);
 
-       memcpy (proto_buf, proto, len);
-       proto_buf[len] = '\0';
-       return g_quark_from_string (proto_buf);
-}
+static const char *http_scheme, *https_scheme;
 
 static inline const char *
-soup_protocol_name (SoupProtocol proto)
+soup_uri_get_scheme (const char *scheme, int len)
 {
-       return g_quark_to_string (proto);
+       if (len == 4 && !strncmp (scheme, "http", 4)) {
+               if (G_UNLIKELY (!http_scheme))
+                       http_scheme = g_intern_static_string ("http");
+               return http_scheme;
+       } else if (len == 5 && !strncmp (scheme, "https", 5)) {
+               if (G_UNLIKELY (!https_scheme))
+                       https_scheme = g_intern_static_string ("https");
+               return https_scheme;
+       } else {
+               char *lower_scheme;
+
+               lower_scheme = g_ascii_strdown (scheme, len);
+               scheme = g_intern_string (lower_scheme);
+               g_free (lower_scheme);
+               return scheme;
+       }
 }
 
 static inline guint
-soup_protocol_default_port (SoupProtocol proto)
+soup_scheme_default_port (const char *scheme)
 {
-       if (proto == SOUP_PROTOCOL_HTTP)
+       if (scheme == http_scheme)
                return 80;
-       else if (proto == SOUP_PROTOCOL_HTTPS)
+       else if (scheme == https_scheme)
                return 443;
        else
                return 0;
@@ -49,38 +122,43 @@ soup_protocol_default_port (SoupProtocol proto)
  *
  * Parses @uri_string relative to @base.
  *
- * Return value: a parsed #SoupUri.
+ * Return value: a parsed #SoupURI.
  **/
-SoupUri *
-soup_uri_new_with_base (const SoupUri *base, const char *uri_string)
+SoupURI *
+soup_uri_new_with_base (SoupURI *base, const char *uri_string)
 {
-       SoupUri *uri;
-       const char *end, *hash, *colon, *at, *slash, *question;
-       const char *p;
+       SoupURI *uri;
+       const char *end, *hash, *colon, *at, *path, *question;
+       const char *p, *hostend;
+       gboolean remove_dot_segments = TRUE;
 
-       uri = g_new0 (SoupUri, 1);
+       uri = g_slice_new0 (SoupURI);
 
-       /* See RFC2396 for details. IF YOU CHANGE ANYTHING IN THIS
+       /* See RFC 3986 for details. IF YOU CHANGE ANYTHING IN THIS
         * FUNCTION, RUN tests/uri-parsing AFTERWARDS.
         */
 
        /* Find fragment. */
        end = hash = strchr (uri_string, '#');
        if (hash && hash[1]) {
-               uri->fragment = g_strdup (hash + 1);
-               soup_uri_decode (uri->fragment);
+               uri->fragment = uri_normalized_copy (hash + 1, strlen (hash + 1),
+                                                    NULL);
+               if (!uri->fragment) {
+                       soup_uri_free (uri);
+                       return NULL;
+               }
        } else
                end = uri_string + strlen (uri_string);
 
-       /* Find protocol: initial [a-z+.-]* substring until ":" */
+       /* Find scheme: initial [a-z+.-]* substring until ":" */
        p = uri_string;
-       while (p < end && (isalnum ((unsigned char)*p) ||
+       while (p < end && (g_ascii_isalnum (*p) ||
                           *p == '.' || *p == '+' || *p == '-'))
                p++;
 
        if (p > uri_string && *p == ':') {
-               uri->protocol = soup_uri_get_protocol (uri_string, p - uri_string);
-               if (!uri->protocol) {
+               uri->scheme = soup_uri_get_scheme (uri_string, p - uri_string);
+               if (!uri->scheme) {
                        soup_uri_free (uri);
                        return NULL;
                }
@@ -94,75 +172,108 @@ soup_uri_new_with_base (const SoupUri *base, const char *uri_string)
        if (strncmp (uri_string, "//", 2) == 0) {
                uri_string += 2;
 
-               slash = uri_string + strcspn (uri_string, "/#");
+               path = uri_string + strcspn (uri_string, "/?#");
                at = strchr (uri_string, '@');
-               if (at && at < slash) {
+               if (at && at < path) {
                        colon = strchr (uri_string, ':');
                        if (colon && colon < at) {
-                               uri->passwd = g_strndup (colon + 1,
-                                                        at - colon - 1);
-                               soup_uri_decode (uri->passwd);
+                               uri->password = uri_decoded_copy (colon + 1,
+                                                                 at - colon - 1);
+                               if (!uri->password) {
+                                       soup_uri_free (uri);
+                                       return NULL;
+                               }
                        } else {
-                               uri->passwd = NULL;
+                               uri->password = NULL;
                                colon = at;
                        }
 
-                       uri->user = g_strndup (uri_string, colon - uri_string);
-                       soup_uri_decode (uri->user);
+                       uri->user = uri_decoded_copy (uri_string,
+                                                     colon - uri_string);
+                       if (!uri->user) {
+                               soup_uri_free (uri);
+                               return NULL;
+                       }
                        uri_string = at + 1;
                } else
-                       uri->user = uri->passwd = NULL;
+                       uri->user = uri->password = NULL;
 
                /* Find host and port. */
-               colon = strchr (uri_string, ':');
-               if (colon && colon < slash) {
-                       uri->host = g_strndup (uri_string, colon - uri_string);
-                       uri->port = strtoul (colon + 1, NULL, 10);
+               if (*uri_string == '[') {
+                       uri_string++;
+                       hostend = strchr (uri_string, ']');
+                       if (!hostend || hostend > path) {
+                               soup_uri_free (uri);
+                               return NULL;
+                       }
+                       if (*(hostend + 1) == ':')
+                               colon = hostend + 1;
+                       else
+                               colon = NULL;
                } else {
-                       uri->host = g_strndup (uri_string, slash - uri_string);
-                       soup_uri_decode (uri->host);
+                       colon = memchr (uri_string, ':', path - uri_string);
+                       hostend = colon ? colon : path;
                }
 
-               uri_string = slash;
+               uri->host = uri_decoded_copy (uri_string, hostend - uri_string);
+               if (!uri->host) {
+                       soup_uri_free (uri);
+                       return NULL;
+               }
+
+               if (colon && colon != path - 1) {
+                       char *portend;
+                       uri->port = strtoul (colon + 1, &portend, 10);
+                       if (portend != (char *)path) {
+                               soup_uri_free (uri);
+                               return NULL;
+                       }
+               }
+
+               uri_string = path;
        }
 
        /* Find query */
        question = memchr (uri_string, '?', end - uri_string);
        if (question) {
                if (question[1]) {
-                       uri->query = g_strndup (question + 1,
-                                               end - (question + 1));
-                       soup_uri_decode (uri->query);
+                       uri->query = uri_normalized_copy (question + 1,
+                                                         end - (question + 1),
+                                                         NULL);
+                       if (!uri->query) {
+                               soup_uri_free (uri);
+                               return NULL;
+                       }
                }
                end = question;
        }
 
        if (end != uri_string) {
-               uri->path = g_strndup (uri_string, end - uri_string);
-               soup_uri_decode (uri->path);
+               uri->path = uri_normalized_copy (uri_string, end - uri_string,
+                                                NULL);
+               if (!uri->path) {
+                       soup_uri_free (uri);
+                       return NULL;
+               }
        }
 
-       /* Apply base URI. Again, this is spelled out in RFC 2396. */
-       if (base && !uri->protocol && uri->host)
-               uri->protocol = base->protocol;
-       else if (base && !uri->protocol) {
-               uri->protocol = base->protocol;
+       /* Apply base URI. Again, this is spelled out in RFC 3986. */
+       if (base && !uri->scheme && uri->host)
+               uri->scheme = base->scheme;
+       else if (base && !uri->scheme) {
+               uri->scheme = base->scheme;
                uri->user = g_strdup (base->user);
-               uri->passwd = g_strdup (base->passwd);
+               uri->password = g_strdup (base->password);
                uri->host = g_strdup (base->host);
                uri->port = base->port;
 
                if (!uri->path) {
-                       if (uri->query)
-                               uri->path = g_strdup ("");
-                       else {
-                               uri->path = g_strdup (base->path);
+                       uri->path = g_strdup (base->path);
+                       if (!uri->query)
                                uri->query = g_strdup (base->query);
-                       }
-               }
-
-               if (*uri->path != '/') {
-                       char *newpath, *last, *p, *q;
+                       remove_dot_segments = FALSE;
+               } else if (*uri->path != '/') {
+                       char *newpath, *last;
 
                        last = strrchr (base->path, '/');
                        if (last) {
@@ -173,58 +284,72 @@ soup_uri_new_with_base (const SoupUri *base, const char *uri_string)
                        } else
                                newpath = g_strdup_printf ("/%s", uri->path);
 
-                       /* Remove "./" where "." is a complete segment. */
-                       for (p = newpath + 1; *p; ) {
-                               if (*(p - 1) == '/' &&
-                                   *p == '.' && *(p + 1) == '/')
-                                       memmove (p, p + 2, strlen (p + 2) + 1);
-                               else
-                                       p++;
-                       }
-                       /* Remove "." at end. */
-                       if (p > newpath + 2 &&
-                           *(p - 1) == '.' && *(p - 2) == '/')
-                               *(p - 1) = '\0';
-                       /* Remove "<segment>/../" where <segment> != ".." */
-                       for (p = newpath + 1; *p; ) {
-                               if (!strncmp (p, "../", 3)) {
-                                       p += 3;
-                                       continue;
-                               }
-                               q = strchr (p + 1, '/');
-                               if (!q)
-                                       break;
-                               if (strncmp (q, "/../", 4) != 0) {
-                                       p = q + 1;
-                                       continue;
-                               }
-                               memmove (p, q + 4, strlen (q + 4) + 1);
-                               p = newpath + 1;
-                       }
-                       /* Remove "<segment>/.." at end where <segment> != ".." */
-                       q = strrchr (newpath, '/');
-                       if (q && !strcmp (q, "/..")) {
-                               p = q - 1;
-                               while (p > newpath && *p != '/')
-                                       p--;
-                               if (strncmp (p, "/../", 4) != 0)
-                                       *(p + 1) = 0;
-                       }
-
                        g_free (uri->path);
                        uri->path = newpath;
                }
        }
 
-       /* Sanity check */
-       if ((uri->protocol == SOUP_PROTOCOL_HTTP ||
-            uri->protocol == SOUP_PROTOCOL_HTTPS) && !uri->host) {
-               soup_uri_free (uri);
-               return NULL;
+       if (remove_dot_segments && uri->path && *uri->path) {
+               char *p = uri->path, *q;
+
+               /* Remove "./" where "." is a complete segment. */
+               for (p = uri->path + 1; *p; ) {
+                       if (*(p - 1) == '/' &&
+                           *p == '.' && *(p + 1) == '/')
+                               memmove (p, p + 2, strlen (p + 2) + 1);
+                       else
+                               p++;
+               }
+               /* Remove "." at end. */
+               if (p > uri->path + 2 &&
+                   *(p - 1) == '.' && *(p - 2) == '/')
+                       *(p - 1) = '\0';
+
+               /* Remove "<segment>/../" where <segment> != ".." */
+               for (p = uri->path + 1; *p; ) {
+                       if (!strncmp (p, "../", 3)) {
+                               p += 3;
+                               continue;
+                       }
+                       q = strchr (p + 1, '/');
+                       if (!q)
+                               break;
+                       if (strncmp (q, "/../", 4) != 0) {
+                               p = q + 1;
+                               continue;
+                       }
+                       memmove (p, q + 4, strlen (q + 4) + 1);
+                       p = uri->path + 1;
+               }
+               /* Remove "<segment>/.." at end where <segment> != ".." */
+               q = strrchr (uri->path, '/');
+               if (q && !strcmp (q, "/..")) {
+                       p = q - 1;
+                       while (p > uri->path && *p != '/')
+                               p--;
+                       if (strncmp (p, "/../", 4) != 0)
+                               *(p + 1) = 0;
+               }
+
+               /* Remove extraneous initial "/.."s */
+               while (!strncmp (uri->path, "/../", 4))
+                       memmove (uri->path, uri->path + 3, strlen (uri->path) - 2);
+               if (!strcmp (uri->path, "/.."))
+                       uri->path[1] = '\0';
+       }
+
+       /* HTTP-specific stuff */
+       if (uri->scheme == http_scheme || uri->scheme == https_scheme) {
+               if (!uri->host) {
+                       soup_uri_free (uri);
+                       return NULL;
+               }
+               if (!uri->path)
+                       uri->path = g_strdup ("/");
        }
 
        if (!uri->port)
-               uri->port = soup_protocol_default_port (uri->protocol);
+               uri->port = soup_scheme_default_port (uri->scheme);
        if (!uri->path)
                uri->path = g_strdup ("");
 
@@ -237,17 +362,23 @@ soup_uri_new_with_base (const SoupUri *base, const char *uri_string)
  *
  * Parses an absolute URI.
  *
- * Return value: a #SoupUri, or %NULL.
+ * You can also pass %NULL for @uri_string if you want to get back an
+ * "empty" #SoupURI that you can fill in by hand.
+ *
+ * Return value: a #SoupURI, or %NULL.
  **/
-SoupUri *
+SoupURI *
 soup_uri_new (const char *uri_string)
 {
-       SoupUri *uri;
+       SoupURI *uri;
+
+       if (!uri_string)
+               return g_slice_new0 (SoupURI);
 
        uri = soup_uri_new_with_base (NULL, uri_string);
        if (!uri)
                return NULL;
-       if (!uri->protocol) {
+       if (!uri->scheme) {
                soup_uri_free (uri);
                return NULL;
        }
@@ -256,31 +387,24 @@ soup_uri_new (const char *uri_string)
 }
 
 
-static inline void
-append_uri (GString *str, const char *in, const char *extra_enc_chars,
-           gboolean pre_encoded)
-{
-       if (pre_encoded)
-               g_string_append (str, in);
-       else
-               append_uri_encoded (str, in, extra_enc_chars);
-}
-
 /**
  * soup_uri_to_string:
- * @uri: a #SoupUri
- * @just_path: if %TRUE, output just the path and query portions
+ * @uri: a #SoupURI
+ * @just_path_and_query: if %TRUE, output just the path and query portions
  *
  * Returns a string representing @uri.
  *
+ * If @just_path_and_query is %TRUE, this concatenates the path and query
+ * together. That is, it constructs the string that would be needed in
+ * the Request-Line of an HTTP request for @uri.
+ *
  * Return value: a string representing @uri, which the caller must free.
  **/
 char *
-soup_uri_to_string (const SoupUri *uri, gboolean just_path)
+soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
 {
        GString *str;
        char *return_result;
-       gboolean pre_encoded = uri->broken_encoding;
 
        /* IF YOU CHANGE ANYTHING IN THIS FUNCTION, RUN
         * tests/uri-parsing AFTERWARD.
@@ -288,33 +412,36 @@ soup_uri_to_string (const SoupUri *uri, gboolean just_path)
 
        str = g_string_sized_new (20);
 
-       if (uri->protocol && !just_path)
-               g_string_sprintfa (str, "%s:", soup_protocol_name (uri->protocol));
-       if (uri->host && !just_path) {
+       if (uri->scheme && !just_path_and_query)
+               g_string_sprintfa (str, "%s:", uri->scheme);
+       if (uri->host && !just_path_and_query) {
                g_string_append (str, "//");
                if (uri->user) {
-                       append_uri (str, uri->user, ":;@/", pre_encoded);
+                       append_uri_encoded (str, uri->user, ":;@?/");
                        g_string_append_c (str, '@');
                }
-               append_uri (str, uri->host, ":/", pre_encoded);
-               if (uri->port && uri->port != soup_protocol_default_port (uri->protocol))
+               if (strchr (uri->host, ':')) {
+                       g_string_append_c (str, '[');
+                       g_string_append (str, uri->host);
+                       g_string_append_c (str, ']');
+               } else
+                       append_uri_encoded (str, uri->host, ":/");
+               if (uri->port && uri->port != soup_scheme_default_port (uri->scheme))
                        g_string_append_printf (str, ":%d", uri->port);
                if (!uri->path && (uri->query || uri->fragment))
                        g_string_append_c (str, '/');
        }
 
        if (uri->path && *uri->path)
-               append_uri (str, uri->path, "?", pre_encoded);
-       else if (just_path)
-               g_string_append_c (str, '/');
+               g_string_append (str, uri->path);
 
        if (uri->query) {
                g_string_append_c (str, '?');
-               append_uri (str, uri->query, NULL, pre_encoded);
+               g_string_append (str, uri->query);
        }
-       if (uri->fragment && !just_path) {
+       if (uri->fragment && !just_path_and_query) {
                g_string_append_c (str, '#');
-               append_uri (str, uri->fragment, NULL, pre_encoded);
+               g_string_append (str, uri->fragment);
        }
 
        return_result = str->str;
@@ -325,89 +452,105 @@ soup_uri_to_string (const SoupUri *uri, gboolean just_path)
 
 /**
  * soup_uri_copy:
- * @uri: a #SoupUri
+ * @uri: a #SoupURI
  *
  * Copies @uri
  *
  * Return value: a copy of @uri, which must be freed with soup_uri_free()
  **/
-SoupUri *
-soup_uri_copy (const SoupUri *uri)
+SoupURI *
+soup_uri_copy (SoupURI *uri)
 {
-       SoupUri *dup;
+       SoupURI *dup;
 
        g_return_val_if_fail (uri != NULL, NULL);
 
-       dup = g_new0 (SoupUri, 1);
-       dup->protocol = uri->protocol;
+       dup = g_slice_new0 (SoupURI);
+       dup->scheme   = uri->scheme;
        dup->user     = g_strdup (uri->user);
-       dup->passwd   = g_strdup (uri->passwd);
+       dup->password = g_strdup (uri->password);
        dup->host     = g_strdup (uri->host);
        dup->port     = uri->port;
        dup->path     = g_strdup (uri->path);
        dup->query    = g_strdup (uri->query);
        dup->fragment = g_strdup (uri->fragment);
 
-       dup->broken_encoding = uri->broken_encoding;
-
        return dup;
 }
 
-/**
- * soup_uri_copy_root:
- * @uri: a #SoupUri
- *
- * Copies the protocol, host, and port of @uri into a new #SoupUri
- * (all other fields in the new URI will be empty.)
- *
- * Return value: a partial copy of @uri, which must be freed with
- * soup_uri_free()
- **/
-SoupUri *
-soup_uri_copy_root (const SoupUri *uri)
+/* Temporarily still used by SoupSession, but no longer public */
+SoupURI *soup_uri_copy_root (SoupURI *uri);
+gboolean soup_uri_host_equal (gconstpointer v1, gconstpointer v2);
+guint    soup_uri_host_hash (gconstpointer key);
+
+SoupURI *
+soup_uri_copy_root (SoupURI *uri)
 {
-       SoupUri *dup;
+       SoupURI *dup;
 
        g_return_val_if_fail (uri != NULL, NULL);
 
-       dup = g_new0 (SoupUri, 1);
-       dup->protocol = uri->protocol;
-       dup->host     = g_strdup (uri->host);
-       dup->port     = uri->port;
+       dup = g_slice_new0 (SoupURI);
+       dup->scheme = uri->scheme;
+       dup->host   = g_strdup (uri->host);
+       dup->port   = uri->port;
 
        return dup;
 }
 
+guint
+soup_uri_host_hash (gconstpointer key)
+{
+       const SoupURI *uri = key;
+
+       return GPOINTER_TO_UINT (uri->scheme) + uri->port +
+               soup_str_case_hash (uri->host);
+}
+
+gboolean
+soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
+{
+       const SoupURI *one = v1;
+       const SoupURI *two = v2;
+
+       if (one->scheme != two->scheme)
+               return FALSE;
+       if (one->port != two->port)
+               return FALSE;
+
+       return g_ascii_strcasecmp (one->host, two->host) == 0;
+}
+
 static inline gboolean
-parts_equal (const char *one, const char *two)
+parts_equal (const char *one, const char *two, gboolean insensitive)
 {
        if (!one && !two)
                return TRUE;
        if (!one || !two)
                return FALSE;
-       return !strcmp (one, two);
+       return insensitive ? !g_ascii_strcasecmp (one, two) : !strcmp (one, two);
 }
 
 /**
  * soup_uri_equal:
- * @uri1: a #SoupUri
- * @uri2: another #SoupUri
+ * @uri1: a #SoupURI
+ * @uri2: another #SoupURI
  *
  * Tests whether or not @uri1 and @uri2 are equal in all parts
  *
  * Return value: %TRUE or %FALSE
  **/
 gboolean 
-soup_uri_equal (const SoupUri *uri1, const SoupUri *uri2)
+soup_uri_equal (SoupURI *uri1, SoupURI *uri2)
 {
-       if (uri1->protocol != uri2->protocol              ||
-           uri1->port     != uri2->port                  ||
-           !parts_equal (uri1->user, uri2->user)         ||
-           !parts_equal (uri1->passwd, uri2->passwd)     ||
-           !parts_equal (uri1->host, uri2->host)         ||
-           !parts_equal (uri1->path, uri2->path)         ||
-           !parts_equal (uri1->query, uri2->query)       ||
-           !parts_equal (uri1->fragment, uri2->fragment))
+       if (uri1->scheme != uri2->scheme                         ||
+           uri1->port   != uri2->port                           ||
+           !parts_equal (uri1->user, uri2->user, FALSE)         ||
+           !parts_equal (uri1->password, uri2->password, FALSE) ||
+           !parts_equal (uri1->host, uri2->host, TRUE)          ||
+           !parts_equal (uri1->path, uri2->path, FALSE)         ||
+           !parts_equal (uri1->query, uri2->query, FALSE)       ||
+           !parts_equal (uri1->fragment, uri2->fragment, FALSE))
                return FALSE;
 
        return TRUE;
@@ -415,35 +558,39 @@ soup_uri_equal (const SoupUri *uri1, const SoupUri *uri2)
 
 /**
  * soup_uri_free:
- * @uri: a #SoupUri
+ * @uri: a #SoupURI
  *
  * Frees @uri.
  **/
 void
-soup_uri_free (SoupUri *uri)
+soup_uri_free (SoupURI *uri)
 {
        g_return_if_fail (uri != NULL);
 
        g_free (uri->user);
-       g_free (uri->passwd);
+       g_free (uri->password);
        g_free (uri->host);
        g_free (uri->path);
        g_free (uri->query);
        g_free (uri->fragment);
 
-       g_free (uri);
+       g_slice_free (SoupURI, uri);
 }
 
-/* From RFC 2396 2.4.3, the characters that should always be encoded */
+/* From RFC 3986 */
+#define SOUP_URI_UNRESERVED  0
+#define SOUP_URI_PCT_ENCODED 1
+#define SOUP_URI_GEN_DELIMS  2
+#define SOUP_URI_SUB_DELIMS  4
 static const char uri_encoded_char[] = {
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x00 - 0x0f */
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x10 - 0x1f */
-       1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  ' ' - '/'  */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  '0' - '?'  */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '@' - 'O'  */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  'P' - '_'  */
-       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '`' - 'o'  */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  'p' - 0x7f */
+       1, 4, 1, 2, 4, 1, 4, 4, 4, 4, 4, 4, 4, 0, 0, 2,  /*  !"#$%&'()*+,-./ */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 4, 1, 2,  /* 0123456789:;<=>? */
+       2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* @ABCDEFGHIJKLMNO */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 0,  /* PQRSTUVWXYZ[\]^_ */
+       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* `abcdefghijklmno */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /* pqrstuvwxyz{|}~  */
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -460,9 +607,9 @@ append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars)
        const unsigned char *s = (const unsigned char *)in;
 
        while (*s) {
-               if (uri_encoded_char[*s] ||
+               if ((uri_encoded_char[*s] & (SOUP_URI_PCT_ENCODED | SOUP_URI_GEN_DELIMS)) ||
                    (extra_enc_chars && strchr (extra_enc_chars, *s)))
-                       g_string_append_printf (str, "%%%02x", (int)*s++);
+                       g_string_append_printf (str, "%%%02X", (int)*s++);
                else
                        g_string_append_c (str, *s++);
        }
@@ -471,11 +618,11 @@ append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars)
 /**
  * soup_uri_encode:
  * @part: a URI part
- * @escape_extra: additional characters beyond " \"%#<>{}|\^[]`"
- * to escape (or %NULL)
+ * @escape_extra: additional reserved characters to escape (or %NULL)
  *
- * This %-encodes the given URI part and returns the escaped version
- * in allocated memory, which the caller must free when it is done.
+ * This %<!-- -->-encodes the given URI part and returns the escaped
+ * version in allocated memory, which the caller must free when it is
+ * done.
  *
  * Return value: the encoded URI part
  **/
@@ -493,42 +640,284 @@ soup_uri_encode (const char *part, const char *escape_extra)
        return encoded;
 }
 
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+char *
+uri_decoded_copy (const char *part, int length)
+{
+       unsigned char *s, *d;
+       char *decoded = g_strndup (part, length);
+
+       s = d = (unsigned char *)decoded;
+       do {
+               if (*s == '%') {
+                       if (!g_ascii_isxdigit (s[1]) ||
+                           !g_ascii_isxdigit (s[2])) {
+                               g_free (decoded);
+                               return NULL;
+                       }
+                       *d++ = HEXCHAR (s);
+                       s += 2;
+               } else
+                       *d++ = *s;
+       } while (*s++);
+
+       return decoded;
+}
+
 /**
  * soup_uri_decode:
  * @part: a URI part
  *
- * %-decodes the passed-in URI *in place*. The decoded version is
- * never longer than the encoded version, so there does not need to
- * be any additional space at the end of the string.
+ * Fully %<!-- -->-decodes @part.
+ *
+ * Return value: the decoded URI part, or %NULL if an invalid percent
+ * code was encountered.
  */
-void
-soup_uri_decode (char *part)
+char *
+soup_uri_decode (const char *part)
 {
-       unsigned char *s, *d;
+       return uri_decoded_copy (part, strlen (part));
+}
 
-#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+char *
+uri_normalized_copy (const char *part, int length, const char *unescape_extra)
+{
+       unsigned char *s, *d, c;
+       char *normalized = g_strndup (part, length);
 
-       s = d = (unsigned char *)part;
+       s = d = (unsigned char *)normalized;
        do {
-               if (*s == '%' && s[1] && s[2]) {
-                       *d++ = (XDIGIT (s[1]) << 4) + XDIGIT (s[2]);
-                       s += 2;
+               if (*s == '%') {
+                       if (!g_ascii_isxdigit (s[1]) ||
+                           !g_ascii_isxdigit (s[2])) {
+                               g_free (normalized);
+                               return NULL;
+                       }
+
+                       c = HEXCHAR (s);
+                       if (uri_encoded_char[c] == SOUP_URI_UNRESERVED ||
+                           (unescape_extra && strchr (unescape_extra, c))) {
+                               *d++ = c;
+                               s += 2;
+                       } else {
+                               *d++ = *s++;
+                               *d++ = g_ascii_toupper (*s++);
+                               *d++ = g_ascii_toupper (*s);
+                       }
                } else
                        *d++ = *s;
        } while (*s++);
+
+       return normalized;
 }
 
 /**
+ * soup_uri_normalize:
+ * @part: a URI part
+ * @unescape_extra: reserved characters to unescape (or %NULL)
+ *
+ * %<!-- -->-decodes any "unreserved" characters (or characters in
+ * @unescape_extra) in @part.
+ *
+ * "Unreserved" characters are those that are not allowed to be used
+ * for punctuation according to the URI spec. For example, letters are
+ * unreserved, so soup_uri_normalize() will turn
+ * <literal>http://example.com/foo/b%<!-- -->61r</literal> into
+ * <literal>http://example.com/foo/bar</literal>, which is guaranteed
+ * to mean the same thing. However, "/" is "reserved", so
+ * <literal>http://example.com/foo%<!-- -->2Fbar</literal> would not
+ * be changed, because it might mean something different to the
+ * server.
+ *
+ * Return value: the normalized URI part, or %NULL if an invalid percent
+ * code was encountered.
+ */
+char *
+soup_uri_normalize (const char *part, const char *unescape_extra)
+{
+       return uri_normalized_copy (part, strlen (part), unescape_extra);
+}
+
+
+/**
  * soup_uri_uses_default_port:
- * @uri: a #SoupUri
+ * @uri: a #SoupURI
  *
- * Tests if @uri uses the default port for its protocol. (Eg, 80 for
- * http.)
+ * Tests if @uri uses the default port for its scheme. (Eg, 80 for
+ * http.) (This only works for http and https; libsoup does not know
+ * the default ports of other protocols.)
  *
  * Return value: %TRUE or %FALSE
  **/
 gboolean
-soup_uri_uses_default_port (const SoupUri *uri)
+soup_uri_uses_default_port (SoupURI *uri)
+{
+       g_return_val_if_fail (uri->scheme == http_scheme ||
+                             uri->scheme == https_scheme, FALSE);
+
+       return uri->port == soup_scheme_default_port (uri->scheme);
+}
+
+/**
+ * SOUP_URI_SCHEME_HTTP:
+ *
+ * "http" as an interned string. This can be compared directly against
+ * the value of a #SoupURI's <structfield>scheme</structfield>
+ **/
+
+/**
+ * SOUP_URI_SCHEME_HTTPS:
+ *
+ * "https" as an interned string. This can be compared directly
+ * against the value of a #SoupURI's <structfield>scheme</structfield>
+ **/
+
+/**
+ * soup_uri_set_scheme:
+ * @uri: a #SoupURI
+ * @scheme: the URI scheme
+ *
+ * Sets @uri's scheme to @scheme. This will also set @uri's port to
+ * the default port for @scheme, if known.
+ **/
+void
+soup_uri_set_scheme (SoupURI *uri, const char *scheme)
+{
+       uri->scheme = soup_uri_get_scheme (scheme, strlen (scheme));
+       uri->port = soup_scheme_default_port (uri->scheme);
+}
+
+/**
+ * soup_uri_set_user:
+ * @uri: a #SoupURI
+ * @user: the username, or %NULL
+ *
+ * Sets @uri's user to @user.
+ **/
+void
+soup_uri_set_user (SoupURI *uri, const char *user)
+{
+       g_free (uri->user);
+       uri->user = g_strdup (user);
+}
+
+/**
+ * soup_uri_set_password:
+ * @uri: a #SoupURI
+ * @password: the password, or %NULL
+ *
+ * Sets @uri's password to @password.
+ **/
+void
+soup_uri_set_password (SoupURI *uri, const char *password)
+{
+       g_free (uri->password);
+       uri->password = g_strdup (password);
+}
+
+/**
+ * soup_uri_set_host:
+ * @uri: a #SoupURI
+ * @host: the hostname or IP address, or %NULL
+ *
+ * Sets @uri's host to @host.
+ *
+ * If @host is an IPv6 IP address, it should not include the brackets
+ * required by the URI syntax; they will be added automatically when
+ * converting @uri to a string.
+ **/
+void
+soup_uri_set_host (SoupURI *uri, const char *host)
 {
-       return uri->port == soup_protocol_default_port (uri->protocol);
+       g_free (uri->host);
+       uri->host = g_strdup (host);
+}
+
+/**
+ * soup_uri_set_port:
+ * @uri: a #SoupURI
+ * @port: the port, or 0
+ *
+ * Sets @uri's port to @port. If @port is 0, @uri will not have an
+ * explicitly-specified port.
+ **/
+void
+soup_uri_set_port (SoupURI *uri, guint port)
+{
+       uri->port = port;
+}
+
+/**
+ * soup_uri_set_path:
+ * @uri: a #SoupURI
+ * @path: the path
+ *
+ * Sets @uri's path to @path.
+ **/
+void
+soup_uri_set_path (SoupURI *uri, const char *path)
+{
+       g_free (uri->path);
+       uri->path = g_strdup (path);
+}
+
+/**
+ * soup_uri_set_query:
+ * @uri: a #SoupURI
+ * @query: the query
+ *
+ * Sets @uri's query to @query.
+ **/
+void
+soup_uri_set_query (SoupURI *uri, const char *query)
+{
+       g_free (uri->query);
+       uri->query = g_strdup (query);
+}
+
+/**
+ * soup_uri_set_query_from_form:
+ * @uri: a #SoupURI
+ * @form: a #GHashTable containing HTML form information
+ *
+ * Sets @uri's query to the result of encoding @form according to the
+ * HTML form rules. See soup_form_encode_urlencoded() for more
+ * information.
+ **/
+void
+soup_uri_set_query_from_form (SoupURI *uri, GHashTable *form)
+{
+       g_free (uri->query);
+       uri->query = soup_form_encode_urlencoded (form);
+}
+
+/**
+ * soup_uri_set_fragment:
+ * @uri: a #SoupURI
+ * @fragment: the fragment
+ *
+ * Sets @uri's fragment to @fragment.
+ **/
+void
+soup_uri_set_fragment (SoupURI *uri, const char *fragment)
+{
+       g_free (uri->fragment);
+       uri->fragment = g_strdup (fragment);
+}
+
+
+GType
+soup_uri_get_type (void)
+{
+       static GType type = 0;
+
+       if (G_UNLIKELY (type == 0)) {
+               type = g_boxed_type_register_static (
+                       g_intern_static_string ("SoupURI"),
+                       (GBoxedCopyFunc)soup_uri_copy,
+                       (GBoxedFreeFunc)soup_uri_free);
+       }
+       return type;
 }
index c9c9e78..e939b1b 100644 (file)
@@ -1,5 +1,4 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* url-util.h : utility functions to parse URLs */
 
 /* 
  * Copyright 1999-2002 Ximian, Inc.
 
 G_BEGIN_DECLS
 
-/**
- * SoupProtocol:
- *
- * #GQuark is used for SoupProtocol so that the protocol of a #SoupUri
- * can be tested quickly.
- **/
-typedef GQuark SoupProtocol;
+struct SoupURI {
+       const char *scheme;
 
-/**
- * SOUP_PROTOCOL_HTTP:
- *
- * This returns the #SoupProtocol value for "http".
- **/
-#define SOUP_PROTOCOL_HTTP (g_quark_from_static_string ("http"))
+       char       *user;
+       char       *password;
 
-/**
- * SOUP_PROTOCOL_HTTPS:
- *
- * This returns the #SoupProtocol value for "https".
-**/
-#define SOUP_PROTOCOL_HTTPS (g_quark_from_static_string ("https"))
+       char       *host;
+       guint       port;
 
-struct SoupUri {
-       SoupProtocol  protocol;
+       char       *path;
+       char       *query;
 
-       char         *user;
-       char         *passwd;
-
-       char         *host;
-       guint         port;
-
-       char         *path;
-       char         *query;
-
-       char         *fragment;
-
-       /* Don't use this */
-       gboolean      broken_encoding;
+       char       *fragment;
 };
 
-SoupUri  *soup_uri_new_with_base     (const SoupUri *base,
-                                     const char    *uri_string);
-SoupUri  *soup_uri_new               (const char    *uri_string);
-
-char     *soup_uri_to_string         (const SoupUri *uri, 
-                                     gboolean       just_path);
-
-SoupUri  *soup_uri_copy              (const SoupUri *uri);
-SoupUri  *soup_uri_copy_root         (const SoupUri *uri);
-
-gboolean  soup_uri_equal             (const SoupUri *uri1, 
-                                     const SoupUri *uri2);
-
-void      soup_uri_free              (SoupUri       *uri);
-
-char     *soup_uri_encode            (const char    *part,
-                                     const char    *escape_extra);
-void      soup_uri_decode            (char          *part);
-
-gboolean  soup_uri_uses_default_port (const SoupUri *uri);
+GType     soup_uri_get_type          (void);
+#define SOUP_TYPE_URI (soup_uri_get_type ())
+
+#define SOUP_URI_SCHEME_HTTP  (g_intern_static_string ("http"))
+#define SOUP_URI_SCHEME_HTTPS (g_intern_static_string ("https"))
+
+SoupURI  *soup_uri_new_with_base       (SoupURI    *base,
+                                       const char *uri_string);
+SoupURI  *soup_uri_new                 (const char *uri_string);
+
+char     *soup_uri_to_string           (SoupURI    *uri, 
+                                       gboolean    just_path_and_query);
+
+SoupURI  *soup_uri_copy                (SoupURI    *uri);
+
+gboolean  soup_uri_equal               (SoupURI    *uri1, 
+                                       SoupURI    *uri2);
+
+void      soup_uri_free                (SoupURI    *uri);
+
+char     *soup_uri_encode              (const char *part,
+                                       const char *escape_extra);
+char     *soup_uri_decode              (const char *part);
+char     *soup_uri_normalize           (const char *part,
+                                       const char *unescape_extra);
+
+gboolean  soup_uri_uses_default_port   (SoupURI    *uri);
+
+void      soup_uri_set_scheme          (SoupURI    *uri,
+                                       const char *scheme);
+void      soup_uri_set_user            (SoupURI    *uri,
+                                       const char *user);
+void      soup_uri_set_password        (SoupURI    *uri,
+                                       const char *password);
+void      soup_uri_set_host            (SoupURI    *uri,
+                                       const char *host);
+void      soup_uri_set_port            (SoupURI    *uri,
+                                       guint       port);
+void      soup_uri_set_path            (SoupURI    *uri,
+                                       const char *path);
+void      soup_uri_set_query           (SoupURI    *uri,
+                                       const char *query);
+void      soup_uri_set_query_from_form (SoupURI    *uri,
+                                       GHashTable *form);
+void      soup_uri_set_fragment        (SoupURI    *uri,
+                                       const char *fragment);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-value-utils.c b/libsoup/soup-value-utils.c
new file mode 100644 (file)
index 0000000..af214f3
--- /dev/null
@@ -0,0 +1,292 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-type-utils.c: GValue and GType-related utilities
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#include "soup-value-utils.h"
+
+#include <string.h>
+
+/**
+ * SECTION:soup-value-utils
+ * @short_description: #GValue utilities
+ *
+ **/
+
+/**
+ * SOUP_VALUE_SETV:
+ * @val: a #GValue
+ * @type: a #GType
+ * @args: #va_list pointing to a value of type @type
+ *
+ * Copies an argument of type @type from @args into @val. @val will
+ * point directly to the value in @args rather than copying it, so you
+ * must g_value_copy() it if you want it to remain valid.
+ **/
+
+/**
+ * SOUP_VALUE_GETV:
+ * @val: a #GValue
+ * @type: a #GType
+ * @args: #va_list pointing to a value of type pointer-to-@type
+ *
+ * Extracts a value of type @type from @val into @args. The return
+ * value will point to the same data as @val rather than being a copy
+ * of it.
+ **/
+
+static void
+soup_value_hash_value_free (gpointer val)
+{
+       g_value_unset (val);
+       g_free (val);
+}
+
+/**
+ * soup_value_hash_new:
+ *
+ * Creates a #GHashTable whose keys are strings and whose values
+ * are #GValue.
+ **/
+GHashTable *
+soup_value_hash_new (void)
+{
+       return g_hash_table_new_full (g_str_hash, g_str_equal,
+                                     g_free, soup_value_hash_value_free);
+}
+
+/**
+ * soup_value_hash_insert_value:
+ * @hash: a value hash
+ * @key: the key
+ * @value: a value
+ *
+ * Inserts @value into @hash. (Unlike with g_hash_table_insert(), both
+ * the key and the value are copied).
+ **/
+void
+soup_value_hash_insert_value (GHashTable *hash, const char *key, GValue *value)
+{
+       GValue *copy = g_new0 (GValue, 1);
+
+       g_value_init (copy, G_VALUE_TYPE (value));
+       g_value_copy (value, copy);
+       g_hash_table_insert (hash, g_strdup (key), copy);
+}
+
+/**
+ * soup_value_hash_insert:
+ * @hash: a value hash
+ * @key: the key
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Inserts the provided value of type @type into @hash. (Unlike with
+ * g_hash_table_insert(), both the key and the value are copied).
+ **/
+void
+soup_value_hash_insert (GHashTable *hash, const char *key, GType type, ...)
+{
+       va_list args;
+       GValue val;
+
+       va_start (args, type);
+       SOUP_VALUE_SETV (&val, type, args);
+       va_end (args);
+       soup_value_hash_insert_value (hash, key, &val);
+}
+
+/**
+ * soup_value_hash_lookup:
+ * @hash: a value hash
+ * @key: the key to look up
+ * @type: a #GType
+ * @...: a value of type pointer-to-@type
+ *
+ * Looks up @key in @hash and stores its value into the provided
+ * location.
+ *
+ * Return value: %TRUE if @hash contained a value with key @key and
+ * type @type, %FALSE if not.
+ **/
+gboolean
+soup_value_hash_lookup (GHashTable *hash, const char *key, GType type, ...)
+{
+       va_list args;
+       GValue *value;
+
+       value = g_hash_table_lookup (hash, key);
+       if (!value || !G_VALUE_HOLDS (value, type))
+               return FALSE;
+
+       va_start (args, type);
+       SOUP_VALUE_GETV (value, type, args);
+       va_end (args);
+
+       return TRUE;
+}
+
+
+/**
+ * soup_value_array_from_args:
+ * @args: arguments to create a #GValueArray from
+ *
+ * Creates a #GValueArray from the provided arguments, which must
+ * consist of pairs of a #GType and a value of that type, terminated
+ * by %G_TYPE_INVALID. (The array will contain copies of the provided
+ * data rather than pointing to the passed-in data directly.)
+ *
+ * Return value: a new #GValueArray, or %NULL if an error occurred.
+ **/
+GValueArray *
+soup_value_array_from_args (va_list args)
+{
+       GValueArray *array;
+       GType type;
+       GValue val;
+
+       array = g_value_array_new (1);
+       while ((type = va_arg (args, GType)) != G_TYPE_INVALID) {
+               SOUP_VALUE_SETV (&val, type, args);
+               g_value_array_append (array, &val);
+       }
+       return array;
+}
+
+/**
+ * soup_value_array_to_args:
+ * @array: a #GValueArray
+ * @args: arguments to extract @array into
+ *
+ * Extracts a #GValueArray into the provided arguments, which must
+ * consist of pairs of a #GType and a value of pointer-to-that-type,
+ * terminated by %G_TYPE_INVALID. The returned values will point to the
+ * same memory as the values in the array.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_value_array_to_args (GValueArray *array, va_list args)
+{
+       GType type;
+       GValue *value;
+       int i;
+
+       for (i = 0; i < array->n_values; i++) {
+               type = va_arg (args, GType);
+               if (type == G_TYPE_INVALID)
+                       return FALSE;
+               value = g_value_array_get_nth (array, i);
+               if (!G_VALUE_HOLDS (value, type))
+                       return FALSE;
+               SOUP_VALUE_GETV (value, type, args);
+       }
+       return TRUE;
+}
+
+/**
+ * soup_value_array_insert:
+ * @array: a #GValueArray
+ * @index_: the index to insert at
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Inserts the provided value of type @type into @array as with
+ * g_value_array_insert(). (The provided data is copied rather than
+ * being inserted directly.)
+ **/
+void
+soup_value_array_insert (GValueArray *array, guint index_, GType type, ...)
+{
+       va_list args;
+       GValue val;
+
+       va_start (args, type);
+       SOUP_VALUE_SETV (&val, type, args);
+       va_end (args);
+       g_value_array_insert (array, index_, &val);
+}
+
+/**
+ * soup_value_array_append:
+ * @array: a #GValueArray
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Appends the provided value of type @type to @array as with
+ * g_value_array_append(). (The provided data is copied rather than
+ * being inserted directly.)
+ **/
+void
+soup_value_array_append (GValueArray *array, GType type, ...)
+{
+       va_list args;
+       GValue val;
+
+       va_start (args, type);
+       SOUP_VALUE_SETV (&val, type, args);
+       va_end (args);
+       g_value_array_append (array, &val);
+}
+
+/**
+ * soup_value_array_get_nth:
+ * @array: a #GValueArray
+ * @index_: the index to look up
+ * @type: a #GType
+ * @...: a value of type pointer-to-@type
+ *
+ * Gets the @index_ element of @array and stores its value into the
+ * provided location.
+ *
+ * Return value: %TRUE if @array contained a value with index @index_
+ * and type @type, %FALSE if not.
+ **/
+gboolean
+soup_value_array_get_nth (GValueArray *array, guint index_, GType type, ...)
+{
+       GValue *value;
+       va_list args;
+
+       value = g_value_array_get_nth (array, index_);
+       if (!value || !G_VALUE_HOLDS (value, type))
+               return FALSE;
+
+       va_start (args, type);
+       SOUP_VALUE_GETV (value, type, args);
+       va_end (args);
+       return TRUE;
+}
+
+
+static GByteArray *
+soup_byte_array_copy (GByteArray *ba)
+{
+       GByteArray *copy;
+
+       copy = g_byte_array_sized_new (ba->len);
+       g_byte_array_append (copy, ba->data, ba->len);
+       return copy;
+}
+
+static void
+soup_byte_array_free (GByteArray *ba)
+{
+       g_byte_array_free (ba, TRUE);
+}
+
+GType
+soup_byte_array_get_type (void)
+{
+       static GType type = 0;
+
+       if (type == 0) {
+               type = g_boxed_type_register_static (
+                       g_intern_static_string ("GByteArray"),
+                       (GBoxedCopyFunc)soup_byte_array_copy,
+                       (GBoxedFreeFunc)soup_byte_array_free);
+       }
+       return type;
+}
diff --git a/libsoup/soup-value-utils.h b/libsoup/soup-value-utils.h
new file mode 100644 (file)
index 0000000..19f7cce
--- /dev/null
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_VALUE_UTILS_H
+#define SOUP_VALUE_UTILS_H 1
+
+#include <libsoup/soup-types.h>
+#include <gobject/gvaluecollector.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_VALUE_SETV(val, type, args)                               \
+G_STMT_START {                                                         \
+       char *error = NULL;                                             \
+                                                                       \
+       memset (val, 0, sizeof (GValue));                               \
+       g_value_init (val, type);                                       \
+       G_VALUE_COLLECT (val, args, G_VALUE_NOCOPY_CONTENTS, &error);   \
+       if (error)                                                      \
+               g_free (error);                                         \
+} G_STMT_END
+
+#define SOUP_VALUE_GETV(val, type, args)                               \
+G_STMT_START {                                                         \
+       char *error = NULL;                                             \
+                                                                       \
+       G_VALUE_LCOPY (val, args, G_VALUE_NOCOPY_CONTENTS, &error);     \
+       if (error)                                                      \
+               g_free (error);                                         \
+} G_STMT_END
+
+GHashTable  *soup_value_hash_new          (void);
+void         soup_value_hash_insert_value (GHashTable  *hash,
+                                          const char  *key,
+                                          GValue      *value);
+void         soup_value_hash_insert       (GHashTable  *hash,
+                                          const char  *key,
+                                          GType        type,
+                                          ...);
+gboolean     soup_value_hash_lookup       (GHashTable  *hash,
+                                          const char  *key,
+                                          GType        type,
+                                          ...);
+
+GValueArray *soup_value_array_from_args   (va_list      args);
+gboolean     soup_value_array_to_args     (GValueArray *array,
+                                          va_list      args);
+
+void         soup_value_array_insert      (GValueArray *array,
+                                          guint        index_,
+                                          GType        type,
+                                          ...);
+void         soup_value_array_append      (GValueArray *array,
+                                          GType        type,
+                                          ...);
+gboolean     soup_value_array_get_nth     (GValueArray *array,
+                                          guint        index_,
+                                          GType        type,
+                                          ...);
+
+
+GType        soup_byte_array_get_type     (void);
+#define SOUP_TYPE_BYTE_ARRAY (soup_byte_array_get_type ())
+
+G_END_DECLS
+
+#endif /* SOUP_VALUE_UTILS_H */
diff --git a/libsoup/soup-xmlrpc-message.c b/libsoup/soup-xmlrpc-message.c
deleted file mode 100644 (file)
index b2954b9..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-xmlrpc-message.c: XMLRPC request message
- *
- * Copyright (C) 2003, Novell, Inc.
- * Copyright (C) 2004, Mariano Suarez-Alvarez <mariano@gnome.org>
- * Copyright (C) 2004, Fernando Herrera  <fherrera@onirica.com>
- * Copyright (C) 2005, Jeff Bailey  <jbailey@ubuntu.com>
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <time.h>
-
-#include "soup-date.h"
-#include "soup-misc.h"
-#include "soup-xmlrpc-message.h"
-
-G_DEFINE_TYPE (SoupXmlrpcMessage, soup_xmlrpc_message, SOUP_TYPE_MESSAGE)
-
-typedef struct {
-       xmlDocPtr doc;
-       xmlNodePtr last_node;
-} SoupXmlrpcMessagePrivate;
-#define SOUP_XMLRPC_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_XMLRPC_MESSAGE, SoupXmlrpcMessagePrivate))
-
-static void soup_xmlrpc_message_end_element (SoupXmlrpcMessage *msg);
-
-static void
-finalize (GObject *object)
-{
-       SoupXmlrpcMessagePrivate *priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (object);
-
-       if (priv->doc)
-               xmlFreeDoc (priv->doc);
-
-       G_OBJECT_CLASS (soup_xmlrpc_message_parent_class)->finalize (object);
-}
-
-static void
-soup_xmlrpc_message_class_init (SoupXmlrpcMessageClass *soup_xmlrpc_message_class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (soup_xmlrpc_message_class);
-
-       g_type_class_add_private (soup_xmlrpc_message_class, sizeof (SoupXmlrpcMessagePrivate));
-
-       object_class->finalize = finalize;
-}
-
-static void
-soup_xmlrpc_message_init (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->doc = xmlNewDoc ((const xmlChar *)"1.0");
-       priv->doc->standalone = FALSE;
-       priv->doc->encoding = xmlCharStrdup ("UTF-8");
-}
-
-
-SoupXmlrpcMessage *
-soup_xmlrpc_message_new (const char *uri_string)
-{
-       SoupXmlrpcMessage *msg;
-       SoupUri *uri;
-
-       uri = soup_uri_new (uri_string);
-       if (!uri)
-               return NULL;
-
-       msg = soup_xmlrpc_message_new_from_uri (uri);
-
-       soup_uri_free (uri);
-
-       return msg;
-}
-
-SoupXmlrpcMessage *
-soup_xmlrpc_message_new_from_uri (const SoupUri *uri)
-{
-       SoupXmlrpcMessage *msg;
-
-       msg = g_object_new (SOUP_TYPE_XMLRPC_MESSAGE, NULL);
-       SOUP_MESSAGE (msg)->method = SOUP_METHOD_POST;
-       soup_message_set_uri (SOUP_MESSAGE (msg), uri);
-
-       return msg;
-}
-
-void
-soup_xmlrpc_message_start_call (SoupXmlrpcMessage *msg, const char *method_name)
-{
-       SoupXmlrpcMessagePrivate *priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-       xmlNodePtr root;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       root = xmlNewDocNode (priv->doc, NULL, (const xmlChar *)"methodCall", NULL);
-       xmlDocSetRootElement (priv->doc, root);
-
-       xmlNewChild (root, NULL, (const xmlChar *)"methodName", (const xmlChar *)method_name);
-
-       priv->last_node = root;
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"params", NULL);
-}
-
-void
-soup_xmlrpc_message_end_call (SoupXmlrpcMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       soup_xmlrpc_message_end_element (msg);
-       soup_xmlrpc_message_end_element (msg);
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_start_param (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"param", NULL);
-}
-
-void
-soup_xmlrpc_message_end_param (SoupXmlrpcMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_write_int (SoupXmlrpcMessage *msg, long i)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       char *str;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       str = g_strdup_printf ("%ld", i);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewTextChild (priv->last_node, NULL, (const xmlChar *)"i4", (xmlChar *)str);
-       soup_xmlrpc_message_end_element (msg);
-
-       g_free (str);
-}
-
-void
-soup_xmlrpc_message_write_boolean (SoupXmlrpcMessage *msg, gboolean b)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewChild (priv->last_node, NULL, (const xmlChar *)"boolean", (const xmlChar*)(b ? "1" : "0"));
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_write_string (SoupXmlrpcMessage *msg, const char *str)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewTextChild (priv->last_node, NULL, (const xmlChar *)"string", (const xmlChar *)str);
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_write_double (SoupXmlrpcMessage *msg, double d)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       char *str;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       str = g_strdup_printf ("%f", d);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewTextChild (priv->last_node, NULL, (const xmlChar *)"double", (xmlChar *)str);
-       soup_xmlrpc_message_end_element (msg);
-
-       g_free (str);
-}
-
-void
-soup_xmlrpc_message_write_datetime (SoupXmlrpcMessage *msg, const time_t timeval)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       struct tm time;
-       char str[128];
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       soup_gmtime (&timeval, &time);
-       strftime (str, 128, "%Y%m%dT%H:%M:%S", &time);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewTextChild (priv->last_node, NULL, (const xmlChar *)"dateTime.iso8601", (xmlChar *)str);
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_write_base64 (SoupXmlrpcMessage *msg, gconstpointer buf, int len)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       char *str;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       str = g_base64_encode (buf, len);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       xmlNewTextChild (priv->last_node, NULL, (const xmlChar *)"base64", (xmlChar *)str);
-       soup_xmlrpc_message_end_element (msg);
-
-       g_free (str);
-}
-
-void
-soup_xmlrpc_message_start_struct (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"struct", NULL);
-}
-
-void
-soup_xmlrpc_message_end_struct (SoupXmlrpcMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       soup_xmlrpc_message_end_element (msg);
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_start_member (SoupXmlrpcMessage *msg, const char *name)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"member", NULL);
-       xmlNewChild (priv->last_node, NULL, (const xmlChar *)"name", (const xmlChar *)name);
-}
-
-void
-soup_xmlrpc_message_end_member (SoupXmlrpcMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       soup_xmlrpc_message_end_element (msg);
-}
-
-void
-soup_xmlrpc_message_start_array (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"value", NULL);
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"array", NULL);
-       priv->last_node = xmlNewChild (priv->last_node, NULL, (const xmlChar *)"data", NULL);
-}
-
-void
-soup_xmlrpc_message_end_array (SoupXmlrpcMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-
-       soup_xmlrpc_message_end_element (msg);
-       soup_xmlrpc_message_end_element (msg);
-       soup_xmlrpc_message_end_element (msg);
-}
-
-static void
-soup_xmlrpc_message_end_element (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       priv->last_node = priv->last_node->parent;
-}
-
-xmlChar *
-soup_xmlrpc_message_to_string (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       xmlChar *body;
-       int len;
-
-       g_return_val_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg), NULL);
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       xmlDocDumpMemory (priv->doc, &body, &len);
-
-       return body;
-}
-
-gboolean
-soup_xmlrpc_message_from_string (SoupXmlrpcMessage *message, const char *xmlstr)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       xmlDocPtr newdoc;
-       xmlNodePtr body;
-
-       g_return_val_if_fail (SOUP_IS_XMLRPC_MESSAGE (message), FALSE);
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (message);
-       g_return_val_if_fail (xmlstr != NULL, FALSE);
-
-       newdoc = xmlParseMemory (xmlstr, strlen (xmlstr));
-       if (!newdoc)
-               return FALSE;
-
-       body = xmlDocGetRootElement (newdoc);
-       if (!body || strcmp ((const char *)body->name, "methodCall"))
-               goto bad;
-
-       body = soup_xml_real_node (body->children);
-       if (!body || strcmp ((const char *)body->name, "methodName"))
-               goto bad;
-
-       body = soup_xml_real_node (body->next);
-       if (!body || strcmp ((const char *)body->name, "params"))
-               goto bad;
-
-       body = xmlGetLastChild (body);
-       if (!body)
-               goto bad;
-
-       /* body should be pointing by now to the last param */
-       xmlFreeDoc (priv->doc);
-       priv->doc = newdoc;
-       priv->last_node = body;
-
-       return TRUE;
-
-bad:
-       xmlFreeDoc (newdoc);
-       return FALSE;
-}
-
-void
-soup_xmlrpc_message_persist (SoupXmlrpcMessage *msg)
-{
-       SoupXmlrpcMessagePrivate *priv;
-       xmlChar *body;
-       int len;
-
-       g_return_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg));
-       priv = SOUP_XMLRPC_MESSAGE_GET_PRIVATE (msg);
-
-       xmlDocDumpMemory (priv->doc, &body, &len);
-
-       soup_message_set_request (SOUP_MESSAGE (msg), "text/xml",
-                                 SOUP_BUFFER_SYSTEM_OWNED, (char *)body, len);
-}
-
-SoupXmlrpcResponse *
-soup_xmlrpc_message_parse_response (SoupXmlrpcMessage *msg)
-{
-       char *str;
-       SoupXmlrpcResponse *response;
-
-       g_return_val_if_fail (SOUP_IS_XMLRPC_MESSAGE (msg), NULL);
-
-       str = g_malloc0 (SOUP_MESSAGE (msg)->response.length + 1);
-       strncpy (str, SOUP_MESSAGE (msg)->response.body, SOUP_MESSAGE (msg)->response.length);
-
-       response = soup_xmlrpc_response_new_from_string (str);
-       g_free (str);
-
-       return response;
-}
diff --git a/libsoup/soup-xmlrpc-message.h b/libsoup/soup-xmlrpc-message.h
deleted file mode 100644 (file)
index 64cc132..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * FIXME Copyright
- */
-
-#ifndef SOUP_XMLRPC_MESSAGE_H
-#define SOUP_XMLRPC_MESSAGE_H
-
-#include <time.h>
-#include <libxml/tree.h>
-#include <libsoup/soup-message.h>
-#include <libsoup/soup-uri.h>
-
-#include "soup-xmlrpc-response.h"
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_XMLRPC_MESSAGE            (soup_xmlrpc_message_get_type ())
-#define SOUP_XMLRPC_MESSAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_XMLRPC_MESSAGE, SoupXmlrpcMessage))
-#define SOUP_XMLRPC_MESSAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_XMLRPC_MESSAGE, SoupXmlrpcMessageClass))
-#define SOUP_IS_XMLRPC_MESSAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_XMLRPC_MESSAGE))
-#define SOUP_IS_XMLRPC_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_XMLRPC_MESSAGE))
-#define SOUP_XMLRPC_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_XMLRPC_MESSAGE, SoupXmlrpcMessageClass))
-
-typedef struct {
-       SoupMessage parent;
-
-} SoupXmlrpcMessage;
-
-typedef struct {
-       SoupMessageClass parent_class;
-} SoupXmlrpcMessageClass;
-
-GType soup_xmlrpc_message_get_type (void);
-
-SoupXmlrpcMessage *soup_xmlrpc_message_new          (const char *uri_string);
-SoupXmlrpcMessage *soup_xmlrpc_message_new_from_uri (const SoupUri *uri);
-
-void soup_xmlrpc_message_start_call     (SoupXmlrpcMessage *msg,
-                                        const char        *method_name);
-void soup_xmlrpc_message_end_call       (SoupXmlrpcMessage *msg);
-
-void soup_xmlrpc_message_start_param    (SoupXmlrpcMessage *msg);
-void soup_xmlrpc_message_end_param      (SoupXmlrpcMessage *msg);
-
-void soup_xmlrpc_message_write_int      (SoupXmlrpcMessage *msg,
-                                        long               i);
-void soup_xmlrpc_message_write_boolean  (SoupXmlrpcMessage *msg,
-                                        gboolean           b);
-void soup_xmlrpc_message_write_string   (SoupXmlrpcMessage *msg,
-                                        const char        *str);
-void soup_xmlrpc_message_write_double   (SoupXmlrpcMessage *msg,
-                                        double             d);
-void soup_xmlrpc_message_write_datetime (SoupXmlrpcMessage *msg,
-                                        const time_t       timeval);
-void soup_xmlrpc_message_write_base64   (SoupXmlrpcMessage *msg,
-                                        gconstpointer      buf,
-                                        int                len);
-
-void soup_xmlrpc_message_start_struct   (SoupXmlrpcMessage *msg);
-void soup_xmlrpc_message_end_struct     (SoupXmlrpcMessage *msg);
-
-void soup_xmlrpc_message_start_member   (SoupXmlrpcMessage *msg,
-                                        const char        *name);
-void soup_xmlrpc_message_end_member     (SoupXmlrpcMessage *msg);
-
-void soup_xmlrpc_message_start_array    (SoupXmlrpcMessage *msg);
-void soup_xmlrpc_message_end_array      (SoupXmlrpcMessage *msg);
-
-gboolean  soup_xmlrpc_message_from_string (SoupXmlrpcMessage *message,
-                                          const char        *xmlstr);
-
-xmlChar  *soup_xmlrpc_message_to_string   (SoupXmlrpcMessage *msg);
-void      soup_xmlrpc_message_persist     (SoupXmlrpcMessage *msg);
-
-SoupXmlrpcResponse *soup_xmlrpc_message_parse_response (SoupXmlrpcMessage *msg);
-
-G_END_DECLS
-
-#endif
diff --git a/libsoup/soup-xmlrpc-response.c b/libsoup/soup-xmlrpc-response.c
deleted file mode 100644 (file)
index ab0f82c..0000000
+++ /dev/null
@@ -1,649 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-xmlrpc-response.c: XMLRPC response message
- *
- * Copyright (C) 2003, Novell, Inc.
- * Copyright (C) 2004, Mariano Suarez-Alvarez <mariano@gnome.org>
- * Copyright (C) 2004, Fernando Herrera  <fherrera@onirica.com>
- * Copyright (C) 2005, Jeff Bailey  <jbailey@ubuntu.com>
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <glib.h>
-#include <libxml/tree.h>
-
-#include "soup-date.h"
-#include "soup-misc.h"
-#include "soup-xmlrpc-response.h"
-
-
-G_DEFINE_TYPE (SoupXmlrpcResponse, soup_xmlrpc_response, G_TYPE_OBJECT)
-
-typedef struct {
-       xmlDocPtr doc;
-       gboolean fault;
-       xmlNodePtr value;
-} SoupXmlrpcResponsePrivate;
-#define SOUP_XMLRPC_RESPONSE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_XMLRPC_RESPONSE, SoupXmlrpcResponsePrivate))
-
-static void
-finalize (GObject *object)
-{
-       SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (object);
-
-       if (priv->doc)
-               xmlFreeDoc (priv->doc);
-
-       G_OBJECT_CLASS (soup_xmlrpc_response_parent_class)->finalize (object);
-}
-
-static void
-soup_xmlrpc_response_class_init (SoupXmlrpcResponseClass *soup_xmlrpc_response_class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (soup_xmlrpc_response_class);
-
-       g_type_class_add_private (soup_xmlrpc_response_class, sizeof (SoupXmlrpcResponsePrivate));
-
-       object_class->finalize = finalize;
-}
-
-static void
-soup_xmlrpc_response_init (SoupXmlrpcResponse *response)
-{
-       SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
-
-       priv->doc = xmlNewDoc ((const xmlChar *)"1.0");
-       priv->fault = FALSE;
-}
-
-
-SoupXmlrpcResponse *
-soup_xmlrpc_response_new (void)
-{
-       SoupXmlrpcResponse *response;
-
-       response = g_object_new (SOUP_TYPE_XMLRPC_RESPONSE, NULL);
-       return response;
-}
-
-SoupXmlrpcResponse *
-soup_xmlrpc_response_new_from_string (const char *xmlstr)
-{
-       SoupXmlrpcResponse *response;
-
-       g_return_val_if_fail (xmlstr != NULL, NULL);
-
-       response = g_object_new (SOUP_TYPE_XMLRPC_RESPONSE, NULL);
-
-       if (!soup_xmlrpc_response_from_string (response, xmlstr)) {
-               g_object_unref (response);
-               return NULL;
-       }
-
-       return response;
-}
-
-static xmlNode *
-exactly_one_child (xmlNode *node)
-{
-       xmlNode *child;
-
-       child = soup_xml_real_node (node->children);
-       if (!child || soup_xml_real_node (child->next))
-               return NULL;
-
-       return child;
-}
-
-gboolean
-soup_xmlrpc_response_from_string (SoupXmlrpcResponse *response, const char *xmlstr)
-{
-       SoupXmlrpcResponsePrivate *priv;
-       xmlDocPtr newdoc;
-       xmlNodePtr body;
-       gboolean fault = TRUE;
-
-       g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
-       priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
-       g_return_val_if_fail (xmlstr != NULL, FALSE);
-
-       newdoc = xmlParseMemory (xmlstr, strlen (xmlstr));
-       if (!newdoc)
-               goto very_bad;
-
-       body = xmlDocGetRootElement (newdoc);
-       if (!body || strcmp ((const char *)body->name, "methodResponse"))
-               goto bad;
-
-       body = exactly_one_child (body);
-       if (!body)
-               goto bad;
-
-       if (strcmp ((const char *)body->name, "params") == 0) {
-               fault = FALSE;
-               body = exactly_one_child (body);
-               if (!body || strcmp ((const char *)body->name, "param"))
-                       goto bad;
-       } else if (strcmp ((const char *)body->name, "fault") != 0)
-               goto bad;
-
-       body = exactly_one_child (body);
-       if (!body || strcmp ((const char *)body->name, "value"))
-               goto bad;
-
-       /* body should be pointing by now to the struct of a fault, or the value of a
-        * normal response
-        */
-
-       xmlFreeDoc (priv->doc);
-       priv->doc = newdoc;
-       priv->value = body;
-       priv->fault = fault;
-
-       return TRUE;
-
-bad:
-       xmlFreeDoc (newdoc);
-very_bad:
-       return FALSE;
-}
-
-xmlChar *
-soup_xmlrpc_response_to_string (SoupXmlrpcResponse *response)
-{
-       SoupXmlrpcResponsePrivate *priv;
-       xmlChar *str;
-       int size;
-
-       g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
-       priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
-
-       xmlDocDumpMemoryEnc (priv->doc, &str, &size, "UTF-8");
-
-       return str;
-}
-
-gboolean
-soup_xmlrpc_response_is_fault (SoupXmlrpcResponse *response)
-{
-       SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
-
-       return priv->fault;
-}
-
-SoupXmlrpcValue *
-soup_xmlrpc_response_get_value (SoupXmlrpcResponse *response)
-{
-       SoupXmlrpcResponsePrivate *priv;
-       g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
-       priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
-
-       return (SoupXmlrpcValue*) priv->value;
-}
-
-SoupXmlrpcValueType
-soup_xmlrpc_value_get_type (SoupXmlrpcValue *value)
-{
-       xmlNode *xml;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return SOUP_XMLRPC_VALUE_TYPE_BAD;
-
-       xml = exactly_one_child (xml);
-       if (!xml)
-               return SOUP_XMLRPC_VALUE_TYPE_BAD;
-
-       if (xml->type == XML_TEXT_NODE)
-               return SOUP_XMLRPC_VALUE_TYPE_STRING;
-       else if (xml->type != XML_ELEMENT_NODE)
-               return SOUP_XMLRPC_VALUE_TYPE_BAD;
-       
-       if (strcmp ((const char *)xml->name, "i4") == 0 || strcmp ((const char *)xml->name, "int") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_INT;
-       else if (strcmp ((const char *)xml->name, "boolean") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_BOOLEAN;
-       else if (strcmp ((const char *)xml->name, "string") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_STRING;
-       else if (strcmp ((const char *)xml->name, "double") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_DOUBLE;
-       else if (strcmp ((const char *)xml->name, "dateTime.iso8601") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_DATETIME;
-       else if (strcmp ((const char *)xml->name, "base64") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_BASE64;
-       else if (strcmp ((const char *)xml->name, "struct") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_STRUCT;
-       else if (strcmp ((const char *)xml->name, "array") == 0)
-               return SOUP_XMLRPC_VALUE_TYPE_ARRAY;
-       else
-               return SOUP_XMLRPC_VALUE_TYPE_BAD;
-}
-
-gboolean
-soup_xmlrpc_value_get_int (SoupXmlrpcValue *value, long *i)
-{
-       xmlNode *xml;
-       xmlChar *content;
-       char *tail;
-       gboolean ok;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml || (strcmp ((const char *)xml->name, "int") && strcmp ((const char *)xml->name, "i4")))
-               return FALSE;
-
-       /* FIXME this should be exactly one text node */
-       content = xmlNodeGetContent (xml);
-       *i = strtol ((char *)content, &tail, 10);
-       ok = (*tail == '\0');
-       xmlFree (content);
-
-       return ok;
-}
-
-gboolean
-soup_xmlrpc_value_get_double (SoupXmlrpcValue *value, double *d)
-{
-       xmlNode *xml;
-       xmlChar *content;
-       char *tail;
-       gboolean ok;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml || (strcmp ((const char *)xml->name, "double")))
-               return FALSE;
-
-       /* FIXME this should be exactly one text node */
-       content = xmlNodeGetContent (xml);
-       *d = g_ascii_strtod ((char *)content, &tail);
-       ok = (*tail == '\0');
-       xmlFree (content);
-
-       return ok;
-}
-
-gboolean
-soup_xmlrpc_value_get_boolean (SoupXmlrpcValue *value, gboolean *b)
-{
-       xmlNode *xml;
-       xmlChar *content;
-       char *tail;
-       gboolean ok;
-       int i;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml || strcmp ((const char *)xml->name, "boolean"))
-               return FALSE;
-
-       content = xmlNodeGetContent (xml);
-       i = strtol ((char *)content, &tail, 10);
-       *b = (i == 1);
-       ok = (*tail == '\0');
-       xmlFree (content);
-
-       return ok;
-}
-
-gboolean
-soup_xmlrpc_value_get_string (SoupXmlrpcValue *value, char **str)
-{
-       xmlNode *xml;
-       xmlChar *content;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml)
-               return FALSE;
-       if (xml->type == XML_ELEMENT_NODE) {
-               if (strcmp ((char *)xml->name, "string"))
-                       return FALSE;
-       } else if (xml->type != XML_TEXT_NODE)
-               return FALSE;
-       
-       content = xmlNodeGetContent (xml);
-       *str = content ? g_strdup ((char *)content) : g_strdup ("");
-       xmlFree (content);
-
-       return TRUE;
-}
-
-gboolean
-soup_xmlrpc_value_get_datetime (SoupXmlrpcValue *value, time_t *timeval)
-{
-       xmlNode *xml;
-       xmlChar *content;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml || (strcmp ((const char *)xml->name, "dateTime.iso8601")))
-               return FALSE;
-
-       /* FIXME this should be exactly one text node */
-       content = xmlNodeGetContent (xml);
-       if (xmlStrlen (content) != 17) {
-               xmlFree (content);
-               return FALSE;
-       }
-
-       *timeval = soup_date_iso8601_parse ((char *)content);
-       xmlFree (content);
-       return TRUE;
-}
-
-gboolean
-soup_xmlrpc_value_get_base64 (SoupXmlrpcValue *value, GByteArray **data)
-{
-       xmlNode *xml;
-       xmlChar *content;
-       guchar *decoded;
-       gsize len;
-
-       xml = (xmlNode *) value;
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-       if (!xml || strcmp ((const char *)xml->name, "base64"))
-               return FALSE;
-
-       content = xmlNodeGetContent (xml);
-       decoded = g_base64_decode ((const char *)content, &len);
-       xmlFree (content);
-
-       *data = g_byte_array_new ();
-       g_byte_array_append (*data, decoded, len);
-       g_free (decoded);
-
-       return TRUE;
-}
-
-
-gboolean
-soup_xmlrpc_value_get_struct (SoupXmlrpcValue *value, GHashTable **table)
-{
-       xmlNode *xml;
-       GHashTable *t;
-
-       xml = (xmlNode *) value;
-
-       if (strcmp ((const char *)xml->name, "value"))
-               return FALSE;
-       xml = exactly_one_child (xml);
-
-       if (!xml || strcmp ((const char *)xml->name, "struct"))
-               return FALSE;
-
-       t = g_hash_table_new_full (g_str_hash, g_str_equal, xmlFree, NULL);
-
-       for (xml = soup_xml_real_node (xml->children);
-            xml;
-            xml = soup_xml_real_node (xml->next)) {
-               xmlChar *name;
-               xmlNode *val, *cur;
-
-               if (strcmp ((const char *)xml->name, "member") ||
-                   !xml->children)
-                       goto bad;
-
-               name = NULL;
-               val = NULL;
-
-               for (cur = soup_xml_real_node (xml->children);
-                    cur;
-                    cur = soup_xml_real_node (cur->next)) {
-                       if (strcmp((const char *)cur->name, "name") == 0) {
-                               if (name)
-                                       goto local_bad;
-                               name = xmlNodeGetContent (cur);
-                       } else if (strcmp ((const char *)cur->name, "value") == 0)
-                               val = cur;
-                       else
-                               goto local_bad;
-
-                       continue;
-local_bad:
-                       if (name)
-                               xmlFree (name);
-                       goto bad;
-               }
-
-               if (!name || !val) {
-                       if (name)
-                               xmlFree (name);
-                       goto bad;
-               }
-               g_hash_table_insert (t, name, val);
-       }
-
-       *table = t;
-       return TRUE;
-
-bad:
-       g_hash_table_destroy (t);
-       return FALSE;
-}
-
-gboolean
-soup_xmlrpc_value_array_get_iterator (SoupXmlrpcValue *value, SoupXmlrpcValueArrayIterator **iter)
-{
-       xmlNode *xml, *array, *data;
-
-       xml = (xmlNode *) value;
-
-       array = soup_xml_real_node (xml->children);
-       if (!array || strcmp((const char *)array->name, "array") != 0 ||
-           soup_xml_real_node (array->next))
-               return FALSE;
-
-       data = soup_xml_real_node (array->children);
-       if (!data || strcmp((const char *)data->name, "data") != 0 ||
-           soup_xml_real_node (data->next))
-               return FALSE;
-
-       *iter = (SoupXmlrpcValueArrayIterator *) soup_xml_real_node (data->children);
-       return TRUE;
-}
-
-
-SoupXmlrpcValueArrayIterator *
-soup_xmlrpc_value_array_iterator_prev (SoupXmlrpcValueArrayIterator *iter)
-{
-       xmlNode *xml, *prev;
-
-       xml = (xmlNode *) iter;
-
-       prev = xml->prev;
-       while (prev != soup_xml_real_node (prev))
-               prev = prev->prev;
-
-       return (SoupXmlrpcValueArrayIterator *) prev;
-}
-
-SoupXmlrpcValueArrayIterator *
-soup_xmlrpc_value_array_iterator_next (SoupXmlrpcValueArrayIterator *iter)
-{
-       xmlNode *xml;
-
-       xml = (xmlNode *) iter;
-
-       return (SoupXmlrpcValueArrayIterator *) soup_xml_real_node (xml->next);
-}
-
-gboolean
-soup_xmlrpc_value_array_iterator_get_value (SoupXmlrpcValueArrayIterator *iter,
-                                           SoupXmlrpcValue **value)
-{
-       *value = (SoupXmlrpcValue *) iter;
-
-       return TRUE;
-}
-
-static void
-indent (int d)
-{
-       while (d--)
-               g_printerr (" ");
-}
-
-static void
-soup_xmlrpc_value_dump_internal (SoupXmlrpcValue *value, int d);
-
-static void
-soup_xmlrpc_value_dump_struct_member (const char *name, SoupXmlrpcValue *value, gpointer d)
-{
-       indent (GPOINTER_TO_INT (d));
-       g_printerr ("MEMBER: %s\n", name);
-       soup_xmlrpc_value_dump_internal (value, GPOINTER_TO_INT (d));
-}
-
-static void
-soup_xmlrpc_value_dump_array_element (const int i, SoupXmlrpcValue *value, gpointer d)
-{
-       indent (GPOINTER_TO_INT (d));
-       g_printerr ("ELEMENT: %d\n", i);
-       soup_xmlrpc_value_dump_internal (value, GPOINTER_TO_INT (d));
-}
-
-static void
-soup_xmlrpc_value_dump_internal (SoupXmlrpcValue *value, int d)
-{
-       long i;
-       gboolean b;
-       char *str;
-       double f;
-       time_t timeval;
-       GByteArray *base64;
-       GHashTable *hash;
-       SoupXmlrpcValueArrayIterator *iter;
-
-       g_printerr ("\n\n[%s]\n", ((xmlNode*)value)->name);
-       switch (soup_xmlrpc_value_get_type (value)) {
-
-               case SOUP_XMLRPC_VALUE_TYPE_BAD:
-                       indent (d);
-                       g_printerr ("BAD\n");
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_INT:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_int (value, &i))
-                               g_printerr ("BAD INT\n");
-                       else
-                               g_printerr ("INT: %ld\n", i);
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_BOOLEAN:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_boolean (value, &b))
-                               g_printerr ("BAD BOOLEAN\n");
-                       else
-                               g_printerr ("BOOLEAN: %s\n", b ? "true" : "false");
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_STRING:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_string (value, &str))
-                               g_printerr ("BAD STRING\n");
-                       else {
-                               g_printerr ("STRING: \"%s\"\n", str);
-                               g_free (str);
-                       }
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_DOUBLE:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_double (value, &f))
-                               g_printerr ("BAD DOUBLE\n");
-                       else
-                               g_printerr ("DOUBLE: %f\n", f);
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_DATETIME:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_datetime (value, &timeval))
-                               g_printerr ("BAD DATETIME\n");
-                       else
-                               g_printerr ("DATETIME: %s\n", asctime (gmtime (&timeval)));
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_BASE64:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_base64 (value, &base64))
-                               g_printerr ("BAD BASE64\n");
-                       else {
-                               GString *hex = g_string_new (NULL);
-                               int i;
-
-                               for (i = 0; i < base64->len; i++)
-                                       g_string_append_printf (hex, "%02x", base64->data[i]);
-
-                               g_printerr ("BASE64: %s\n", hex->str);
-                               g_string_free (hex, TRUE);
-                               g_byte_array_free (base64, TRUE);
-                       }
-
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_STRUCT:
-                       indent (d);
-                       if (!soup_xmlrpc_value_get_struct (value, &hash))
-                               g_printerr ("BAD STRUCT\n");
-                       else {
-                               g_printerr ("STRUCT\n");
-                               g_hash_table_foreach (hash, (GHFunc) soup_xmlrpc_value_dump_struct_member,
-                                                     GINT_TO_POINTER (d+1));
-                               g_hash_table_destroy (hash);
-                       }
-                       break;
-
-               case SOUP_XMLRPC_VALUE_TYPE_ARRAY:
-                       indent (d);
-                       if (!soup_xmlrpc_value_array_get_iterator (value, &iter))
-                               g_printerr ("BAD ARRAY\n");
-                       else {
-                               SoupXmlrpcValue *evalue;
-                               int i = 0;
-                               g_printerr ("ARRAY\n");
-                               while (iter != NULL) {
-                                       soup_xmlrpc_value_array_iterator_get_value (iter, &evalue);
-                                       soup_xmlrpc_value_dump_array_element (i, evalue, GINT_TO_POINTER (d+1));
-                                       iter = soup_xmlrpc_value_array_iterator_next (iter);
-                                       i++;
-                               }
-                       }
-                       break;
-       }
-
-}
-
-void
-soup_xmlrpc_value_dump (SoupXmlrpcValue *value)
-{
-       soup_xmlrpc_value_dump_internal (value, 0);
-}
-
diff --git a/libsoup/soup-xmlrpc-response.h b/libsoup/soup-xmlrpc-response.h
deleted file mode 100644 (file)
index f2207c7..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * FIXME Copyright
- */
-
-#ifndef SOUP_XMLRPC_RESPONSE_H
-#define SOUP_XMLRPC_RESPONSE_H
-
-#include <glib-object.h>
-#include <libxml/tree.h>
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_XMLRPC_RESPONSE            (soup_xmlrpc_response_get_type ())
-#define SOUP_XMLRPC_RESPONSE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_XMLRPC_RESPONSE, SoupXmlrpcResponse))
-#define SOUP_XMLRPC_RESPONSE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_XMLRPC_RESPONSE, SoupXmlrpcResponseClass))
-#define SOUP_IS_XMLRPC_RESPONSE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_XMLRPC_RESPONSE))
-#define SOUP_IS_XMLRPC_RESPONSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_XMLRPC_RESPONSE))
-#define SOUP_XMLRPC_RESPONSE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_XMLRPC_RESPONSE, SoupXmlrpcResponseClass))
-
-typedef struct {
-       GObject parent;
-
-} SoupXmlrpcResponse;
-
-typedef struct {
-       GObjectClass parent_class;
-} SoupXmlrpcResponseClass;
-
-GType               soup_xmlrpc_response_get_type (void);
-
-SoupXmlrpcResponse *soup_xmlrpc_response_new             (void);
-SoupXmlrpcResponse *soup_xmlrpc_response_new_from_string (const char *xmlstr);
-
-gboolean            soup_xmlrpc_response_from_string     (SoupXmlrpcResponse *response,
-                                                         const char         *xmlstr);
-xmlChar            *soup_xmlrpc_response_to_string       (SoupXmlrpcResponse *response);
-
-typedef xmlNode *SoupXmlrpcValue;
-
-typedef enum {
-       SOUP_XMLRPC_VALUE_TYPE_BAD,
-       SOUP_XMLRPC_VALUE_TYPE_INT,
-       SOUP_XMLRPC_VALUE_TYPE_BOOLEAN,
-       SOUP_XMLRPC_VALUE_TYPE_STRING,
-       SOUP_XMLRPC_VALUE_TYPE_DOUBLE,
-       SOUP_XMLRPC_VALUE_TYPE_DATETIME,
-       SOUP_XMLRPC_VALUE_TYPE_BASE64,
-       SOUP_XMLRPC_VALUE_TYPE_STRUCT,
-       SOUP_XMLRPC_VALUE_TYPE_ARRAY
-} SoupXmlrpcValueType;
-
-gboolean             soup_xmlrpc_response_is_fault  (SoupXmlrpcResponse *response);
-SoupXmlrpcValue     *soup_xmlrpc_response_get_value (SoupXmlrpcResponse *response);
-SoupXmlrpcValueType  soup_xmlrpc_value_get_type     (SoupXmlrpcValue *value);
-
-gboolean soup_xmlrpc_value_get_int      (SoupXmlrpcValue  *value,
-                                        long             *i);
-gboolean soup_xmlrpc_value_get_double   (SoupXmlrpcValue  *value,
-                                        double           *b);
-gboolean soup_xmlrpc_value_get_boolean  (SoupXmlrpcValue  *value,
-                                        gboolean         *b);
-gboolean soup_xmlrpc_value_get_string   (SoupXmlrpcValue  *value,
-                                        char            **str);
-gboolean soup_xmlrpc_value_get_datetime (SoupXmlrpcValue  *value,
-                                        time_t           *timeval);
-gboolean soup_xmlrpc_value_get_base64   (SoupXmlrpcValue  *value,
-                                        GByteArray      **data);
-
-gboolean soup_xmlrpc_value_get_struct   (SoupXmlrpcValue  *value,
-                                        GHashTable      **table);
-
-
-typedef xmlNodePtr SoupXmlrpcValueArrayIterator;
-
-gboolean                      soup_xmlrpc_value_array_get_iterator       (SoupXmlrpcValue               *value,
-                                                                         SoupXmlrpcValueArrayIterator **iter);
-
-SoupXmlrpcValueArrayIterator *soup_xmlrpc_value_array_iterator_prev      (SoupXmlrpcValueArrayIterator  *iter);
-SoupXmlrpcValueArrayIterator *soup_xmlrpc_value_array_iterator_next      (SoupXmlrpcValueArrayIterator  *iter);
-gboolean                      soup_xmlrpc_value_array_iterator_get_value (SoupXmlrpcValueArrayIterator  *iter,
-                                                                          SoupXmlrpcValue              **value);
-
-void soup_xmlrpc_value_dump (SoupXmlrpcValue *value);
-
-G_END_DECLS
-
-#endif
diff --git a/libsoup/soup-xmlrpc.c b/libsoup/soup-xmlrpc.c
new file mode 100644 (file)
index 0000000..9ebfe83
--- /dev/null
@@ -0,0 +1,796 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-xmlrpc.c: XML-RPC parser/generator
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include <libxml/tree.h>
+
+#include "soup-xmlrpc.h"
+#include "soup-value-utils.h"
+#include "soup-date.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-session.h"
+
+/**
+ * SECTION:soup-xmlrpc
+ * @short_description: XML-RPC support
+ *
+ **/
+
+static xmlNode *find_real_node (xmlNode *node);
+
+static gboolean insert_value (xmlNode *parent, GValue *value);
+
+static void
+insert_member (gpointer name, gpointer value, gpointer data)
+{
+       xmlNode *member, **struct_node = data;
+
+       if (!*struct_node)
+               return;
+
+       member = xmlNewChild (*struct_node, NULL,
+                             (const xmlChar *)"member", NULL);
+       xmlNewTextChild (member, NULL,
+                        (const xmlChar *)"name", (const xmlChar *)name);
+       if (!insert_value (member, value)) {
+               xmlFreeNode (*struct_node);
+               *struct_node = NULL;
+       }
+}
+
+static gboolean
+insert_value (xmlNode *parent, GValue *value)
+{
+       GType type = G_VALUE_TYPE (value);
+       xmlNode *xvalue;
+       char buf[128];
+
+       xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
+
+       if (type == G_TYPE_INT) {
+               snprintf (buf, sizeof (buf), "%d", g_value_get_int (value));
+               xmlNewChild (xvalue, NULL,
+                            (const xmlChar *)"int",
+                            (const xmlChar *)buf);
+       } else if (type == G_TYPE_BOOLEAN) {
+               snprintf (buf, sizeof (buf), "%d", g_value_get_boolean (value));
+               xmlNewChild (xvalue, NULL,
+                            (const xmlChar *)"boolean",
+                            (const xmlChar *)buf);
+       } else if (type == G_TYPE_STRING) {
+               xmlNewTextChild (xvalue, NULL,
+                                (const xmlChar *)"string",
+                                (const xmlChar *)g_value_get_string (value));
+       } else if (type == G_TYPE_DOUBLE) {
+               g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
+               xmlNewChild (xvalue, NULL,
+                            (const xmlChar *)"double",
+                            (const xmlChar *)buf);
+       } else if (type == SOUP_TYPE_DATE) {
+               SoupDate *date = g_value_get_boxed (value);
+               char *timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
+               xmlNewChild (xvalue, NULL,
+                            (const xmlChar *)"dateTime.iso8601",
+                            (const xmlChar *)timestamp);
+               g_free (timestamp);
+       } else if (type == SOUP_TYPE_BYTE_ARRAY) {
+               GByteArray *ba = g_value_get_boxed (value);
+               char *encoded;
+
+               encoded = g_base64_encode (ba->data, ba->len);
+               xmlNewChild (xvalue, NULL,
+                            (const xmlChar *)"base64",
+                            (const xmlChar *)encoded);
+               g_free (encoded);
+       } else if (type == G_TYPE_HASH_TABLE) {
+               GHashTable *hash = g_value_get_boxed (value);
+               xmlNode *struct_node;
+
+               struct_node = xmlNewChild (xvalue, NULL,
+                                          (const xmlChar *)"struct", NULL);
+               g_hash_table_foreach (hash, insert_member, &struct_node);
+               if (!struct_node)
+                       return FALSE;
+       } else if (type == G_TYPE_VALUE_ARRAY) {
+               GValueArray *va = g_value_get_boxed (value);
+               xmlNode *node;
+               int i;
+
+               node = xmlNewChild (xvalue, NULL,
+                                   (const xmlChar *)"array", NULL);
+               node = xmlNewChild (node, NULL,
+                                   (const xmlChar *)"data", NULL);
+               for (i = 0; i < va->n_values; i++) {
+                       if (!insert_value (node, &va->values[i]))
+                               return FALSE;
+               }
+       } else
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * soup_xmlrpc_build_method_call:
+ * @method_name: the name of the XML-RPC method
+ * @params: arguments to @method
+ * @n_params: length of @params
+ *
+ * This creates an XML-RPC methodCall and returns it as a string.
+ * This is the low-level method that soup_xmlrpc_request_new() and
+ * soup_xmlrpc_call() are built on.
+ *
+ * @params is an array of #GValue representing the parameters to
+ * @method. (It is *not* a #GValueArray, although if you have a
+ * #GValueArray, you can just pass its %values and %n_values fields.)
+ *
+ * The correspondence between glib types and XML-RPC types is:
+ *
+ *   int: #int (%G_TYPE_INT)
+ *   boolean: #gboolean (%G_TYPE_BOOLEAN)
+ *   string: #char* (%G_TYPE_STRING)
+ *   double: #double (%G_TYPE_DOUBLE)
+ *   datetime.iso8601: #SoupDate (%SOUP_TYPE_DATE)
+ *   base64: #GByteArray (%SOUP_TYPE_BYTE_ARRAY)
+ *   struct: #GHashTable (%G_TYPE_HASH_TABLE)
+ *   array: #GValueArray (%G_TYPE_VALUE_ARRAY)
+ *
+ * For structs, use a #GHashTable that maps strings to #GValue;
+ * soup_value_hash_new() and related methods can help with this.
+ *
+ * Return value: the text of the methodCall, or %NULL on error
+ **/
+char *
+soup_xmlrpc_build_method_call (const char *method_name,
+                              GValue *params, int n_params)
+{
+       xmlDoc *doc;
+       xmlNode *node, *param;
+       xmlChar *xmlbody;
+       int i, len;
+       char *body;
+
+       doc = xmlNewDoc ((const xmlChar *)"1.0");
+       doc->standalone = FALSE;
+       doc->encoding = xmlCharStrdup ("UTF-8");
+
+       node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
+       xmlDocSetRootElement (doc, node);
+       xmlNewChild (node, NULL, (const xmlChar *)"methodName",
+                    (const xmlChar *)method_name);
+
+       node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
+       for (i = 0; i < n_params; i++) {
+               param  = xmlNewChild (node, NULL,
+                                     (const xmlChar *)"param", NULL);
+               if (!insert_value (param, &params[i])) {
+                       xmlFreeDoc (doc);
+                       return NULL;
+               }
+       }
+
+       xmlDocDumpMemory (doc, &xmlbody, &len);
+       body = g_strndup ((char *)xmlbody, len);
+       xmlFree (xmlbody);
+       xmlFreeDoc (doc);
+       return body;
+}
+
+static SoupMessage *
+soup_xmlrpc_request_newv (const char *uri, const char *method_name, va_list args)
+{
+       SoupMessage *msg;
+       GValueArray *params;
+       char *body;
+
+       params = soup_value_array_from_args (args);
+       if (!params)
+               return NULL;
+
+       body = soup_xmlrpc_build_method_call (method_name, params->values,
+                                             params->n_values);
+       g_value_array_free (params);
+       if (!body)
+               return NULL;
+
+       msg = soup_message_new ("POST", uri);
+       soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+                                 body, strlen (body));
+       return msg;
+}
+
+/**
+ * soup_xmlrpc_request_new:
+ * @uri: URI of the XML-RPC service
+ * @method_name: the name of the XML-RPC method to invoke at @uri
+ * @...: parameters for @method
+ *
+ * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
+ * to send, for that method call.
+ *
+ * The parameters are passed as type/value pairs; ie, first a #GType,
+ * and then a value of the appropriate type, finally terminated by
+ * %G_TYPE_INVALID.
+ *
+ * Return value: a #SoupMessage encoding the indicated XML-RPC
+ * request.
+ **/
+SoupMessage *
+soup_xmlrpc_request_new (const char *uri, const char *method_name, ...)
+{
+       SoupMessage *msg;
+       va_list args;
+
+       va_start (args, method_name);
+       msg = soup_xmlrpc_request_newv (uri, method_name, args);
+       va_end (args);
+       return msg;
+}
+
+/**
+ * soup_xmlrpc_build_method_response:
+ * @value: the return value
+ *
+ * This creates a (successful) XML-RPC methodResponse and returns it
+ * as a string. To create a fault response, use
+ * soup_xmlrpc_build_fault().
+ *
+ * The glib type to XML-RPC type mapping is as with
+ * soup_xmlrpc_build_method_call(), qv.
+ *
+ * Return value: the text of the methodResponse, or %NULL on error
+ **/
+char *
+soup_xmlrpc_build_method_response (GValue *value)
+{
+       xmlDoc *doc;
+       xmlNode *node;
+       xmlChar *xmlbody;
+       char *body;
+       int len;
+
+       doc = xmlNewDoc ((const xmlChar *)"1.0");
+       doc->standalone = FALSE;
+       doc->encoding = xmlCharStrdup ("UTF-8");
+
+       node = xmlNewDocNode (doc, NULL,
+                             (const xmlChar *)"methodResponse", NULL);
+       xmlDocSetRootElement (doc, node);
+
+       node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
+       node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
+       if (!insert_value (node, value)) {
+               xmlFreeDoc (doc);
+               return FALSE;
+       }
+
+       xmlDocDumpMemory (doc, &xmlbody, &len);
+       body = g_strndup ((char *)xmlbody, len);
+       xmlFree (xmlbody);
+       xmlFreeDoc (doc);
+       return body;
+}
+
+static char *
+soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
+{
+       xmlDoc *doc;
+       xmlNode *node, *member;
+       GValue value;
+       xmlChar *xmlbody;
+       char *fault_string, *body;
+       int len;
+
+       fault_string = g_strdup_vprintf (fault_format, args);
+
+       doc = xmlNewDoc ((const xmlChar *)"1.0");
+       doc->standalone = FALSE;
+       doc->encoding = xmlCharStrdup ("UTF-8");
+
+       node = xmlNewDocNode (doc, NULL,
+                             (const xmlChar *)"methodResponse", NULL);
+       xmlDocSetRootElement (doc, node);
+       node = xmlNewDocNode (doc, NULL, (const xmlChar *)"fault", NULL);
+       node = xmlNewDocNode (doc, NULL, (const xmlChar *)"value", NULL);
+       node = xmlNewDocNode (doc, NULL, (const xmlChar *)"struct", NULL);
+
+       memset (&value, 0, sizeof (value));
+
+       member = xmlNewDocNode (doc, NULL, (const xmlChar *)"member", NULL);
+       xmlNewDocNode (doc, NULL,
+                      (const xmlChar *)"name", (const xmlChar *)"faultCode");
+       g_value_init (&value, G_TYPE_INT);
+       g_value_set_int (&value, fault_code);
+       insert_value (member, &value);
+       g_value_unset (&value);
+
+       member = xmlNewDocNode (doc, NULL, (const xmlChar *)"member", NULL);
+       xmlNewDocNode (doc, NULL,
+                      (const xmlChar *)"name", (const xmlChar *)"faultString");
+       g_value_init (&value, G_TYPE_STRING);
+       g_value_take_string (&value, fault_string);
+       insert_value (member, &value);
+       g_value_unset (&value);
+
+       xmlDocDumpMemory (doc, &xmlbody, &len);
+       body = g_strndup ((char *)xmlbody, len);
+       xmlFree (xmlbody);
+       xmlFreeDoc (doc);
+
+       return body;
+}
+
+/**
+ * soup_xmlrpc_build_fault:
+ * @fault_code: the fault code
+ * @fault_format: a printf()-style format string
+ * @...: the parameters to @fault_format
+ *
+ * This creates an XML-RPC fault response and returns it as a string.
+ * (To create a successful response, use
+ * soup_xmlrpc_build_method_response().)
+ *
+ * Return value: the text of the fault
+ **/
+char *
+soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
+{
+       va_list args;
+       char *body;
+
+       va_start (args, fault_format);
+       body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
+       va_end (args);
+       return body;
+}
+
+/**
+ * soup_xmlrpc_set_response:
+ * @msg: an XML-RPC request
+ * @type: the type of the response value
+ * @...: the response value
+ *
+ * Sets the status code and response body of @msg to indicate a
+ * successful XML-RPC call, with a return value given by @type and the
+ * following varargs argument, of the type indicated by @type.
+ **/
+void
+soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
+{
+       va_list args;
+       GValue value;
+       char *body;
+
+       va_start (args, type);
+       SOUP_VALUE_GETV (&value, type, args);
+       va_end (args);
+
+       body = soup_xmlrpc_build_method_response (&value);
+       g_value_unset (&value);
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+                                 body, strlen (body));
+}
+
+/**
+ * soup_xmlrpc_set_fault:
+ * @msg: an XML-RPC request
+ * @fault_code: the fault code
+ * @fault_format: a printf()-style format string
+ * @...: the parameters to @fault_format
+ *
+ * Sets the status code and response body of @msg to indicate an
+ * unsuccessful XML-RPC call, with the error described by @fault_code
+ * and @fault_format.
+ **/
+void
+soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
+                      const char *fault_format, ...)
+{
+       va_list args;
+       char *body;
+
+       va_start (args, fault_format);
+       body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
+       va_end (args);
+
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+                                 body, strlen (body));
+}
+
+
+
+static gboolean
+parse_value (xmlNode *xmlvalue, GValue *value)
+{
+       xmlNode *typenode;
+       const char *typename;
+       xmlChar *content;
+
+       memset (value, 0, sizeof (GValue));
+
+       typenode = find_real_node (xmlvalue->children);
+       if (!typenode) {
+               /* If no type node, it's a string */
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, G_TYPE_STRING);
+               g_value_set_string (value, (char *)content);
+               xmlFree (content);
+               return TRUE;
+       }
+
+       typename = (const char *)typenode->name;
+
+       if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, G_TYPE_INT);
+               g_value_set_int (value, atoi ((char *)content));
+               xmlFree (content);
+       } else if (!strcmp (typename, "boolean")) {
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, G_TYPE_BOOLEAN);
+               g_value_set_boolean (value, atoi ((char *)content));
+               xmlFree (content);
+       } else if (!strcmp (typename, "string")) {
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, G_TYPE_STRING);
+               g_value_set_string (value, (char *)content);
+               xmlFree (content);
+       } else if (!strcmp (typename, "double")) {
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, G_TYPE_DOUBLE);
+               g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
+               xmlFree (content);
+       } else if (!strcmp (typename, "dateTime.iso8601")) {
+               content = xmlNodeGetContent (typenode);
+               g_value_init (value, SOUP_TYPE_DATE);
+               g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
+               xmlFree (content);
+       } else if (!strcmp (typename, "base64")) {
+               GByteArray *ba;
+               guchar *decoded;
+               gsize len;
+
+               content = xmlNodeGetContent (typenode);
+               decoded = g_base64_decode ((char *)content, &len);
+               ba = g_byte_array_sized_new (len);
+               g_byte_array_append (ba, decoded, len);
+               g_free (decoded);
+               xmlFree (content);
+               g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
+               g_value_take_boxed (value, ba);
+       } else if (!strcmp (typename, "struct")) {
+               xmlNode *member, *child, *mname, *mxval;
+               GHashTable *hash;
+               GValue mgval;
+               
+               hash = soup_value_hash_new ();
+               for (member = find_real_node (typenode->children);
+                    member;
+                    member = find_real_node (member->next)) {
+                       if (strcmp ((const char *)member->name, "member") != 0) {
+                               g_hash_table_destroy (hash);
+                               return FALSE;
+                       }
+                       mname = mxval = NULL;
+                       memset (&mgval, 0, sizeof (mgval));
+
+                       for (child = find_real_node (member->children);
+                            child;
+                            child = find_real_node (child->next)) {
+                               if (!strcmp ((const char *)child->name, "name"))
+                                       mname = child;
+                               else if (!strcmp ((const char *)child->name, "value"))
+                                       mxval = child;
+                               else
+                                       break;
+                       }
+
+                       if (!mname || !mxval || !parse_value (mxval, &mgval)) {
+                               g_hash_table_destroy (hash);
+                               return FALSE;
+                       }
+
+                       content = xmlNodeGetContent (mname);
+                       soup_value_hash_insert_value (hash, (char *)content, &mgval);
+                       xmlFree (content);
+               }
+               g_value_init (value, G_TYPE_HASH_TABLE);
+               g_value_take_boxed (value, hash);
+       } else if (!strcmp (typename, "array")) {
+               xmlNode *data, *xval;
+               GValueArray *array;
+               GValue gval;
+
+               data = find_real_node (typenode->children);
+               if (!data || strcmp ((const char *)data->name, "data") != 0)
+                       return FALSE;
+
+               array = g_value_array_new (1);
+               for (xval = find_real_node (data->children);
+                    xval;
+                    xval = find_real_node (xval->next)) {
+                       memset (&gval, 0, sizeof (gval));
+                       if (strcmp ((const char *)xval->name, "value") != 0 ||
+                           !parse_value (xval, &gval)) {
+                               g_value_array_free (array);
+                               return FALSE;
+                       }
+
+                       g_value_array_append (array, &gval);
+                       g_value_unset (&gval);
+               }
+               g_value_init (value, G_TYPE_VALUE_ARRAY);
+               g_value_take_boxed (value, array);
+       } else
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * soup_xmlrpc_parse_method_call:
+ * @method_call: the XML-RPC methodCall string
+ * @length: the length of @method_call, or -1 if it is NUL-terminated
+ * @method_name: on return, the methodName from @method_call
+ * @params: on return, the parameters from @method_call
+ *
+ * Parses @method_call to get the name and parameters, and returns the
+ * parameter values in a #GValueArray; see also
+ * soup_xmlrpc_extract_method_call(), which is more convenient if you
+ * know in advance what the types of the parameters will be.
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_xmlrpc_parse_method_call (const char *method_call, int length,
+                              char **method_name, GValueArray **params)
+{
+       xmlDoc *doc;
+       xmlNode *node, *param, *xval;
+       xmlChar *xmlMethodName = NULL;
+       gboolean success = FALSE;
+       GValue value;
+
+       doc = xmlParseMemory (method_call,
+                             length == -1 ? strlen (method_call) : length);
+       if (!doc)
+               return FALSE;
+
+       node = xmlDocGetRootElement (doc);
+       if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
+               goto fail;
+
+       node = find_real_node (node->children);
+       if (!node || strcmp ((const char *)node->name, "methodName") != 0)
+               goto fail;
+       xmlMethodName = xmlNodeGetContent (node);
+
+       node = find_real_node (node->next);
+       if (!node || strcmp ((const char *)node->name, "params") != 0)
+               goto fail;
+
+       *params = g_value_array_new (1);
+       param = find_real_node (node->children);
+       while (param && !strcmp ((const char *)param->name, "param")) {
+               xval = find_real_node (param->children);
+               if (!xval || !strcmp ((const char *)xval->name, "value") ||
+                   !parse_value (xval, &value)) {
+                       g_value_array_free (*params);
+                       goto fail;
+               }
+               g_value_array_append (*params, &value);
+               g_value_unset (&value);
+       }
+
+       success = TRUE;
+       *method_name = g_strdup ((char *)xmlMethodName);
+
+fail:
+       xmlFreeDoc (doc);
+       if (xmlMethodName)
+               xmlFree (xmlMethodName);
+       return success;
+}
+
+/**
+ * soup_xmlrpc_extract_method_call:
+ * @method_call: the XML-RPC methodCall string
+ * @length: the length of @method_call, or -1 if it is NUL-terminated
+ * @method_name: on return, the methodName from @method_call
+ * @...: return types and locations for parameters
+ *
+ * Parses @method_call to get the name and parameters, and puts
+ * the parameters into variables of the appropriate types.
+ *
+ * The parameters are handled similarly to
+ * @soup_xmlrpc_build_method_call, with pairs of types and values,
+ * terminated by %G_TYPE_INVALID, except that values are pointers to
+ * variables of the indicated type, rather than values of the type.
+ *
+ * See also soup_xmlrpc_parse_method_call(), which can be used if
+ * you don't know the types of the parameters.
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_xmlrpc_extract_method_call (const char *method_call, int length,
+                                char **method_name, ...)
+{
+       GValueArray *params;
+       gboolean success;
+       va_list args;
+
+       if (!soup_xmlrpc_parse_method_call (method_call, length,
+                                           method_name, &params))
+               return FALSE;
+
+       va_start (args, method_name);
+       success = soup_value_array_to_args (params, args);
+       va_end (args);
+
+       g_value_array_free (params);
+       return success;
+}
+
+/**
+ * soup_xmlrpc_parse_method_response:
+ * @method_response: the XML-RPC methodResponse string
+ * @length: the length of @method_response, or -1 if it is NUL-terminated
+ * @value: on return, the return value from @method_call
+ * @error: error return value
+ *
+ * Parses @method_response and returns the return value in @value. If
+ * @method_response is a fault, @value will be unchanged, and @error
+ * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
+ * #code containing the fault code, and the error #message containing
+ * the fault string. (If @method_response cannot be parsed at all,
+ * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
+ * will be unset.)
+ *
+ * Return value: %TRUE if a return value was parsed, %FALSE if the
+ * response could not be parsed, or contained a fault.
+ **/
+gboolean
+soup_xmlrpc_parse_method_response (const char *method_response, int length,
+                                  GValue *value, GError **error)
+{
+       xmlDoc *doc;
+       xmlNode *node;
+       gboolean success = FALSE;
+
+       doc = xmlParseMemory (method_response,
+                             length == -1 ? strlen (method_response) : length);
+       if (!doc)
+               return FALSE;
+
+       node = xmlDocGetRootElement (doc);
+       if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
+               goto fail;
+
+       node = find_real_node (node->children);
+       if (!node)
+               goto fail;
+
+       if (!strcmp ((const char *)node->name, "fault") && error) {
+               int fault_code = -1;
+               xmlChar *fault_string = NULL;
+
+               for (node = find_real_node (node->children);
+                    node;
+                    node = find_real_node (node->next)) {
+                       if (!strcmp ((const char *)node->name, "faultCode")) {
+                               xmlChar *content = xmlNodeGetContent (node);
+                               fault_code = atoi ((char *)content);
+                               xmlFree (content);
+                       } else if (!strcmp ((const char *)node->name, "faultString")) {
+                               fault_string = xmlNodeGetContent (node);
+                       } else {
+                               if (fault_string)
+                                       xmlFree (fault_string);
+                               goto fail;
+                       }
+               }
+               if (fault_code != -1 && fault_string != NULL) {
+                       g_set_error (error, SOUP_XMLRPC_FAULT,
+                                    fault_code, "%s", fault_string);
+               }
+               if (fault_string)
+                       xmlFree (fault_string);
+       } else if (!strcmp ((const char *)node->name, "params")) {
+               node = find_real_node (node->children);
+               if (!node || strcmp ((const char *)node->name, "param") != 0)
+                       goto fail;
+               node = find_real_node (node->children);
+               if (!node || strcmp ((const char *)node->name, "value") != 0)
+                       goto fail;
+               if (!parse_value (node, value))
+                       goto fail;
+               success = TRUE;
+       }
+
+fail:
+       xmlFreeDoc (doc);
+       return success;
+}
+
+/**
+ * soup_xmlrpc_extract_method_response:
+ * @method_response: the XML-RPC methodResponse string
+ * @length: the length of @method_response, or -1 if it is NUL-terminated
+ * @error: error return value
+ * @type: the expected type of the return value
+ * @...: location for return value
+ *
+ * Parses @method_response and extracts the return value into
+ * a variable of the correct type.
+ *
+ * If @method_response is a fault, the return value will be unset,
+ * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
+ * the error #code containing the fault code, and the error #message
+ * containing the fault string. (If @method_response cannot be parsed
+ * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
+ * but @error will be unset.)
+ *
+ * Return value: %TRUE if a return value was parsed, %FALSE if the
+ * response was of the wrong type, or contained a fault.
+ **/
+gboolean
+soup_xmlrpc_extract_method_response (const char *method_response, int length,
+                                    GError **error, GType type, ...)
+{
+       GValue value;
+       va_list args;
+
+       if (!soup_xmlrpc_parse_method_response (method_response, length,
+                                               &value, error))
+               return FALSE;
+       if (!G_VALUE_HOLDS (&value, type))
+               return FALSE;
+
+       va_start (args, type);
+       SOUP_VALUE_GETV (&value, type, args);
+       va_end (args);
+
+       return TRUE;
+}
+
+
+GQuark
+soup_xmlrpc_error_quark (void)
+{
+       static GQuark error;
+       if (!error)
+               error = g_quark_from_static_string ("soup_xmlrpc_error_quark");
+       return error;
+}
+
+GQuark
+soup_xmlrpc_fault_quark (void)
+{
+       static GQuark error;
+       if (!error)
+               error = g_quark_from_static_string ("soup_xmlrpc_fault_quark");
+       return error;
+}
+
+static xmlNode *
+find_real_node (xmlNode *node)
+{
+       while (node && (node->type == XML_COMMENT_NODE ||
+                       xmlIsBlankNode (node)))
+               node = node->next;
+       return node;
+}
diff --git a/libsoup/soup-xmlrpc.h b/libsoup/soup-xmlrpc.h
new file mode 100644 (file)
index 0000000..faf84f6
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_XMLRPC_H
+#define SOUP_XMLRPC_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+/* XML-RPC client */
+char        *soup_xmlrpc_build_method_call       (const char   *method_name,
+                                                 GValue       *params,
+                                                 int           n_params);
+SoupMessage *soup_xmlrpc_request_new             (const char   *uri,
+                                                 const char   *method_name,
+                                                 ...);
+gboolean     soup_xmlrpc_parse_method_response   (const char   *method_response,
+                                                 int           length,
+                                                 GValue       *value,
+                                                 GError      **error);
+gboolean     soup_xmlrpc_extract_method_response (const char   *method_response,
+                                                 int           length,
+                                                 GError      **error,
+                                                 GType         type,
+                                                 ...);
+
+/* XML-RPC server */
+gboolean     soup_xmlrpc_parse_method_call       (const char   *method_call,
+                                                 int           length,
+                                                 char        **method_name,
+                                                 GValueArray **params);
+gboolean     soup_xmlrpc_extract_method_call     (const char   *method_call,
+                                                 int           length,
+                                                 char        **method_name,
+                                                 ...);
+char        *soup_xmlrpc_build_method_response   (GValue       *value);
+char        *soup_xmlrpc_build_fault             (int           fault_code,
+                                                 const char   *fault_format,
+                                                 ...);
+void         soup_xmlrpc_set_response            (SoupMessage  *msg,
+                                                 GType         type,
+                                                 ...);
+void         soup_xmlrpc_set_fault               (SoupMessage  *msg,
+                                                 int           fault_code,
+                                                 const char   *fault_format,
+                                                 ...);
+
+
+/* Errors */
+#define SOUP_XMLRPC_ERROR soup_xmlrpc_error_quark()
+GQuark soup_xmlrpc_error_quark (void);
+
+typedef enum {
+       SOUP_XMLRPC_ERROR_ARGUMENTS,
+       SOUP_XMLRPC_ERROR_RETVAL
+} SoupXMLRPCError;
+
+#define SOUP_XMLRPC_FAULT soup_xmlrpc_fault_quark()
+GQuark soup_xmlrpc_fault_quark (void);
+
+
+G_END_DECLS
+
+#endif /* SOUP_XMLRPC_H */
index da67d87..6ed7828 100644 (file)
 extern "C" {
 #endif
 
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-auth.h>
+#include <libsoup/soup-auth-domain.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-date.h>
+#include <libsoup/soup-enum-types.h>
+#include <libsoup/soup-form.h>
+#include <libsoup/soup-headers.h>
+#include <libsoup/soup-logger.h>
 #include <libsoup/soup-message.h>
+#include <libsoup/soup-method.h>
 #include <libsoup/soup-misc.h>
+#include <libsoup/soup-server.h>
 #include <libsoup/soup-session-async.h>
 #include <libsoup/soup-session-sync.h>
 #include <libsoup/soup-socket.h>
+#include <libsoup/soup-status.h>
 #include <libsoup/soup-uri.h>
+#include <libsoup/soup-value-utils.h>
+#include <libsoup/soup-xmlrpc.h>
 
 #ifdef __cplusplus
 }
index 9929507..3f9ce55 100644 (file)
@@ -9,42 +9,48 @@ LIBS = $(top_builddir)/libsoup/libsoup-$(SOUP_API_VERSION).la
 
 noinst_PROGRAMS =      \
        context-test    \
+       continue-test   \
        date            \
-       dict            \
        dns             \
        get             \
        getbug          \
        header-parsing  \
        ntlm-test       \
-       revserver       \
        simple-httpd    \
        simple-proxy    \
        uri-parsing     \
+       $(CURL_TESTS)   \
        $(APACHE_TESTS) \
        $(SSL_TESTS)    \
        $(XMLRPC_TESTS)
 
-auth_test_SOURCES = auth-test.c apache-wrapper.c apache-wrapper.h
-context_test_SOURCES = context-test.c
-date_SOURCES = date.c
-dict_SOURCES = dict.c
+TEST_SRCS = test-utils.c test-utils.h
+
+auth_test_SOURCES = auth-test.c $(TEST_SRCS)
+context_test_SOURCES = context-test.c $(TEST_SRCS)
+continue_test_SOURCES = continue-test.c $(TEST_SRCS)
+date_SOURCES = date.c $(TEST_SRCS)
 dns_SOURCES = dns.c
 get_SOURCES = get.c
 getbug_SOURCES = getbug.c
-header_parsing_SOURCES = header-parsing.c
-ntlm_test_SOURCES = ntlm-test.c
-proxy_test_SOURCES = proxy-test.c apache-wrapper.c apache-wrapper.h
-pull_api_SOURCES = pull-api.c apache-wrapper.c apache-wrapper.h
-revserver_SOURCES = revserver.c
+header_parsing_SOURCES = header-parsing.c $(TEST_SRCS)
+ntlm_test_SOURCES = ntlm-test.c $(TEST_SRCS)
+proxy_test_SOURCES = proxy-test.c $(TEST_SRCS)
+pull_api_SOURCES = pull-api.c $(TEST_SRCS)
+query_test_SOURCES = query-test.c $(TEST_SRCS)
+server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
-ssl_test_SOURCES = ssl-test.c
-uri_parsing_SOURCES = uri-parsing.c
-xmlrpc_test_SOURCES = xmlrpc-test.c apache-wrapper.c apache-wrapper.h
+ssl_test_SOURCES = ssl-test.c $(TEST_SRCS)
+uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS)
+xmlrpc_test_SOURCES = xmlrpc-test.c $(TEST_SRCS)
 
 if HAVE_APACHE
 APACHE_TESTS = auth-test proxy-test pull-api
 endif
+if HAVE_CURL
+CURL_TESTS = query-test server-auth-test
+endif
 if HAVE_SSL
 SSL_TESTS = ssl-test
 endif
@@ -52,7 +58,17 @@ if HAVE_XMLRPC_EPI_PHP
 XMLRPC_TESTS = xmlrpc-test
 endif
 
-TESTS = context-test date header-parsing uri-parsing ntlm-test $(APACHE_TESTS) $(SSL_TESTS) $(XMLRPC_TESTS)
+TESTS =                        \
+       context-test    \
+       continue-test   \
+       date            \
+       header-parsing  \
+       uri-parsing     \
+       ntlm-test       \
+       $(APACHE_TESTS) \
+       $(CURL_TESTS)   \
+       $(SSL_TESTS)    \
+       $(XMLRPC_TESTS)
 
 EXTRA_DIST =           \
        libsoup.supp    \
diff --git a/tests/apache-wrapper.c b/tests/apache-wrapper.c
deleted file mode 100644 (file)
index bc859ae..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef HAVE_APACHE
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "apache-wrapper.h"
-
-static gboolean
-apache_cmd (char *cmd)
-{
-       char *argv[8];
-       char *cwd, *conf;
-       int status;
-       gboolean ok;
-
-       cwd = g_get_current_dir ();
-       conf = g_build_filename (cwd, "httpd.conf", NULL);
-
-       argv[0] = APACHE_HTTPD;
-       argv[1] = "-d";
-       argv[2] = cwd;
-       argv[3] = "-f";
-       argv[4] = conf;
-       argv[5] = "-k";
-       argv[6] = cmd;
-       argv[7] = NULL;
-
-       ok = g_spawn_sync (cwd, argv, NULL, 0, NULL, NULL,
-                          NULL, NULL, &status, NULL);
-       if (ok)
-               ok = (status == 0);
-
-       g_free (cwd);
-       g_free (conf);
-
-       return ok;
-}
-
-gboolean
-apache_init (void)
-{
-       return apache_cmd ("start");
-}
-
-void
-apache_cleanup (void)
-{
-       pid_t pid;
-       char *contents;
-
-       if (g_file_get_contents ("httpd.pid", &contents, NULL, NULL)) {
-               pid = strtoul (contents, NULL, 10);
-               g_free (contents);
-       } else
-               pid = 0;
-
-       apache_cmd ("graceful-stop");
-
-       if (pid) {
-               while (kill (pid, 0) == 0)
-                       g_usleep (100);
-       }
-}
-
-#endif /* HAVE_APACHE */
diff --git a/tests/apache-wrapper.h b/tests/apache-wrapper.h
deleted file mode 100644 (file)
index 58302a9..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <glib.h>
-
-gboolean apache_init    (void);
-void     apache_cleanup (void);
index cf4473d..4ef5e3c 100644 (file)
 #include "libsoup/soup-auth.h"
 #include "libsoup/soup-session.h"
 
-#include "apache-wrapper.h"
+#include "test-utils.h"
 
 GMainLoop *loop;
-int errors = 0;
-gboolean debug = FALSE;
-
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
-
-       if (!debug)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
 
 typedef struct {
        /* Explanation of what you should see */
@@ -181,16 +166,16 @@ identify_auth (SoupMessage *msg)
        const char *header;
        int num;
 
-       header = soup_message_get_header (msg->request_headers,
-                                         "Authorization");
+       header = soup_message_headers_get (msg->request_headers,
+                                           "Authorization");
        if (!header)
                return 0;
 
        if (!g_ascii_strncasecmp (header, "Basic ", 6)) {
                char *token;
-               int len;
+               gsize len;
 
-               token = soup_base64_decode (header + 6, &len);
+               token = (char *)g_base64_decode (header + 6, &len);
                num = token[len - 1] - '0';
                g_free (token);
        } else {
@@ -216,46 +201,45 @@ handler (SoupMessage *msg, gpointer data)
 
        auth = identify_auth (msg);
 
-       dprintf ("  %d %s (using %s)\n", msg->status_code, msg->reason_phrase,
-                auths[auth]);
+       debug_printf (1, "  %d %s (using %s)\n",
+                     msg->status_code, msg->reason_phrase,
+                     auths[auth]);
 
        if (*expected) {
                exp = *expected - '0';
                if (auth != exp) {
-                       dprintf ("    expected %s!\n", auths[exp]);
+                       debug_printf (1, "    expected %s!\n", auths[exp]);
                        errors++;
                }
                memmove (expected, expected + 1, strlen (expected));
        } else {
-               dprintf ("    expected to be finished\n");
+               debug_printf (1, "    expected to be finished\n");
                errors++;
        }
 }
 
 static void
 authenticate (SoupSession *session, SoupMessage *msg,
-             const char *auth_type, const char *auth_realm,
-             char **username, char **password, gpointer data)
+             SoupAuth *auth, gboolean retrying, gpointer data)
 {
        int *i = data;
+       char *username, *password;
+       char num;
 
-       if (tests[*i].provided[0]) {
-               *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, SoupMessage *msg, 
-               const char *auth_type, const char *auth_realm,
-               char **username, char **password, gpointer data)
-{
-       int *i = data;
+       if (!tests[*i].provided[0])
+               return;
+       if (retrying) {
+               if (!tests[*i].provided[1])
+                       return;
+               num = tests[*i].provided[1];
+       } else
+               num = tests[*i].provided[0];
 
-       if (tests[*i].provided[0] && tests[*i].provided[1]) {
-               *username = g_strdup_printf ("user%c", tests[*i].provided[1]);
-               *password = g_strdup_printf ("realm%c", tests[*i].provided[1]);
-       }
+       username = g_strdup_printf ("user%c", num);
+       password = g_strdup_printf ("realm%c", num);
+       soup_auth_authenticate (auth, username, password);
+       g_free (username);
+       g_free (password);
 }
 
 static void
@@ -266,46 +250,44 @@ bug271540_sent (SoupMessage *msg, gpointer data)
        int auth = identify_auth (msg);
 
        if (!*authenticated && auth) {
-               dprintf ("    using auth on message %d before authenticating!!??\n", n);
+               debug_printf (1, "    using auth on message %d before authenticating!!??\n", n);
                errors++;
        } else if (*authenticated && !auth) {
-               dprintf ("    sent unauthenticated message %d after authenticating!\n", n);
+               debug_printf (1, "    sent unauthenticated message %d after authenticating!\n", n);
                errors++;
        }
 }
 
 static void
 bug271540_authenticate (SoupSession *session, SoupMessage *msg,
-                       const char *auth_type, const char *auth_realm,
-                       char **username, char **password, gpointer data)
+                       SoupAuth *auth, gboolean retrying, gpointer data)
 {
        int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
        gboolean *authenticated = data;
 
-       if (strcmp (auth_type, "Basic") != 0 ||
-           strcmp (auth_realm, "realm1") != 0)
+       if (strcmp (soup_auth_get_scheme_name (auth), "Basic") != 0 ||
+           strcmp (soup_auth_get_realm (auth), "realm1") != 0)
                return;
 
        if (!*authenticated) {
-               dprintf ("    authenticating message %d\n", n);
-               *username = g_strdup ("user1");
-               *password = g_strdup ("realm1");
+               debug_printf (1, "    authenticating message %d\n", n);
+               soup_auth_authenticate (auth, "user1", "realm1");
                *authenticated = TRUE;
        } else {
-               dprintf ("    asked to authenticate message %d after authenticating!\n", n);
+               debug_printf (1, "    asked to authenticate message %d after authenticating!\n", n);
                errors++;
        }
 }
 
 static void
-bug271540_finished (SoupMessage *msg, gpointer data)
+bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data)
 {
        int *left = data;
        int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
-               dprintf ("      got status '%d %s' on message %d!\n",
-                       msg->status_code, msg->reason_phrase, n);
+               debug_printf (1, "      got status '%d %s' on message %d!\n",
+                             msg->status_code, msg->reason_phrase, n);
                errors++;
        }
 
@@ -314,6 +296,62 @@ bug271540_finished (SoupMessage *msg, gpointer data)
                g_main_loop_quit (loop);
 }
 
+static void
+digest_nonce_authenticate (SoupSession *session, SoupMessage *msg,
+                          SoupAuth *auth, gboolean retrying, gpointer data)
+{
+       if (retrying)
+               return;
+
+       if (strcmp (soup_auth_get_scheme_name (auth), "Digest") != 0 ||
+           strcmp (soup_auth_get_realm (auth), "realm1") != 0)
+               return;
+
+       soup_auth_authenticate (auth, "user1", "realm1");
+}
+
+static void
+digest_nonce_unauthorized (SoupMessage *msg, gpointer data)
+{
+       gboolean *got_401 = data;
+       *got_401 = TRUE;
+}
+
+static void
+do_digest_nonce_test (SoupSession *session,
+                     const char *nth, const char *uri,
+                     gboolean expect_401, gboolean expect_signal)
+{
+       SoupMessage *msg;
+       gboolean got_401;
+
+       msg = soup_message_new (SOUP_METHOD_GET, uri);
+       if (expect_signal) {
+               g_signal_connect (session, "authenticate",
+                                 G_CALLBACK (digest_nonce_authenticate),
+                                 NULL);
+       }
+       soup_message_add_status_code_handler (msg, "got_headers",
+                                             SOUP_STATUS_UNAUTHORIZED,
+                                             G_CALLBACK (digest_nonce_unauthorized),
+                                             &got_401);
+       got_401 = FALSE;
+       soup_session_send_message (session, msg);
+       if (got_401 != expect_401) {
+               debug_printf (1, "  %s request %s a 401 Unauthorized!\n", nth,
+                             got_401 ? "got" : "did not get");
+               errors++;
+       }
+       if (msg->status_code != SOUP_STATUS_OK) {
+               debug_printf (1, "  %s request got status %d %s!\n", nth,
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       }
+       if (errors == 0)
+               debug_printf (1, "  %s request succeeded\n", nth);
+       g_object_unref (msg);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -321,39 +359,22 @@ main (int argc, char **argv)
        SoupMessage *msg;
        char *base_uri, *uri, *expected;
        gboolean authenticated;
-       int i, opt;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n", argv[0]);
-                       return 1;
-               }
-       }
+       int i;
+
+       test_init (argc, argv, NULL);
+       apache_init ();
 
-       if (!apache_init ()) {
-               fprintf (stderr, "Could not start apache\n");
-               return 1;
-       }
        base_uri = "http://localhost:47524/";
 
-       session = soup_session_async_new ();
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), &i);
-       g_signal_connect (session, "reauthenticate",
-                         G_CALLBACK (reauthenticate), &i);
 
        for (i = 0; i < ntests; i++) {
-               dprintf ("Test %d: %s\n", i + 1, tests[i].explanation);
+               debug_printf (1, "Test %d: %s\n", i + 1, tests[i].explanation);
 
                uri = g_strconcat (base_uri, tests[i].url, NULL);
-               dprintf ("  GET %s\n", uri);
+               debug_printf (1, "  GET %s\n", uri);
 
                msg = soup_message_new (SOUP_METHOD_GET, uri);
                g_free (uri);
@@ -364,39 +385,41 @@ main (int argc, char **argv)
 
                expected = g_strdup (tests[i].expected);
                soup_message_add_status_code_handler (
-                       msg, SOUP_STATUS_UNAUTHORIZED,
-                       SOUP_HANDLER_PRE_BODY, handler, expected);
+                       msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
+                       G_CALLBACK (handler), expected);
                soup_message_add_status_code_handler (
-                       msg, SOUP_STATUS_OK, SOUP_HANDLER_PRE_BODY,
-                       handler, expected);
+                       msg, "got_headers", SOUP_STATUS_OK,
+                       G_CALLBACK (handler), expected);
                soup_session_send_message (session, msg);
                if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
                    msg->status_code != SOUP_STATUS_OK) {
-                       dprintf ("  %d %s !\n", msg->status_code,
-                               msg->reason_phrase);
+                       debug_printf (1, "  %d %s !\n", msg->status_code,
+                                     msg->reason_phrase);
                        errors++;
                }
                if (*expected) {
-                       dprintf ("  expected %d more round(s)\n",
-                               (int)strlen (expected));
+                       debug_printf (1, "  expected %d more round(s)\n",
+                                     (int)strlen (expected));
                        errors++;
                }
                g_free (expected);
 
-               if (msg->status_code != tests[i].final_status)
-                       dprintf ("  expected %d\n", tests[i].final_status);
+               if (msg->status_code != tests[i].final_status) {
+                       debug_printf (1, "  expected %d\n",
+                                     tests[i].final_status);
+               }
 
-               dprintf ("\n");
+               debug_printf (1, "\n");
 
                g_object_unref (msg);
        }
        soup_session_abort (session);
        g_object_unref (session);
 
-       /* And now for a regression test */
+       /* And now for some regression tests */
 
-       dprintf ("Regression test for bug 271540:\n");
-       session = soup_session_async_new ();
+       debug_printf (1, "Testing pipelined auth (bug 271540):\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
 
        authenticated = FALSE;
        g_signal_connect (session, "authenticate",
@@ -416,19 +439,100 @@ main (int argc, char **argv)
 
        loop = g_main_loop_new (NULL, TRUE);
        g_main_loop_run (loop);
+
+       debug_printf (1, "\nTesting digest nonce expiration:\n");
+
+       /* We test two different things here:
+        *
+        *   1. If we get a 401 response with
+        *      "WWW-Authenticate: Digest stale=true...", we should
+        *      retry and succeed *without* the session asking for a
+        *      password again.
+        *
+        *   2. If we get a successful response with
+        *      "Authentication-Info: nextnonce=...", we should update
+        *      the nonce automatically so as to avoid getting a
+        *      stale nonce error on the next request.
+        *
+        * In our Apache config, /Digest/realm1 and
+        * /Digest/realm1/expire are set up to use the same auth info,
+        * but only the latter has an AuthDigestNonceLifetime (of 2
+        * seconds). The way nonces work in Apache, a nonce received
+        * from /Digest/realm1 will still expire in
+        * /Digest/realm1/expire, but it won't issue a nextnonce for a
+        * request in /Digest/realm1. This lets us test both
+        * behaviors.
+        *
+        * The expected conversation is:
+        *
+        * First message
+        *   GET /Digest/realm1
+        *
+        *   401 Unauthorized
+        *   WWW-Authenticate: Digest nonce=A
+        *
+        *   [emit 'authenticate']
+        *
+        *   GET /Digest/realm1
+        *   Authorization: Digest nonce=A
+        *
+        *   200 OK
+        *   [No Authentication-Info]
+        *
+        * [sleep 2 seconds: nonce A is no longer valid, but we have no
+        * way of knowing that]
+        *
+        * Second message
+        *   GET /Digest/realm1/expire/
+        *   Authorization: Digest nonce=A
+        *
+        *   401 Unauthorized
+        *   WWW-Authenticate: Digest stale=true nonce=B
+        *
+        *   GET /Digest/realm1/expire/
+        *   Authorization: Digest nonce=B
+        *
+        *   200 OK
+        *   Authentication-Info: nextnonce=C
+        *
+        * [sleep 1 second]
+        *
+        * Third message
+        *   GET /Digest/realm1/expire/
+        *   Authorization: Digest nonce=C
+        *   [nonce=B would work here too]
+        *
+        *   200 OK
+        *   Authentication-Info: nextnonce=D
+        *
+        * [sleep 1 second; nonces B and C are no longer valid, but D is]
+        *
+        * Fourth message
+        *   GET /Digest/realm1/expire/
+        *   Authorization: Digest nonce=D
+        *
+        *   200 OK
+        *   Authentication-Info: nextnonce=D
+        *
+        */
+
+       uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
+       do_digest_nonce_test (session, "First", uri, TRUE, TRUE);
+       g_free (uri);
+       sleep (2);
+       uri = g_strconcat (base_uri, "Digest/realm1/expire/", NULL);
+       do_digest_nonce_test (session, "Second", uri, TRUE, FALSE);
+       sleep (1);
+       do_digest_nonce_test (session, "Third", uri, FALSE, FALSE);
+       sleep (1);
+       do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE);
+       g_free (uri);
+
        g_main_loop_unref (loop);
-       g_main_context_unref (g_main_context_default ());
 
        soup_session_abort (session);
        g_object_unref (session);
 
-       apache_cleanup ();
-
-       dprintf ("\n");
-       if (errors) {
-               printf ("auth-test: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("auth-test: OK\n");
-       return errors;
+       test_cleanup ();
+       return errors != 0;
 }
index b44f070..fe1b1ba 100644 (file)
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-misc.h>
 #include <libsoup/soup-server.h>
-#include <libsoup/soup-server-message.h>
 #include <libsoup/soup-session-async.h>
 #include <libsoup/soup-session-sync.h>
 
-gboolean debug = FALSE;
-int errors = 0;
-GThread *server_thread;
-char *base_uri;
-
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
+#include "test-utils.h"
 
-       if (!debug)
-               return;
+char *base_uri;
 
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+typedef struct {
+       SoupServer *server;
+       SoupMessage *msg;
+       GSource *timeout;
+} SlowData;
 
 static void
-request_failed (SoupMessage *msg, gpointer timeout)
+request_failed (SoupMessage *msg, gpointer data)
 {
+       SlowData *sd = data;
+
        if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
-               g_source_destroy (timeout);
+               g_source_destroy (sd->timeout);
+       g_free (sd);
 }
 
 static gboolean
 add_body_chunk (gpointer data)
 {
-       SoupMessage *msg = data;
+       SlowData *sd = data;
 
-       soup_message_add_chunk (msg, SOUP_BUFFER_STATIC,
-                               "OK\r\n", 4);
-       soup_message_add_final_chunk (msg);
-       soup_message_io_unpause (msg);
-       g_object_unref (msg);
+       soup_message_body_append (sd->msg->response_body,
+                                 SOUP_MEMORY_STATIC, "OK\r\n", 4);
+       soup_message_body_complete (sd->msg->response_body);
+       soup_server_unpause_message (sd->server, sd->msg);
+       g_object_unref (sd->msg);
 
        return FALSE;
 }
 
 static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
 {
-       GSource *timeout;
+       SlowData *sd;
 
-       if (soup_method_get_id (msg->method) != SOUP_METHOD_ID_GET) {
+       if (msg->method != SOUP_METHOD_GET) {
                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
                return;
        }
 
-       if (!strcmp (context->path, "/shutdown")) {
-               soup_server_quit (context->server);
-               return;
-       }
-
        soup_message_set_status (msg, SOUP_STATUS_OK);
-       if (!strcmp (context->path, "/fast")) {
+       if (!strcmp (path, "/fast")) {
                soup_message_set_response (msg, "text/plain",
-                                          SOUP_BUFFER_STATIC, "OK\r\n", 4);
+                                          SOUP_MEMORY_STATIC, "OK\r\n", 4);
                return;
        }
 
-       soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg),
-                                         SOUP_TRANSFER_CHUNKED);
+       soup_message_headers_set_encoding (msg->response_headers,
+                                          SOUP_ENCODING_CHUNKED);
        g_object_ref (msg);
-       soup_message_io_pause (msg);
-
-       timeout = soup_add_timeout (
-               soup_server_get_async_context (context->server),
-               200, add_body_chunk, msg);
+       soup_server_pause_message (server, msg);
+
+       sd = g_new (SlowData, 1);
+       sd->server = server;
+       sd->msg = msg;
+       sd->timeout = soup_add_timeout (
+               soup_server_get_async_context (server),
+               200, add_body_chunk, sd);
        g_signal_connect (msg, "finished",
-                         G_CALLBACK (request_failed), timeout);
-}
-
-static gpointer
-run_server_thread (gpointer user_data)
-{
-       SoupServer *server = user_data;
-
-       soup_server_add_handler (server, NULL, NULL,
-                                server_callback, NULL, NULL);
-       soup_server_run (server);
-       g_object_unref (server);
-
-       return NULL;
-}
-
-static guint
-create_server (void)
-{
-       SoupServer *server;
-       GMainContext *async_context;
-       guint port;
-
-       async_context = g_main_context_new ();
-       server = soup_server_new (SOUP_SERVER_PORT, 0,
-                                 SOUP_SERVER_ASYNC_CONTEXT, async_context,
-                                 NULL);
-       g_main_context_unref (async_context);
-
-       if (!server) {
-               fprintf (stderr, "Unable to bind server\n");
-               exit (1);
-       }
-
-       port = soup_server_get_port (server);
-       server_thread = g_thread_create (run_server_thread, server, TRUE, NULL);
-
-       return port;
-}
-
-static void
-shutdown_server (void)
-{
-       SoupSession *session;
-       char *uri;
-       SoupMessage *msg;
-
-       session = soup_session_sync_new ();
-       uri = g_build_filename (base_uri, "shutdown", NULL);
-       msg = soup_message_new ("GET", uri);
-       soup_session_send_message (session, msg);
-       g_object_unref (msg);
-       g_free (uri);
-
-       soup_session_abort (session);
-       g_object_unref (session);
-
-       g_thread_join (server_thread);
+                         G_CALLBACK (request_failed), sd);
 }
 
 /* Test 1: An async session in another thread with its own
@@ -168,7 +105,7 @@ do_test1 (void)
 {
        GMainLoop *loop;
 
-       dprintf ("Test 1: blocking the main thread does not block other thread\n");
+       debug_printf (1, "Test 1: blocking the main thread does not block other thread\n");
 
        test1_cond = g_cond_new ();
        test1_mutex = g_mutex_new ();
@@ -196,7 +133,7 @@ idle_start_test1_thread (gpointer loop)
        if (g_cond_timed_wait (test1_cond, test1_mutex, &time))
                g_thread_join (thread);
        else {
-               dprintf ("  timeout!\n");
+               debug_printf (1, "  timeout!\n");
                errors++;
        }
 
@@ -206,7 +143,7 @@ idle_start_test1_thread (gpointer loop)
 }
 
 static void
-test1_finished (SoupMessage *msg, gpointer loop)
+test1_finished (SoupSession *session, SoupMessage *msg, gpointer loop)
 {
        g_main_loop_quit (loop);
 }
@@ -225,24 +162,25 @@ test1_thread (gpointer user_data)
        g_mutex_unlock (test1_mutex);
 
        async_context = g_main_context_new ();
-       session = soup_session_async_new_with_options (
+       session = soup_test_session_new (
+               SOUP_TYPE_SESSION_ASYNC,
                SOUP_SESSION_ASYNC_CONTEXT, async_context,
                NULL);
        g_main_context_unref (async_context);
 
        uri = g_build_filename (base_uri, "slow", NULL);
 
-       dprintf ("  send_message\n");
+       debug_printf (1, "  send_message\n");
        msg = soup_message_new ("GET", uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != SOUP_STATUS_OK) {
-               dprintf ("    unexpected status: %d %s\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "    unexpected status: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
        }
        g_object_unref (msg);
 
-       dprintf ("  queue_message\n");
+       debug_printf (1, "  queue_message\n");
        msg = soup_message_new ("GET", uri);
        loop = g_main_loop_new (async_context, FALSE);
        g_object_ref (msg);
@@ -250,8 +188,8 @@ test1_thread (gpointer user_data)
        g_main_loop_run (loop);
        g_main_loop_unref (loop);
        if (msg->status_code != SOUP_STATUS_OK) {
-               dprintf ("    unexpected status: %d %s\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "    unexpected status: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
        }
        g_object_unref (msg);
@@ -279,24 +217,25 @@ do_test2 (void)
        char *uri;
        SoupMessage *msg;
 
-       dprintf ("Test 2: a session with its own context is independent of the main loop.\n");
+       debug_printf (1, "Test 2: a session with its own context is independent of the main loop.\n");
 
        idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL);
 
        async_context = g_main_context_new ();
-       session = soup_session_async_new_with_options (
+       session = soup_test_session_new (
+               SOUP_TYPE_SESSION_ASYNC,
                SOUP_SESSION_ASYNC_CONTEXT, async_context,
                NULL);
        g_main_context_unref (async_context);
 
        uri = g_build_filename (base_uri, "slow", NULL);
 
-       dprintf ("  send_message\n");
+       debug_printf (1, "  send_message\n");
        msg = soup_message_new ("GET", uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != SOUP_STATUS_OK) {
-               dprintf ("    unexpected status: %d %s\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "    unexpected status: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
        }
        g_object_unref (msg);
@@ -311,56 +250,29 @@ do_test2 (void)
 static gboolean
 idle_test2_fail (gpointer user_data)
 {
-       dprintf ("  idle ran!\n");
+       debug_printf (1, "  idle ran!\n");
        errors++;
        return FALSE;
 }
 
 
-static void
-quit (int sig)
-{
-       /* Exit cleanly on ^C in case we're valgrinding. */
-       exit (0);
-}
-
 int
 main (int argc, char **argv)
 {
-       int opt;
-       guint port;
-
-       g_type_init ();
-       g_thread_init (NULL);
-       signal (SIGINT, quit);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n",
-                                argv[0]);
-                       exit (1);
-               }
-       }
+       SoupServer *server;
+
+       test_init (argc, argv, NULL);
 
-       port = create_server ();
-       base_uri = g_strdup_printf ("http://localhost:%u/", port);
+       server = soup_test_server_new (TRUE);
+       soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+       base_uri = g_strdup_printf ("http://localhost:%u/",
+                                   soup_server_get_port (server));
 
        do_test1 ();
        do_test2 ();
 
-       shutdown_server ();
        g_free (base_uri);
-       g_main_context_unref (g_main_context_default ());
-
-       dprintf ("\n");
-       if (errors) {
-               printf ("context-test: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("context-test: OK\n");
+
+       test_cleanup ();
        return errors != 0;
 }
diff --git a/tests/continue-test.c b/tests/continue-test.c
new file mode 100644 (file)
index 0000000..cba7abb
--- /dev/null
@@ -0,0 +1,456 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-server.h>
+
+#include "test-utils.h"
+
+#define SHORT_BODY "This is a test.\r\n"
+#define LONG_BODY (SHORT_BODY SHORT_BODY)
+
+#define MAX_POST_LENGTH (sizeof (SHORT_BODY))
+
+int port;
+GSList *events;
+
+static void
+event (SoupMessage *msg, char *side, char *message)
+{
+       char *data = g_strdup_printf ("%s-%s", side, message);
+       gboolean record_status =
+               (!strcmp (data, "server-wrote_headers") ||
+                !strcmp (data, "server-wrote_informational"));
+
+       debug_printf (2, "  %s", data);
+       if (record_status)
+               debug_printf (2, " (%s)", msg->reason_phrase);
+       debug_printf (2, "\n");
+
+       events = g_slist_append (events, data);
+       if (record_status)
+               events = g_slist_append (events, GUINT_TO_POINTER (msg->status_code));
+}
+
+#define EVENT_HANDLER(name)                    \
+static void                                    \
+name (SoupMessage *msg, gpointer side)         \
+{                                              \
+       event (msg, side, #name);               \
+}
+
+EVENT_HANDLER (got_informational);
+EVENT_HANDLER (got_headers);
+EVENT_HANDLER (got_body);
+EVENT_HANDLER (wrote_informational);
+EVENT_HANDLER (wrote_headers);
+EVENT_HANDLER (wrote_body);
+EVENT_HANDLER (finished);
+
+static void
+do_message (const char *path, gboolean long_body,
+           gboolean expect_continue, gboolean auth,
+           ...)
+{
+       SoupSession *session;
+       SoupMessage *msg;
+       char *uri, *body;
+       va_list ap;
+       const char *expected_event;
+       char *actual_event;
+       int expected_status, actual_status;
+       static int count = 1;
+
+       debug_printf (1, "%d. /%s, %s body, %sExpect, %s password\n",
+                     count++, path,
+                     long_body ? "long" : "short",
+                     expect_continue ? "" : "no ",
+                     auth ? "with" : "without");
+
+       uri = g_strdup_printf ("http://%slocalhost:%d/%s",
+                              auth ? "user:pass@" : "",
+                              port, path);
+       msg = soup_message_new ("POST", uri);
+       g_free (uri);
+
+       body = long_body ? LONG_BODY : SHORT_BODY;
+       soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC,
+                                 body, strlen (body));
+       soup_message_headers_append (msg->request_headers, "Connection", "close");
+       if (expect_continue) {
+               soup_message_headers_set_expectations (msg->request_headers,
+                                                      SOUP_EXPECTATION_CONTINUE);
+       }
+
+       g_signal_connect (msg, "got_informational",
+                         G_CALLBACK (got_informational), "client");
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (got_headers), "client");
+       g_signal_connect (msg, "got_body",
+                         G_CALLBACK (got_body), "client");
+       g_signal_connect (msg, "wrote_informational",
+                         G_CALLBACK (wrote_informational), "client");
+       g_signal_connect (msg, "wrote_headers",
+                         G_CALLBACK (wrote_headers), "client");
+       g_signal_connect (msg, "wrote_body",
+                         G_CALLBACK (wrote_body), "client");
+       g_signal_connect (msg, "finished",
+                         G_CALLBACK (finished), "client");
+
+       events = NULL;
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+       soup_session_send_message (session, msg);
+       soup_session_abort (session);
+       g_object_unref (session);
+
+       va_start (ap, auth);
+       while ((expected_event = va_arg (ap, const char *))) {
+
+               if (!events) {
+                       actual_event = "";
+                       debug_printf (1, "  Expected '%s', got end of list\n",
+                                     expected_event);
+                       errors++;
+               } else {
+                       actual_event = events->data;
+                       if (strcmp (expected_event, actual_event) != 0) {
+                               debug_printf (1, "  Expected '%s', got '%s'\n",
+                                             expected_event, actual_event);
+                               errors++;
+                       }
+                       events = g_slist_delete_link (events, events);
+               }
+
+               if (!strcmp (expected_event, "server-wrote_headers") ||
+                   !strcmp (expected_event, "server-wrote_informational"))
+                       expected_status = va_arg (ap, int);
+               else
+                       expected_status = -1;
+               if (!strcmp (actual_event, "server-wrote_headers") ||
+                   !strcmp (actual_event, "server-wrote_informational")) {
+                       actual_status = GPOINTER_TO_INT (events->data);
+                       events = g_slist_delete_link (events, events);
+               } else
+                       expected_status = -1;
+
+               if (expected_status != -1 && actual_status != -1 &&
+                   expected_status != actual_status) {
+                       debug_printf (1, "  Expected status '%s', got '%s'\n",
+                                     soup_status_get_phrase (expected_status),
+                                     soup_status_get_phrase (actual_status));
+                       errors++;
+               }
+
+               g_free (actual_event);
+       }
+       va_end (ap);
+       while (events) {
+               actual_event = events->data;
+               debug_printf (1, "  Expected to be done, got '%s'\n", actual_event);
+               errors++;
+               events = g_slist_delete_link (events, events);
+
+               if (!strcmp (actual_event, "server-wrote_headers") ||
+                   !strcmp (actual_event, "server-wrote_informational"))
+                       events = g_slist_delete_link (events, events);
+       }
+       g_object_unref (msg);
+}
+
+static void
+run_tests (void)
+{
+       do_message ("unauth", FALSE, FALSE, FALSE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_CREATED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("unauth", TRUE, FALSE, FALSE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("unauth", FALSE, TRUE, FALSE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_informational", SOUP_STATUS_CONTINUE,
+                   "client-got_informational",
+                   "client-wrote_body",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_CREATED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("unauth", TRUE, TRUE, FALSE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+
+       do_message ("auth", FALSE, FALSE, FALSE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", TRUE, FALSE, FALSE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", FALSE, TRUE, FALSE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", TRUE, TRUE, FALSE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+
+       do_message ("auth", FALSE, FALSE, TRUE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_CREATED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", TRUE, FALSE, TRUE,
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-wrote_headers",
+                   "client-wrote_body",
+                   "server-got_headers",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", FALSE, TRUE, TRUE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_informational", SOUP_STATUS_CONTINUE,
+                   "client-got_informational",
+                   "client-wrote_body",
+                   "server-got_body",
+                   "server-wrote_headers", SOUP_STATUS_CREATED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+       do_message ("auth", TRUE, TRUE, TRUE,
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-wrote_headers",
+                   "server-got_headers",
+                   "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+                   "server-wrote_body",
+                   "server-finished",
+                   "client-got_headers",
+                   "client-got_body",
+                   "client-finished",
+                   NULL);
+}
+
+
+/* SERVER */
+
+static void
+server_got_headers (SoupMessage *msg, gpointer server)
+{
+       /* FIXME */
+       if (msg->status_code != SOUP_STATUS_CONTINUE &&
+           msg->status_code != 0)
+               return;
+
+       if (soup_message_headers_get_expectations (msg->request_headers) &
+           SOUP_EXPECTATION_CONTINUE) {
+               const char *length;
+
+               length = soup_message_headers_get (msg->request_headers,
+                                                   "Content-Length");
+               if (length && atoi (length) > MAX_POST_LENGTH) {
+                       soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
+                       soup_message_headers_append (msg->response_headers, "Connection", "close");
+               }
+       }
+}      
+
+static void
+request_started (SoupServer *server, SoupMessage *msg,
+                SoupClientContext *client, gpointer user_data)
+{
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (server_got_headers), server);
+
+       g_signal_connect (msg, "got_informational",
+                         G_CALLBACK (got_informational), "server");
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (got_headers), "server");
+       g_signal_connect (msg, "got_body",
+                         G_CALLBACK (got_body), "server");
+       g_signal_connect (msg, "wrote_informational",
+                         G_CALLBACK (wrote_informational), "server");
+       g_signal_connect (msg, "wrote_headers",
+                         G_CALLBACK (wrote_headers), "server");
+       g_signal_connect (msg, "wrote_body",
+                         G_CALLBACK (wrote_body), "server");
+       g_signal_connect (msg, "finished",
+                         G_CALLBACK (finished), "server");
+}
+
+static gboolean
+auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+              const char *username, const char *password, gpointer user_data)
+{
+       return !strcmp (username, "user") && !strcmp (password, "pass");
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
+{
+       if (msg->method != SOUP_METHOD_POST) {
+               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+               soup_message_headers_append (msg->response_headers, "Connection", "close");
+       } else if (msg->request_body->length > MAX_POST_LENGTH) {
+               soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
+               soup_message_headers_append (msg->response_headers, "Connection", "close");
+       } else
+               soup_message_set_status (msg, SOUP_STATUS_CREATED);
+}
+
+static SoupServer *
+setup_server (void)
+{
+       SoupServer *server;
+       SoupAuthDomain *auth_domain;
+
+       server = soup_test_server_new (FALSE);
+
+       g_signal_connect (server, "request-started",
+                         G_CALLBACK (request_started), NULL);
+
+       soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+
+       auth_domain = soup_auth_domain_basic_new (
+               SOUP_AUTH_DOMAIN_REALM, "continue-test",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/auth",
+               SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+               NULL);
+       soup_server_add_auth_domain (server, auth_domain);
+
+       return server;
+}
+
+/* MAIN */
+
+int
+main (int argc, char **argv)
+{
+       SoupServer *server;
+
+       test_init (argc, argv, NULL);
+
+       server = setup_server ();
+       port = soup_server_get_port (server);
+
+       run_tests ();
+
+       test_cleanup ();
+       return errors != 0;
+}
index ab87a77..e47d3be 100644 (file)
@@ -9,55 +9,94 @@
 #include <libsoup/soup-date.h>
 #include <glib.h>
 
-static int errors = 0;
+#include "test-utils.h"
 
-#define RFC1123_DATE   "Sun, 06 Nov 1994 08:49:37 GMT"
-#define RFC850_DATE    "Sunday, 06-Nov-94 08:49:37 GMT"
-#define ASCTIME_DATE   "Sun Nov  6 08:49:37 1994"
-#define ISO8601_1_DATE "1994-11-06T08:49:37Z"
-#define ISO8601_2_DATE "19941106T08:49:37Z"
-#define ISO8601_3_DATE "19941106T08:49:37+00:00"
-#define ISO8601_4_DATE "19941106T084937+00:00"
+const char *date_tests[] = {
+       /* rfc1123-date, and broken variants */
+       "Sun, 06 Nov 2004 08:09:07 GMT",
+       "Sun, 6 Nov 2004 08:09:07 GMT",
+       "Sun,  6 Nov 2004 08:09:07 GMT",
 
-#define EXPECTED     784111777
+       /* rfc850-date, and broken variants */
+       "Sunday, 06-Nov-04 08:09:07 GMT",
+       "Sunday, 6-Nov-04 08:09:07 GMT",
+       "Sunday,  6-Nov-04 08:09:07 GMT",
+       "Sunday, 06-Nov-104 08:09:07 GMT",
+
+       /* asctime-date, and broken variants */
+       "Sun Nov  6 08:09:07 2004",
+       "Sun Nov 06 08:09:07 2004",
+       "Sun Nov 6 08:09:07 2004",
+       "Sun Nov  6 08:09:07 2004 GMT",
+
+       /* ISO 8601 */
+       "2004-11-06T08:09:07Z",
+       "20041106T08:09:07Z",
+       "20041106T08:09:07+00:00",
+       "20041106T080907+00:00",
+
+       /* Netscape cookie spec date, and broken variants */
+       "Sun, 06-Nov-2004 08:09:07 GMT",
+       "Sun, 6-Nov-2004 08:09:07 GMT",
+       "Sun,  6-Nov-2004 08:09:07 GMT",
+
+       /* Original version of Netscape cookie spec, and broken variants */
+       "Sun, 06-Nov-04 08:09:07 GMT",
+       "Sun, 6-Nov-04 08:09:07 GMT",
+       "Sun,  6-Nov-04 08:09:07 GMT",
+       "Sun, 06-Nov-104 08:09:07 GMT",
+
+       /* Netscape cookie spec example syntax, and broken variants */
+       "Sunday, 06-Nov-04 08:09:07 GMT",
+       "Sunday, 6-Nov-04 08:09:07 GMT",
+       "Sunday,  6-Nov-04 08:09:07 GMT",
+       "Sunday, 06-Nov-104 08:09:07 GMT",
+       "Sunday, 06-Nov-2004 08:09:07 GMT",
+       "Sunday, 6-Nov-2004 08:09:07 GMT",
+       "Sunday,  6-Nov-2004 08:09:07 GMT",
+
+       /* Miscellaneous broken formats seen on the web */
+       "Sun 06-Nov-2004  08:9:07",
+       "Sunday, 06-Nov-04 8:9:07 GMT",
+       "Sun, 06 Nov 2004 08:09:7 GMT",
+       "Sun, 06-Nov-2004 08:09:07"
+};
+
+#define TIME_T 1099728547L
+#define TIME_T_STRING "1099728547"
 
 static void
-check (const char *test, const char *date, time_t got)
+check (const char *strdate, SoupDate *date)
 {
-       if (got == EXPECTED)
+       if (date &&
+           date->year == 2004 && date->month == 11 && date->day == 6 &&
+           date->hour == 8 && date->minute == 9 && date->second == 7) {
+               soup_date_free (date);
                return;
+       }
 
-       fprintf (stderr, "%s date parsing failed for '%s'.\n", test, date);
-       fprintf (stderr, "  expected: %lu, got: %lu\n\n",
-                (unsigned long)EXPECTED, (unsigned long)got);
+       fprintf (stderr, "date parsing failed for '%s'.\n", strdate);
+       if (date) {
+               fprintf (stderr, "  got: %d %d %d - %d %d %d\n\n",
+                        date->year, date->month, date->day,
+                        date->hour, date->minute, date->second);
+               soup_date_free (date);
+       }
        errors++;
 }
 
 int
 main (int argc, char **argv)
 {
-       char *date;
-
-       check ("RFC1123", RFC1123_DATE, soup_date_parse (RFC1123_DATE));
-       check ("RFC850", RFC850_DATE, soup_date_parse (RFC850_DATE));
-       check ("asctime", ASCTIME_DATE, soup_date_parse (ASCTIME_DATE));
-       check ("iso8610[1]", ISO8601_1_DATE, soup_date_iso8601_parse (ISO8601_1_DATE));
-       check ("iso8610[2]", ISO8601_2_DATE, soup_date_iso8601_parse (ISO8601_2_DATE));
-       check ("iso8610[3]", ISO8601_3_DATE, soup_date_iso8601_parse (ISO8601_3_DATE));
-       check ("iso8610[4]", ISO8601_4_DATE, soup_date_iso8601_parse (ISO8601_4_DATE));
-
-       date = soup_date_generate (EXPECTED);
-       if (strcmp (date, RFC1123_DATE) != 0) {
-               fprintf (stderr, "date generation failed.\n");
-               fprintf (stderr, "  expected: %s\n  got:      %s\n\n",
-                        RFC1123_DATE, date);
-               errors++;
+       int i;
+
+       test_init (argc, argv, NULL);
+
+       for (i = 0; i < G_N_ELEMENTS (date_tests); i++) {
+               check (date_tests[i], soup_date_new_from_string (date_tests[i]));
        }
-       g_free (date);
+       check (TIME_T_STRING, soup_date_new_from_time_t (TIME_T));
 
-       if (errors == 0)
-               printf ("date: OK\n");
-       else
-               fprintf (stderr, "date: %d errors\n", errors);
-       return errors;
+       test_cleanup ();
+       return errors != 0;
 }
diff --git a/tests/dict.c b/tests/dict.c
deleted file mode 100644 (file)
index afaed0e..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2001-2003, Ximian, Inc.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <libsoup/soup.h>
-#include <libsoup/soup-soap-message.h>
-#include <libsoup/soup-soap-response.h>
-
-SoupSession *session;
-GMainLoop *loop;
-
-static void
-got_response (SoupMessage *msg, gpointer user_data)
-{
-       SoupSoapResponse *response;
-       SoupSoapParameter *param, *subparam;
-       char *word, *dict, *def;
-       int count = 0;
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
-               fprintf (stderr, "%d %s\n", msg->status_code, msg->reason_phrase);
-               exit (1);
-       }
-
-       response = soup_soap_message_parse_response (SOUP_SOAP_MESSAGE (msg));
-       if (!response) {
-               fprintf (stderr, "Could not parse SOAP response\n");
-               exit (1);
-       }
-
-       param = soup_soap_response_get_first_parameter_by_name (response, "DefineResult");
-       if (!param) {
-               fprintf (stderr, "Could not find result in SOAP response\n");
-               exit (1);
-       }
-
-       param = soup_soap_parameter_get_first_child_by_name (param, "Definitions");
-       if (!param)
-               goto done;
-
-       for (param = soup_soap_parameter_get_first_child_by_name (param, "Definition");
-            param;
-            param = soup_soap_parameter_get_next_child_by_name (param, "Definition")) {
-               subparam = soup_soap_parameter_get_first_child_by_name (param, "Word");
-               if (!subparam)
-                       continue;
-               word = soup_soap_parameter_get_string_value (subparam);
-
-               subparam = soup_soap_parameter_get_first_child_by_name (param, "Dictionary");
-               if (subparam)
-                       subparam = soup_soap_parameter_get_first_child_by_name (subparam, "Name");
-               if (subparam)
-                       dict = soup_soap_parameter_get_string_value (subparam);
-               else
-                       dict = NULL;
-
-               printf ("% 2d. %s (%s):\n", ++count, word, dict);
-               g_free (word);
-               g_free (dict);
-
-               subparam = soup_soap_parameter_get_first_child_by_name (param, "WordDefinition");
-               if (subparam) {
-                       def = soup_soap_parameter_get_string_value (subparam);
-                       printf ("%s\n", def);
-                       g_free (def);
-               }
-       }
-
- done:
-       if (count == 0)
-               printf ("No definition\n");
-
-       g_object_unref (response);
-       g_main_quit (loop);
-}
-
-static void
-usage (void)
-{
-       fprintf (stderr, "Usage: dict [-p proxy_uri] WORD\n");
-       exit (1);
-}
-
-int
-main (int argc, char **argv)
-{
-       SoupUri *proxy = NULL;
-       SoupSoapMessage *msg;
-       int opt;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "p:")) != -1) {
-               switch (opt) {
-               case 'p':
-                       proxy = soup_uri_new (optarg);
-                       if (!proxy) {
-                               fprintf (stderr, "Could not parse %s as URI\n",
-                                        optarg);
-                               exit (1);
-                       }
-                       break;
-
-               case '?':
-                       usage ();
-                       break;
-               }
-       }
-       argc -= optind;
-       argv += optind;
-
-       if (argc != 1)
-               usage ();
-
-       session = soup_session_async_new_with_options (
-               SOUP_SESSION_PROXY_URI, proxy,
-               NULL);
-
-       msg = soup_soap_message_new ("POST",
-                                    "http://services.aonaware.com/DictService/DictService.asmx",
-                                    FALSE, NULL, NULL, NULL);
-       if (!msg) {
-               fprintf (stderr, "Could not create web service request\n");
-               exit (1);
-       }
-
-       soup_message_add_header (SOUP_MESSAGE (msg)->request_headers,
-                                "SOAPAction", "http://services.aonaware.com/webservices/Define");
-
-       soup_soap_message_start_envelope (msg);
-       soup_soap_message_start_body (msg);
-
-       soup_soap_message_start_element (msg, "Define", NULL,
-                                        "http://services.aonaware.com/webservices/");
-       soup_soap_message_add_namespace (msg, NULL, "http://services.aonaware.com/webservices/");
-       soup_soap_message_start_element (msg, "word", NULL, NULL);
-       soup_soap_message_write_string (msg, argv[0]);
-       soup_soap_message_end_element (msg);
-       soup_soap_message_end_element (msg);
-
-       soup_soap_message_end_body (msg);
-       soup_soap_message_end_envelope (msg);
-       soup_soap_message_persist (msg);
-
-       soup_session_queue_message (session, SOUP_MESSAGE (msg),
-                                   got_response, NULL);
-
-       loop = g_main_loop_new (NULL, TRUE);
-       g_main_run (loop);
-       g_main_loop_unref (loop);
-
-       return 0;
-}
index 755ff69..10d6b98 100644 (file)
@@ -51,7 +51,8 @@ main (int argc, char **argv)
                        exit (1);
                }
 
-               soup_address_resolve_async (addr, resolve_callback, NULL);
+               soup_address_resolve_async (addr, NULL, NULL,
+                                           resolve_callback, NULL);
                nlookups++;
        }
 
index 4c196df..fdb3ca1 100644 (file)
 SoupSession *session;
 GMainLoop *loop;
 gboolean recurse = FALSE, debug = FALSE;
-const char *method = SOUP_METHOD_GET;
+const char *method;
 char *base;
-SoupUri *base_uri;
+SoupURI *base_uri;
 int pending;
 GHashTable *fetched_urls;
 
 static GPtrArray *
-find_hrefs (const SoupUri *base, const char *body, int length)
+find_hrefs (SoupURI *base, const char *body, int length)
 {
        GPtrArray *hrefs = g_ptr_array_new ();
        char *buf = g_strndup (body, length);
        char *start = buf, *end;
        char *href, *frag;
-       SoupUri *uri;
+       SoupURI *uri;
 
        while ((start = strstr (start, "href"))) {
                start += 4;
@@ -63,7 +63,7 @@ find_hrefs (const SoupUri *base, const char *body, int length)
 
                if (!uri)
                        continue;
-               if (base->protocol != uri->protocol ||
+               if (base->scheme != uri->scheme ||
                    base->port != uri->port ||
                    g_ascii_strcasecmp (base->host, uri->host) != 0) {
                        soup_uri_free (uri);
@@ -100,9 +100,9 @@ mkdirs (const char *path)
 }
 
 static void
-print_header (gpointer name, gpointer value, gpointer data)
+print_header (const char *name, const char *value, gpointer data)
 {
-       printf ("%s: %s\n", (const char *)name, (const char *)value);
+       printf ("%s: %s\n", name, value);
 }
 
 static void
@@ -111,7 +111,7 @@ get_url (const char *url)
        char *url_to_get, *slash, *name;
        SoupMessage *msg;
        int fd, i;
-       SoupUri *uri;
+       SoupURI *uri;
        GPtrArray *hrefs;
        const char *header;
 
@@ -164,7 +164,7 @@ get_url (const char *url)
                printf ("HTTP/1.%d %d %s\n",
                        soup_message_get_http_version (msg),
                        msg->status_code, msg->reason_phrase);
-               soup_message_foreach_header (msg->response_headers, print_header, NULL);
+               soup_message_headers_foreach (msg->response_headers, print_header, NULL);
                printf ("\n");
        } else
                printf ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
@@ -176,7 +176,7 @@ get_url (const char *url)
        if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
                if (recurse)
                        unlink (name);
-               header = soup_message_get_header (msg->response_headers, "Location");
+               header = soup_message_headers_get (msg->response_headers, "Location");
                if (header) {
                        if (!debug)
                                printf ("  -> %s\n", header);
@@ -192,17 +192,17 @@ get_url (const char *url)
                fd = open (name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        else
                fd = STDOUT_FILENO;
-       write (fd, msg->response.body, msg->response.length);
+       write (fd, msg->response_body->data, msg->response_body->length);
        if (!recurse)
                return;
        close (fd);
 
-       header = soup_message_get_header (msg->response_headers, "Content-Type");
+       header = soup_message_headers_get (msg->response_headers, "Content-Type");
        if (header && g_ascii_strncasecmp (header, "text/html", 9) != 0)
                return;
 
        uri = soup_uri_new (url);
-       hrefs = find_hrefs (uri, msg->response.body, msg->response.length);
+       hrefs = find_hrefs (uri, msg->response_body->data, msg->response_body->length);
        soup_uri_free (uri);
        for (i = 0; i < hrefs->len; i++) {
                get_url (hrefs->pdata[i]);
@@ -222,13 +222,15 @@ int
 main (int argc, char **argv)
 {
        const char *cafile = NULL;
-       SoupUri *proxy = NULL;
+       SoupURI *proxy = NULL;
        gboolean synchronous = FALSE;
        int opt;
 
        g_type_init ();
        g_thread_init (NULL);
 
+       method = SOUP_METHOD_GET;
+
        while ((opt = getopt (argc, argv, "c:dhp:rs")) != -1) {
                switch (opt) {
                case 'c':
index e50f6e0..5cef372 100644 (file)
 #include <unistd.h>
 
 #include <libsoup/soup.h>
-#include <libsoup/soup-xmlrpc-message.h>
-#include <libsoup/soup-xmlrpc-response.h>
 
-SoupSession *session;
 GMainLoop *loop;
 
 static void
+print_value (GValue *value)
+{
+       if (G_VALUE_HOLDS_STRING (value))
+               printf ("%s", g_value_get_string (value));
+       else if (G_VALUE_HOLDS_INT (value))
+               printf ("%d", g_value_get_int (value));
+       else if (G_VALUE_HOLDS_DOUBLE (value))
+               printf ("%f", g_value_get_double (value));
+       else if (G_VALUE_TYPE (value) == G_TYPE_VALUE_ARRAY) {
+               GValueArray *array = g_value_get_boxed (value);
+               int i;
+               printf ("[ ");
+               for (i = 0; i < array->n_values; i++) {
+                       if (i != 0)
+                               printf (", ");
+                       print_value (&array->values[i]);
+               }
+               printf (" ]");
+       } else
+               printf ("(%s)", g_type_name (G_VALUE_TYPE (value)));
+}
+
+static void
 print_struct_field (gpointer key, gpointer value, gpointer data)
 {
-       char *str;
-       if (soup_xmlrpc_value_get_string (value, &str))
-               printf ("%s: %s\n", (char *)key, str);
+       printf ("%s: ", (char *)key);
+       print_value (value);
+       printf ("\n");
 }
 
 static void
-got_response (SoupMessage *msg, gpointer user_data)
+got_response (SoupSession *session, SoupMessage *msg, gpointer user_data)
 {
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
        GHashTable *hash;
+       GError *error = NULL;
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
                fprintf (stderr, "%d %s\n", msg->status_code, msg->reason_phrase);
                exit (1);
        }
 
-       response = soup_xmlrpc_message_parse_response (SOUP_XMLRPC_MESSAGE (msg));
-       if (!response) {
-               fprintf (stderr, "Could not parse XMLRPC response\n");
-               exit (1);
-       }
-
-       value = soup_xmlrpc_response_get_value (response);
-       if (!value) {
-               fprintf (stderr, "No response value in XMLRPC response\n");
-               exit (1);
-       }
-
-       if (!soup_xmlrpc_value_get_struct (value, &hash)) {
-               fprintf (stderr, "Could not extract result from XMLRPC response\n");
+       if (!soup_xmlrpc_extract_method_response (msg->response_body->data,
+                                                 msg->response_body->length,
+                                                 &error,
+                                                 G_TYPE_HASH_TABLE, &hash)) {
+               if (!error) {
+                       fprintf (stderr, "Could not parse XMLRPC response:\n%d %s\n\n",
+                                msg->status_code, msg->reason_phrase);
+                       fprintf (stderr, "%s\n", msg->response_body->data);
+               } else {
+                       fprintf (stderr, "XML-RPC error: %d %s",
+                                error->code, error->message);
+               }
                exit (1);
        }
 
        g_hash_table_foreach (hash, print_struct_field, NULL);
        g_hash_table_destroy (hash);
 
-       g_object_unref (response);
        g_main_quit (loop);
 }
 
@@ -73,8 +89,9 @@ usage (void)
 int
 main (int argc, char **argv)
 {
-       SoupUri *proxy = NULL;
-       SoupXmlrpcMessage *msg;
+       SoupSession *session;
+       SoupURI *proxy = NULL;
+       SoupMessage *msg;
        const char *uri = "http://bugzilla.redhat.com/bugzilla/xmlrpc.cgi";
        int opt, bug;
 
@@ -113,19 +130,13 @@ main (int argc, char **argv)
                SOUP_SESSION_PROXY_URI, proxy,
                NULL);
 
-       msg = soup_xmlrpc_message_new (uri);
+       msg = soup_xmlrpc_request_new (uri, "bugzilla.getBug",
+                                      G_TYPE_INT, bug,
+                                      G_TYPE_INVALID);
        if (!msg) {
                fprintf (stderr, "Could not create web service request to '%s'\n", uri);
                exit (1);
        }
-
-       soup_xmlrpc_message_start_call (msg, "bugzilla.getBug");
-       soup_xmlrpc_message_start_param (msg);
-       soup_xmlrpc_message_write_int (msg, bug);
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
-
-       soup_xmlrpc_message_persist (msg);
        soup_session_queue_message (session, SOUP_MESSAGE (msg),
                                    got_response, NULL);
 
index c6b61cd..63fcae3 100644 (file)
@@ -7,30 +7,20 @@
 #include "libsoup/soup-message.h"
 #include "libsoup/soup-headers.h"
 
-gboolean debug = FALSE;
+#include "test-utils.h"
 
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
-
-       if (!debug)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+typedef struct {
+       char *name, *value;
+} Header;
 
 struct RequestTest {
        char *description;
        char *request;
        int length;
+       guint status;
        char *method, *path;
-       SoupHttpVersion version;
-       struct {
-               char *name, *value;
-       } headers[4];
+       SoupHTTPVersion version;
+       Header headers[4];
 } reqtests[] = {
        /**********************/
        /*** VALID REQUESTS ***/
@@ -38,12 +28,14 @@ struct RequestTest {
 
        { "HTTP 1.0 request with no headers",
          "GET / HTTP/1.0\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_0,
          { { NULL } }
        },
 
        { "Req w/ 1 header",
          "GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -52,6 +44,7 @@ struct RequestTest {
 
        { "Req w/ 1 header, no leading whitespace",
          "GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -60,6 +53,7 @@ struct RequestTest {
 
        { "Req w/ 1 header including trailing whitespace",
          "GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -68,6 +62,7 @@ struct RequestTest {
 
        { "Req w/ 1 header, wrapped",
          "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Foo", "bar baz" },
            { NULL }
@@ -76,6 +71,7 @@ struct RequestTest {
 
        { "Req w/ 1 header, wrapped with additional whitespace",
          "GET / HTTP/1.1\r\nFoo: bar \r\n  baz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Foo", "bar baz" },
            { NULL }
@@ -84,6 +80,7 @@ struct RequestTest {
 
        { "Req w/ 1 header, wrapped with tab",
          "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Foo", "bar baz" },
            { NULL }
@@ -92,6 +89,7 @@ struct RequestTest {
 
        { "Req w/ 1 header, wrapped before value",
          "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Foo", "bar baz" },
            { NULL }
@@ -100,6 +98,7 @@ struct RequestTest {
 
        { "Req w/ 1 header with empty value",
          "GET / HTTP/1.1\r\nHost:\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "" },
            { NULL }
@@ -108,6 +107,7 @@ struct RequestTest {
 
        { "Req w/ 2 headers",
          "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { "Connection", "close" },
@@ -117,6 +117,7 @@ struct RequestTest {
 
        { "Req w/ 3 headers",
          "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { "Connection", "close" },
@@ -127,6 +128,7 @@ struct RequestTest {
 
        { "Req w/ 3 headers, 1st wrapped",
          "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Foo", "bar baz" },
            { "Connection", "close" },
@@ -137,16 +139,18 @@ struct RequestTest {
 
        { "Req w/ 3 headers, 2nd wrapped",
          "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Connection", "close" },
-           { "Foo", "bar baz" },
            { "Blah", "blah" },
+           { "Foo", "bar baz" },
            { NULL }
          }
        },
 
        { "Req w/ 3 headers, 3rd wrapped",
          "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Connection", "close" },
            { "Blah", "blah" },
@@ -155,6 +159,15 @@ struct RequestTest {
          }
        },
 
+       { "Req w/ same header multiple times",
+         "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
+         SOUP_STATUS_OK,
+         "GET", "/", SOUP_HTTP_1_1,
+         { { "Foo", "bar, baz, quux" },
+           { NULL }
+         }
+       },
+
        /****************************/
        /*** RECOVERABLE REQUESTS ***/
        /****************************/
@@ -163,6 +176,18 @@ struct RequestTest {
 
        { "Spurious leading CRLF",
          "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
+         "GET", "/", SOUP_HTTP_1_1,
+         { { "Host", "example.com" },
+           { NULL }
+         }
+       },
+
+       /* RFC 2616 section 3.1 says we MUST accept this */
+
+       { "HTTP/01.01 request",
+         "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -173,6 +198,7 @@ struct RequestTest {
 
        { "LF instead of CRLF after header",
          "GET / HTTP/1.1\nHost: example.com\nConnection: close\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { "Connection", "close" },
@@ -182,6 +208,7 @@ struct RequestTest {
 
        { "LF instead of CRLF after Request-Line",
          "GET / HTTP/1.1\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -190,6 +217,7 @@ struct RequestTest {
 
        { "Req w/ incorrect whitespace in Request-Line",
          "GET  /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -198,6 +226,7 @@ struct RequestTest {
 
        { "Req w/ incorrect whitespace after Request-Line",
          "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_OK,
          "GET", "/", SOUP_HTTP_1_1,
          { { "Host", "example.com" },
            { NULL }
@@ -210,72 +239,91 @@ struct RequestTest {
 
        { "HTTP 0.9 request; not supported",
          "GET /\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
-       { "HTTP 1.2 request; not supported (no such thing)",
+       { "HTTP 1.2 request (no such thing)",
          "GET / HTTP/1.2\r\n", -1,
+         SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+         NULL, NULL, -1,
+         { { NULL } }
+       },
+
+       { "HTTP 2000 request (no such thing)",
+         "GET / HTTP/2000.0\r\n", -1,
+         SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Non-HTTP request",
          "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Junk after Request-Line",
          "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "NUL in Method",
          "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "NUL in Path",
          "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "NUL in Header",
          "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Header line with no ':'",
          "GET / HTTP/1.1\r\nHost example.com\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "No terminating CRLF",
          "GET / HTTP/1.1\r\nHost: example.com", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Blank line before headers",
          "GET / HTTP/1.1\r\n\r\nHost: example.com\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Blank line in headers",
          "GET / HTTP/1.1\r\nHost: example.com\r\n\r\nConnection: close\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
 
        { "Blank line after headers",
          "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n", -1,
+         SOUP_STATUS_BAD_REQUEST,
          NULL, NULL, -1,
          { { NULL } }
        },
@@ -287,12 +335,10 @@ struct ResponseTest {
        char *description;
        char *response;
        int length;
-       SoupHttpVersion version;
+       SoupHTTPVersion version;
        guint status_code;
        char *reason_phrase;
-       struct {
-               char *name, *value;
-       } headers[4];
+       Header headers[4];
 } resptests[] = {
        /***********************/
        /*** VALID RESPONSES ***/
@@ -333,6 +379,14 @@ struct ResponseTest {
          }
        },
 
+       { "Response w/ same header multiple times",
+         "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
+         SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+         { { "Foo", "bar, baz, quux" },
+           { NULL }
+         }
+       },
+
        { "Response w/ no reason phrase",
          "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
          SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
@@ -345,6 +399,16 @@ struct ResponseTest {
        /*** RECOVERABLE RESPONSES ***/
        /*****************************/
 
+       /* RFC 2616 section 3.1 says we MUST accept this */
+
+       { "HTTP/01.01 response",
+         "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
+         SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+         { { "Foo", "bar" },
+           { NULL }
+         }
+       },
+
        /* RFC 2616 section 19.3 says we SHOULD accept these */
 
        { "Response w/ LF instead of CRLF after Status-Line",
@@ -451,53 +515,82 @@ struct ResponseTest {
 };
 static const int num_resptests = G_N_ELEMENTS (resptests);
 
+struct QValueTest {
+       char *header_value;
+       char *acceptable[7];
+       char *unacceptable[2];
+} qvaluetests[] = {
+       { "text/plain; q=0.5, text/html,\t  text/x-dvi; q=0.8, text/x-c",
+         { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
+         { NULL },
+       },
+
+       { "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
+         { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
+           "text/*", NULL },
+         { NULL }
+       },
+
+       { "gzip;q=1.0, identity; q=0.5, *;q=0",
+         { "gzip", "identity", NULL },
+         { "*", NULL },
+       }
+};
+static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
+
 static void
-print_header (gpointer key, gpointer value, gpointer data)
+print_header (const char *name, const char *value, gpointer data)
 {
-       GSList *values = value;
-       dprintf ("              '%s': '%s'\n",
-                (char *)key, (char*)values->data);
+       debug_printf (1, "              '%s': '%s'\n", name, value);
 }
 
-static void
-free_headers (gpointer value)
+typedef struct {
+       Header *headers;
+       int i;
+       gboolean ok;
+} HeaderForeachData;
+
+static gboolean
+check_headers (Header *headers, SoupMessageHeaders *hdrs)
 {
-       GSList *headers = value;
+       int i;
+       const char *value;
 
-       /* We know that there are no duplicate headers in any of the
-        * test cases, so...
-        */
-       g_free (headers->data);
-       g_slist_free (headers);
+       for (i = 0; headers[i].name; i++) {
+               value = soup_message_headers_get (hdrs, headers[i].name);
+               if (strcmp (value, headers[i].value) != 0)
+                       return FALSE;
+       }
+       return TRUE;
 }
 
-static int
+static void
 do_request_tests (void)
 {
-       int i, len, h, errors = 0;
+       int i, len, h;
        char *method, *path;
-       GSList *values;
-       SoupHttpVersion version;
-       GHashTable *headers;
+       SoupHTTPVersion version;
+       SoupMessageHeaders *headers;
+       guint status;
 
-       dprintf ("Request tests\n");
-       for (i = 0; i < 1; i++) {
+       debug_printf (1, "Request tests\n");
+       for (i = 0; i < num_reqtests; i++) {
                gboolean ok = TRUE;
 
-               dprintf ("%2d. %s (%s): ", i + 1, reqtests[i].description,
-                        reqtests[i].method ? "should parse" : "should NOT parse");
+               debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
+                             soup_status_get_phrase (reqtests[i].status));
 
-               headers = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                g_free, free_headers);
+               headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
                method = path = NULL;
 
                if (reqtests[i].length == -1)
                        len = strlen (reqtests[i].request);
                else
                        len = reqtests[i].length;
-               if (soup_headers_parse_request (reqtests[i].request, len,
-                                               headers, &method, &path,
-                                               &version)) {
+               status = soup_headers_parse_request (reqtests[i].request, len,
+                                                    headers, &method, &path,
+                                                    &version);
+               if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                        if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
                                ok = FALSE;
                        if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
@@ -505,71 +598,66 @@ do_request_tests (void)
                        if (reqtests[i].version != version)
                                ok = FALSE;
 
-                       for (h = 0; reqtests[i].headers[h].name; h++) {
-                               values = g_hash_table_lookup (headers, reqtests[i].headers[h].name);
-                               if (!values || values->next ||
-                                   strcmp (reqtests[i].headers[h].value, values->data) != 0)
-                                       ok = FALSE;
-                       }
-                       if (g_hash_table_size (headers) != h)
+                       if (!check_headers (reqtests[i].headers, headers))
                                ok = FALSE;
                } else {
-                       if (reqtests[i].method)
+                       if (status != reqtests[i].status)
                                ok = FALSE;
                }
 
                if (ok)
-                       dprintf ("OK!\n");
+                       debug_printf (1, "OK!\n");
                else {
-                       dprintf ("BAD!\n");
+                       debug_printf (1, "BAD!\n");
                        errors++;
                        if (reqtests[i].method) {
-                               dprintf ("    expected: '%s' '%s' 'HTTP/1.%d'\n",
-                                        reqtests[i].method, reqtests[i].path,
-                                        reqtests[i].version);
+                               debug_printf (1, "    expected: '%s' '%s' 'HTTP/1.%d'\n",
+                                             reqtests[i].method,
+                                             reqtests[i].path,
+                                             reqtests[i].version);
                                for (h = 0; reqtests[i].headers[h].name; h++) {
-                                       dprintf ("              '%s': '%s'\n",
-                                                reqtests[i].headers[h].name,
-                                                reqtests[i].headers[h].value);
+                                       debug_printf (1, "              '%s': '%s'\n",
+                                                     reqtests[i].headers[h].name,
+                                                     reqtests[i].headers[h].value);
                                }
-                       } else
-                               dprintf ("    expected: parse error\n");
+                       } else {
+                               debug_printf (1, "    expected: %s\n",
+                                             soup_status_get_phrase (reqtests[i].status));
+                       }
                        if (method) {
-                               dprintf ("         got: '%s' '%s' 'HTTP/1.%d'\n",
-                                       method, path, version);
-                               g_hash_table_foreach (headers, print_header, NULL);
-                       } else
-                               dprintf ("         got: parse error\n");
+                               debug_printf (1, "         got: '%s' '%s' 'HTTP/1.%d'\n",
+                                             method, path, version);
+                               soup_message_headers_foreach (headers, print_header, NULL);
+                       } else {
+                               debug_printf (1, "         got: %s\n",
+                                             soup_status_get_phrase (status));
+                       }
                }
 
                g_free (method);
                g_free (path);
-               g_hash_table_destroy (headers);
+               soup_message_headers_free (headers);
        }
-       dprintf ("\n");
-
-       return errors;
+       debug_printf (1, "\n");
 }
 
-static int
+static void
 do_response_tests (void)
 {
-       int i, len, h, errors = 0;
+       int i, len, h;
        guint status_code;
        char *reason_phrase;
-       GSList *values;
-       SoupHttpVersion version;
-       GHashTable *headers;
+       SoupHTTPVersion version;
+       SoupMessageHeaders *headers;
 
-       dprintf ("Response tests\n");
+       debug_printf (1, "Response tests\n");
        for (i = 0; i < num_resptests; i++) {
                gboolean ok = TRUE;
 
-               dprintf ("%2d. %s (%s): ", i + 1, resptests[i].description,
-                        resptests[i].reason_phrase ? "should parse" : "should NOT parse");
+               debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
+                             resptests[i].reason_phrase ? "should parse" : "should NOT parse");
 
-               headers = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                g_free, free_headers);
+               headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
                reason_phrase = NULL;
 
                if (resptests[i].length == -1)
@@ -586,13 +674,7 @@ do_response_tests (void)
                        if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
                                ok = FALSE;
 
-                       for (h = 0; resptests[i].headers[h].name; h++) {
-                               values = g_hash_table_lookup (headers, resptests[i].headers[h].name);
-                               if (!values || values->next ||
-                                   strcmp (resptests[i].headers[h].value, values->data) != 0)
-                                       ok = FALSE;
-                       }
-                       if (g_hash_table_size (headers) != h)
+                       if (!check_headers (resptests[i].headers, headers))
                                ok = FALSE;
                } else {
                        if (resptests[i].reason_phrase)
@@ -600,62 +682,104 @@ do_response_tests (void)
                }
 
                if (ok)
-                       dprintf ("OK!\n");
+                       debug_printf (1, "OK!\n");
                else {
-                       dprintf ("BAD!\n");
+                       debug_printf (1, "BAD!\n");
                        errors++;
                        if (resptests[i].reason_phrase) {
-                               dprintf ("    expected: 'HTTP/1.%d' '%03d' '%s'\n",
-                                        resptests[i].version,
-                                        resptests[i].status_code,
-                                        resptests[i].reason_phrase);
+                               debug_printf (1, "    expected: 'HTTP/1.%d' '%03d' '%s'\n",
+                                             resptests[i].version,
+                                             resptests[i].status_code,
+                                             resptests[i].reason_phrase);
                                for (h = 0; resptests[i].headers[h].name; h++) {
-                                       dprintf ("              '%s': '%s'\n",
-                                                resptests[i].headers[h].name,
-                                                resptests[i].headers[h].value);
+                                       debug_printf (1, "              '%s': '%s'\n",
+                                                     resptests[i].headers[h].name,
+                                                     resptests[i].headers[h].value);
                                }
                        } else
-                               dprintf ("    expected: parse error\n");
+                               debug_printf (1, "    expected: parse error\n");
                        if (reason_phrase) {
-                               dprintf ("         got: 'HTTP/1.%d' '%03d' '%s'\n",
-                                        version, status_code, reason_phrase);
-                               g_hash_table_foreach (headers, print_header, NULL);
+                               debug_printf (1, "         got: 'HTTP/1.%d' '%03d' '%s'\n",
+                                             version, status_code, reason_phrase);
+                               soup_message_headers_foreach (headers, print_header, NULL);
                        } else
-                               dprintf ("         got: parse error\n");
+                               debug_printf (1, "         got: parse error\n");
                }
 
                g_free (reason_phrase);
-               g_hash_table_destroy (headers);
+               soup_message_headers_free (headers);
        }
-       dprintf ("\n");
+       debug_printf (1, "\n");
+}
 
-       return errors;
+static void
+do_qvalue_tests (void)
+{
+       int i, j;
+       GSList *acceptable, *unacceptable, *iter;
+       gboolean wrong;
+
+       debug_printf (1, "qvalue tests\n");
+       for (i = 0; i < num_qvaluetests; i++) {
+               debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
+
+               unacceptable = NULL;
+               acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
+                                                            &unacceptable);
+
+               debug_printf (1, "    acceptable: ");
+               wrong = FALSE;
+               if (acceptable) {
+                       for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
+                               debug_printf (1, "%s ", iter->data);
+                               if (!qvaluetests[i].acceptable[j] ||
+                                   strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
+                                       wrong = TRUE;
+                       }
+                       debug_printf (1, "\n");
+               } else
+                       debug_printf (1, "(none)\n");
+               if (wrong) {
+                       debug_printf (1, "    WRONG! expected: ");
+                       for (j = 0; qvaluetests[i].acceptable[j]; j++)
+                               debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
+                       debug_printf (1, "\n");
+                       errors++;
+               }
+
+               debug_printf (1, "  unacceptable: ");
+               wrong = FALSE;
+               if (unacceptable) {
+                       for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
+                               debug_printf (1, "%s ", iter->data);
+                               if (!qvaluetests[i].unacceptable[j] ||
+                                   strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
+                                       wrong = TRUE;
+                       }
+                       debug_printf (1, "\n");
+               } else
+                       debug_printf (1, "(none)\n");
+               if (wrong) {
+                       debug_printf (1, "    WRONG! expected: ");
+                       for (j = 0; qvaluetests[i].unacceptable[j]; j++)
+                               debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
+                       debug_printf (1, "\n");
+                       errors++;
+               }
+
+               debug_printf (1, "\n");
+       }
 }
 
 int
 main (int argc, char **argv)
 {
-       int opt, errors;
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n", argv[0]);
-                       return 1;
-               }
-       }
+       test_init (argc, argv, NULL);
 
-       errors = do_request_tests ();
-       errors += do_response_tests ();
+       do_request_tests ();
+       do_response_tests ();
+       do_qvalue_tests ();
 
-       dprintf ("\n");
-       if (errors) {
-               printf ("header-parsing: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("header-parsing: OK\n");
-       return errors;
+       test_cleanup ();
+       return errors != 0;
 }
index ca9ebdf..c2e5881 100644 (file)
@@ -217,6 +217,7 @@ Alias /Basic @srcdir@
 Alias /Digest/realm1/realm2/realm1 @srcdir@
 Alias /Digest/realm1/realm2 @srcdir@
 Alias /Digest/realm1/subdir @srcdir@
+Alias /Digest/realm1/expire @srcdir@
 Alias /Digest/realm1/not @srcdir@
 Alias /Digest/realm1 @srcdir@
 Alias /Digest/realm2 @srcdir@
@@ -231,6 +232,15 @@ Alias /Digest @srcdir@
   Require valid-user
 </Location>
 
+<Location /Digest/realm1/expire>
+  AuthType Digest
+  AuthName realm1
+  AuthUserFile @srcdir@/htdigest
+  AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
+  AuthDigestNonceLifetime 2
+  Require valid-user
+</Location>
+
 <Location /Digest/realm1/not>
   AuthType Digest
   AuthName realm1
index 30e01da..5a4515b 100644 (file)
 
 #include <glib.h>
 #include <libsoup/soup-address.h>
+#include <libsoup/soup-auth.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
-#include <libsoup/soup-server-message.h>
 #include <libsoup/soup-session-async.h>
 
-gboolean debug = FALSE;
+#include "test-utils.h"
 
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
-
-       if (!debug)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+GHashTable *connections;
 
 typedef enum {
        NTLM_UNAUTHENTICATED,
@@ -57,20 +46,20 @@ typedef enum {
 #define NTLM_RESPONSE_USER(response) ((response)[87] == 'h' ? NTLM_AUTHENTICATED_ALICE : NTLM_AUTHENTICATED_BOB)
 
 static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *client, gpointer data)
 {
        GHashTable *connections = data;
        const char *auth;
-       char *path;
        NTLMServerState state, required_user;
        gboolean not_found = FALSE;
 
-       if (soup_method_get_id (msg->method) != SOUP_METHOD_ID_GET) {
+       if (msg->method != SOUP_METHOD_GET) {
                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
                return;
        }
 
-       path = soup_uri_to_string (soup_message_get_uri (msg), TRUE);
        if (!strcmp (path, "/noauth"))
                required_user = 0;
        else if (!strncmp (path, "/alice", 6))
@@ -79,10 +68,9 @@ server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
                required_user = NTLM_AUTHENTICATED_BOB;
        if (strstr (path, "/404"))
                not_found = TRUE;
-       g_free (path);
 
-       state = GPOINTER_TO_INT (g_hash_table_lookup (connections, context->sock));
-       auth = soup_message_get_header (msg->request_headers, "Authorization");
+       state = GPOINTER_TO_INT (g_hash_table_lookup (connections, soup_client_context_get_socket (client)));
+       auth = soup_message_headers_get (msg->request_headers, "Authorization");
 
        if (auth && !strncmp (auth, "NTLM ", 5)) {
                if (!strncmp (auth + 5, NTLM_REQUEST_START,
@@ -98,39 +86,36 @@ server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
 
        if (state == NTLM_RECEIVED_REQUEST) {
                soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
-               soup_message_add_header (msg->response_headers,
-                                        "WWW-Authenticate", "NTLM " NTLM_CHALLENGE);
+               soup_message_headers_append (msg->response_headers,
+                                            "WWW-Authenticate",
+                                            "NTLM " NTLM_CHALLENGE);
                state = NTLM_SENT_CHALLENGE;
        } else if (!required_user || required_user == state) {
                if (not_found)
                        soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
                else {
                        soup_message_set_response (msg, "text/plain",
-                                                  SOUP_BUFFER_STATIC,
+                                                  SOUP_MEMORY_STATIC,
                                                   "OK\r\n", 4);
                        soup_message_set_status (msg, SOUP_STATUS_OK);
                }
        } else {
                soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
-               soup_message_add_header (msg->response_headers,
-                                        "WWW-Authenticate", "NTLM");
-               soup_message_add_header (msg->response_headers,
-                                        "Connection", "close");
+               soup_message_headers_append (msg->response_headers,
+                                            "WWW-Authenticate", "NTLM");
+               soup_message_headers_append (msg->response_headers,
+                                            "Connection", "close");
        }
 
-       g_hash_table_insert (connections, context->sock,
+       g_hash_table_insert (connections, soup_client_context_get_socket (client),
                             GINT_TO_POINTER (state));
 }
 
 static void
 authenticate (SoupSession *session, SoupMessage *msg,
-             const char *auth_type, const char *auth_realm,
-             char **username, char **password, gpointer data)
+             SoupAuth *auth, gboolean retrying, gpointer user)
 {
-       const char *user = data;
-
-       *username = g_strdup (user);
-       *password = g_strdup ("password");
+       soup_auth_authenticate (auth, user, "password");
 }
 
 typedef struct {
@@ -148,8 +133,8 @@ ntlm_prompt_check (SoupMessage *msg, gpointer user_data)
 
        if (state->sent_request)
                return;
-       header = soup_message_get_header (msg->response_headers,
-                                         "WWW-Authenticate");
+       header = soup_message_headers_get (msg->response_headers,
+                                           "WWW-Authenticate");
        if (header && !strcmp (header, "NTLM"))
                state->got_prompt = TRUE;
 }
@@ -160,8 +145,8 @@ ntlm_challenge_check (SoupMessage *msg, gpointer user_data)
        NTLMState *state = user_data;
        const char *header;
 
-       header = soup_message_get_header (msg->response_headers,
-                                         "WWW-Authenticate");
+       header = soup_message_headers_get (msg->response_headers,
+                                           "WWW-Authenticate");
        if (header && !strncmp (header, "NTLM ", 5))
                state->got_challenge = TRUE;
 }
@@ -172,8 +157,8 @@ ntlm_request_check (SoupMessage *msg, gpointer user_data)
        NTLMState *state = user_data;
        const char *header;
 
-       header = soup_message_get_header (msg->request_headers,
-                                         "Authorization");
+       header = soup_message_headers_get (msg->request_headers,
+                                           "Authorization");
        if (header && !strncmp (header, "NTLM " NTLM_REQUEST_START,
                                strlen ("NTLM " NTLM_REQUEST_START)))
                state->sent_request = TRUE;
@@ -185,163 +170,144 @@ ntlm_response_check (SoupMessage *msg, gpointer user_data)
        NTLMState *state = user_data;
        const char *header;
 
-       header = soup_message_get_header (msg->request_headers,
-                                         "Authorization");
+       header = soup_message_headers_get (msg->request_headers,
+                                           "Authorization");
        if (header && !strncmp (header, "NTLM " NTLM_RESPONSE_START,
                                strlen ("NTLM " NTLM_RESPONSE_START)))
                state->sent_response = TRUE;
 }
 
-static int
-do_message (SoupSession *session, SoupUri *base_uri, const char *path,
+static void
+do_message (SoupSession *session, SoupURI *base_uri, const char *path,
            gboolean get_prompt, gboolean do_ntlm, guint status_code)
 {
-       SoupUri *uri;
+       SoupURI *uri;
        SoupMessage *msg;
        NTLMState state = { FALSE, FALSE, FALSE, FALSE };
-       int errors = 0;
 
-       uri = soup_uri_copy (base_uri);
-       g_free (uri->path);
-       uri->path = g_strdup (path);
+       uri = soup_uri_new_with_base (base_uri, path);
        msg = soup_message_new_from_uri ("GET", uri);
        soup_uri_free (uri);
 
-       soup_message_add_header_handler (msg, "WWW-Authenticate",
-                                        SOUP_HANDLER_PRE_BODY,
-                                        ntlm_prompt_check, &state);
-       soup_message_add_header_handler (msg, "WWW-Authenticate",
-                                        SOUP_HANDLER_PRE_BODY,
-                                        ntlm_challenge_check, &state);
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (ntlm_prompt_check), &state);
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (ntlm_challenge_check), &state);
        g_signal_connect (msg, "wrote-headers",
                          G_CALLBACK (ntlm_request_check), &state);
        g_signal_connect (msg, "wrote-headers",
                          G_CALLBACK (ntlm_response_check), &state);
 
        soup_session_send_message (session, msg);
-       dprintf ("  %-10s -> ", path);
+       debug_printf (1, "  %-10s -> ", path);
 
        if (state.got_prompt) {
-               dprintf (" PROMPT");
+               debug_printf (1, " PROMPT");
                if (!get_prompt) {
-                       dprintf ("???");
+                       debug_printf (1, "???");
                        errors++;
                }
        } else if (get_prompt) {
-               dprintf (" no-prompt???");
+               debug_printf (1, " no-prompt???");
                errors++;
        }
 
        if (state.sent_request) {
-               dprintf (" REQUEST");
+               debug_printf (1, " REQUEST");
                if (!do_ntlm) {
-                       dprintf ("???");
+                       debug_printf (1, "???");
                        errors++;
                }
        } else if (do_ntlm) {
-               dprintf (" no-request???");
+               debug_printf (1, " no-request???");
                errors++;
        }
 
        if (state.got_challenge) {
-               dprintf (" CHALLENGE");
+               debug_printf (1, " CHALLENGE");
                if (!do_ntlm) {
-                       dprintf ("???");
+                       debug_printf (1, "???");
                        errors++;
                }
        } else if (do_ntlm) {
-               dprintf (" no-challenge???");
+               debug_printf (1, " no-challenge???");
                errors++;
        }
 
        if (state.sent_response) {
-               dprintf (" RESPONSE");
+               debug_printf (1, " RESPONSE");
                if (!do_ntlm) {
-                       dprintf ("???");
+                       debug_printf (1, "???");
                        errors++;
                }
        } else if (do_ntlm) {
-               dprintf (" no-response???");
+               debug_printf (1, " no-response???");
                errors++;
        }
 
-       dprintf (" -> %s", msg->reason_phrase);
+       debug_printf (1, " -> %s", msg->reason_phrase);
        if (msg->status_code != status_code) {
-               dprintf ("???");
+               debug_printf (1, "???");
                errors++;
        }
-       dprintf ("\n");
+       debug_printf (1, "\n");
 
        g_object_unref (msg);
-       return errors;
 }
 
-static int
-do_ntlm_round (SoupUri *base_uri, const char *user)
+static void
+do_ntlm_round (SoupURI *base_uri, const char *user)
 {
        SoupSession *session;
-       int errors = 0;
        gboolean use_ntlm = user != NULL;
        gboolean alice = use_ntlm && !strcmp (user, "alice");
        gboolean bob = use_ntlm && !strcmp (user, "bob");
 
-       g_return_val_if_fail (use_ntlm || !alice, 0);
+       g_return_if_fail (use_ntlm || !alice);
 
-       session = soup_session_async_new_with_options (
+       session = soup_test_session_new (
+               SOUP_TYPE_SESSION_ASYNC,
                SOUP_SESSION_USE_NTLM, use_ntlm,
                NULL);
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), (char *)user);
 
-       errors += do_message (session, base_uri, "/noauth",
-                             FALSE, use_ntlm, SOUP_STATUS_OK);
-       errors += do_message (session, base_uri, "/alice",
-                             !use_ntlm || bob, FALSE,
-                             alice ? SOUP_STATUS_OK :
-                             SOUP_STATUS_UNAUTHORIZED);
-       errors += do_message (session, base_uri, "/alice/404",
-                             !use_ntlm, bob,
-                             alice ? SOUP_STATUS_NOT_FOUND :
-                             SOUP_STATUS_UNAUTHORIZED);
-       errors += do_message (session, base_uri, "/alice",
-                             !use_ntlm, bob,
-                             alice ? SOUP_STATUS_OK :
-                             SOUP_STATUS_UNAUTHORIZED);
-       errors += do_message (session, base_uri, "/bob",
-                             !use_ntlm || alice, bob,
-                             bob ? SOUP_STATUS_OK :
-                             SOUP_STATUS_UNAUTHORIZED);
-       errors += do_message (session, base_uri, "/alice",
-                             !use_ntlm || bob, alice,
-                             alice ? SOUP_STATUS_OK :
-                             SOUP_STATUS_UNAUTHORIZED);
+       do_message (session, base_uri, "/noauth",
+                   FALSE, use_ntlm, SOUP_STATUS_OK);
+       do_message (session, base_uri, "/alice",
+                   !use_ntlm || bob, FALSE,
+                   alice ? SOUP_STATUS_OK :
+                   SOUP_STATUS_UNAUTHORIZED);
+       do_message (session, base_uri, "/alice/404",
+                   !use_ntlm, bob,
+                   alice ? SOUP_STATUS_NOT_FOUND :
+                   SOUP_STATUS_UNAUTHORIZED);
+       do_message (session, base_uri, "/alice",
+                   !use_ntlm, bob,
+                   alice ? SOUP_STATUS_OK :
+                   SOUP_STATUS_UNAUTHORIZED);
+       do_message (session, base_uri, "/bob",
+                   !use_ntlm || alice, bob,
+                   bob ? SOUP_STATUS_OK :
+                   SOUP_STATUS_UNAUTHORIZED);
+       do_message (session, base_uri, "/alice",
+                   !use_ntlm || bob, alice,
+                   alice ? SOUP_STATUS_OK :
+                   SOUP_STATUS_UNAUTHORIZED);
 
        soup_session_abort (session);
        g_object_unref (session);
-
-       return errors;
-}
-
-static int
-do_ntlm_tests (SoupUri *base_uri)
-{
-       int errors = 0;
-
-       dprintf ("Round 1: Non-NTLM Connection\n");
-       errors += do_ntlm_round (base_uri, NULL);
-       dprintf ("Round 2: NTLM Connection, user=alice\n");
-       errors += do_ntlm_round (base_uri, "alice");
-       dprintf ("Round 3: NTLM Connection, user=bob\n");
-       errors += do_ntlm_round (base_uri, "bob");
-
-       return errors;
 }
 
 static void
-quit (int sig)
+do_ntlm_tests (SoupURI *base_uri)
 {
-       /* Exit cleanly on ^C in case we're valgrinding. */
-       exit (0);
+       debug_printf (1, "Round 1: Non-NTLM Connection\n");
+       do_ntlm_round (base_uri, NULL);
+       debug_printf (1, "Round 2: NTLM Connection, user=alice\n");
+       do_ntlm_round (base_uri, "alice");
+       debug_printf (1, "Round 3: NTLM Connection, user=bob\n");
+       do_ntlm_round (base_uri, "bob");
 }
 
 int
@@ -349,59 +315,26 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        SoupServer *server;
-       int opt;
        GHashTable *connections;
-       SoupUri *uri;
-       int errors;
-
-       g_type_init ();
-       g_thread_init (NULL);
-       signal (SIGINT, quit);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n",
-                                argv[0]);
-                       exit (1);
-               }
-       }
+       SoupURI *uri;
 
-       connections = g_hash_table_new (NULL, NULL);
+       test_init (argc, argv, NULL);
 
-       server = soup_server_new (SOUP_SERVER_PORT, 0,
-                                 NULL);
-       if (!server) {
-               fprintf (stderr, "Unable to bind server\n");
-               exit (1);
-       }
-       soup_server_add_handler (server, NULL, NULL,
-                                server_callback, NULL, connections);
-       soup_server_run_async (server);
+       server = soup_test_server_new (FALSE);
+       connections = g_hash_table_new (NULL, NULL);
+       soup_server_add_handler (server, NULL,
+                                server_callback, connections, NULL);
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       uri = g_new0 (SoupUri, 1);
-       uri->protocol = SOUP_PROTOCOL_HTTP;
-       uri->host = g_strdup ("localhost");
-       uri->port = soup_server_get_port (server);
-       errors = do_ntlm_tests (uri);
+       uri = soup_uri_new ("http://localhost/");
+       soup_uri_set_port (uri, soup_server_get_port (server));
+       do_ntlm_tests (uri);
        soup_uri_free (uri);
 
-       soup_server_quit (server);
-       g_object_unref (server);
        g_main_loop_unref (loop);
        g_hash_table_destroy (connections);
-       g_main_context_unref (g_main_context_default ());
-
-       dprintf ("\n");
-       if (errors) {
-               printf ("ntlm-test: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("ntlm-test: OK\n");
+
+       test_cleanup ();
        return errors != 0;
 }
index 2133db7..5d238c8 100644 (file)
@@ -8,23 +8,7 @@
 #include <unistd.h>
 
 #include "libsoup/soup.h"
-#include "apache-wrapper.h"
-
-int errors = 0;
-gboolean debug = FALSE;
-
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
-
-       if (!debug)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+#include "test-utils.h"
 
 typedef struct {
        const char *explanation;
@@ -62,21 +46,20 @@ static const char *proxy_names[] = {
 
 static void
 authenticate (SoupSession *session, SoupMessage *msg,
-             const char *auth_type, const char *auth_realm,
-             char **username, char **password, gpointer data)
+             SoupAuth *auth, gboolean retrying, gpointer data)
 {
-       *username = g_strdup ("user1");
-       *password = g_strdup ("realm1");
+       if (!retrying)
+               soup_auth_authenticate (auth, "user1", "realm1");
 }
 
 static void
 test_url (const char *url, int proxy, guint expected, gboolean sync)
 {
        SoupSession *session;
-       SoupUri *proxy_uri;
+       SoupURI *proxy_uri;
        SoupMessage *msg;
 
-       dprintf ("  GET %s via %s\n", url, proxy_names[proxy]);
+       debug_printf (1, "  GET %s via %s\n", url, proxy_names[proxy]);
        if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
                expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
 
@@ -99,9 +82,9 @@ test_url (const char *url, int proxy, guint expected, gboolean sync)
 
        soup_session_send_message (session, msg);
 
-       dprintf ("  %d %s\n", msg->status_code, msg->reason_phrase);
+       debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
        if (msg->status_code != expected) {
-               dprintf ("  EXPECTED %d!\n", expected);
+               debug_printf (1, "  EXPECTED %d!\n", expected);
                errors++;
        }
 
@@ -115,8 +98,8 @@ run_test (int i, gboolean sync)
 {
        char *http_url, *https_url;
 
-       dprintf ("Test %d: %s (%s)\n", i + 1, tests[i].explanation,
-                sync ? "sync" : "async");
+       debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation,
+                     sync ? "sync" : "async");
 
        if (!strncmp (tests[i].url, "http", 4)) {
                http_url = g_strdup (tests[i].url);
@@ -141,46 +124,22 @@ run_test (int i, gboolean sync)
        g_free (http_url);
        g_free (https_url);
 
-       dprintf ("\n");
+       debug_printf (1, "\n");
 }
 
 int
 main (int argc, char **argv)
 {
-       int i, opt;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n", argv[0]);
-                       return 1;
-               }
-       }
+       int i;
 
-       if (!apache_init ()) {
-               fprintf (stderr, "Could not start apache\n");
-               return 1;
-       }
+       test_init (argc, argv, NULL);
+       apache_init ();
 
        for (i = 0; i < ntests; i++) {
                run_test (i, FALSE);
                run_test (i, TRUE);
        }
 
-       apache_cleanup ();
-       g_main_context_unref (g_main_context_default ());
-
-       dprintf ("\n");
-       if (errors) {
-               printf ("proxy-test: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("proxy-test: OK\n");
-       return errors;
+       test_cleanup ();
+       return errors != 0;
 }
index 723cc88..e84eb08 100644 (file)
 #include "libsoup/soup.h"
 #include "libsoup/soup-session.h"
 
-#include "apache-wrapper.h"
+#include "test-utils.h"
 
-int errors = 0;
-int debug = 0;
-char *correct_response;
-guint correct_response_len;
-
-static void
-dprintf (int level, const char *format, ...)
-{
-       va_list args;
-
-       if (debug < level)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+SoupBuffer *correct_response;
 
 static void
 authenticate (SoupSession *session, SoupMessage *msg,
-             const char *auth_type, const char *auth_realm,
-             char **username, char **password, gpointer data)
+             SoupAuth *auth, gboolean retrying, gpointer data)
 {
-       *username = g_strdup ("user2");
-       *password = g_strdup ("realm2");
+       if (!retrying)
+               soup_auth_authenticate (auth, "user2", "realm2");
 }
 
 static void
@@ -46,7 +29,7 @@ get_correct_response (const char *uri)
        SoupSession *session;
        SoupMessage *msg;
 
-       session = soup_session_async_new ();
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        msg = soup_message_new (SOUP_METHOD_GET, uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != SOUP_STATUS_OK) {
@@ -55,8 +38,7 @@ get_correct_response (const char *uri)
                exit (1);
        }
 
-       correct_response_len = msg->response.length;
-       correct_response = g_strndup (msg->response.body, correct_response_len);
+       correct_response = soup_message_body_flatten (msg->response_body);
 
        g_object_unref (msg);
        soup_session_abort (session);
@@ -71,6 +53,7 @@ get_correct_response (const char *uri)
 
 typedef struct {
        GMainLoop *loop;
+       SoupSession *session;
        SoupMessage *msg;
        guint timeout;
        gboolean chunks_ready;
@@ -81,8 +64,10 @@ typedef struct {
 } FullyAsyncData;
 
 static void fully_async_got_headers (SoupMessage *msg, gpointer user_data);
-static void fully_async_got_chunk   (SoupMessage *msg, gpointer user_data);
-static void fully_async_finished    (SoupMessage *msg, gpointer user_data);
+static void fully_async_got_chunk   (SoupMessage *msg, SoupBuffer *chunk,
+                                    gpointer user_data);
+static void fully_async_finished    (SoupSession *session, SoupMessage *msg,
+                                    gpointer user_data);
 static gboolean fully_async_request_chunk (gpointer user_data);
 
 static void
@@ -98,12 +83,13 @@ do_fully_async_test (SoupSession *session,
        loop = g_main_loop_new (NULL, FALSE);
 
        uri = g_build_filename (base_uri, sub_uri, NULL);
-       dprintf (1, "GET %s\n", uri);
+       debug_printf (1, "GET %s\n", uri);
 
        msg = soup_message_new (SOUP_METHOD_GET, uri);
        g_free (uri);
 
        ad.loop = loop;
+       ad.session = session;
        ad.msg = msg;
        ad.chunks_ready = FALSE;
        ad.chunk_wanted = FALSE;
@@ -112,8 +98,8 @@ do_fully_async_test (SoupSession *session,
        ad.expected_status = expected_status;
 
        /* Since we aren't going to look at the final value of
-        * msg->response.body, we set OVERWRITE_CHUNKS, to tell
-        * libsoup to not even bother generating it.
+        * msg->response, we set OVERWRITE_CHUNKS, to tell libsoup to
+        * not even bother generating it.
         */
        soup_message_set_flags (msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
 
@@ -152,23 +138,23 @@ fully_async_request_chunk (gpointer user_data)
        FullyAsyncData *ad = user_data;
 
        if (!ad->did_first_timeout) {
-               dprintf (1, "  first timeout\n");
+               debug_printf (1, "  first timeout\n");
                ad->did_first_timeout = TRUE;
        } else
-               dprintf (2, "  timeout\n");
+               debug_printf (2, "  timeout\n");
        ad->timeout = 0;
 
        /* ad->chunks_ready and ad->chunk_wanted are used because
         * there's a race condition between the application requesting
         * the first chunk, and the message reaching a point where
         * it's actually ready to read chunks. If chunks_ready has
-        * been set, we can just call soup_message_io_unpause() to
+        * been set, we can just call soup_session_unpause_message() to
         * cause the first chunk to be read. But if it's not, we just
         * set chunk_wanted, to let the got_headers handler below know
         * that a chunk has already been requested.
         */
        if (ad->chunks_ready)
-               soup_message_io_unpause (ad->msg);
+               soup_session_unpause_message (ad->session, ad->msg);
        else
                ad->chunk_wanted = TRUE;
 
@@ -180,15 +166,15 @@ fully_async_got_headers (SoupMessage *msg, gpointer user_data)
 {
        FullyAsyncData *ad = user_data;
 
-       dprintf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
+       debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
        if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
                /* Let soup handle this one; this got_headers handler
                 * will get called again next time around.
                 */
                return;
        } else if (msg->status_code != SOUP_STATUS_OK) {
-               dprintf (1, "  unexpected status: %d %s\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "  unexpected status: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
                return;
        }
@@ -202,42 +188,35 @@ fully_async_got_headers (SoupMessage *msg, gpointer user_data)
        g_signal_connect (msg, "got_chunk",
                          G_CALLBACK (fully_async_got_chunk), ad);
        if (!ad->chunk_wanted)
-               soup_message_io_pause (msg);
+               soup_session_pause_message (ad->session, msg);
 }
 
 static void
-fully_async_got_chunk (SoupMessage *msg, gpointer user_data)
+fully_async_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
 {
        FullyAsyncData *ad = user_data;
 
-       dprintf (2, "  got chunk from %lu - %lu\n",
-                (unsigned long) ad->read_so_far,
-                (unsigned long) ad->read_so_far + msg->response.length);
+       debug_printf (2, "  got chunk from %lu - %lu\n",
+                     (unsigned long) ad->read_so_far,
+                     (unsigned long) ad->read_so_far + chunk->length);
 
        /* We've got a chunk, let's process it. In the case of the
         * test program, that means comparing it against
         * correct_response to make sure that we got the right data.
-        * We're using SOUP_MESSAGE_OVERWRITE_CHUNKS, so msg->response
-        * contains just the latest chunk. ad->read_so_far tells us
-        * how far we've read so far.
-        *
-        * Note that since we're using OVERWRITE_CHUNKS, msg->response
-        * is only good until we return from this signal handler; if
-        * you wanted to process it later, you'd need to copy it
-        * somewhere.
         */
-       if (ad->read_so_far + msg->response.length > correct_response_len) {
-               dprintf (1, "  read too far! (%lu > %lu)\n",
-                        (unsigned long) (ad->read_so_far + msg->response.length),
-                        (unsigned long) correct_response_len);
+       if (ad->read_so_far + chunk->length > correct_response->length) {
+               debug_printf (1, "  read too far! (%lu > %lu)\n",
+                             (unsigned long) (ad->read_so_far + chunk->length),
+                             (unsigned long) correct_response->length);
                errors++;
-       } else if (memcmp (msg->response.body, correct_response + ad->read_so_far,
-                          msg->response.length) != 0) {
-               dprintf (1, "  data mismatch in block starting at %lu\n",
-                        (unsigned long) ad->read_so_far);
+       } else if (memcmp (chunk->data,
+                          correct_response->data + ad->read_so_far,
+                          chunk->length) != 0) {
+               debug_printf (1, "  data mismatch in block starting at %lu\n",
+                             (unsigned long) ad->read_so_far);
                errors++;
        }
-       ad->read_so_far += msg->response.length;
+       ad->read_so_far += chunk->length;
 
        /* Now pause I/O, and prepare to read another chunk later.
         * (Again, the timeout just abstractly represents the idea of
@@ -245,20 +224,21 @@ fully_async_got_chunk (SoupMessage *msg, gpointer user_data)
         * point in the future. You wouldn't be using a timeout in a
         * real program.)
         */
-       soup_message_io_pause (msg);
+       soup_session_pause_message (ad->session, msg);
        ad->chunk_wanted = FALSE;
 
        ad->timeout = g_timeout_add (10, fully_async_request_chunk, ad);
 }
 
 static void
-fully_async_finished (SoupMessage *msg, gpointer user_data)
+fully_async_finished (SoupSession *session, SoupMessage *msg,
+                     gpointer user_data)
 {
        FullyAsyncData *ad = user_data;
 
        if (msg->status_code != ad->expected_status) {
-               dprintf (1, "  unexpected final status: %d %s !\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "  unexpected final status: %d %s !\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
        }
 
@@ -277,17 +257,21 @@ fully_async_finished (SoupMessage *msg, gpointer user_data)
 
 typedef struct {
        GMainLoop *loop;
-       GByteArray *chunk;
+       SoupSession *session;
+       SoupBuffer *chunk;
 } SyncAsyncData;
 
 static void        sync_async_send       (SoupSession *session,
                                          SoupMessage *msg);
-static GByteArray *sync_async_read_chunk (SoupMessage *msg);
+static gboolean    sync_async_is_finished(SoupMessage *msg);
+static SoupBuffer *sync_async_read_chunk (SoupMessage *msg);
 static void        sync_async_cleanup    (SoupMessage *msg);
 
 static void sync_async_got_headers (SoupMessage *msg, gpointer user_data);
-static void sync_async_copy_chunk  (SoupMessage *msg, gpointer user_data);
-static void sync_async_finished    (SoupMessage *msg, gpointer user_data);
+static void sync_async_copy_chunk  (SoupMessage *msg, SoupBuffer *chunk,
+                                   gpointer user_data);
+static void sync_async_finished    (SoupSession *session, SoupMessage *msg,
+                                   gpointer user_data);
 
 static void
 do_synchronously_async_test (SoupSession *session,
@@ -297,10 +281,10 @@ do_synchronously_async_test (SoupSession *session,
        SoupMessage *msg;
        char *uri;
        gsize read_so_far;
-       GByteArray *chunk;
+       SoupBuffer *chunk;
 
        uri = g_build_filename (base_uri, sub_uri, NULL);
-       dprintf (1, "GET %s\n", uri);
+       debug_printf (1, "GET %s\n", uri);
 
        msg = soup_message_new (SOUP_METHOD_GET, uri);
        g_free (uri);
@@ -312,13 +296,13 @@ do_synchronously_async_test (SoupSession *session,
 
        /* Send the message, get back headers */
        sync_async_send (session, msg);
-       if (msg->status == SOUP_MESSAGE_STATUS_FINISHED &&
+       if (sync_async_is_finished (msg) &&
            expected_status == SOUP_STATUS_OK) {
-               dprintf (1, "  finished without reading response!\n");
+               debug_printf (1, "  finished without reading response!\n");
                errors++;
-       } else if (msg->status != SOUP_MESSAGE_STATUS_FINISHED &&
+       } else if (!sync_async_is_finished (msg) &&
                   expected_status != SOUP_STATUS_OK) {
-               dprintf (1, "  request failed to fail!\n");
+               debug_printf (1, "  request failed to fail!\n");
                errors++;
        }
 
@@ -327,34 +311,34 @@ do_synchronously_async_test (SoupSession *session,
         */
        read_so_far = 0;
        while ((chunk = sync_async_read_chunk (msg))) {
-               dprintf (2, "  read chunk from %lu - %lu\n",
-                        (unsigned long) read_so_far,
-                        (unsigned long) read_so_far + chunk->len);
-
-               if (read_so_far + chunk->len > correct_response_len) {
-                       dprintf (1, "  read too far! (%lu > %lu)\n",
-                                (unsigned long) read_so_far + chunk->len,
-                                (unsigned long) correct_response_len);
+               debug_printf (2, "  read chunk from %lu - %lu\n",
+                             (unsigned long) read_so_far,
+                             (unsigned long) read_so_far + chunk->length);
+
+               if (read_so_far + chunk->length > correct_response->length) {
+                       debug_printf (1, "  read too far! (%lu > %lu)\n",
+                                     (unsigned long) read_so_far + chunk->length,
+                                     (unsigned long) correct_response->length);
                        errors++;
                } else if (memcmp (chunk->data,
-                                  correct_response + read_so_far,
-                                  chunk->len) != 0) {
-                       dprintf (1, "  data mismatch in block starting at %lu\n",
-                                (unsigned long) read_so_far);
+                                  correct_response->data + read_so_far,
+                                  chunk->length) != 0) {
+                       debug_printf (1, "  data mismatch in block starting at %lu\n",
+                                     (unsigned long) read_so_far);
                        errors++;
                }
-               read_so_far += chunk->len;
-               g_byte_array_free (chunk, TRUE);
+               read_so_far += chunk->length;
+               soup_buffer_free (chunk);
        }
 
-       if (msg->status != SOUP_MESSAGE_STATUS_FINISHED ||
+       if (!sync_async_is_finished (msg) ||
            (msg->status_code == SOUP_STATUS_OK &&
-            read_so_far != correct_response_len)) {
-               dprintf (1, "  loop ended before message was fully read!\n");
+            read_so_far != correct_response->length)) {
+               debug_printf (1, "  loop ended before message was fully read!\n");
                errors++;
        } else if (msg->status_code != expected_status) {
-               dprintf (1, "  unexpected final status: %d %s !\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "  unexpected final status: %d %s !\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
        }
 
@@ -382,6 +366,7 @@ sync_async_send (SoupSession *session, SoupMessage *msg)
         * want to pass that, rather than NULL, here.
         */
        ad->loop = g_main_loop_new (NULL, FALSE);
+       ad->session = session;
 
        g_signal_connect (msg, "got_headers",
                          G_CALLBACK (sync_async_got_headers), ad);
@@ -413,42 +398,48 @@ sync_async_got_headers (SoupMessage *msg, gpointer user_data)
 {
        SyncAsyncData *ad = user_data;
 
-       dprintf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
+       debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
        if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
                /* Let soup handle this one; this got_headers handler
                 * will get called again next time around.
                 */
                return;
        } else if (msg->status_code != SOUP_STATUS_OK) {
-               dprintf (1, "  unexpected status: %d %s\n",
-                        msg->status_code, msg->reason_phrase);
+               debug_printf (1, "  unexpected status: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
                errors++;
                return;
        }
 
        /* Stop I/O and return to the caller */
-       soup_message_io_pause (msg);
+       soup_session_pause_message (ad->session, msg);
        g_main_loop_quit (ad->loop);
 }
 
-/* Tries to read a chunk. Returns %NULL on error/end-of-response. (The
- * cases can be distinguished by looking at msg->status and
- * msg->status_code.)
- */
-static GByteArray *
+static gboolean
+sync_async_is_finished (SoupMessage *msg)
+{
+       SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
+
+       /* sync_async_finished clears ad->loop */
+       return ad->loop == NULL;
+}
+
+/* Tries to read a chunk. Returns %NULL on error/end-of-response. */
+static SoupBuffer *
 sync_async_read_chunk (SoupMessage *msg)
 {
        SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
        guint handler;
 
-       if (msg->status == SOUP_MESSAGE_STATUS_FINISHED)
+       if (sync_async_is_finished (msg))
                return NULL;
 
        ad->chunk = NULL;
        handler = g_signal_connect (msg, "got_chunk",
                                    G_CALLBACK (sync_async_copy_chunk),
                                    ad);
-       soup_message_io_unpause (msg);
+       soup_session_unpause_message (ad->session, msg);
        g_main_loop_run (ad->loop);
        g_signal_handler_disconnect (msg, handler);
 
@@ -456,27 +447,21 @@ sync_async_read_chunk (SoupMessage *msg)
 }
 
 static void
-sync_async_copy_chunk (SoupMessage *msg, gpointer user_data)
+sync_async_copy_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
 {
        SyncAsyncData *ad = user_data;
 
-       /* It's unfortunate that we have to do an extra copy here,
-        * but the data in msg->response.body won't last beyond
-        * the invocation of this handler.
-        */
-       ad->chunk = g_byte_array_new ();
-       g_byte_array_append (ad->chunk, (gpointer)msg->response.body,
-                            msg->response.length);
+       ad->chunk = soup_buffer_copy (chunk);
 
        /* Now pause and return from the g_main_loop_run() call in
         * sync_async_read_chunk().
         */
-       soup_message_io_pause (msg);
+       soup_session_pause_message (ad->session, msg);
        g_main_loop_quit (ad->loop);
 }
 
 static void
-sync_async_finished (SoupMessage *msg, gpointer user_data)
+sync_async_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
 {
        SyncAsyncData *ad = user_data;
 
@@ -486,6 +471,8 @@ sync_async_finished (SoupMessage *msg, gpointer user_data)
         * the final tests there.
         */
        g_main_loop_quit (ad->loop);
+       g_main_loop_unref (ad->loop);
+       ad->loop = NULL;
 }
 
 static void
@@ -493,7 +480,8 @@ sync_async_cleanup (SoupMessage *msg)
 {
        SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
 
-       g_main_loop_unref (ad->loop);
+       if (ad->loop)
+               g_main_loop_unref (ad->loop);
        g_free (ad);
 }
 
@@ -503,31 +491,15 @@ main (int argc, char **argv)
 {
        SoupSession *session;
        char *base_uri;
-       int opt;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug++;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d [-d]]\n", argv[0]);
-                       return 1;
-               }
-       }
 
-       if (!apache_init ()) {
-               fprintf (stderr, "Could not start apache\n");
-               return 1;
-       }
+       test_init (argc, argv, NULL);
+       apache_init ();
+
        base_uri = "http://localhost:47524/";
        get_correct_response (base_uri);
 
-       dprintf (1, "\nFully async, fast requests\n");
-       session = soup_session_async_new ();
+       debug_printf (1, "\nFully async, fast requests\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), NULL);
        do_fully_async_test (session, base_uri, "/",
@@ -539,8 +511,8 @@ main (int argc, char **argv)
        soup_session_abort (session);
        g_object_unref (session);
 
-       dprintf (1, "\nFully async, slow requests\n");
-       session = soup_session_async_new ();
+       debug_printf (1, "\nFully async, slow requests\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), NULL);
        do_fully_async_test (session, base_uri, "/",
@@ -552,8 +524,8 @@ main (int argc, char **argv)
        soup_session_abort (session);
        g_object_unref (session);
 
-       dprintf (1, "\nSynchronously async\n");
-       session = soup_session_async_new ();
+       debug_printf (1, "\nSynchronously async\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), NULL);
        do_synchronously_async_test (session, base_uri, "/",
@@ -566,16 +538,8 @@ main (int argc, char **argv)
        soup_session_abort (session);
        g_object_unref (session);
 
-       g_free (correct_response);
-
-       apache_cleanup ();
-       g_main_context_unref (g_main_context_default ());
+       soup_buffer_free (correct_response);
 
-       dprintf (1, "\n");
-       if (errors) {
-               printf ("pull-api: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("pull-api: OK\n");
-       return errors;
+       test_cleanup ();
+       return errors != 0;
 }
diff --git a/tests/query-test.c b/tests/query-test.c
new file mode 100644 (file)
index 0000000..682e938
--- /dev/null
@@ -0,0 +1,231 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup-form.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-session-sync.h>
+
+#include "test-utils.h"
+
+GMainLoop *loop;
+
+struct {
+       char *title, *name;
+       char *result;
+} tests[] = {
+       /* Both fields must be filled in */
+       { NULL, "Name", "" },
+       { "Mr.", NULL, "" },
+
+       /* Filled-in but empty is OK */
+       { "", "", "Hello,  " },
+       { "", "Name", "Hello,  Name" },
+       { "Mr.", "", "Hello, MR. " },
+
+       /* Simple */
+       { "Mr.", "Name", "Hello, MR. Name" },
+
+       /* Encoding of spaces */
+       { "Mr.", "Full Name", "Hello, MR. Full Name" },
+       { "Mr. and Mrs.", "Full Name", "Hello, MR. AND MRS. Full Name" },
+
+       /* Encoding of "+" */
+       { "Mr.+Mrs.", "Full Name", "Hello, MR.+MRS. Full Name" },
+
+       /* Encoding of non-ASCII. */
+       { "Se\xC3\xB1or", "Nombre", "Hello, SE\xC3\xB1OR Nombre" },
+
+       /* Encoding of '%' */
+       { "Mr.", "Foo %2f Bar", "Hello, MR. Foo %2f Bar" },
+};
+
+static void
+do_test (int n, gboolean extra, const char *uri)
+{
+       GPtrArray *args;
+       GHashTable *form_data_set;
+       char *title_arg = NULL, *name_arg = NULL;
+       char *stdout = NULL;
+
+       debug_printf (1, "%2d. '%s' '%s'%s: ", n * 2 + (extra ? 2 : 1),
+                     tests[n].title ? tests[n].title : "(null)",
+                     tests[n].name  ? tests[n].name  : "(null)",
+                     extra ? " + extra" : "");
+
+       form_data_set = g_hash_table_new (g_str_hash, g_str_equal);
+
+       args = g_ptr_array_new ();
+       g_ptr_array_add (args, "curl");
+       g_ptr_array_add (args, "-G");
+       if (tests[n].title) {
+               g_hash_table_insert (form_data_set, "title", tests[n].title);
+               title_arg = soup_form_encode_urlencoded (form_data_set);
+               g_hash_table_remove_all (form_data_set);
+
+               g_ptr_array_add (args, "-d");
+               g_ptr_array_add (args, title_arg);
+       }
+       if (tests[n].name) {
+               g_hash_table_insert (form_data_set, "name", tests[n].name);
+               name_arg = soup_form_encode_urlencoded (form_data_set);
+               g_hash_table_remove_all (form_data_set);
+
+               g_ptr_array_add (args, "-d");
+               g_ptr_array_add (args, name_arg);
+       }
+       if (extra) {
+               g_ptr_array_add (args, "-d");
+               g_ptr_array_add (args, "extra=something");
+       }
+       g_ptr_array_add (args, (char *)uri);
+       g_ptr_array_add (args, NULL);
+
+       if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
+                         G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+                         NULL, NULL,
+                         &stdout, NULL, NULL, NULL)) {
+               if (stdout && !strcmp (stdout, tests[n].result))
+                       debug_printf (1, "OK!\n");
+               else {
+                       debug_printf (1, "WRONG!\n");
+                       debug_printf (1, "  expected '%s', got '%s'\n",
+                                     tests[n].result,
+                                     stdout ? stdout : "(error)");
+                       errors++;
+               }
+               g_free (stdout);
+       } else {
+               debug_printf (1, "ERROR!\n");
+               errors++;
+       }
+       g_ptr_array_free (args, TRUE);
+       g_hash_table_destroy (form_data_set);
+       g_free (title_arg);
+       g_free (name_arg);
+}
+
+static void
+do_query_tests (const char *uri)
+{
+       int n;
+
+       for (n = 0; n < G_N_ELEMENTS (tests); n++) {
+               do_test (n, FALSE, uri);
+               do_test (n, TRUE, uri);
+       }
+}
+
+GThread *server_thread;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
+{
+       char *title, *name, *fmt;
+       const char *content_type;
+       GString *buf;
+
+       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
+               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+               return;
+       }
+
+       if (query) {
+               title = g_hash_table_lookup (query, "title");
+               name = g_hash_table_lookup (query, "name");
+               fmt = g_hash_table_lookup (query, "fmt");
+       } else
+               title = name = fmt = NULL;
+
+       buf = g_string_new (NULL);
+       if (!query || (fmt && !strcmp (fmt, "html"))) {
+               content_type = "text/html";
+               g_string_append (buf, "<html><head><title>query-test</title></head><body>\r\n");
+               if (title && name) {
+                       /* mumble mumble html-escape... */
+                       g_string_append_printf (buf, "<p>Hello, <b><em>%s</em> %s</b></p>\r\n",
+                                               title, name);
+               }
+               g_string_append (buf, "<form action='/' method='get'>"
+                                "<p>Title: <input name='title'></p>"
+                                "<p>Name: <input name='name'></p>"
+                                "<p><input type=hidden name='fmt' value='html'></p>"
+                                "<p><input type=submit></p>"
+                                "</form>\r\n");
+               g_string_append (buf, "</body></html>\r\n");
+       } else {
+               content_type = "text/plain";
+               if (title && name) {
+                       char *uptitle = g_ascii_strup (title, -1);
+                       g_string_append_printf (buf, "Hello, %s %s",
+                                               uptitle, name);
+                       g_free (uptitle);
+               }
+       }
+
+       soup_message_set_response (msg, content_type,
+                                  SOUP_MEMORY_TAKE,
+                                  buf->str, buf->len);
+       g_string_free (buf, FALSE);
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+        { "no-tests", 'n', G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_REVERSE,
+          G_OPTION_ARG_NONE, &run_tests,
+          "Don't run tests, just run the test server", NULL },
+        { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+       GMainLoop *loop;
+       SoupServer *server;
+       guint port;
+       char *uri_str;
+
+       test_init (argc, argv, no_test_entry);
+
+       server = soup_test_server_new (TRUE);
+       soup_server_add_handler (server, NULL,
+                                server_callback, NULL, NULL);
+       port =  soup_server_get_port (server);
+
+       loop = g_main_loop_new (NULL, TRUE);
+
+       if (run_tests) {
+               uri_str = g_strdup_printf ("http://localhost:%u", port);
+               do_query_tests (uri_str);
+               g_free (uri_str);
+       } else {
+               printf ("Listening on port %d\n", port);
+               g_main_loop_run (loop);
+       }
+
+       g_main_loop_unref (loop);
+
+       if (run_tests)
+               test_cleanup ();
+       return errors != 0;
+}
diff --git a/tests/revserver.c b/tests/revserver.c
deleted file mode 100644 (file)
index a09b7bc..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <libsoup/soup-address.h>
-#include <libsoup/soup-socket.h>
-
-#include <glib/gthread.h>
-
-static void rev_read (SoupSocket *sock, GString *buf);
-static void rev_write (SoupSocket *sock, GString *buf);
-
-static void
-reverse (GString *buf)
-{
-       char tmp, *a, *b;
-
-       a = buf->str;
-       b = buf->str + buf->len - 1;
-
-       while (isspace ((unsigned char)*b) && b > a)
-               b--;
-
-       while (a < b) {
-               tmp = *a;
-               *a++ = *b;
-               *b-- = tmp;
-       }
-}
-
-static void
-rev_done (SoupSocket *sock, GString *buf)
-{
-       g_object_unref (sock);
-       g_string_free (buf, TRUE);
-}
-
-static void
-rev_write (SoupSocket *sock, GString *buf)
-{
-       SoupSocketIOStatus status;
-       gsize nwrote;
-
-       do {
-               status = soup_socket_write (sock, buf->str, buf->len, &nwrote);
-               memmove (buf->str, buf->str + nwrote, buf->len - nwrote);
-               buf->len -= nwrote;
-       } while (status == SOUP_SOCKET_OK && buf->len);
-
-       switch (status) {
-       case SOUP_SOCKET_OK:
-               rev_read (sock, buf);
-               break;
-
-       case SOUP_SOCKET_WOULD_BLOCK:
-               g_error ("Can't happen");
-               break;
-
-       default:
-               g_warning ("Socket error");
-               /* fall through */
-
-       case SOUP_SOCKET_EOF:
-               rev_done (sock, buf);
-               break;
-       }
-}
-
-static void
-rev_read (SoupSocket *sock, GString *buf)
-{
-       SoupSocketIOStatus status;
-       char tmp[10];
-       gsize nread;
-       gboolean eol;
-
-       do {
-               status = soup_socket_read_until (sock, tmp, sizeof (tmp),
-                                                "\n", 1, &nread, &eol);
-               if (status == SOUP_SOCKET_OK)
-                       g_string_append_len (buf, tmp, nread);
-       } while (status == SOUP_SOCKET_OK && !eol);
-
-       switch (status) {
-       case SOUP_SOCKET_OK:
-               reverse (buf);
-               rev_write (sock, buf);
-               break;
-
-       case SOUP_SOCKET_WOULD_BLOCK:
-               g_error ("Can't happen");
-               break;
-
-       default:
-               g_warning ("Socket error");
-               /* fall through */
-
-       case SOUP_SOCKET_EOF:
-               rev_done (sock, buf);
-               break;
-       }
-}
-
-static void *
-start_thread (void *client)
-{
-       rev_read (client, g_string_new (NULL));
-
-       return NULL;
-}
-
-static void
-new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data)
-{
-       GThread *thread;
-       GError *error = NULL;
-
-       g_object_ref (client);
-       g_object_set (G_OBJECT (client),
-                     SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
-                     NULL);
-
-       thread = g_thread_create (start_thread, client, FALSE, &error);
-       if (thread == NULL) {
-               g_warning ("Could not start thread: %s", error->message);
-               g_error_free (error);
-               g_object_unref (client);
-       }
-}
-
-int
-main (int argc, char **argv)
-{
-       SoupSocket *listener;
-       SoupAddressFamily family = SOUP_ADDRESS_FAMILY_IPV4;
-       guint port = SOUP_ADDRESS_ANY_PORT;
-       SoupAddress *addr;
-       GMainLoop *loop;
-       int opt;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "6p:")) != -1) {
-               switch (opt) {
-               case '6':
-                       family = SOUP_ADDRESS_FAMILY_IPV6;
-                       break;
-               case 'p':
-                       port = atoi (optarg);
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-6] [-p port]\n",
-                                argv[0]);
-                       exit (1);
-               }
-       }
-
-       addr = soup_address_new_any (family, port);
-       if (!addr) {
-               fprintf (stderr, "Could not create listener address\n");
-               exit (1);
-       }
-
-       listener = soup_socket_server_new (addr, NULL,
-                                          new_connection, NULL);
-       g_object_unref (addr);
-       if (!listener) {
-               fprintf (stderr, "Could not create listening socket\n");
-               exit (1);
-       }
-       printf ("Listening on port %d\n",
-               soup_address_get_port (
-                       soup_socket_get_local_address (listener)));
-
-       loop = g_main_loop_new (NULL, TRUE);
-       g_main_loop_run (loop);
-
-       g_object_unref (listener);
-       return 0;
-}
diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c
new file mode 100644 (file)
index 0000000..0d188f1
--- /dev/null
@@ -0,0 +1,351 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-server.h>
+
+#include "test-utils.h"
+
+GMainLoop *loop;
+
+struct {
+       gboolean client_sent_basic, client_sent_digest;
+       gboolean server_requested_basic, server_requested_digest;
+       gboolean succeeded;
+} test_data;
+
+static void
+curl_exited (GPid pid, int status, gpointer data)
+{
+       gboolean *done = data;
+
+       *done = TRUE;
+       test_data.succeeded = (status == 0);
+}
+
+static void
+do_test (int n, SoupURI *base_uri, const char *path, gboolean good_password,
+        gboolean offer_basic, gboolean offer_digest,
+        gboolean client_sends_basic, gboolean client_sends_digest,
+        gboolean server_requests_basic, gboolean server_requests_digest,
+        gboolean success)
+{
+       SoupURI *uri;
+       char *uri_str;
+       GPtrArray *args;
+       GPid pid;
+       gboolean done;
+
+       debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s password\n",
+                     n, path, offer_basic ? "" : "don't ",
+                     offer_digest ? "" : "don't ",
+                     good_password ? "good" : "bad");
+
+       uri = soup_uri_new_with_base (base_uri, path);
+       uri_str = soup_uri_to_string (uri, FALSE);
+       soup_uri_free (uri);
+
+       args = g_ptr_array_new ();
+       g_ptr_array_add (args, "curl");
+       g_ptr_array_add (args, "-f");
+       g_ptr_array_add (args, "-s");
+       if (offer_basic || offer_digest) {
+               g_ptr_array_add (args, "-u");
+               if (good_password)
+                       g_ptr_array_add (args, "user:password");
+               else
+                       g_ptr_array_add (args, "user:badpassword");
+
+               if (offer_basic && offer_digest)
+                       g_ptr_array_add (args, "--anyauth");
+               else if (offer_basic)
+                       g_ptr_array_add (args, "--basic");
+               else
+                       g_ptr_array_add (args, "--digest");
+       }
+       g_ptr_array_add (args, uri_str);
+       g_ptr_array_add (args, NULL);
+
+       memset (&test_data, 0, sizeof (test_data));
+       if (g_spawn_async (NULL, (char **)args->pdata, NULL,
+                          G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
+                          NULL, NULL, &pid, NULL)) {
+               done = FALSE;
+               g_child_watch_add (pid, curl_exited, &done);
+
+               while (!done)
+                       g_main_iteration (TRUE);
+       } else
+               test_data.succeeded = FALSE;
+       g_ptr_array_free (args, TRUE);
+       g_free (uri_str);
+
+       if (server_requests_basic != test_data.server_requested_basic) {
+               errors++;
+               if (test_data.server_requested_basic)
+                       debug_printf (1, "  Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
+               else
+                       debug_printf (1, "  Server didn't send WWW-Authenticate: Basic, but should have!\n");
+       }
+       if (server_requests_digest != test_data.server_requested_digest) {
+               errors++;
+               if (test_data.server_requested_digest)
+                       debug_printf (1, "  Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
+               else
+                       debug_printf (1, "  Server didn't send WWW-Authenticate: Digest, but should have!\n");
+       }
+       if (client_sends_basic != test_data.client_sent_basic) {
+               errors++;
+               if (test_data.client_sent_basic)
+                       debug_printf (1, "  Client sent Authorization: Basic, but shouldn't have!\n");
+               else
+                       debug_printf (1, "  Client didn't send Authorization: Basic, but should have!\n");
+       }
+       if (client_sends_digest != test_data.client_sent_digest) {
+               errors++;
+               if (test_data.client_sent_digest)
+                       debug_printf (1, "  Client sent Authorization: Digest, but shouldn't have!\n");
+               else
+                       debug_printf (1, "  Client didn't send Authorization: Digest, but should have!\n");
+       }
+       if (success && !test_data.succeeded) {
+               errors++;
+               debug_printf (1, "  Should have succeeded, but didn't!\n");
+       } else if (!success && test_data.succeeded) {
+               errors++;
+               debug_printf (1, "  Should not have succeeded, but did!\n");
+       }
+}
+
+static void
+do_auth_tests (SoupURI *base_uri)
+{
+       int i, n = 1;
+       gboolean use_basic, use_digest, good_password;
+       gboolean preemptive_basic;
+
+       for (i = 0; i < 8; i++) {
+               use_basic     = (i & 1) == 1;
+               use_digest    = (i & 2) == 2;
+               good_password = (i & 4) == 4;
+
+               /* Curl will preemptively send Basic if it's told to
+                * use Basic but not Digest.
+                */
+               preemptive_basic = use_basic && !use_digest;
+
+               /* 1. No auth required. The server will ignore the
+                * Authorization headers completely, and the request
+                * will always succeed.
+                */
+               do_test (n++, base_uri, "/foo", good_password,
+                        /* request */
+                        use_basic, use_digest,
+                        /* expected from client */
+                        preemptive_basic, FALSE,
+                        /* expected from server */
+                        FALSE, FALSE,
+                        /* success? */
+                        TRUE);
+
+               /* 2. Basic auth required. The server will send
+                * "WWW-Authenticate: Basic" if the client fails to
+                * send an Authorization: Basic on the first request,
+                * or if it sends a bad password.
+                */
+               do_test (n++, base_uri, "/Basic/foo", good_password,
+                        /* request */
+                        use_basic, use_digest,
+                        /* expected from client */
+                        use_basic, FALSE,
+                        /* expected from server */
+                        !preemptive_basic || !good_password, FALSE,
+                        /* success? */
+                        use_basic && good_password);
+
+               /* 3. Digest auth required. Simpler than the basic
+                * case because the client can't send Digest auth
+                * premptively.
+                */
+               do_test (n++, base_uri, "/Digest/foo", good_password,
+                        /* request */
+                        use_basic, use_digest,
+                        /* expected from client */
+                        preemptive_basic, use_digest,
+                        /* expected from server */
+                        FALSE, TRUE,
+                        /* success? */
+                        use_digest && good_password);
+
+               /* 4. Any auth required. */
+               do_test (n++, base_uri, "/Any/foo", good_password,
+                        /* request */
+                        use_basic, use_digest,
+                        /* expected from client */
+                        preemptive_basic, use_digest,
+                        /* expected from server */
+                        !preemptive_basic || !good_password, !preemptive_basic || !good_password,
+                        /* success? */
+                        (use_basic || use_digest) && good_password);
+       }
+}
+
+static gboolean
+basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+                    const char *username, const char *password, gpointer data)
+{
+       return !strcmp (username, "user") && !strcmp (password, "password");
+}
+
+static char *
+digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+                     const char *username, gpointer data)
+{
+       if (strcmp (username, "user") != 0)
+               return NULL;
+
+       /* Note: this is exactly how you *shouldn't* do it in the real
+        * world; you should have the pre-encoded password stored in a
+        * database of some sort rather than using the cleartext
+        * password in the callback.
+        */
+       return soup_auth_domain_digest_encode_password ("user",
+                                                       "server-auth-test",
+                                                       "password");
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
+{
+       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
+               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+               return;
+       }
+
+       soup_message_set_response (msg, "text/plain",
+                                  SOUP_MEMORY_STATIC,
+                                  "OK\r\n", 4);
+       soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+got_headers_callback (SoupMessage *msg, gpointer data)
+{
+       const char *header;
+
+       header = soup_message_headers_get (msg->request_headers,
+                                          "Authorization");
+       if (header) {
+               if (strstr (header, "Basic "))
+                       test_data.client_sent_basic = TRUE;
+               if (strstr (header, "Digest "))
+                       test_data.client_sent_digest = TRUE;
+       }
+}
+
+static void
+wrote_headers_callback (SoupMessage *msg, gpointer data)
+{
+       const char *header;
+
+       header = soup_message_headers_get (msg->response_headers,
+                                          "WWW-Authenticate");
+       if (header) {
+               if (strstr (header, "Basic "))
+                       test_data.server_requested_basic = TRUE;
+               if (strstr (header, "Digest "))
+                       test_data.server_requested_digest = TRUE;
+       }
+}
+
+static void
+request_started_callback (SoupServer *server, SoupMessage *msg,
+                         SoupClientContext *client, gpointer data)
+{
+       g_signal_connect (msg, "got_headers",
+                         G_CALLBACK (got_headers_callback), NULL);
+       g_signal_connect (msg, "wrote_headers",
+                         G_CALLBACK (wrote_headers_callback), NULL);
+}
+
+gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+        { "no-tests", 'n', G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_REVERSE,
+          G_OPTION_ARG_NONE, &run_tests,
+          "Don't run tests, just run the test server", NULL },
+        { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+       GMainLoop *loop;
+       SoupServer *server;
+       SoupURI *uri;
+       SoupAuthDomain *auth_domain;
+       gboolean run_tests = TRUE;
+
+       test_init (argc, argv, no_test_entry);
+
+       server = soup_test_server_new (FALSE);
+       g_signal_connect (server, "request_started",
+                         G_CALLBACK (request_started_callback), NULL);
+       soup_server_add_handler (server, NULL,
+                                server_callback, NULL, NULL);
+
+       auth_domain = soup_auth_domain_basic_new (
+               SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
+               SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
+               NULL);
+       soup_server_add_auth_domain (server, auth_domain);
+
+       auth_domain = soup_auth_domain_digest_new (
+               SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
+               SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
+               SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
+               NULL);
+       soup_server_add_auth_domain (server, auth_domain);
+
+       loop = g_main_loop_new (NULL, TRUE);
+
+       if (run_tests) {
+               uri = soup_uri_new ("http://localhost");
+               soup_uri_set_port (uri, soup_server_get_port (server));
+               do_auth_tests (uri);
+               soup_uri_free (uri);
+       } else {
+               printf ("Listening on port %d\n", soup_server_get_port (server));
+               g_main_loop_run (loop);
+       }
+
+       g_main_loop_unref (loop);
+
+       if (run_tests)
+               test_cleanup ();
+       return errors != 0;
+}
index ea9a54a..f0c568b 100644 (file)
 #include <libsoup/soup-address.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
-#include <libsoup/soup-server-message.h>
 
 static void
-print_header (gpointer name, gpointer value, gpointer data)
+do_get (SoupServer *server, SoupMessage *msg, const char *path)
 {
-       printf ("%s: %s\n", (char *)name, (char *)value);
-}
-
-static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
-{
-       char *path, *path_to_open, *slash;
-       SoupMethodId method;
+       char *slash;
        struct stat st;
        int fd;
 
-       path = soup_uri_to_string (soup_message_get_uri (msg), TRUE);
-       printf ("%s %s HTTP/1.%d\n", msg->method, path,
-               soup_message_get_http_version (msg));
-       soup_message_foreach_header (msg->request_headers, print_header, NULL);
-       if (msg->request.length)
-               printf ("%.*s\n", msg->request.length, msg->request.body);
-
-       method = soup_method_get_id (msg->method);
-       if (method != SOUP_METHOD_ID_GET && method != SOUP_METHOD_ID_HEAD) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
-               goto DONE;
-       }
-
-       if (path) {
-               if (*path != '/') {
-                       soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
-                       goto DONE;
-               }
-       } else
-               path = g_strdup ("");
-
-       path_to_open = g_strdup_printf (".%s", path);
-
- AGAIN:
-       if (stat (path_to_open, &st) == -1) {
-               g_free (path_to_open);
+       if (stat (path, &st) == -1) {
                if (errno == EPERM)
                        soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
                else if (errno == ENOENT)
                        soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
                else
                        soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
-               goto DONE;
+               return;
        }
 
        if (S_ISDIR (st.st_mode)) {
-               slash = strrchr (path_to_open, '/');
+               char *index_path;
+
+               slash = strrchr (path, '/');
                if (!slash || slash[1]) {
                        char *uri, *redir_uri;
 
                        uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
                        redir_uri = g_strdup_printf ("%s/", uri);
-                       soup_message_add_header (msg->response_headers,
-                                                "Location", redir_uri);
+                       soup_message_headers_append (msg->response_headers,
+                                                    "Location", redir_uri);
                        soup_message_set_status (msg, SOUP_STATUS_MOVED_PERMANENTLY);
                        g_free (redir_uri);
                        g_free (uri);
-                       g_free (path_to_open);
-                       goto DONE;
+                       return;
                }
 
-               g_free (path_to_open);
-               path_to_open = g_strdup_printf (".%s/index.html", path);
-               goto AGAIN;
+               index_path = g_strdup_printf ("%s/index.html", path);
+               do_get (server, msg, index_path);
+               g_free (index_path);
+               return;
        }
 
-       fd = open (path_to_open, O_RDONLY);
-       g_free (path_to_open);
+       fd = open (path, O_RDONLY);
        if (fd == -1) {
                soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
-               goto DONE;
+               return;
        }
 
-       msg->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
-       msg->response.length = st.st_size;
-
-       if (method == SOUP_METHOD_ID_GET) {
-               msg->response.body = g_malloc (msg->response.length);
-               read (fd, msg->response.body, msg->response.length);
-       } else /* method == SOUP_METHOD_ID_HEAD */ {
-               /* SoupServer will ignore response.body and only use
-                * response.length when responding to HEAD, so we
-                * could just use the same code for both GET and HEAD.
-                * But we'll optimize and avoid the extra malloc.
+       if (msg->method == SOUP_METHOD_GET) {
+               char *buf;
+
+               buf = g_malloc (st.st_size);
+               read (fd, buf, st.st_size);
+               close (fd);
+               soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
+                                         buf, st.st_size);
+       } else /* msg->method == SOUP_METHOD_HEAD */ {
+               char *length;
+
+               /* We could just use the same code for both GET and
+                * HEAD. But we'll optimize and avoid the extra
+                * malloc.
                 */
-               msg->response.body = NULL;
+               length = g_strdup_printf ("%lu", (gulong)st.st_size);
+               soup_message_headers_append (msg->response_headers,
+                                            "Content-Length", length);
+               g_free (length);
        }
 
-       close (fd);
-
        soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+do_put (SoupServer *server, SoupMessage *msg, const char *path)
+{
+       struct stat st;
+       FILE *f;
+       gboolean created = TRUE;
+
+       if (stat (path, &st) != -1) {
+               const char *match = soup_message_headers_get (msg->request_headers, "If-None-Match");
+               if (match && !strcmp (match, "*")) {
+                       soup_message_set_status (msg, SOUP_STATUS_CONFLICT);
+                       return;
+               }
+
+               if (!S_ISREG (st.st_mode)) {
+                       soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+                       return;
+               }
+
+               created = FALSE;
+       }
+
+       f = fopen (path, "w");
+       if (!f) {
+               soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+               return;
+       }
+
+       fwrite (msg->request_body->data, 1, msg->request_body->length, f);
+       fclose (f);
+
+       soup_message_set_status (msg, created ? SOUP_STATUS_CREATED : SOUP_STATUS_OK);
+}
+
+static void
+print_header (const char *name, const char *value, gpointer data)
+{
+       printf ("%s: %s\n", name, value);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
+{
+       char *file_path;
+
+       printf ("%s %s HTTP/1.%d\n", msg->method, path,
+               soup_message_get_http_version (msg));
+       soup_message_headers_foreach (msg->request_headers, print_header, NULL);
+       if (msg->request_body->length)
+               printf ("%s\n", msg->request_body->data);
+
+       file_path = g_strdup_printf (".%s", path);
+
+       if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
+               do_get (server, msg, file_path);
+       else if (msg->method == SOUP_METHOD_PUT)
+               do_put (server, msg, file_path);
+       else
+               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
 
- DONE:
-       g_free (path);
+       g_free (file_path);
        printf ("  -> %d %s\n\n", msg->status_code, msg->reason_phrase);
 }
 
@@ -168,7 +202,7 @@ main (int argc, char **argv)
                fprintf (stderr, "Unable to bind to server port %d\n", port);
                exit (1);
        }
-       soup_server_add_handler (server, NULL, NULL,
+       soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
        printf ("\nStarting Server on port %d\n",
                soup_server_get_port (server));
@@ -185,7 +219,7 @@ main (int argc, char **argv)
                        fprintf (stderr, "Unable to bind to SSL server port %d\n", ssl_port);
                        exit (1);
                }
-               soup_server_add_handler (ssl_server, NULL, NULL,
+               soup_server_add_handler (ssl_server, NULL,
                                         server_callback, NULL, NULL);
                printf ("Starting SSL Server on port %d\n", 
                        soup_server_get_port (ssl_server));
index bcfb9ef..cc3da2b 100644 (file)
@@ -17,7 +17,6 @@
 #include <libsoup/soup-address.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
-#include <libsoup/soup-server-message.h>
 #include <libsoup/soup-session-async.h>
 
 /* WARNING: this is really really really not especially compliant with
  */
 
 SoupSession *session;
+SoupServer *server;
 
 static void
-copy_header (gpointer name, gpointer value, gpointer dest_headers)
+copy_header (const char *name, const char *value, gpointer dest_headers)
 {
-       soup_message_add_header (dest_headers, name, value);
+       soup_message_headers_append (dest_headers, name, value);
 }
 
 static void
@@ -41,42 +41,45 @@ send_headers (SoupMessage *from, SoupMessage *to)
 
        soup_message_set_status_full (to, from->status_code,
                                      from->reason_phrase);
-       soup_message_foreach_header (from->response_headers, copy_header,
-                                    to->response_headers);
-       soup_message_remove_header (to->response_headers, "Content-Length");
-       soup_message_io_unpause (to);
+       soup_message_headers_foreach (from->response_headers, copy_header,
+                                     to->response_headers);
+       soup_message_headers_remove (to->response_headers, "Content-Length");
+       soup_server_unpause_message (server, to);
 }
 
 static void
-send_chunk (SoupMessage *from, SoupMessage *to)
+send_chunk (SoupMessage *from, SoupBuffer *chunk, SoupMessage *to)
 {
-       printf ("[%p]   writing chunk of %d bytes\n", to, from->response.length);
+       printf ("[%p]   writing chunk of %lu bytes\n", to,
+               (unsigned long)chunk->length);
 
-       soup_message_add_chunk (to, SOUP_BUFFER_USER_OWNED,
-                               from->response.body, from->response.length);
-       soup_message_io_unpause (to);
+       soup_message_body_append_buffer (to->response_body, chunk);
+       soup_server_unpause_message (server, to);
 }
 
 static void
 client_msg_failed (SoupMessage *msg, gpointer msg2)
 {
-       soup_message_set_status (msg2, SOUP_STATUS_IO_ERROR);
-       soup_session_cancel_message (session, msg2);
+       soup_session_cancel_message (session, msg2, SOUP_STATUS_IO_ERROR);
 }
 
 static void
-finish_msg (SoupMessage *msg2, gpointer msg)
+finish_msg (SoupSession *session, SoupMessage *msg2, gpointer data)
 {
+       SoupMessage *msg = data;
+
        printf ("[%p]   done\n\n", msg);
        g_signal_handlers_disconnect_by_func (msg, client_msg_failed, msg2);
 
-       soup_message_add_final_chunk (msg);
-       soup_message_io_unpause (msg);
+       soup_message_body_complete (msg->response_body);
+       soup_server_unpause_message (server, msg);
        g_object_unref (msg);
 }
 
 static void
-server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
+server_callback (SoupServer *server, SoupMessage *msg,
+                const char *path, GHashTable *query,
+                SoupClientContext *context, gpointer data)
 {
        SoupMessage *msg2;
        char *uristr;
@@ -85,37 +88,37 @@ server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
        printf ("[%p] %s %s HTTP/1.%d\n", msg, msg->method, uristr,
                soup_message_get_http_version (msg));
 
-       if (soup_method_get_id (msg->method) == SOUP_METHOD_ID_CONNECT) {
+       if (msg->method == SOUP_METHOD_CONNECT) {
                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
                return;
        }
 
        msg2 = soup_message_new (msg->method, uristr);
-       soup_message_foreach_header (msg->request_headers, copy_header,
-                                    msg2->request_headers);
-       soup_message_remove_header (msg2->request_headers, "Host");
-       soup_message_remove_header (msg2->request_headers, "Connection");
-
-       if (msg->request.length) {
-               msg2->request.owner = SOUP_BUFFER_USER_OWNED;
-               msg2->request.body = msg->request.body;
-               msg2->request.length = msg->request.length;
+        msg2 = soup_message_new (msg->method, uristr);
+       soup_message_headers_foreach (msg->request_headers, copy_header,
+                                     msg2->request_headers);
+       soup_message_headers_remove (msg2->request_headers, "Host");
+       soup_message_headers_remove (msg2->request_headers, "Connection");
+
+       if (msg->request_body->length) {
+               SoupBuffer *request = soup_message_body_flatten (msg->request_body);
+               soup_message_body_append_buffer (msg2->request_body, request);
+               soup_buffer_free (request);
        }
-       soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg),
-                                         SOUP_TRANSFER_CHUNKED);
+       soup_message_headers_set_encoding (msg->response_headers,
+                                          SOUP_ENCODING_CHUNKED);
 
        g_signal_connect (msg2, "got_headers",
                          G_CALLBACK (send_headers), msg);
        g_signal_connect (msg2, "got_chunk",
                          G_CALLBACK (send_chunk), msg);
-       soup_message_set_flags (msg2, SOUP_MESSAGE_OVERWRITE_CHUNKS);
 
        g_signal_connect (msg, "finished", G_CALLBACK (client_msg_failed), msg2);
 
        soup_session_queue_message (session, msg2, finish_msg, msg);
 
        g_object_ref (msg);
-       soup_message_io_pause (msg);
+       soup_server_pause_message (server, msg);
 }
 
 static void
@@ -131,7 +134,6 @@ main (int argc, char **argv)
        GMainLoop *loop;
        int opt;
        int port = SOUP_ADDRESS_ANY_PORT;
-       SoupServer *server;
 
        g_type_init ();
        g_thread_init (NULL);
@@ -155,7 +157,7 @@ main (int argc, char **argv)
                fprintf (stderr, "Unable to bind to server port %d\n", port);
                exit (1);
        }
-       soup_server_add_handler (server, NULL, NULL,
+       soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
 
        printf ("\nStarting proxy on port %d\n",
index cdd88bc..13eed66 100644 (file)
@@ -7,6 +7,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 
+#include "libsoup/soup-address.h"
 #include "libsoup/soup-socket.h"
 #include "libsoup/soup-ssl.h"
 
@@ -132,17 +133,20 @@ async_read (SoupSocket *sock, gpointer user_data)
        AsyncData *data = user_data;
        SoupSocketIOStatus status;
        gsize n;
+       GError *error = NULL;
 
        do {
                status = soup_socket_read (sock, data->readbuf + data->total,
-                                          BUFSIZE - data->total, &n);
+                                          BUFSIZE - data->total, &n,
+                                          NULL, &error);
                if (status == SOUP_SOCKET_OK)
                        data->total += n;
        } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE);
 
-       if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF)
-               g_error ("Async read got status %d", status);
-       else if (status == SOUP_SOCKET_WOULD_BLOCK)
+       if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF) {
+               g_error ("Async read got status %d: %s", status,
+                        error ? error->message : "(unknown)");
+       } else if (status == SOUP_SOCKET_WOULD_BLOCK)
                return;
 
        if (memcmp (data->writebuf, data->readbuf, BUFSIZE) != 0)
@@ -158,17 +162,20 @@ async_write (SoupSocket *sock, gpointer user_data)
        AsyncData *data = user_data;
        SoupSocketIOStatus status;
        gsize n;
+       GError *error = NULL;
 
        do {
                status = soup_socket_write (sock, data->writebuf + data->total,
-                                           BUFSIZE - data->total, &n);
+                                           BUFSIZE - data->total, &n,
+                                           NULL, &error);
                if (status == SOUP_SOCKET_OK)
                        data->total += n;
        } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE);
 
-       if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF)
-               g_error ("Async write got status %d", status);
-       else if (status == SOUP_SOCKET_WOULD_BLOCK)
+       if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF) {
+               g_error ("Async write got status %d: %s", status,
+                        error ? error->message : "(unknown)");
+       } else if (status == SOUP_SOCKET_WOULD_BLOCK)
                return;
 
        data->total = 0;
@@ -211,10 +218,12 @@ main (int argc, char **argv)
        struct sockaddr_in sin;
        GThread *server;
        char writebuf[BUFSIZE], readbuf[BUFSIZE];
+       SoupAddress *addr;
        SoupSSLCredentials *creds;
        SoupSocket *sock;
        gsize n, total;
        SoupSocketIOStatus status;
+       GError *error = NULL;
 
        g_type_init ();
        g_thread_init (NULL);
@@ -269,15 +278,20 @@ main (int argc, char **argv)
        port = ntohs (sin.sin_port);
 
        /* Create the client */
+       addr = soup_address_new ("127.0.0.1", port);
        creds = soup_ssl_get_client_credentials (NULL);
-       sock = soup_socket_client_new_sync ("127.0.0.1", port,
-                                           creds, &status);
+       sock = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
+                               SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
+                               SOUP_SOCKET_SSL_CREDENTIALS, creds,
+                               NULL);
+       g_object_unref (addr);
+       status = soup_socket_connect_sync (sock, NULL);
        if (status != SOUP_STATUS_OK) {
                g_error ("Could not create client socket: %s",
                         soup_status_get_phrase (status));
        }
 
-       soup_socket_start_ssl (sock);
+       soup_socket_start_ssl (sock, NULL);
 
        /* Now spawn server thread */
        server = g_thread_create (server_thread, GINT_TO_POINTER (listener),
@@ -290,18 +304,22 @@ main (int argc, char **argv)
        total = 0;
        while (total < BUFSIZE) {
                status = soup_socket_write (sock, writebuf + total,
-                                           BUFSIZE - total, &n);
+                                           BUFSIZE - total, &n,
+                                           NULL, &error);
                if (status != SOUP_SOCKET_OK)
-                       g_error ("Sync write got status %d", status);
+                       g_error ("Sync write got status %d: %s", status,
+                                error ? error->message : "(unknown)");
                total += n;
        }
 
        total = 0;
        while (total < BUFSIZE) {
                status = soup_socket_read (sock, readbuf + total,
-                                          BUFSIZE - total, &n);
+                                          BUFSIZE - total, &n,
+                                          NULL, &error);
                if (status != SOUP_SOCKET_OK)
-                       g_error ("Sync read got status %d", status);
+                       g_error ("Sync read got status %d: %s", status,
+                                error ? error->message : "(unknown)");
                total += n;
        }
 
diff --git a/tests/test-utils.c b/tests/test-utils.c
new file mode 100644 (file)
index 0000000..b7564a4
--- /dev/null
@@ -0,0 +1,284 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "test-utils.h"
+#include "libsoup/soup-logger.h"
+#include "libsoup/soup-misc.h"
+#include "libsoup/soup-server.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_APACHE
+static gboolean apache_running;
+#endif
+static SoupServer *test_server;
+GThread *server_thread;
+static void test_server_shutdown (void);
+
+static SoupLogger *logger;
+
+int debug_level, http_debug_level, errors;
+
+static gboolean
+increment_debug_level (const char *option_name, const char *value,
+                      gpointer data, GError **error)
+{
+       debug_level++;
+       return TRUE;
+}
+
+static gboolean
+increment_http_debug_level (const char *option_name, const char *value,
+                           gpointer data, GError **error)
+{
+       http_debug_level++;
+       return TRUE;
+}
+
+static GOptionEntry debug_entry[] = {
+       { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+         G_OPTION_ARG_CALLBACK, increment_debug_level,
+         "Enable (or increase) test-specific debugging", NULL },
+       { "http-debug", 'h', G_OPTION_FLAG_NO_ARG,
+         G_OPTION_ARG_CALLBACK, increment_http_debug_level,
+         "Enable (or increase) HTTP-level debugging", NULL },
+       { NULL }
+};
+
+static void
+quit (int sig)
+{
+#ifdef HAVE_APACHE
+       if (apache_running)
+               apache_cleanup ();
+#endif
+       if (test_server)
+               test_server_shutdown ();
+
+       exit (1);
+}
+
+void
+test_init (int argc, char **argv, GOptionEntry *entries)
+{
+       GOptionContext *opts;
+       char *name;
+       GError *error = NULL;
+
+       g_type_init ();
+       g_thread_init (NULL);
+
+       name = strrchr (argv[0], '/');
+       if (!name++)
+               name = argv[0];
+       if (!strncmp (name, "lt-", 3))
+               name += 3;
+       g_set_prgname (name);
+
+       opts = g_option_context_new (NULL);
+       g_option_context_add_main_entries (opts, debug_entry, NULL);
+       if (entries)
+               g_option_context_add_main_entries (opts, entries, NULL);
+
+       if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+               fprintf (stderr, "Could not parse arguments: %s\n",
+                        error->message);
+               fprintf (stderr, "%s",
+                        g_option_context_get_help (opts, TRUE, NULL));
+               exit (1);
+       }
+       g_option_context_free (opts);
+
+       /* Exit cleanly on ^C in case we're valgrinding. */
+       signal (SIGINT, quit);
+}
+
+void
+test_cleanup (void)
+{
+       debug_printf (1, "\n");
+       if (errors) {
+               printf ("%s: %d error(s).%s\n",
+                       g_get_prgname (), errors,
+                       debug_level == 0 ? " Run with '-d' for details" : "");
+       } else
+               printf ("%s: OK\n", g_get_prgname ());
+
+#ifdef HAVE_APACHE
+       if (apache_running)
+               apache_cleanup ();
+#endif
+       if (test_server)
+               test_server_shutdown ();
+
+       if (logger)
+               g_object_unref (logger);
+
+       g_main_context_unref (g_main_context_default ());
+}
+
+void
+debug_printf (int level, const char *format, ...)
+{
+       va_list args;
+
+       if (debug_level < level)
+               return;
+
+       va_start (args, format);
+       vprintf (format, args);
+       va_end (args);
+}
+
+#ifdef HAVE_APACHE
+
+static gboolean
+apache_cmd (char *cmd)
+{
+       char *argv[8];
+       char *cwd, *conf;
+       int status;
+       gboolean ok;
+
+       cwd = g_get_current_dir ();
+       conf = g_build_filename (cwd, "httpd.conf", NULL);
+
+       argv[0] = APACHE_HTTPD;
+       argv[1] = "-d";
+       argv[2] = cwd;
+       argv[3] = "-f";
+       argv[4] = conf;
+       argv[5] = "-k";
+       argv[6] = cmd;
+       argv[7] = NULL;
+
+       ok = g_spawn_sync (cwd, argv, NULL, 0, NULL, NULL,
+                          NULL, NULL, &status, NULL);
+       if (ok)
+               ok = (status == 0);
+
+       g_free (cwd);
+       g_free (conf);
+
+       return ok;
+}
+
+void
+apache_init (void)
+{
+       if (!apache_cmd ("start")) {
+               fprintf (stderr, "Could not start apache\n");
+               exit (1);
+       }
+       apache_running = TRUE;
+}
+
+void
+apache_cleanup (void)
+{
+       pid_t pid;
+       char *contents;
+
+       if (g_file_get_contents ("httpd.pid", &contents, NULL, NULL)) {
+               pid = strtoul (contents, NULL, 10);
+               g_free (contents);
+       } else
+               pid = 0;
+
+       if (!apache_cmd ("graceful-stop"))
+               return;
+       apache_running = FALSE;
+
+       if (pid) {
+               while (kill (pid, 0) == 0)
+                       g_usleep (100);
+       }
+}
+
+#endif /* HAVE_APACHE */
+
+SoupSession *
+soup_test_session_new (GType type, ...)
+{
+       va_list args;
+       const char *propname;
+       SoupSession *session;
+
+       va_start (args, type);
+       propname = va_arg (args, const char *);
+       session = (SoupSession *)g_object_new_valist (type, propname, args);
+       va_end (args);
+
+       if (http_debug_level && !logger) {
+               SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
+
+               logger = soup_logger_new (level, -1);
+       }
+
+       if (logger)
+               soup_logger_attach (logger, session);
+
+       return session;
+}
+
+static gpointer run_server_thread (gpointer user_data);
+
+SoupServer *
+soup_test_server_new (gboolean in_own_thread)
+{
+       GMainContext *async_context;
+
+       async_context = in_own_thread ? g_main_context_new () : NULL;
+       test_server = soup_server_new (SOUP_SERVER_ASYNC_CONTEXT, async_context,
+                                      NULL);
+       if (async_context)
+               g_main_context_unref (async_context);
+
+       if (!test_server) {
+               fprintf (stderr, "Unable to create server\n");
+               exit (1);
+       }
+
+       if (in_own_thread) {
+               server_thread = g_thread_create (run_server_thread, test_server,
+                                                TRUE, NULL);
+       } else
+               soup_server_run_async (test_server);
+
+       return test_server;
+}
+
+static gpointer
+run_server_thread (gpointer user_data)
+{
+       SoupServer *server = user_data;
+
+       soup_server_run (server);
+       return NULL;
+}
+
+static gboolean
+idle_quit_server (gpointer server)
+{
+       soup_server_quit (server);
+       return FALSE;
+}
+
+static void
+test_server_shutdown (void)
+{
+       if (server_thread) {
+               soup_add_idle (soup_server_get_async_context (test_server),
+                              idle_quit_server, test_server);
+               g_thread_join (server_thread);
+       } else
+               soup_server_quit (test_server);
+       g_object_unref (test_server);
+}
+
+
diff --git a/tests/test-utils.h b/tests/test-utils.h
new file mode 100644 (file)
index 0000000..fa0dc13
--- /dev/null
@@ -0,0 +1,19 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "libsoup/soup-types.h"
+
+void test_init    (int argc, char **argv, GOptionEntry *entries);
+void test_cleanup (void);
+
+extern int debug_level, errors;
+void debug_printf (int level, const char *format, ...);
+
+#ifdef HAVE_APACHE
+void apache_init    (void);
+void apache_cleanup (void);
+#endif
+
+SoupSession *soup_test_session_new (GType type, ...);
+SoupServer  *soup_test_server_new  (gboolean in_own_thread);
index fa73292..cbafc53 100644 (file)
@@ -5,22 +5,10 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include "libsoup/soup-uri.h"
-
-gboolean debug = FALSE;
-
-static void
-dprintf (const char *format, ...)
-{
-       va_list args;
 
-       if (!debug)
-               return;
+#include "libsoup/soup-uri.h"
 
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
+#include "test-utils.h"
 
 struct {
        const char *uri_string, *result;
@@ -32,23 +20,48 @@ struct {
        { "ftp://user@host:9999/path", "ftp://user@host:9999/path" },
        { "ftp://user:password@host/path", "ftp://user@host/path" },
        { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path" },
-       { "http://us%65r@host", "http://user@host" },
-       { "http://us%40r@host", "http://us%40r@host" },
-       { "http://us%3ar@host", "http://us%3ar@host" },
-       { "http://us%2fr@host", "http://us%2fr@host" },
-
-       { "http://control-chars/%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f%7f",
-         "http://control-chars/%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f%7f"},
+       { "ftp://user:password@host", "ftp://user@host" },
+       { "http://us%65r@host", "http://user@host/" },
+       { "http://us%40r@host", "http://us%40r@host/" },
+       { "http://us%3ar@host", "http://us%3Ar@host/" },
+       { "http://us%2fr@host", "http://us%2Fr@host/" },
+       { "http://us%3fr@host", "http://us%3Fr@host/" },
+       { "http://host?query", "http://host/?query" },
+       { "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value",
+         "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value" },
+       { "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F",
+         "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F"},
        { "http://space/%20",
          "http://space/%20" },
-       { "http://delims/%3c%3e%23%25%22",
-         "http://delims/%3c%3e%23%25%22" },
-       { "http://unwise-chars/%7b%7d%7c%5c%5e%5b%5d%60",
-         "http://unwise-chars/%7b%7d%7c%5c%5e%5b%5d%60" }
+       { "http://delims/%3C%3E%23%25%22",
+         "http://delims/%3C%3E%23%25%22" },
+       { "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60",
+         "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60" },
+       { "http://host/path%", NULL },
+       { "http://host/path%%", NULL },
+       { "http://host/path%%%", NULL },
+       { "http://host/path%/x/", NULL },
+       { "http://host/path%0x/", NULL },
+
+       /* From RFC 2732 */
+       { "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html",
+         "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html" },
+       { "http://[1080:0:0:0:8:800:200C:417A]/index.html",
+         "http://[1080:0:0:0:8:800:200C:417A]/index.html" },
+       { "http://[3ffe:2a00:100:7031::1]",
+         "http://[3ffe:2a00:100:7031::1]/" },
+       { "http://[1080::8:800:200C:417A]/foo",
+         "http://[1080::8:800:200C:417A]/foo" },
+       { "http://[::192.9.5.5]/ipng",
+         "http://[::192.9.5.5]/ipng" },
+       { "http://[::FFFF:129.144.52.38]:80/index.html",
+         "http://[::FFFF:129.144.52.38]/index.html" },
+       { "http://[2010:836B:4179::836B:4179]",
+         "http://[2010:836B:4179::836B:4179]/" }
 };
 int num_abs_tests = G_N_ELEMENTS(abs_tests);
 
-/* From RFC 2396. */
+/* From RFC 3986. */
 const char *base = "http://a/b/c/d;p?q";
 struct {
        const char *uri_string, *result;
@@ -58,8 +71,8 @@ struct {
        { "./g", "http://a/b/c/g" },
        { "g/", "http://a/b/c/g/" },
        { "/g", "http://a/g" },
-       { "//g", "http://g" },
-       { "?y", "http://a/b/c/?y" },
+       { "//g", "http://g/" },
+       { "?y", "http://a/b/c/d;p?y" },
        { "g?y", "http://a/b/c/g?y" },
        { "#s", "http://a/b/c/d;p?q#s" },
        { "g#s", "http://a/b/c/g#s" },
@@ -76,10 +89,10 @@ struct {
        { "../../", "http://a/" },
        { "../../g", "http://a/g" },
        { "", "http://a/b/c/d;p?q" },
-       { "../../../g", "http://a/../g" },
-       { "../../../../g", "http://a/../../g" },
-       { "/./g", "http://a/./g" },
-       { "/../g", "http://a/../g" },
+       { "../../../g", "http://a/g" },
+       { "../../../../g", "http://a/g" },
+       { "/./g", "http://a/g" },
+       { "/../g", "http://a/g" },
        { "g.", "http://a/b/c/g." },
        { ".g", "http://a/b/c/.g" },
        { "g..", "http://a/b/c/g.." },
@@ -95,7 +108,7 @@ struct {
        { "g#s/./x", "http://a/b/c/g#s/./x" },
        { "g#s/../x", "http://a/b/c/g#s/../x" },
 
-       /* RFC 2396 notes that some old parsers will parse this as
+       /* RFC 3986 notes that some old parsers will parse this as
         * a relative URL ("http://a/b/c/g"), but it should be
         * interpreted as absolute. libsoup should parse it
         * correctly as being absolute, but then reject it since it's
@@ -105,29 +118,41 @@ struct {
 };
 int num_rel_tests = G_N_ELEMENTS(rel_tests);
 
+struct {
+       char *one, *two;
+} eq_tests[] = {
+       { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7bfoo%7d" },
+       { "http://example.com", "http://example.com/" },
+       /* From RFC 2616 */
+       { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html" },
+       { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html" },
+       { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html" },
+};
+int num_eq_tests = G_N_ELEMENTS(eq_tests);
+
 static gboolean
-do_uri (SoupUri *base_uri, const char *base_str,
+do_uri (SoupURI *base_uri, const char *base_str,
        const char *in_uri, const char *out_uri)
 {
-       SoupUri *uri;
+       SoupURI *uri;
        char *uri_string;
 
        if (base_uri) {
-               dprintf ("<%s> + <%s> = <%s>? ", base_str, in_uri,
-                        out_uri ? out_uri : "ERR");
+               debug_printf (1, "<%s> + <%s> = <%s>? ", base_str, in_uri,
+                             out_uri ? out_uri : "ERR");
                uri = soup_uri_new_with_base (base_uri, in_uri);
        } else {
-               dprintf ("<%s> => <%s>? ", in_uri,
-                        out_uri ? out_uri : "ERR");
+               debug_printf (1, "<%s> => <%s>? ", in_uri,
+                             out_uri ? out_uri : "ERR");
                uri = soup_uri_new (in_uri);
        }
 
        if (!uri) {
                if (out_uri) {
-                       dprintf ("ERR\n  Could not parse %s\n", in_uri);
+                       debug_printf (1, "ERR\n  Could not parse %s\n", in_uri);
                        return FALSE;
                } else {
-                       dprintf ("OK\n");
+                       debug_printf (1, "OK\n");
                        return TRUE;
                }
        }
@@ -136,47 +161,38 @@ do_uri (SoupUri *base_uri, const char *base_str,
        soup_uri_free (uri);
 
        if (!out_uri) {
-               dprintf ("ERR\n  Got %s\n", uri_string);
+               debug_printf (1, "ERR\n  Got %s\n", uri_string);
                return FALSE;
        }
 
        if (strcmp (uri_string, out_uri) != 0) {
-               dprintf ("NO\n  Unparses to <%s>\n", uri_string);
+               debug_printf (1, "NO\n  Unparses to <%s>\n", uri_string);
                g_free (uri_string);
                return FALSE;
        }
        g_free (uri_string);
 
-       dprintf ("OK\n");
+       debug_printf (1, "OK\n");
        return TRUE;
 }
 
 int
 main (int argc, char **argv)
 {
-       SoupUri *base_uri;
+       SoupURI *base_uri, *uri1, *uri2;
        char *uri_string;
-       int i, errs = 0, opt;
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug = TRUE;
-                       break;
-               default:
-                       fprintf (stderr, "Usage: %s [-d]\n", argv[0]);
-                       return 1;
-               }
-       }
+       int i;
 
-       dprintf ("Absolute URI parsing\n");
+       test_init (argc, argv, NULL);
+
+       debug_printf (1, "Absolute URI parsing\n");
        for (i = 0; i < num_abs_tests; i++) {
                if (!do_uri (NULL, NULL, abs_tests[i].uri_string,
                             abs_tests[i].result))
-                       errs++;
+                       errors++;
        }
 
-       dprintf ("\nRelative URI parsing\n");
+       debug_printf (1, "\nRelative URI parsing\n");
        base_uri = soup_uri_new (base);
        if (!base_uri) {
                fprintf (stderr, "Could not parse %s!\n", base);
@@ -187,22 +203,33 @@ main (int argc, char **argv)
        if (strcmp (uri_string, base) != 0) {
                fprintf (stderr, "URI <%s> unparses to <%s>\n",
                         base, uri_string);
-               errs++;
+               errors++;
        }
        g_free (uri_string);
 
        for (i = 0; i < num_rel_tests; i++) {
                if (!do_uri (base_uri, base, rel_tests[i].uri_string,
                             rel_tests[i].result))
-                       errs++;
+                       errors++;
        }
        soup_uri_free (base_uri);
 
-       dprintf ("\n");
-       if (errs) {
-               printf ("uri-parsing: %d error(s). Run with '-d' for details\n",
-                       errs);
-       } else
-               printf ("uri-parsing: OK\n");
-       return errs;
+       debug_printf (1, "\nURI equality testing\n");
+       for (i = 0; i < num_eq_tests; i++) {
+               uri1 = soup_uri_new (eq_tests[i].one);
+               uri2 = soup_uri_new (eq_tests[i].two);
+               debug_printf (1, "<%s> == <%s>? ", eq_tests[i].one, eq_tests[i].two);
+               if (soup_uri_equal (uri1, uri2))
+                       debug_printf (1, "OK\n");
+               else {
+                       debug_printf (1, "NO\n");
+                       debug_printf (1, "%s : %s : %s\n%s : %s : %s\n",
+                                     uri1->scheme, uri1->host, uri1->path,
+                                     uri2->scheme, uri2->host, uri2->path);
+                       errors++;
+               }
+       }
+
+       test_cleanup ();
+       return errors != 0;
 }
index a77c5b3..8686b71 100644 (file)
@@ -3,33 +3,17 @@
  * Copyright (C) 2001-2003, Ximian, Inc.
  */
 
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <libsoup/soup.h>
-#include <libsoup/soup-date.h>
-#include <libsoup/soup-md5-utils.h>
-#include <libsoup/soup-xmlrpc-message.h>
-#include <libsoup/soup-xmlrpc-response.h>
 
-#include "apache-wrapper.h"
+#include "test-utils.h"
 
 SoupSession *session;
 static const char *uri = "http://localhost:47524/xmlrpc-server.php";
-int debug;
-
-static void
-dprintf (int level, const char *format, ...)
-{
-       va_list args;
-
-       if (debug < level)
-               return;
-
-       va_start (args, format);
-       vprintf (format, args);
-       va_end (args);
-}
 
 static const char *const value_type[] = {
        "BAD",
@@ -43,209 +27,197 @@ static const char *const value_type[] = {
        "array"
 };
 
-static SoupXmlrpcResponse *
-do_xmlrpc (SoupXmlrpcMessage *xmsg, SoupXmlrpcValueType type)
+static gboolean
+do_xmlrpc (const char *method, GValue *retval, ...)
 {
-       SoupMessage *msg = SOUP_MESSAGE (xmsg);
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
-       int status;
+       SoupMessage *msg;
+       va_list args;
+       GValueArray *params;
+       GError *err = NULL;
+       char *body;
 
-       soup_xmlrpc_message_persist (xmsg);
-       status = soup_session_send_message (session, msg);
+       va_start (args, retval);
+       params = soup_value_array_from_args (args);
+       va_end (args);
+
+       body = soup_xmlrpc_build_method_call (method, params->values,
+                                             params->n_values);
+       g_value_array_free (params);
+       if (!body)
+               return FALSE;
 
-       dprintf (3, "\n%.*s\n%d %s\n%.*s\n",
-                msg->request.length, msg->request.body,
-                msg->status_code, msg->reason_phrase,
-                msg->response.length, msg->response.body);
+       msg = soup_message_new ("POST", uri);
+       soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+                                 body, strlen (body));
+       soup_session_send_message (session, msg);
+
+       if (debug_level >= 3) {
+               debug_printf (3, "\n%s\n%d %s\n%s\n",
+                             msg->request_body->data,
+                             msg->status_code, msg->reason_phrase,
+                             msg->response_body->data);
+       }
 
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               dprintf (1, "ERROR: %d %s\n", status, msg->reason_phrase);
+       if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+               debug_printf (1, "ERROR: %d %s\n", msg->status_code,
+                             msg->reason_phrase);
                g_object_unref (msg);
                return FALSE;
        }
 
-       response = soup_xmlrpc_message_parse_response (xmsg);
-       g_object_unref (msg);
-       if (!response || soup_xmlrpc_response_is_fault (response)) {
-               if (!response)
-                       dprintf (1, "ERROR: no response\n");
-               else {
-                       dprintf (1, "ERROR: fault\n");
-                       g_object_unref (response);
-               }
+       if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
+                                               msg->response_body->length,
+                                               retval, &err)) {
+               if (err) {
+                       debug_printf (1, "FAULT: %d %s\n", err->code, err->message);
+                       g_error_free (err);
+               } else
+                       debug_printf (1, "ERROR: could not parse response\n");
+               g_object_unref (msg);
                return FALSE;
        }
+       g_object_unref (msg);
 
-       value = soup_xmlrpc_response_get_value (response);
-       if (!value) {
-               dprintf (1, "ERROR: no value?\n");
-               g_object_unref (response);
-               return NULL;
-       } else if (soup_xmlrpc_value_get_type (value) != type) {
-               dprintf (1, "ERROR: wrong value type; expected %s, got %s\n",
-                        value_type[type], value_type[soup_xmlrpc_value_get_type (value)]);
-               g_object_unref (response);
-               return NULL;
+       return TRUE;
+}
+
+static gboolean
+check_xmlrpc (GValue *value, GType type, ...)
+{
+       va_list args;
+
+       if (!G_VALUE_HOLDS (value, type)) {
+               debug_printf (1, "ERROR: could not parse response\n");
+               g_value_unset (value);
+               return FALSE;
        }
 
-       return response;
+       va_start (args, type);
+       SOUP_VALUE_GETV (value, type, args);
+       va_end (args);
+       return TRUE;
 }
 
 static gboolean
 test_sum (void)
 {
-       SoupXmlrpcMessage *msg;
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
-       int i, val, sum;
-       long result;
-
-       dprintf (1, "sum (array of int -> int): ");
-
-       msg = soup_xmlrpc_message_new (uri);
-       soup_xmlrpc_message_start_call (msg, "sum");
-       soup_xmlrpc_message_start_param (msg);
-       soup_xmlrpc_message_start_array (msg);
+       GValueArray *ints;
+       int i, val, sum, result;
+       GValue retval;
+       gboolean ok;
+
+       debug_printf (1, "sum (array of int -> int): ");
+
+       ints = g_value_array_new (10);
        for (i = sum = 0; i < 10; i++) {
                val = rand () % 100;
-               dprintf (2, "%s%d", i == 0 ? "[" : ", ", val);
-               soup_xmlrpc_message_write_int (msg, val);
+               debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val);
+               soup_value_array_append (ints, G_TYPE_INT, val);
                sum += val;
        }
-       dprintf (2, "] -> ");
-       soup_xmlrpc_message_end_array (msg);
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
+       debug_printf (2, "] -> ");
 
-       response = do_xmlrpc (msg, SOUP_XMLRPC_VALUE_TYPE_INT);
-       if (!response)
-               return FALSE;
-       value = soup_xmlrpc_response_get_value (response);
+       ok = (do_xmlrpc ("sum", &retval,
+                       G_TYPE_VALUE_ARRAY, ints,
+                       G_TYPE_INVALID) &&
+             check_xmlrpc (&retval, G_TYPE_INT, &result));
+       g_value_array_free (ints);
 
-       if (!soup_xmlrpc_value_get_int (value, &result)) {
-               dprintf (1, "wrong type?\n");
-               g_object_unref (response);
+       if (!ok)
                return FALSE;
-       }
-       g_object_unref (response);
 
-       dprintf (2, "%ld: ", result);
-       dprintf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
+       debug_printf (2, "%d: ", result);
+       debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
        return result == sum;
 }
 
 static gboolean
 test_countBools (void)
 {
-       SoupXmlrpcMessage *msg;
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
+       GValueArray *bools;
        int i, trues, falses;
-       long ret_trues, ret_falses;
+       GValue retval;
+       int ret_trues, ret_falses;
        gboolean val, ok;
        GHashTable *result;
 
-       dprintf (1, "countBools (array of boolean -> struct of ints): ");
+       debug_printf (1, "countBools (array of boolean -> struct of ints): ");
 
-       msg = soup_xmlrpc_message_new (uri);
-       soup_xmlrpc_message_start_call (msg, "countBools");
-       soup_xmlrpc_message_start_param (msg);
-       soup_xmlrpc_message_start_array (msg);
+       bools = g_value_array_new (10);
        for (i = trues = falses = 0; i < 10; i++) {
                val = rand () > (RAND_MAX / 2);
-               dprintf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F');
-               soup_xmlrpc_message_write_boolean (msg, val);
+               debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F');
+               soup_value_array_append (bools, G_TYPE_BOOLEAN, val);
                if (val)
                        trues++;
                else
                        falses++;
        }
-       dprintf (2, "] -> ");
-       soup_xmlrpc_message_end_array (msg);
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
-
-       response = do_xmlrpc (msg, SOUP_XMLRPC_VALUE_TYPE_STRUCT);
-       if (!response)
-               return FALSE;
-       value = soup_xmlrpc_response_get_value (response);
-
-       if (!soup_xmlrpc_value_get_struct (value, &result)) {
-               dprintf (1, "wrong type?\n");
-               g_object_unref (response);
+       debug_printf (2, "] -> ");
+
+       ok = (do_xmlrpc ("countBools", &retval,
+                        G_TYPE_VALUE_ARRAY, bools,
+                        G_TYPE_INVALID) &&
+             check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result));
+       g_value_array_free (bools);
+       if (!ok)
                return FALSE;
-       }
 
-       if (!soup_xmlrpc_value_get_int (g_hash_table_lookup (result, "true"), &ret_trues)) {
-               dprintf (1, "NO 'true' value in response\n");
-               g_hash_table_destroy (result);
-               g_object_unref (response);
+       if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) {
+               debug_printf (1, "NO 'true' value in response\n");
                return FALSE;
        }
-       if (!soup_xmlrpc_value_get_int (g_hash_table_lookup (result, "false"), &ret_falses)) {
-               dprintf (1, "NO 'false' value in response\n");
-               g_hash_table_destroy (result);
-               g_object_unref (response);
+       if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) {
+               debug_printf (1, "NO 'false' value in response\n");
                return FALSE;
        }
        g_hash_table_destroy (result);
-       g_object_unref (response);
 
-       dprintf (2, "{ true: %ld, false: %ld } ", ret_trues, ret_falses);
+       debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses);
        ok = (trues == ret_trues) && (falses == ret_falses);
-       dprintf (1, "%s\n", ok ? "OK!" : "WRONG!");
+       debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
        return ok;
 }
 
 static gboolean
 test_md5sum (void)
 {
-       SoupXmlrpcMessage *msg;
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
-       GByteArray *result;
-       char data[512];
+       GByteArray *data, *result;
        int i;
-       SoupMD5Context md5;
+       GChecksum *checksum;
        guchar digest[16];
+       gsize digest_len = sizeof (digest);
+       GValue retval;
        gboolean ok;
 
-       dprintf (1, "md5sum (base64 -> base64): ");
+       debug_printf (1, "md5sum (base64 -> base64): ");
 
-       msg = soup_xmlrpc_message_new (uri);
-       soup_xmlrpc_message_start_call (msg, "md5sum");
-       soup_xmlrpc_message_start_param (msg);
-       for (i = 0; i < sizeof (data); i++)
-               data[i] = (char)(rand () & 0xFF);
-       soup_xmlrpc_message_write_base64 (msg, data, sizeof (data));
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
+       data = g_byte_array_new ();
+       g_byte_array_set_size (data, 256);
+       for (i = 0; i < data->len; i++)
+               data->data[i] = (char)(rand ());
 
-       response = do_xmlrpc (msg, SOUP_XMLRPC_VALUE_TYPE_BASE64);
-       if (!response)
-               return FALSE;
-       value = soup_xmlrpc_response_get_value (response);
+       checksum = g_checksum_new (G_CHECKSUM_MD5);
+       g_checksum_update (checksum, data->data, data->len);
+       g_checksum_get_digest (checksum, digest, &digest_len);
+       g_checksum_free (checksum);
 
-       if (!soup_xmlrpc_value_get_base64 (value, &result)) {
-               dprintf (1, "wrong type?\n");
-               g_object_unref (response);
+       ok = (do_xmlrpc ("md5sum", &retval,
+                        SOUP_TYPE_BYTE_ARRAY, data,
+                        G_TYPE_INVALID) &&
+             check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result));
+       g_byte_array_free (data, TRUE);
+       if (!ok)
                return FALSE;
-       }
-       g_object_unref (response);
 
-       if (result->len != 16) {
-               dprintf (1, "result has WRONG length (%d)\n", result->len);
+       if (result->len != digest_len) {
+               debug_printf (1, "result has WRONG length (%d)\n", result->len);
                g_byte_array_free (result, TRUE);
                return FALSE;
        }
 
-       soup_md5_init (&md5);
-       soup_md5_update (&md5, data, sizeof (data));
-       soup_md5_final (&md5, digest);
-
-       ok = (memcmp (digest, result->data, 16) == 0);
-       dprintf (1, "%s\n", ok ? "OK!" : "WRONG!");
+       ok = (memcmp (digest, result->data, digest_len) == 0);
+       debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
        g_byte_array_free (result, TRUE);
        return ok;
 }
@@ -253,105 +225,96 @@ test_md5sum (void)
 static gboolean
 test_dateChange (void)
 {
-       SoupXmlrpcMessage *msg;
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value;
-       struct tm tm;
-       time_t when, result;
-       char timestamp[128];
-
-       dprintf (1, "dateChange (struct of time and ints -> time): ");
-
-       msg = soup_xmlrpc_message_new (uri);
-       soup_xmlrpc_message_start_call (msg, "dateChange");
-       soup_xmlrpc_message_start_param (msg);
-       soup_xmlrpc_message_start_struct (msg);
-
-       soup_xmlrpc_message_start_member (msg, "date");
-       memset (&tm, 0, sizeof (tm));
-       tm.tm_year = 70 + (rand () % 50);
-       tm.tm_mon = rand () % 12;
-       tm.tm_mday = 1 + (rand () % 28);
-       tm.tm_hour = rand () % 24;
-       tm.tm_min = rand () % 60;
-       tm.tm_sec = rand () % 60;
-       when = soup_mktime_utc (&tm);
-       soup_xmlrpc_message_write_datetime (msg, when);
-       soup_xmlrpc_message_end_member (msg);
-
-       strftime (timestamp, sizeof (timestamp),
-                 "%Y-%m-%dT%H:%M:%S", &tm);
-       dprintf (2, "{ date: %s", timestamp);
+       GHashTable *structval;
+       SoupDate *date, *result;
+       char *timestamp;
+       GValue retval;
+       gboolean ok;
+
+       debug_printf (1, "dateChange (struct of time and ints -> time): ");
+
+       structval = soup_value_hash_new ();
+
+       date = soup_date_new (1970 + (rand () % 50),
+                             1 + rand () % 12,
+                             1 + rand () % 28,
+                             rand () % 24,
+                             rand () % 60,
+                             rand () % 60);
+       soup_value_hash_insert (structval, "date", SOUP_TYPE_DATE, date);
+
+       if (debug_level >= 2) {
+               timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
+               debug_printf (2, "{ date: %s", timestamp);
+               g_free (timestamp);
+       }
 
        if (rand () % 3) {
-               tm.tm_year = 70 + (rand () % 50);
-               dprintf (2, ", tm_year: %d", tm.tm_year);
-               soup_xmlrpc_message_start_member (msg, "tm_year");
-               soup_xmlrpc_message_write_int (msg, tm.tm_year);
-               soup_xmlrpc_message_end_member (msg);
+               date->year = 1970 + (rand () % 50);
+               debug_printf (2, ", tm_year: %d", date->year - 1900);
+               soup_value_hash_insert (structval, "tm_year",
+                                       G_TYPE_INT, date->year - 1900);
        }
        if (rand () % 3) {
-               tm.tm_mon = rand () % 12;
-               dprintf (2, ", tm_mon: %d", tm.tm_mon);
-               soup_xmlrpc_message_start_member (msg, "tm_mon");
-               soup_xmlrpc_message_write_int (msg, tm.tm_mon);
-               soup_xmlrpc_message_end_member (msg);
+               date->month = 1 + rand () % 12;
+               debug_printf (2, ", tm_mon: %d", date->month - 1);
+               soup_value_hash_insert (structval, "tm_mon",
+                                       G_TYPE_INT, date->month - 1);
        }
        if (rand () % 3) {
-               tm.tm_mday = 1 + (rand () % 28);
-               dprintf (2, ", tm_mday: %d", tm.tm_mday);
-               soup_xmlrpc_message_start_member (msg, "tm_mday");
-               soup_xmlrpc_message_write_int (msg, tm.tm_mday);
-               soup_xmlrpc_message_end_member (msg);
+               date->day = 1 + rand () % 28;
+               debug_printf (2, ", tm_mday: %d", date->day);
+               soup_value_hash_insert (structval, "tm_mday",
+                                       G_TYPE_INT, date->day);
        }
        if (rand () % 3) {
-               tm.tm_hour = rand () % 24;
-               dprintf (2, ", tm_hour: %d", tm.tm_hour);
-               soup_xmlrpc_message_start_member (msg, "tm_hour");
-               soup_xmlrpc_message_write_int (msg, tm.tm_hour);
-               soup_xmlrpc_message_end_member (msg);
+               date->hour = rand () % 24;
+               debug_printf (2, ", tm_hour: %d", date->hour);
+               soup_value_hash_insert (structval, "tm_hour",
+                                       G_TYPE_INT, date->hour);
        }
        if (rand () % 3) {
-               tm.tm_min = rand () % 60;
-               dprintf (2, ", tm_min: %d", tm.tm_min);
-               soup_xmlrpc_message_start_member (msg, "tm_min");
-               soup_xmlrpc_message_write_int (msg, tm.tm_min);
-               soup_xmlrpc_message_end_member (msg);
+               date->minute = rand () % 60;
+               debug_printf (2, ", tm_min: %d", date->minute);
+               soup_value_hash_insert (structval, "tm_min",
+                                       G_TYPE_INT, date->minute);
        }
        if (rand () % 3) {
-               tm.tm_sec = rand () % 60;
-               dprintf (2, ", tm_sec: %d", tm.tm_sec);
-               soup_xmlrpc_message_start_member (msg, "tm_sec");
-               soup_xmlrpc_message_write_int (msg, tm.tm_sec);
-               soup_xmlrpc_message_end_member (msg);
+               date->second = rand () % 60;
+               debug_printf (2, ", tm_sec: %d", date->second);
+               soup_value_hash_insert (structval, "tm_sec",
+                                       G_TYPE_INT, date->second);
        }
-       when = soup_mktime_utc (&tm);
 
-       dprintf (2, " } -> ");
+       debug_printf (2, " } -> ");
 
-       soup_xmlrpc_message_end_struct (msg);
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
-
-       response = do_xmlrpc (msg, SOUP_XMLRPC_VALUE_TYPE_DATETIME);
-       if (!response)
+       ok = (do_xmlrpc ("dateChange", &retval,
+                        G_TYPE_HASH_TABLE, structval,
+                        G_TYPE_INVALID) &&
+             check_xmlrpc (&retval, SOUP_TYPE_DATE, &result));
+       g_hash_table_destroy (structval);
+       if (!ok) {
+               soup_date_free (date);
                return FALSE;
-       value = soup_xmlrpc_response_get_value (response);
+       }
 
-       if (!soup_xmlrpc_value_get_datetime (value, &result)) {
-               dprintf (1, "wrong type?\n");
-               g_object_unref (response);
-               return FALSE;
+       if (debug_level >= 2) {
+               timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
+               debug_printf (2, "%s: ", timestamp);
+               g_free (timestamp);
        }
-       g_object_unref (response);
 
-       memset (&tm, 0, sizeof (tm));
-       soup_gmtime (&result, &tm);
-       strftime (timestamp, sizeof (timestamp), "%Y-%m-%dT%H:%M:%S", &tm);
-       dprintf (2, "%s: ", timestamp);
+       ok = ((date->year   == result->year) &&
+             (date->month  == result->month) &&
+             (date->day    == result->day) &&
+             (date->hour   == result->hour) &&
+             (date->minute == result->minute) &&
+             (date->second == result->second));
+       soup_date_free (date);
+       soup_date_free (result);
 
-       dprintf (1, "%s\n", (when == result) ? "OK!" : "WRONG!");
-       return (when == result);
+       debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
+       return ok;
 }
 
 static const char *const echo_strings[] = {
@@ -365,104 +328,64 @@ static const char *const echo_strings[] = {
 static gboolean
 test_echo (void)
 {
-       SoupXmlrpcMessage *msg;
-       SoupXmlrpcResponse *response;
-       SoupXmlrpcValue *value, *elt;
-       SoupXmlrpcValueArrayIterator *iter;
-       char *echo;
+       GValueArray *originals, *echoes;
+       GValue retval;
        int i;
 
-       dprintf (1, "echo (array of string -> array of string): ");
+       debug_printf (1, "echo (array of string -> array of string): ");
 
-       msg = soup_xmlrpc_message_new (uri);
-       soup_xmlrpc_message_start_call (msg, "echo");
-       soup_xmlrpc_message_start_param (msg);
-       soup_xmlrpc_message_start_array (msg);
+       originals = g_value_array_new (N_ECHO_STRINGS);
        for (i = 0; i < N_ECHO_STRINGS; i++) {
-               dprintf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]);
-               soup_xmlrpc_message_write_string (msg, echo_strings[i]);
+               soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]);
+               debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]);
        }
-       dprintf (2, "] -> ");
-       soup_xmlrpc_message_end_array (msg);
-       soup_xmlrpc_message_end_param (msg);
-       soup_xmlrpc_message_end_call (msg);
+       debug_printf (2, "] -> ");
 
-       response = do_xmlrpc (msg, SOUP_XMLRPC_VALUE_TYPE_ARRAY);
-       if (!response)
+       if (!(do_xmlrpc ("echo", &retval,
+                        G_TYPE_VALUE_ARRAY, originals,
+                        G_TYPE_INVALID) &&
+             check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) {
+               g_value_array_free (originals);
                return FALSE;
-       value = soup_xmlrpc_response_get_value (response);
+       }
+       g_value_array_free (originals);
+
+       if (debug_level >= 2) {
+               for (i = 0; i < echoes->n_values; i++) {
+                       debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ",
+                                     g_value_get_string (&echoes->values[i]));
+               }
+               debug_printf (2, "] -> ");
+       }
 
-       if (!soup_xmlrpc_value_array_get_iterator (value, &iter)) {
-               dprintf (1, "wrong type?\n");
-               g_object_unref (response);
+       if (echoes->n_values != N_ECHO_STRINGS) {
+               debug_printf (1, " WRONG! Wrong number of return strings");
+               g_value_array_free (echoes);
                return FALSE;
        }
-       i = 0;
-       while (iter) {
-               if (!soup_xmlrpc_value_array_iterator_get_value (iter, &elt)) {
-                       dprintf (1, " WRONG! Can't get result element %d\n", i + 1);
-                       g_object_unref (response);
-                       return FALSE;
-               }
-               if (!soup_xmlrpc_value_get_string (elt, &echo)) {
-                       dprintf (1, " WRONG! Result element %d is not a string", i + 1);
-                       g_object_unref (response);
-                       return FALSE;
-               }
-               dprintf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo);
-               if (strcmp (echo_strings[i], echo) != 0) {
-                       dprintf (1, " WRONG! Mismatch at %d\n", i + 1);
-                       g_free (echo);
-                       g_object_unref (response);
+
+       for (i = 0; i < echoes->n_values; i++) {
+               if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) {
+                       debug_printf (1, " WRONG! Mismatch at %d\n", i + 1);
+                       g_value_array_free (echoes);
                        return FALSE;
                }
-               g_free (echo);
-
-               iter = soup_xmlrpc_value_array_iterator_next (iter);
-               i++;
        }
-       dprintf (2, "] ");
-       g_object_unref (response);
-
-       dprintf (1, "%s\n", i == N_ECHO_STRINGS ? "OK!" : "WRONG! Too few results");
-       return i == N_ECHO_STRINGS;
-}
 
-static void
-usage (void)
-{
-       fprintf (stderr, "Usage: xmlrpc-test [-d] [-d]\n");
-       exit (1);
+       debug_printf (1, "OK!\n");
+       g_value_array_free (echoes);
+       return TRUE;
 }
 
 int
 main (int argc, char **argv)
 {
-       int opt, errors = 0;
-
-       g_type_init ();
-       g_thread_init (NULL);
-
-       while ((opt = getopt (argc, argv, "d")) != -1) {
-               switch (opt) {
-               case 'd':
-                       debug++;
-                       break;
-
-               case '?':
-                       usage ();
-                       break;
-               }
-       }
+       test_init (argc, argv, NULL);
+       apache_init ();
 
        srand (time (NULL));
 
-       if (!apache_init ()) {
-               fprintf (stderr, "Could not start apache\n");
-               return 1;
-       }
-
-       session = soup_session_sync_new ();
+       session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
 
        if (!test_sum ())
                errors++;
@@ -478,13 +401,6 @@ main (int argc, char **argv)
        soup_session_abort (session);
        g_object_unref (session);
 
-       apache_cleanup ();
-
-       dprintf (1, "\n");
-       if (errors) {
-               printf ("xmlrpc-test: %d error(s). Run with '-d' for details\n",
-                       errors);
-       } else
-               printf ("xmlrpc-test: OK\n");
-       return errors;
+       test_cleanup ();
+       return errors = 0;
 }