Bump development version to 0.7.99.
authorAlex Graveley <alex@ximian.com>
Tue, 12 Mar 2002 00:39:21 +0000 (00:39 +0000)
committerAlex Graveley <orph@src.gnome.org>
Tue, 12 Mar 2002 00:39:21 +0000 (00:39 +0000)
2002-03-11  Alex Graveley  <alex@ximian.com>

* configure.in, src/libsoup/soup-private.h: Bump development
version to 0.7.99.

* Merge all changes from soup-0-6 branch.

30 files changed:
ChangeLog
TODO
configure.in
libsoup/Makefile.am
libsoup/soup-auth.c
libsoup/soup-context.c
libsoup/soup-error.h
libsoup/soup-headers.c
libsoup/soup-headers.h
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-misc.c
libsoup/soup-misc.h
libsoup/soup-ntlm.c
libsoup/soup-ntlm.h
libsoup/soup-openssl.c
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-queue.h
libsoup/soup-server.c
libsoup/soup-server.h
libsoup/soup-socket-unix.c
libsoup/soup-socket.c
libsoup/soup-socket.h
libsoup/soup-ssl-proxy.c
libsoup/soup-ssl.c
libsoup/soup-transfer.c
libsoup/soup-transfer.h
libsoup/soup-uri.c
libsoup/soup-uri.h

index 577814c..d8b6ed9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,554 @@
+2002-03-11  Alex Graveley  <alex@ximian.com>
+
+       * configure.in, src/libsoup/soup-private.h: Bump development
+       version to 0.7.99.
+
+       * Merge all changes from soup-0-6 branch.
+
+2002-03-07  Alex Graveley  <alex@ximian.com>
+
+       * configure.in: SOUP_AGE goes to 1, SOUP_CURRENT goes to 4.
+       Update version to 0.6.99.
+
+       * src/libsoup/soup-private.h (VERSION): Update win version to
+       Win/0.6.99.
+
+2002-03-07  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-context.c (soup_context_connect_cb): Remove
+       debugging g_print.
+
+2002-03-07  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-socket.h: Add soup_address_lookup_in_cache.
+
+       * src/libsoup/soup-socket.c (soup_socket_connect): Handle host
+       address already being resolved by checking in address cache.
+       Allows us to return a non-null state pointer if soup_address_new
+       calls its callback inline, but soup_socket_new does not.
+
+       * src/libsoup/soup-socket-unix.c (lookup_in_cache_internal): Impl,
+       checks the address cache for an existing resolved address or
+       in progress lookup.
+       (soup_address_lookup_in_cache): Impl, return existing resolved
+       address if it exists.
+       (soup_address_new): use lookup_in_cache_internal.
+
+       * src/libsoup/soup-socket-win.c (soup_address_get_cached): Impl,
+       return NULL as windows has no address hash.
+
+2002-02-26  JP Rosevear  <jpr@ximian.com>
+
+       * src/libsoup/soup-socket-unix.c (soup_address_new): inaddr is a
+       different type for inet_addr, calculate sizeof directly
+
+2002-02-25  JP Rosevear  <jpr@ximian.com>
+
+       * src/libsoup/soup-socket-unix.c: apparently some platforms don't
+       define INADDR_NONE either
+
+2002-02-25  JP Rosevear  <jpr@ximian.com>
+       
+       * src/soup-httpd/Makefile.am: add popt cflags to includes
+
+       * src/libsoup/soup-socket.c: apparently INET_ADDRSTRLEN is not
+       defined on some unix platforms
+
+       * src/libsoup/soup-socket-unix.c (soup_address_new): handle not
+       have inet_pton and inet_aton with inet_addr
+
+       * configure.in: check for inet_aton as well and allow config
+       options to specify popt location
+
+2002-02-23  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-queue.c (proxy_connect): Pull proxy connection
+       code out of soup_queue_connect_cb to here.
+       (start_request): Delay calling soup_message_issue_callback here.
+       (soup_queue_connect_cb): Ditto.
+       (soup_idle_handle_new_requests): Call soup_message_issue_callback
+       if req->errorcode is set.  This avoids an FMW when setting the
+       connect tag on a request which has a connect error and has been
+       freed.
+
+       * src/libsoup/soup-message.c (requeue_connect_cb): Call
+       soup_message_issue_callback if req->errorcode is set.
+
+       * src/libsoup/soup-context.c (soup_context_connect_cb): Add
+       timeout handler to retry connect if new connection failed and
+       there are existing connections to this host.
+       (soup_context_get_connection): Drop timeout interval to 150 from 500.
+
+       * src/libsoup/soup-misc.c: Set default connection limit to 10.
+
+2002-02-17  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-socket.c (soup_socket_connect_inetaddr_cb):
+       Check result from soup_socket_new.
+
+       * src/libsoup/soup-queue.c (soup_idle_handle_new_requests): Check
+       result from soup_context_get_connection.
+
+       * src/libsoup/soup-context.c (soup_context_connect_cb): Don't
+       access 'data' after free.
+       (soup_context_get_connection): Check return from
+       soup_socket_connect, as a null result means our callback has been
+       issued and our state freed.
+
+2002-02-15  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-queue.c (soup_queue_shutdown): Only
+       g_source_remove the idle handler if it's been set, to avoid a glib
+       warning.
+
+2002-02-14  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-message.c (soup_message_send): Check if
+       soup_initialized is FALSE signifying that soup_shutdown has been
+       called while processing.
+
+       * src/libsoup/soup-queue.c (soup_queue_shutdown): Set
+       soup_initialized to FALSE so any currently running synch sends
+       will quit.
+
+2002-02-13  Ettore Perazzoli  <ettore@ximian.com>
+
+       * src/libsoup/soup-ntlm.c (soup_ntlm_response): Set `decodelen' to
+       be 5 bytes less than the challenge's length to avoid overflowing
+       the input buffer.
+
+2002-02-13  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-queue.c (soup_queue_add_request,
+       soup_queue_remove_request, soup_queue_first_request,
+       soup_queue_next_request): Routines to all simulataneous iteration
+       and modification of the active request list. Fixes Ximian 14030.
+       (soup_idle_handle_new_requests): Use the above functions.
+       (soup_queue_message): Likewise.
+       (soup_queue_shutdown): Likewise.
+
+       * src/libsoup/soup-message.c (soup_message_cleanup): Use
+       soup_queue_remove_request.
+
+2002-02-10  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-transfer.c (soup_transfer_read_cb): Temporary
+       fix for 100% cpu usage as HUPs are not being caught in some cases.
+
+2002-02-08  Dan Winship  <danw@ximian.com>
+
+       * src/soup-ssl-proxy/soup-openssl.c (soup_openssl_write): Handle
+       SSL_ERROR_WANT_READ here too. Fixes at least Ximian 19792 and
+       maybe others.
+
 2002-01-17  Joe Shaw  <joe@ximian.com>
 
-       * src/libsoup/soup-message.c: Add a
-       soup_message_get_http_version() function.
+       * src/libsoup/soup-message.c: Add soup_message_get_http_version()
+       function.
+
+2002-01-08  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-error.h: Add SOUP_ERROR_MOVED_TEMPORARILY
+       (which was in early HTTP1.1 and HTTP1.0 specs) defined to
+       SOUP_ERROR_FOUND (its replacement).
+
+       * configure.in: Use /usr/lib as the default location for nss and
+       nspr libs. Also, NSS static libs are named differently from their
+       dynamic version.
+
+2002-01-08  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-transfer.c (issue_final_callback): Remove the
+       gio sources from the reader before invoking the read_done
+       callback, since the connection will be marked free before the
+       user's callback is called, so if that callback posts another
+       request synchronously, the callback will end up getting called
+       back again when the new request finishes, which is wrong.
+
+2002-01-08  Alex Graveley  <alex@ximian.com>
+
+       * TODO: Update
+
+       * acconfig.h: undef SSL_PROXY_NAME
+
+       * src/libsoup/soup-ssl.c (soup_ssl_get_iochannel): Execute
+       customly chosen SSL proxy name.
+
+       * src/soup-ssl-proxy/Makefile.am (install-exec-hook): Add
+       install-time hook for customly named SSL proxy.
+
+       * configure.in: Don't let -lpopt sneak into $LIBS. Add option to
+       allow custom name for the openssl proxy.  Add option to allow
+       static linking of the SSL library.
+
+2002-01-02  Rodrigo Moya <rodrigo@ximian.com>
+
+       * src/libsoup-apache/soup-apache.c (soup_apache_handler): updated
+       for the new field names in SoupServerAuthBasic
+
+2001-12-21  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/Makefile.am (libsoup_la_SOURCES): Add
+       soup-server-auth.c.
+
+       * src/libsoup/soup-server-auth.c: Implement server-side
+       basic/digest authentication.
+       (soup_server_auth_get_user): Impl. Allows server auth/path handler
+       to get the username.
+       (soup_server_auth_check_passwd): Impl. Allows server auth/path
+       handler to check a password against the one used in the request.
+
+       * src/libsoup/soup-server.c (call_handler): Create SoupServerAuth
+       and call auth context's callback before calling path handler.
+       (message_new): Don't create temporary context, just pass NULL.
+       (soup_server_register): Copy auth context correctly.
+       (soup_server_message_new): If a server message already exists,
+       return that.
+       (soup_server_message_finish): Start server message as well as
+       finish, in case message_start is never called.
+
+       * src/libsoup/soup-private.h: Include sys/types.h so sockaddr
+       works for FreeBSD.
+
+       * src/libsoup/soup-misc.c: Move base64 encoding/decoding here from
+       soup-ntlm.c.  Remove copyright notice as soup_base64_encode is now
+       taken from evolution.
+       (soup_base64_encode): Use soup_base64_encode_close.
+       (soup_base64_decode): Impl. Use soup_base64_decode_step.
+
+       * src/libsoup/soup-misc.h: Make base64 code public.
+
+       * src/libsoup/soup-ntlm.c: Use base64 code from soup-misc.c.
+
+       * src/libsoup/soup-headers.c: Move header parameter parsing to
+       here from soup-auth.c.
+
+       * src/libsoup/soup-auth.c: Use header parsing from soup-headers.c.
+       (digest_init_func): Handle empty digest
+       realm or user passwd.
+       (decode_data_type): Return 0 on empty value.
+
+       * src/libsoup/soup-uri.c (soup_uri_equal): Impl.
+
+2001-12-19  Alex Graveley  <alex@ximian.com>
+
+       * src/soup-ssl-proxy/soup-openssl.c (soup_openssl_seed): #define
+       RAND_status to constants for older versions of OpenSSL (before
+       0.9.5).
+
+       * src/soup-ssl-proxy/soup-ssl-proxy.c (main): Check that ssh
+       channel is created before adding watches.
+
+2001-12-17  Alex Graveley  <alex@ximian.com>
+
+       * src/soup-ssl-proxy/soup-openssl.c (soup_openssl_seed): Use
+       RAND_status less often.
+
+2001-12-17  Alex Graveley  <alex@ximian.com>
+
+       * src/soup-ssl-proxy/soup-openssl.c (soup_openssl_init): Implement
+       a simple PRNG seed if needed.
+
+2001-12-15  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-server.c (message_new): Don't use temporary
+       context to create message, just pass a NULL one.
+
+2001-12-14  Alex Graveley  <alex@ximian.com>
+
+       * tests/server-test.c (push_callback): Add test for server push,
+       which creates a SoupServerMessage and sends the headers, body, and
+       ends the transfer at 4 second intervals.
+       (soup_server_init): Update for new server api.
+
+       * tests/cgi-test.c (soup_server_init): Update for new server api.
+
+       * src/libsoup/soup-transfer.c (soup_transfer_write_simple):
+       Impl. Just write the headers and data buffer passed.
+       (soup_transfer_write): Use callback to get header data, remove
+       header done callback.
+       (soup_transfer_write_pause): Pause the write by removing the
+       writable io watch.
+       (soup_transfer_write_unpause): Unpause by adding it back again.
+       (write_chunk_sep): Handle (rare) case where 0 length chunk is
+       first chunk.  Final chunks end in \r\n\r\n not just \r\n.
+
+       * src/libsoup/soup-server.c (get_header_cgi_cb): Create header for
+       delayed writes if soup_server_message_start() has been called,
+       otherwise pause write.
+       (get_header_cb): Ditto, use chunked encoding if client supports
+       it.
+       (get_chunk_cb): Free the current chunk, not the last one.  Pause
+       write if no new data.
+       (soup_server_message_start): Impl. Asyncronously send the headers
+       of the request.  Unpause write.
+       (soup_server_message_add_data): Unpause write.
+       (soup_server_message_finish): Unpause write.
+
+       * src/libsoup/soup-queue.c (start_request): Use
+       soup_transfer_write_simple.
+
+       * src/libsoup/soup-socket.c: Comment formatting.
+
+2001-12-13  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-queue.c (start_request): Use
+       soup_transfer_write_simple.
+
+2001-12-13  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup-apache/soup-apache.c (soup_apache_get_server):
+       Impl. Returns the SoupServer which handles this request's port and
+       protocol, creating and initializing the server if this is its
+       first use.
+       (soup_apache_handler): Check auth_type not auth.type.  Update for
+       new SoupServerContext members.
+
+2001-12-09  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-uri.c (normalize_path): Add path normalization
+       code from libxml.
+
+2001-12-06  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-socket.c (soup_socket_connect_tcp_cb): Free
+       state before callback.
+       (soup_socket_connect_inetaddr_cb): Don't shadow param.
+
+2001-12-06  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-method.[ch]: Create.
+
+       * src/libsoup/soup-dav-server.c: Rehash to be controlled by
+       caller through soup_dav_server_process, instead of registering
+       paths with the server internally.  This adds the restricition of
+       only being able to service dav move/copy requests to paths under
+       the caller's registered path.
+
+       * src/libsoup/soup-server.c (call_handler): Add message, request
+       path, and method id to SoupServerContext.
+
+       * src/libsoup/soup-message.h: Remove method defines.
+
+2001-12-05  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-server.c (soup_server_run): Allow recursive
+       calls and/or blocking on a running async server.
+
+2001-12-05  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-fault.c: Formatting nitpicks.
+
+       * src/libsoup/soup-server.c (free_handler): Call unregister handler.
+
+       * src/libsoup/soup-server.h: Add unregister callback to
+       SoupServerHandler so handlers can free temporary resources on
+       server shutdown.
+
+       * src/libsoup/soup-transfer.c (soup_transfer_write_cb): Call
+       headers_done_cb if wrote >= header_len.
+
+       * src/libsoup/soup-dav-server.c (soup_dav_server_register): Pass
+       dav_unregister_handler to soup_server_register, to cleanup state
+       on server free.
+
+       * src/libsoup/soup-env.c: Rehash.
+
+       * src/libsoup/soup-message.c (soup_message_new): Allow NULL
+       contexts.
+       (soup_message_copy): Impl. Copy request/response buffers.
+
+       * src/libsoup/soup-queue.c (soup_queue_message): Handle NULL
+       message contexts by issuing a cancelled callback.  Break out queue
+       initialization to soup_queue_initialize(). 
+
+2001-12-04  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-message.c (soup_message_requeue): Don't try to
+       wait for the rest of the data if the read_tag is 0 (eg, if the
+       write attempt got an EPIPE). Fixes a crash if the server
+       idle-timeouts a keepalived connection.
+
+2001-12-02  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-private.h: Remove req_header from
+       SoupMessagePrivate.
+
+       * src/libsoup/soup-message.c (finalize_message): No more
+       req_header.
+
+       * src/libsoup/soup-queue.c (start_request): Ditto.
+
+       * src/libsoup/soup-server.c (read_done_cgi_cb): Ditto.
+       (read_done_cb): Ditto.
+
+2001-12-01  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-server-auth.h: Pull the SoupServerAuth from
+       soup-server.h to here.
+
+       * tests/cgi-test.c: 
+       * tests/mod-server-test.c: 
+       * tests/server-test.c: Update for new server api. Add a handler
+       that sets the errorphrase, add some response headers, and prints
+       the request body, uri, and headers in the response.
+
+       * src/soup-httpd/soup-httpd.c: Update for new server api, remove
+       module reloading, which trivializes the server code.
+
+       * src/libsoup-apache/soup-apache.c: Update for new server api.
+
+       * src/libsoup/soup-uri.c (soup_uri_copy): Copy element by element,
+       don't stringify/unstringify.
+
+       * src/libsoup/soup-uri.h: Remove query_elems from SoupUri.
+
+       * src/libsoup/soup-transfer.c (soup_transfer_write): Take an
+       encoding argument to allow chunked writes, and a write_chunk_cb to
+       allow new data to be written. Use a secondary buffer to make
+       writes (and especially chunked writes) larger, and also simplify
+       the code.
+       (write_chunk_sep): Impl.
+       (write_chunk): Impl.
+
+       * src/libsoup/soup-socket.c (soup_socket_connect_inetaddr_cb):
+       Free state before issuing failure callback.  Add FIXME, as this
+       shouldn't be needed but avoids a segfault.
+
+       * src/libsoup/soup-socket-unix.c: Formatting.
+
+       * src/libsoup/soup-server.c: Reimplement to support server push
+       through SoupServerMessage, CGI support, and persistent
+       connections. Modules are now passed a SoupServer during init,
+       instead of having well-known global server objects -- these have
+       been removed.
+
+       * src/libsoup/soup-queue.c (start_request): Update for new
+       transfer_write interface, passing content length as the encoding
+       and a NULL write_chunk_cb.
+
+       * src/libsoup/soup-private.h: Add server, server_sock, and
+       server_msg to _SoupMessagePrivate.  Gut _SoupServer.
+
+       * src/libsoup/soup-message.c (soup_message_requeue): Cleanup
+       boolean eval.
+
+       * src/libsoup/soup-headers.c (soup_headers_parse_response): Wrap
+       at 80.
+
+       * src/libsoup/soup-dav-server.[ch]: Update for new server handler
+       interface.
+
+       * configure.in: Check for sys/ioctl.h and sys/filio.h
+
+2001-11-30  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-headers.c (soup_headers_parse_status_line):
+       Split this functionality out of soup_headers_parse_response.
+
+       * src/libsoup/Makefile.am (libsoupinclude_HEADERS): Add
+       soup-message.h to the public headers.
+
+2001-11-21  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-socket.c (soup_address_set_sockaddr): Remove.
+       (soup_address_get_sockaddr): Impl. Return the address's internal
+       sockaddr, and its length.
+
+2001-11-20  Dan Winship  <danw@ximian.com>
+
+       * src/libsoup/soup-ntlm.c (soup_ntlm_request): Simplify. Don't
+       specify a host and domain, and tweak the flags to a new magic
+       value that makes the server return some info such as a default
+       domain to pick.
+       (soup_ntlm_response): Allow "host" and "domain" to be NULL. If
+       domain is NULL, use the default from the server response.
+
+       * src/libsoup/soup-auth.c (ntlm_init): soup_ntlm_request no longer
+       takes host/domain. soup_ntlm_response can take NULL host/domain.
+
+2001-11-19  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-private.h: Add auth member to SoupConnection.
+
+       * src/libsoup/soup-queue.h: Export soup_queue_connect_cb.
+
+       * src/libsoup/soup-queue.c (soup_encode_http_auth): Get auth token
+       from connection's auth.
+
+       * src/libsoup/soup-message.c (soup_message_run_handlers): Break if
+       status after running a handler is also SOUP_STATUS_CONNECTING.
+       (soup_message_requeue): If the connection uses NTLM, reset
+       error/finished callbacks.
+       (requeue_read_finished): Impl. Requeue the message using the
+       current connection on success.
+       (requeue_read_error): Impl. Queue the message again and get a new
+       connection.
+       (requeue_connect_cb): Impl. Use the old connection's auth data
+       before passing off to soup_queue_connect_cb.
+       (soup_message_cleanup): Only reset callbacks if the reader and
+       connection exist.
+       (authorize_handler): Set/get the connection's auth data if it
+       exists and the new auth created uses NTLM.
+
+       * src/libsoup/soup-transfer.c (soup_transfer_read_cb): Dump read
+       data to stderr if DUMP is defined.
+       (soup_transfer_write_cb): ditto for writes.
+
+       * src/libsoup/soup-context.c (soup_context_connect_cb): Don't ref
+       context here, but rather
+       (soup_context_get_connection): Here, in case the context goes away
+       while connecting.
+
+2001-11-14  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-socket-win.c (soup_address_get_name_cb): Don't
+       leak state on failure, pass address's name, not a copy.
+       (soup_address_new_cb): Don't access things after we realloc to a
+       smaller size.
+
+2001-11-13  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-queue.c (soup_queue_error_cb): Use
+       soup_message_requeue.
+       (soup_queue_read_headers_cb): Don't end the transfer if a handler
+       requeues the message.  Close the connection on malformed headers.
+       (soup_queue_read_chunk_cb): Ditto for the former.
+
+       * src/libsoup/soup-message.c (soup_message_requeue): Impl. Requeue
+       the message using existing callback/user_data.
+       (authorize_handler): Use soup_message_requeue.
+       (redirect_handler): Ditto.
+
+       * src/libsoup/soup-transfer.c (soup_transfer_read_set_callbacks):
+       Impl. Allows changing of the reader's callbacks to ignore certain
+       events (like chunk data) and change the user_data.
+
+       * src/libsoup/soup-message.c (soup_message_cleanup): If still
+       reading response, reset the SoupReader's callbacks to only release
+       (on finish) or release and close (on error) the connection,
+       instead of terminating the read which could leave data in the
+       socket to to hork future messages which use this connection.  Also
+       NULL the message's references to the connection and SoupReader so
+       that they are not disposed of on message free.
+
+2001-11-12  Alex Graveley  <alex@ximian.com>
+
+       * src/libsoup/soup-queue.c (start_request): Impl.  Move request
+       sending code here from soup_queue_connect_cb.
+       (proxy_https_connect): Impl.  Send intermediate CONNECT request to
+       proxy for SSL connections.  If successful, convert connection to
+       SSL.
+       (soup_queue_connect_cb): Call proxy_https_connect if we are using
+       a proxy and the destination is SSL.  Call start_request.
+       (soup_idle_handle_new_requests): If request already has a
+       connection, just call start_request.
+       (soup_get_request_header): Special case URI is hostname:port for
+       CONNECT requests.
+       (soup_queue_read_headers_cb): Special case connection persistence
+       for CONNECT requests: if message is successful, assume valid
+       connection.  Special case body handling for CONNECT requests:
+       assume zero length body.
 
 2001-11-08  Alex Graveley  <alex@ximian.com>
 
 
 2001-11-08  Alex Graveley  <alex@ximian.com>
 
-       * configure.in: Bump HEAD version to 0.6.99.
-       * src/libsoup/soup-private.h (VERSION): Ditto.
-
-2001-11-08  Alex Graveley  <alex@ximian.com>
-
        * configure.in: Version 0.6.0.
        * src/libsoup/soup-private.h (VERSION): Ditto.
 
diff --git a/TODO b/TODO
index 388afa0..6d6e9c9 100644 (file)
--- a/TODO
+++ b/TODO
@@ -11,13 +11,11 @@ TODO:
 
 * Simple Soap Example clients and servers (time, updates, terraserver, ...)
 
-* More SOCKS testing
+* Auto-detect SOCKS version
 
 * Use gconf for config data
 
-* Handle gzip encoded responses, requests
-
-* Server Digest and NTLM authentication
+* Handle gzip transfer encoding
 
 * UDDI support
 
index dbcc441..74aa944 100644 (file)
@@ -5,10 +5,10 @@ dnl *******************************************
 AC_INIT(src/libsoup/soup.h)
 
 # Increment on interface addition. Reset on removal.
-SOUP_AGE=0
+SOUP_AGE=1
 
 # Increment on interface add, remove, or change.
-SOUP_CURRENT=3
+SOUP_CURRENT=4
 
 # Increment on source change. Reset when CURRENT changes.
 SOUP_REVISION=0
@@ -18,7 +18,7 @@ AC_SUBST(SOUP_REVISION)
 AC_SUBST(SOUP_AGE)
 
 # Update in src/soup-core/soup-private.h for Windows
-AM_INIT_AUTOMAKE(soup, 0.6.99)
+AM_INIT_AUTOMAKE(soup, 0.7.99)
 
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
@@ -136,11 +136,28 @@ dnl ***********************
 dnl *** Checks for popt ***
 dnl ***********************
 
-AC_CHECK_LIB(popt, poptGetContext,, AC_MSG_ERROR([popt is required]))
-AC_CHECK_HEADERS(popt.h,, AC_MSG_ERROR([popt.h is required]))
-
-POPT_CFLAGS="$CPPFLAGS"
-POPT_LIBS="$LIBS"
+AC_ARG_WITH(popt-includes,
+           [  --with-popt-includes     Specify location of popt headers],
+           [popt_inc_prefix=-I$withval])
+
+AC_ARG_WITH(popt-libs,
+           [  --with-popt-libs         Specify location of popt libs],
+           [popt_prefix=$withval],
+           [popt_prefix=/usr/lib])
+       
+save_CPPFLAGS=$CPPFLAGS
+save_LIBS=$LIBS
+CPPFLAGS="$CPPFLAGS $popt_inc_prefix"
+LIBS="$LIBS -L$prefix"
+
+AC_CHECK_LIB(popt, 
+            poptGetContext, 
+            [POPT_LIBS="$LIBS -lpopt"] , 
+            AC_MSG_ERROR([popt is required]))
+AC_CHECK_HEADERS(popt.h, [POPT_CFLAGS="$CFLAGS"], AC_MSG_ERROR([popt.h is required]))
+
+CPPFLAGS=$save_CPPFLAGS
+LIBS=$save_LIBS
 
 AC_SUBST(POPT_CFLAGS)
 AC_SUBST(POPT_LIBS)
@@ -185,11 +202,12 @@ dnl *********************************
 AC_CHECK_HEADERS(unistd.h)
 AC_CHECK_HEADERS(netinet/in.h netinet/tcp.h)
 AC_CHECK_HEADERS(sys/socket.h sys/sockio.h sys/poll.h sys/param.h sys/wait.h)
+AC_CHECK_HEADERS(sys/ioctl.h sys/filio.h)
 
 AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
 AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname))
 
-AC_CHECK_FUNCS(inet_pton)
+AC_CHECK_FUNCS(inet_pton inet_aton)
 
 ### Check if we have gethostbyname_r (if so, assume gethostbyaddr_r).
 AC_CHECK_FUNC(gethostbyname_r,
@@ -284,7 +302,9 @@ AC_ARG_WITH(nspr-includes,
            [nspr_inc_prefix=-I$withval])
 
 AC_ARG_WITH(nspr-libs,
-           [  --with-nspr-libs         Specify location of Netscape Portable Runtime libs],[nspr_prefix=-L$withval])
+           [  --with-nspr-libs         Specify location of Netscape Portable Runtime libs],
+           [nspr_prefix=$withval],
+           [nspr_prefix=/usr/lib])
 
 AC_ARG_WITH(nss-includes,
            [  --with-nss-includes      Specify location of NSS header files],
@@ -292,7 +312,8 @@ AC_ARG_WITH(nss-includes,
 
 AC_ARG_WITH(nss-libs,
            [  --with-nss-libs          Specify location of NSS libs],
-           [nss_prefix=-L$withval])
+           [nss_prefix=$withval],
+           [nss_prefix=/usr/lib])
 
 
 AC_ARG_WITH(openssl-includes,
@@ -301,49 +322,80 @@ AC_ARG_WITH(openssl-includes,
 
 AC_ARG_WITH(openssl-libs,
            [  --with-openssl-libs      Specify location of OpenSSL libs],
-           [openssl_prefix=-L$withval], 
-           [openssl_prefix=-L/usr/lib])
-
-enable_openssl="no"
-enable_nss="no"
+           [openssl_prefix=$withval], 
+           [openssl_prefix=/usr/lib])
+
+###
+### Allow for a custom SSL proxy name
+###
+AC_ARG_WITH(ssl-proxy-name,
+           [  --with-ssl-proxy-name    Custom name for ssl proxy executable [default=soup-ssl-proxy]],
+           [SSL_PROXY_NAME=$withval],
+           [SSL_PROXY_NAME=soup-ssl-proxy])
+
+AC_DEFINE_UNQUOTED(SSL_PROXY_NAME, "${SSL_PROXY_NAME}")
+AC_SUBST(SSL_PROXY_NAME)
+
+###
+### Try to link statically with the SSL library
+###
+AC_ARG_ENABLE(ssl-link-static, 
+             [  --enable-static-ssl      Link with SSL library statically [default=no]],
+             [enable_static_ssl=yes])
 
 if test "x$enable_ssl" = xyes; then
+       ###
+       ### Check for OpenSSL
+       ###
        save_CPPFLAGS=$CPPFLAGS
-       CPPFLAGS="$openssl_inc_prefix"
+       CPPFLAGS="$CPPFLAGS $openssl_inc_prefix"
        AC_CHECK_LIB(dl, dlopen, DL_LDFLAGS="-ldl", DL_LDFLAGS="")
        AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h,
-                        [OPENSSL_CFLAGS="$CPPFLAGS" 
-                         OPENSSL_LIBS="$openssl_prefix -lssl -lcrypto $DL_LDFLAGS"
-                         enable_openssl="yes"],
-                        [OPENSSL_CFLAGS="" 
-                         OPENSSL_LIBS="" 
-                         enable_openssl="no"
-                         break])
-       AC_SUBST(OPENSSL_CFLAGS)
-       AC_SUBST(OPENSSL_LIBS)
-       CPPFLAGS=$save_CPPFLAGS
+                        [enable_openssl="yes"],
+                        [enable_openssl="no"; break])
 
        if test "x$enable_openssl" = xyes; then
+               if test "x$enable_static_ssl" = "xyes"; then
+                       OPENSSL_LIBS="$openssl_prefix/libssl.a $openssl_prefix/libcrypto.a"
+               else
+                       OPENSSL_LIBS="-L$openssl_prefix -lssl -lcrypto $DL_LDFLAGS"
+               fi
+               OPENSSL_CFLAGS=$CPPFLAGS
                AC_DEFINE(HAVE_OPENSSL)
+       else
+               OPENSSL_LIBS=
+               OPENSSL_CFLAGS=
        fi
 
+       AC_SUBST(OPENSSL_CFLAGS)
+       AC_SUBST(OPENSSL_LIBS)
+       CPPFLAGS=$save_CPPFLAGS
+
+       ###
+       ### Check for Mozilla NSS
+       ###
        save_CPPFLAGS=$CPPFLAGS
-       CPPFLAGS="$nspr_inc_prefix $nss_inc_prefix"
+       CPPFLAGS="$CPPFLAGS $nspr_inc_prefix $nss_inc_prefix"
        AC_CHECK_HEADERS(nss.h ssl.h pk11func.h,
-                        [NSS_CFLAGS="$CPPFLAGS" 
-                         NSS_LIBS="-lpthread $nspr_prefix -lnspr4 -lplc4 -lplds4 $nss_prefix -lnss3 -lssl3"
-                         enable_nss="yes"],
-                        [NSS_CFLAGS="" 
-                         NSS_LIBS="" 
-                         enable_nss="no"
-                         break])
-       AC_SUBST(NSS_CFLAGS)
-       AC_SUBST(NSS_LIBS)
-       CPPFLAGS=$save_CPPFLAGS
+                        [enable_nss="yes"],
+                        [enable_nss="no"; break])
 
        if test "x$enable_nss" = xyes; then
+               if test "x$enable_static_ssl" = "xyes"; then
+                       NSS_LIBS="-lpthread $nspr_prefix/libnspr4.a $nspr_prefix/libplc4.a $nspr_prefix/libplds4.a $nss_prefix/libnssb.a"
+               else
+                       NSS_LIBS="-lpthread -L$nspr_prefix -lnspr4 -lplc4 -lplds4 $nss_prefix -lnss3 -lssl3"
+               fi
+               NSS_CFLAGS=$CPPFLAGS
                AC_DEFINE(HAVE_NSS)
+       else
+               NSS_LIBS=
+               NSS_CFLAGS=
        fi
+
+       AC_SUBST(NSS_CFLAGS)
+       AC_SUBST(NSS_LIBS)
+       CPPFLAGS=$save_CPPFLAGS
 fi
 
 
index 9387118..e47c575 100644 (file)
@@ -22,10 +22,13 @@ libsoupinclude_HEADERS =    \
        soup-env.h              \
        soup-error.h            \
        soup-fault.h            \
+       soup-headers.h          \
        soup-message.h          \
+       soup-method.h           \
        soup-misc.h             \
        soup-parser.h           \
        soup-serializer.h       \
+       soup-server-auth.h      \
        soup-server.h           \
        soup-socket.h           \
        soup-uri.h
@@ -50,9 +53,9 @@ libsoup_la_SOURCES =          \
        soup-env.c              \
        soup-error.c            \
        soup-fault.c            \
-       soup-headers.h          \
        soup-headers.c          \
        soup-message.c          \
+       soup-method.c           \
        soup-misc.c             \
        soup-nss.h              \
        soup-nss.c              \
@@ -64,6 +67,7 @@ libsoup_la_SOURCES =          \
        soup-queue.c            \
        soup-serializer.c       \
        soup-server.c           \
+       soup-server-auth.c      \
        soup-socket.c           \
        soup-socket-unix.c      \
        soup-socks.h            \
index b5c1813..dd28292 100644 (file)
 #include <stdlib.h>
 #include <glib.h>
 
-#include <ctype.h>
 #include <stdio.h>
 #include <time.h>
 
 #include "md5-utils.h"
 #include "soup-auth.h"
 #include "soup-context.h"
+#include "soup-headers.h"
 #include "soup-message.h"
 #include "soup-private.h"
 #include "soup-ntlm.h"
 
-static char       *copy_token_if_exists (GHashTable  *tokens, char *t);
-static char       *decode_token         (char       **in);
-static GHashTable *parse_param_list     (const char  *header);
-static void        destroy_param_hash   (GHashTable  *table);
-
-
 /* 
  * Basic Authentication Support 
  */
@@ -73,12 +67,12 @@ basic_parse_func (SoupAuth *auth, const char *header)
 
        header += sizeof ("Basic");
 
-       tokens = parse_param_list (header);
+       tokens = soup_header_param_parse_list (header);
        if (!tokens) return;
 
-       auth->realm = copy_token_if_exists (tokens, "realm");
+       auth->realm = soup_header_param_copy_token (tokens, "realm");
 
-       destroy_param_hash (tokens);
+       soup_header_param_destroy_hash (tokens);
 }
 
 static void
@@ -319,6 +313,9 @@ decode_data_type (DataType *dtype, const char *name)
 {
         int i;
 
+       if (!name)
+               return 0;
+
         for (i = 0; dtype[i].name; i++) {
                 if (!g_strcasecmp (dtype[i].name, name))
                        return dtype[i].type;
@@ -348,20 +345,20 @@ digest_parse_func (SoupAuth *auth, const char *header)
 
        header += sizeof ("Digest");
 
-       tokens = parse_param_list (header);
+       tokens = soup_header_param_parse_list (header);
        if (!tokens) return;
 
-       auth->realm = copy_token_if_exists (tokens, "realm");
+       auth->realm = soup_header_param_copy_token (tokens, "realm");
 
-       digest->nonce = copy_token_if_exists (tokens, "nonce");
+       digest->nonce = soup_header_param_copy_token (tokens, "nonce");
 
-       tmp = copy_token_if_exists (tokens, "qop");
+       tmp = soup_header_param_copy_token (tokens, "qop");
        ptr = tmp;
 
        while (ptr && *ptr) {
                char *token;
 
-               token = decode_token (&ptr);
+               token = soup_header_param_decode_token (&ptr);
                if (token)
                        digest->qop_options |= decode_qop (token);
                g_free (token);
@@ -372,7 +369,7 @@ digest_parse_func (SoupAuth *auth, const char *header)
 
        g_free (tmp);
 
-       tmp = copy_token_if_exists (tokens, "stale");
+       tmp = soup_header_param_copy_token (tokens, "stale");
 
        if (tmp && g_strcasecmp (tmp, "true") == 0)
                digest->stale = TRUE;
@@ -381,11 +378,11 @@ digest_parse_func (SoupAuth *auth, const char *header)
 
        g_free (tmp);
 
-       tmp = copy_token_if_exists (tokens, "algorithm");
+       tmp = soup_header_param_copy_token (tokens, "algorithm");
        digest->algorithm = decode_algorithm (tmp);
        g_free (tmp);
 
-       destroy_param_hash (tokens);
+       soup_header_param_destroy_hash (tokens);
 }
 
 static void
@@ -394,18 +391,21 @@ digest_init_func (SoupAuth *auth, const SoupUri *uri)
        SoupAuthDigest *digest = (SoupAuthDigest *) auth;
        MD5Context ctx;
        guchar d[16];
-       char *tmp;
 
        digest->user = g_strdup (uri->user);
 
        /* compute A1 */
        md5_init (&ctx);
+
        md5_update (&ctx, uri->user, strlen (uri->user));
+
        md5_update (&ctx, ":", 1);
-       md5_update (&ctx, auth->realm, strlen (auth->realm));
+       if (auth->realm)
+               md5_update (&ctx, auth->realm, strlen (auth->realm));
+
        md5_update (&ctx, ":", 1);
-       tmp = uri->passwd ? uri->passwd : "";
-       md5_update (&ctx, tmp, strlen (tmp));
+       if (uri->passwd)
+               md5_update (&ctx, uri->passwd, strlen (uri->passwd));
 
        if (digest->algorithm == ALGORITHM_MD5_SESS) {
                md5_final (&ctx, d);
@@ -546,9 +546,7 @@ ntlm_init (SoupAuth *sa, const SoupUri *uri)
        domain = ntlm_get_authmech_token (uri, "domain=");
 
        if (strlen (auth->header) < sizeof ("NTLM"))
-               auth->response = 
-                       soup_ntlm_request (host ? host : "UNKNOWN", 
-                                          domain ? domain : "UNKNOWN");
+               auth->response = soup_ntlm_request ();
        else {
                gchar lm_hash [21], nt_hash [21];
 
@@ -560,8 +558,8 @@ ntlm_init (SoupAuth *sa, const SoupUri *uri)
                                            uri->user,
                                            (gchar *) &lm_hash,
                                            (gchar *) &nt_hash,
-                                           host ? host : "UNKNOWN",
-                                           domain ? domain : "UNKNOWN");
+                                           host,
+                                           domain);
                auth->completed = TRUE;
        }
 
@@ -761,152 +759,3 @@ soup_auth_invalidates_prior (SoupAuth *new_auth, SoupAuth *old_auth)
 
        return new_auth->compare_func (new_auth, old_auth);
 }
-
-
-/*
- * Internal parsing routines
- */
-
-static char *
-copy_token_if_exists (GHashTable *tokens, char *t)
-{
-       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;
-}
-
-static void
-decode_lwsp (char **in)
-{
-       char *inptr = *in;
-
-       while (isspace (*inptr))
-               inptr++;
-
-       *in = inptr;
-}
-
-static char *
-decode_quoted_string (char **in)
-{
-       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;
-}
-
-static char *
-decode_token (char **in)
-{
-       char *inptr = *in;
-       char *start;
-
-       decode_lwsp (&inptr);
-       start = inptr;
-
-       while (*inptr && *inptr != '=' && *inptr != ',')
-               inptr++;
-
-       if (inptr > start) {
-               *in = inptr;
-               return g_strndup (start, inptr - start);
-       }
-       else
-               return NULL;
-}
-
-static char *
-decode_value (char **in)
-{
-       char *inptr = *in;
-
-       decode_lwsp (&inptr);
-       if (*inptr == '"')
-               return decode_quoted_string (in);
-       else
-               return decode_token (in);
-}
-
-static GHashTable *
-parse_param_list (const char *header)
-{
-       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 = decode_token (&ptr);
-               if (*ptr == '=') {
-                       ptr++;
-                       value = decode_value (&ptr);
-                       g_hash_table_insert (params, name, value);
-                       added = TRUE;
-               }
-
-               if (*ptr == ',')
-                       ptr++;
-       }
-
-       if (!added) {
-               g_hash_table_destroy (params);
-               params = NULL;
-       }
-
-       return params;
-}
-
-static void
-destroy_param_hash_elements (gpointer key, gpointer value, gpointer user_data)
-{
-       g_free (key);
-       g_free (value);
-}
-
-static void
-destroy_param_hash (GHashTable *table)
-{
-       g_hash_table_foreach (table, destroy_param_hash_elements, NULL);
-       g_hash_table_destroy (table);
-}
index 94070c3..28d2c5f 100644 (file)
@@ -284,19 +284,53 @@ struct SoupConnectData {
 };
 
 static void
+prune_connection_foreach (gchar               *hostname,
+                         SoupHost            *serv,
+                         SoupConnection     **last)
+{
+       GSList *conns = serv->connections;
+
+       while (conns) {
+               SoupConnection *conn = conns->data;
+
+               if (!conn->in_use) {
+                       if (*last == NULL ||
+                           (*last)->last_used_id > conn->last_used_id)
+                               *last = conn;
+               }
+
+               conns = conns->next;
+       }
+}
+
+static gboolean
+prune_least_used_connection (void)
+{
+       SoupConnection *last = NULL;
+
+       g_hash_table_foreach (soup_hosts, 
+                             (GHFunc) prune_connection_foreach, 
+                             &last);
+       if (last) {
+               connection_free (last);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean retry_connect_timeout_cb (struct SoupConnectData *data);
+
+static void
 soup_context_connect_cb (SoupSocket              *socket,
                         SoupSocketConnectStatus  status,
                         gpointer                 user_data)
 {
        struct SoupConnectData *data = user_data;
        SoupContext            *ctx = data->ctx;
-       SoupConnectCallbackFn   cb = data->cb;
-       gpointer                cb_data = data->user_data;
        SoupConnection         *new_conn;
        GIOChannel             *chan;
 
-       g_free (data);
-
        switch (status) {
        case SOUP_SOCKET_CONNECT_ERROR_NONE:
                new_conn = g_new0 (SoupConnection, 1);
@@ -307,8 +341,8 @@ soup_context_connect_cb (SoupSocket              *socket,
                new_conn->in_use = TRUE;
                new_conn->last_used_id = 0;
 
-               soup_context_ref (ctx);
                new_conn->context = ctx;
+               soup_context_ref (ctx);
 
                chan = soup_connection_get_iochannel (new_conn);
                new_conn->death_tag = 
@@ -319,95 +353,129 @@ soup_context_connect_cb (SoupSocket              *socket,
                g_io_channel_unref (chan);
 
                ctx->server->connections =
-                       g_slist_prepend (ctx->server->connections,
-                                        new_conn);
+                       g_slist_prepend (ctx->server->connections, new_conn);
 
-               (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, new_conn, cb_data);
+               (*data->cb) (ctx, 
+                            SOUP_CONNECT_ERROR_NONE, 
+                            new_conn, 
+                            data->user_data);
                break;
        case SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE:
                connection_count--;
 
-               (*cb) (ctx, SOUP_CONNECT_ERROR_ADDR_RESOLVE, NULL, cb_data);
+               (*data->cb) (ctx, 
+                            SOUP_CONNECT_ERROR_ADDR_RESOLVE, 
+                            NULL, 
+                            data->user_data);
                break;
        case SOUP_SOCKET_CONNECT_ERROR_NETWORK:
                connection_count--;
 
-               (*cb) (ctx, SOUP_CONNECT_ERROR_NETWORK, NULL, cb_data);
+               /*
+                * Check if another connection exists to this server
+                * before reporting error. 
+                */
+               if (ctx->server->connections) {
+                       data->timeout_tag =
+                               g_timeout_add (
+                                       150,
+                                       (GSourceFunc) retry_connect_timeout_cb,
+                                       data);
+                       return;
+               }
+
+               (*data->cb) (ctx, 
+                            SOUP_CONNECT_ERROR_NETWORK, 
+                            NULL, 
+                            data->user_data);
                break;
        }
+
+       soup_context_unref (ctx);
+       g_free (data);
 }
 
-static SoupConnection *
-soup_try_existing_connections (SoupContext *ctx)
+static gboolean
+try_existing_connections (SoupContext           *ctx,
+                         SoupConnectCallbackFn  cb,
+                         gpointer               user_data)
 {
        GSList *conns = ctx->server->connections;
        
        while (conns) {
                SoupConnection *conn = conns->data;
 
-               if (!conn->in_use &&
-                   conn->port == (guint) ctx->uri->port &&
-                   conn->keep_alive)
-                       return conn;
-
-               conns = conns->next;
-       }
-
-       return NULL;
-}
-
-static void
-soup_prune_foreach (gchar               *hostname,
-                   SoupHost            *serv,
-                   SoupConnection     **last)
-{
-       GSList *conns = serv->connections;
+               if (conn->in_use == FALSE &&
+                   conn->keep_alive == TRUE &&
+                   conn->port == (guint) ctx->uri->port) {
+                       /* Set connection to in use */
+                       conn->in_use = TRUE;
 
-       while (conns) {
-               SoupConnection *conn = conns->data;
+                       /* Reset connection context */
+                       soup_context_ref (ctx);
+                       soup_context_unref (conn->context);
+                       conn->context = ctx;
 
-               if (!conn->in_use) {
-                       if (*last == NULL ||
-                           (*last)->last_used_id > conn->last_used_id)
-                               *last = conn;
+                       /* Issue success callback */
+                       (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data);
+                       return TRUE;
                }
 
                conns = conns->next;
        }
-}
-
-static gboolean
-soup_prune_least_used_connection (void)
-{
-       SoupConnection *last = NULL;
-
-       g_hash_table_foreach (soup_hosts, 
-                             (GHFunc) soup_prune_foreach, 
-                             &last);
-
-       if (last) {
-               connection_free (last);
-               return TRUE;
-       }
 
        return FALSE;
 }
 
 static gboolean
-soup_prune_timeout (struct SoupConnectData *data)
+try_create_connection (struct SoupConnectData **dataptr)
 {
+       struct SoupConnectData *data = *dataptr;
        gint conn_limit = soup_get_connection_limit ();
+       gpointer connect_tag;
 
+       /* 
+        * Check if we are allowed to create a new connection, otherwise wait
+        * for next timeout.  
+        */
        if (conn_limit &&
            connection_count >= conn_limit &&
-           !soup_try_existing_connections (data->ctx) &&
-           !soup_prune_least_used_connection ())
-               return TRUE;
+           !prune_least_used_connection ()) {
+               data->connect_tag = 0;
+               return FALSE;
+       }
 
-       soup_context_get_connection (data->ctx, data->cb, data->user_data);
-       g_free (data);
+       connection_count++;
+
+       data->timeout_tag = 0;
+       connect_tag = soup_socket_connect (data->ctx->uri->host,
+                                          data->ctx->uri->port,
+                                          soup_context_connect_cb,
+                                          data);
+       /* 
+        * NOTE: soup_socket_connect can fail immediately and call our
+        * callback which will delete the state.  
+        */
+       if (connect_tag)
+               data->connect_tag = connect_tag;
+       else
+               *dataptr = NULL;
 
-       return FALSE;
+       return TRUE;
+}
+
+static gboolean
+retry_connect_timeout_cb (struct SoupConnectData *data)
+{
+       if (try_existing_connections (data->ctx, 
+                                     data->cb, 
+                                     data->user_data)) {
+               soup_context_unref (data->ctx);
+               g_free (data);
+               return FALSE;
+       }
+
+       return try_create_connection (&data) == FALSE;
 }
 
 /**
@@ -436,46 +504,26 @@ soup_context_get_connection (SoupContext           *ctx,
                             SoupConnectCallbackFn  cb,
                             gpointer               user_data)
 {
-       SoupConnection *conn;
        struct SoupConnectData *data;
-       gint conn_limit;
 
        g_return_val_if_fail (ctx != NULL, NULL);
 
-       if ((conn = soup_try_existing_connections (ctx))) {
-               conn->in_use = TRUE;
-
-               soup_context_ref (ctx);
-               soup_context_unref (conn->context);
-               conn->context = ctx;
-
-               (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data);
+       /* Look for an existing unused connection */
+       if (try_existing_connections (ctx, cb, user_data))
                return NULL;
-       }
 
        data = g_new0 (struct SoupConnectData, 1);
-       data->ctx = ctx;
        data->cb = cb;
        data->user_data = user_data;
 
-       conn_limit = soup_get_connection_limit ();
+       data->ctx = ctx;
+       soup_context_ref (ctx);
 
-       if (conn_limit &&
-           connection_count >= conn_limit &&
-           !soup_prune_least_used_connection ())
+       if (!try_create_connection (&data))
                data->timeout_tag =
-                       g_timeout_add (500,
-                                      (GSourceFunc) soup_prune_timeout,
+                       g_timeout_add (150,
+                                      (GSourceFunc) retry_connect_timeout_cb,
                                       data);
-       else {
-               connection_count++;
-
-               data->connect_tag =
-                       soup_socket_connect (ctx->uri->host,
-                                            ctx->uri->port,
-                                            soup_context_connect_cb,
-                                            data);
-       }
 
        return data;
 }
index 79fe298..496cd80 100644 (file)
@@ -61,6 +61,7 @@ typedef enum {
        SOUP_ERROR_MULTIPLE_CHOICES         = 300,
        SOUP_ERROR_MOVED_PERMANANTLY        = 301,
        SOUP_ERROR_FOUND                    = 302,
+       SOUP_ERROR_MOVED_TEMPORARILY        = SOUP_ERROR_FOUND,
        SOUP_ERROR_SEE_OTHER                = 303,
        SOUP_ERROR_NOT_MODIFIED             = 304,
        SOUP_ERROR_USE_PROXY                = 305,
index 6212774..99cf232 100644 (file)
 
 #include <string.h>
 #include <stdio.h>
+#include <ctype.h>
 
 #include "soup-headers.h"
+#include "soup-private.h"
 
 /*
  * "HTTP/1.1 200 OK\r\nContent-Length: 1234\r\n          567\r\n\r\n"
@@ -150,29 +152,21 @@ soup_headers_parse_request (gchar            *str,
 }
 
 gboolean
-soup_headers_parse_response (gchar            *str, 
-                            gint              len, 
-                            GHashTable       *dest,
-                            SoupHttpVersion  *ver,
-                            guint            *status_code,
-                            gchar           **status_phrase)
+soup_headers_parse_status_line (const char       *status_line,
+                               SoupHttpVersion  *ver,
+                               guint            *status_code,
+                               gchar           **status_phrase)
 {
-       guint http_major, http_minor;
+       guint http_major, http_minor, code;
        guint phrase_start = 0;
 
-       if (!str || !*str || len < sizeof ("HTTP/0.0 000 A\r\n\r\n"))
-               goto THROW_MALFORMED_HEADER;
-
-       if (sscanf (str, 
+       if (sscanf (status_line, 
                    "HTTP/%1u.%1u %3u %n", 
                    &http_major,
                    &http_minor,
-                   status_code, 
+                   &code, 
                    &phrase_start) < 3 || !phrase_start)
-               goto THROW_MALFORMED_HEADER;
-
-       if (!soup_headers_parse (str, len, dest)) 
-               goto THROW_MALFORMED_HEADER;
+               return FALSE;
 
        if (ver) {
                if (http_major == 1 && http_minor == 1) 
@@ -181,10 +175,186 @@ soup_headers_parse_response (gchar            *str,
                        *ver = SOUP_HTTP_1_0;
        }
 
-       *status_phrase = g_strdup (&str [phrase_start]);
+       if (status_code)
+               *status_code = code;
+
+       if (status_phrase)
+               *status_phrase = g_strdup (status_line + phrase_start);
+
+       return TRUE;
+}
+
+gboolean
+soup_headers_parse_response (gchar            *str, 
+                            gint              len, 
+                            GHashTable       *dest,
+                            SoupHttpVersion  *ver,
+                            guint            *status_code,
+                            gchar           **status_phrase)
+{
+       if (!str || !*str || len < sizeof ("HTTP/0.0 000 A\r\n\r\n"))
+               goto THROW_MALFORMED_HEADER;
+
+       if (!soup_headers_parse (str, len, dest)) 
+               goto THROW_MALFORMED_HEADER;
+
+       if (!soup_headers_parse_status_line (str, 
+                                            ver, 
+                                            status_code, 
+                                            status_phrase))
+               goto THROW_MALFORMED_HEADER;
 
        return TRUE;
 
  THROW_MALFORMED_HEADER:
        return FALSE;
 }
+
+
+/*
+ * HTTP parameterized header parsing
+ */
+
+char *
+soup_header_param_copy_token (GHashTable *tokens, char *t)
+{
+       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;
+}
+
+static void
+decode_lwsp (char **in)
+{
+       char *inptr = *in;
+
+       while (isspace (*inptr))
+               inptr++;
+
+       *in = inptr;
+}
+
+static char *
+decode_quoted_string (char **in)
+{
+       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;
+}
+
+char *
+soup_header_param_decode_token (char **in)
+{
+       char *inptr = *in;
+       char *start;
+
+       decode_lwsp (&inptr);
+       start = inptr;
+
+       while (*inptr && *inptr != '=' && *inptr != ',')
+               inptr++;
+
+       if (inptr > start) {
+               *in = inptr;
+               return g_strndup (start, inptr - start);
+       }
+       else
+               return NULL;
+}
+
+static char *
+decode_value (char **in)
+{
+       char *inptr = *in;
+
+       decode_lwsp (&inptr);
+       if (*inptr == '"')
+               return decode_quoted_string (in);
+       else
+               return soup_header_param_decode_token (in);
+}
+
+GHashTable *
+soup_header_param_parse_list (const char *header)
+{
+       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;
+               }
+
+               if (*ptr == ',')
+                       ptr++;
+       }
+
+       if (!added) {
+               g_hash_table_destroy (params);
+               params = NULL;
+       }
+
+       return params;
+}
+
+static void
+destroy_param_hash_elements (gpointer key, gpointer value, gpointer user_data)
+{
+       g_free (key);
+       g_free (value);
+}
+
+void
+soup_header_param_destroy_hash (GHashTable *table)
+{
+       g_hash_table_foreach (table, destroy_param_hash_elements, NULL);
+       g_hash_table_destroy (table);
+}
index 6139269..5f33480 100644 (file)
 
 /* HTTP Header Parsing */
 
-gboolean       soup_headers_parse_request  (gchar            *str, 
-                                           gint              len, 
-                                           GHashTable       *dest, 
-                                           gchar           **req_method,
-                                           gchar           **req_path,
-                                           SoupHttpVersion  *ver);
-
-gboolean       soup_headers_parse_response (gchar            *str, 
-                                           gint              len, 
-                                           GHashTable       *dest,
-                                           SoupHttpVersion  *ver,
-                                           guint            *status_code,
-                                           gchar           **status_phrase);
+gboolean    soup_headers_parse_request      (gchar            *str, 
+                                            gint              len, 
+                                            GHashTable       *dest, 
+                                            gchar           **req_method,
+                                            gchar           **req_path,
+                                            SoupHttpVersion  *ver);
+
+gboolean    soup_headers_parse_status_line  (const char        *status_line,
+                                            SoupHttpVersion  *ver,
+                                            guint            *status_code,
+                                            gchar           **status_phrase);
+
+gboolean    soup_headers_parse_response     (gchar            *str, 
+                                            gint              len, 
+                                            GHashTable       *dest,
+                                            SoupHttpVersion  *ver,
+                                            guint            *status_code,
+                                            gchar           **status_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);
 
 #endif /*SOUP_HEADERS_H*/
index f8919c4..654ee19 100644 (file)
@@ -47,12 +47,9 @@ soup_message_new (SoupContext *context, const gchar *method)
 {
        SoupMessage *ret;
 
-       g_return_val_if_fail (context, NULL);
-
        ret          = g_new0 (SoupMessage, 1);
        ret->priv    = g_new0 (SoupMessagePrivate, 1);
        ret->status  = SOUP_STATUS_IDLE;
-       ret->context = context;
        ret->method  = method ? method : SOUP_METHOD_GET;
 
        ret->request_headers = g_hash_table_new (soup_str_case_hash, 
@@ -63,7 +60,7 @@ soup_message_new (SoupContext *context, const gchar *method)
 
        ret->priv->http_version = SOUP_HTTP_1_1;
 
-       soup_context_ref (context);
+       soup_message_set_context (ret, context);
 
        return ret;
 }
@@ -102,6 +99,76 @@ soup_message_new_full (SoupContext   *context,
        return ret;
 }
 
+static void
+copy_header (gchar *key, gchar *value, GHashTable *dest_hash)
+{
+       soup_message_add_header (dest_hash, key, value);
+}
+
+/*
+ * Copy context, method, error, request buffer, response buffer, request
+ * headers, response headers, message flags, http version.  
+ *
+ * Callback function, content handlers, message status, server, server socket,
+ * and server message are NOT copied.  
+ */
+SoupMessage *
+soup_message_copy (SoupMessage *req)
+{
+       SoupMessage *cpy;
+
+       cpy = soup_message_new (req->context, req->method);
+
+       cpy->errorcode = req->errorcode;
+       cpy->errorclass = req->errorclass;
+       cpy->errorphrase = req->errorphrase;
+
+       cpy->request.owner = SOUP_BUFFER_SYSTEM_OWNED;
+       cpy->request.length = cpy->request.length;
+       cpy->request.body = g_memdup (req->request.body, 
+                                     req->request.length);
+
+       cpy->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+       cpy->response.length = cpy->response.length;
+       cpy->response.body = g_memdup (req->response.body, 
+                                      req->response.length);
+
+       soup_message_foreach_header (req->request_headers,
+                                    (GHFunc) copy_header,
+                                    cpy->request_headers);
+
+       soup_message_foreach_header (req->response_headers,
+                                    (GHFunc) copy_header,
+                                    cpy->response_headers);
+
+       cpy->priv->msg_flags = req->priv->msg_flags;
+
+       /* 
+        * Note: We don't copy content handlers or callback function as this is
+        * a nightmare double free situation.  
+        */
+
+       cpy->priv->http_version = req->priv->http_version;
+
+       return cpy;
+}
+
+static void 
+release_connection (const SoupDataBuffer *data,
+                   gpointer              user_data)
+{
+       SoupConnection *conn = user_data;
+       soup_connection_release (conn);
+}
+
+static void 
+release_and_close_connection (gboolean headers_done, gpointer user_data)
+{
+       SoupConnection *conn = user_data;
+       soup_connection_set_keep_alive (conn, FALSE);
+       soup_connection_release (conn);
+}
+
 /**
  * soup_message_cleanup:
  * @req: a %SoupMessage.
@@ -115,6 +182,19 @@ soup_message_cleanup (SoupMessage *req)
 {
        g_return_if_fail (req != NULL);
 
+       if (req->connection && 
+           req->priv->read_tag &&
+           req->status == SOUP_STATUS_READING_RESPONSE) {
+               soup_transfer_read_set_callbacks (req->priv->read_tag,
+                                                 NULL,
+                                                 NULL,
+                                                 release_connection,
+                                                 release_and_close_connection,
+                                                 req->connection);
+               req->priv->read_tag = 0;
+               req->connection = NULL;
+       }
+
        if (req->priv->read_tag) {
                soup_transfer_read_cancel (req->priv->read_tag);
                req->priv->read_tag = 0;
@@ -129,12 +209,13 @@ soup_message_cleanup (SoupMessage *req)
                soup_context_cancel_connect (req->priv->connect_tag);
                req->priv->connect_tag = NULL;
        }
+
        if (req->connection) {
                soup_connection_release (req->connection);
                req->connection = NULL;
        }
 
-       soup_active_requests = g_slist_remove (soup_active_requests, req);
+       soup_queue_remove_request (req);
 }
 
 static void
@@ -156,16 +237,14 @@ handler_free (SoupHandlerData *data)
 static void
 finalize_message (SoupMessage *req)
 {
-       soup_context_unref (req->context);
+       if (req->context)
+               soup_context_unref (req->context);
 
        if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (req->request.body);
        if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (req->response.body);
 
-       if (req->priv->req_header) 
-               g_string_free (req->priv->req_header, TRUE);
-
        soup_message_clear_headers (req->request_headers);
        g_hash_table_destroy (req->request_headers);
 
@@ -458,9 +537,114 @@ soup_message_queue (SoupMessage    *req,
                    SoupCallbackFn  callback, 
                    gpointer        user_data)
 {
+       g_return_if_fail (req != NULL);
        soup_queue_message (req, callback, user_data);
 }
 
+typedef struct {
+       SoupMessage *msg;
+       SoupAuth    *conn_auth;
+} RequeueConnectData;
+
+static void
+requeue_connect_cb (SoupContext          *ctx,
+                   SoupConnectErrorCode  err,
+                   SoupConnection       *conn,
+                   gpointer              user_data)
+{
+       RequeueConnectData *data = user_data;
+
+       if (conn && !conn->auth)
+               conn->auth = data->conn_auth;
+       else
+               soup_auth_free (data->conn_auth);
+
+       soup_queue_connect_cb (ctx, err, conn, data->msg);
+       if (data->msg->errorcode)
+               soup_message_issue_callback (data->msg);
+
+       g_free (data);
+}
+
+static void
+requeue_read_error (gboolean body_started, gpointer user_data)
+{
+       RequeueConnectData *data = user_data;
+       SoupMessage *msg = data->msg;
+       SoupContext *dest_ctx = msg->connection->context;
+
+       soup_context_ref (dest_ctx);
+
+       soup_connection_set_keep_alive (msg->connection, FALSE);
+       soup_connection_release (msg->connection);
+       msg->connection = NULL;
+
+       soup_queue_message (msg, 
+                           msg->priv->callback, 
+                           msg->priv->user_data);
+
+       msg->status = SOUP_STATUS_CONNECTING;
+
+       msg->priv->connect_tag =
+               soup_context_get_connection (dest_ctx, 
+                                            requeue_connect_cb, 
+                                            data);
+
+       soup_context_unref (dest_ctx);
+}
+
+static void
+requeue_read_finished (const SoupDataBuffer *buf,
+                      gpointer        user_data)
+{
+       RequeueConnectData *data = user_data;
+       SoupMessage *msg = data->msg;
+       SoupConnection *conn = msg->connection;
+
+       if (!soup_connection_is_keep_alive (msg->connection))
+               requeue_read_error (FALSE, data);
+       else {
+               msg->connection = NULL;
+
+               soup_queue_message (msg, 
+                                   msg->priv->callback, 
+                                   msg->priv->user_data);
+
+               msg->connection = conn;
+       }
+}
+
+/**
+ * soup_message_requeue:
+ * @req: a %SoupMessage
+ *
+ * This causes @req to be placed back on the queue to be attempted again.
+ **/
+void
+soup_message_requeue (SoupMessage *req)
+{
+       g_return_if_fail (req != NULL);
+
+       if (req->connection && req->connection->auth && req->priv->read_tag) {
+               RequeueConnectData *data = NULL;
+
+               data = g_new0 (RequeueConnectData, 1);
+               data->msg = req;
+               data->conn_auth = req->connection->auth;
+
+               soup_transfer_read_set_callbacks (req->priv->read_tag,
+                                                 NULL,
+                                                 NULL,
+                                                 requeue_read_finished,
+                                                 requeue_read_error,
+                                                 data);
+               req->priv->read_tag = 0;
+       } else
+               soup_queue_message (req, 
+                                   req->priv->callback, 
+                                   req->priv->user_data);
+}
+
 /**
  * soup_message_send:
  * @msg: a %SoupMessage.
@@ -480,9 +664,14 @@ soup_message_send (SoupMessage *msg)
 
        while (1) {
                g_main_iteration (TRUE); 
+
                if (msg->status == SOUP_STATUS_FINISHED || 
                    SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
                        break;
+
+               /* Quit if soup_shutdown has been called */ 
+               if (!soup_initialized)
+                       return SOUP_ERROR_CANCELLED;
        }
 
        return msg->errorclass;
@@ -539,7 +728,11 @@ authorize_handler (SoupMessage *msg, gpointer user_data)
         */
        soup_auth_initialize (auth, uri);
 
-       old_auth = soup_auth_lookup (ctx);
+       if (auth->type == SOUP_AUTH_TYPE_NTLM)
+               old_auth = msg->connection->auth;
+       else
+               old_auth = soup_auth_lookup (ctx);
+
        if (old_auth) {
                if (!soup_auth_invalidates_prior (auth, old_auth)) {
                        soup_auth_free (auth);
@@ -547,7 +740,12 @@ authorize_handler (SoupMessage *msg, gpointer user_data)
                }
        }
 
-       soup_auth_set_context (auth, ctx);
+       if (auth->type == SOUP_AUTH_TYPE_NTLM) {
+               if (old_auth) 
+                       soup_auth_free (old_auth);
+               msg->connection->auth = auth;
+       } else
+               soup_auth_set_context (auth, ctx);
 
         return SOUP_HANDLER_RESEND;
 
@@ -810,7 +1008,6 @@ timeout_handler (gpointer user_data)
        SoupHandlerData *data = user_data;
        SoupMessage *msg = data->msg;
        SoupHandlerResult result;
-       GSList *iter;
 
        switch (data->type) {
        case SOUP_HANDLER_PREPARE:
@@ -824,13 +1021,6 @@ timeout_handler (gpointer user_data)
        case SOUP_HANDLER_FINISHED:
                if (msg->status == SOUP_STATUS_FINISHED)
                        goto REMOVE_SOURCE;
-       case SOUP_HANDLER_DATA_SENT:
-               iter = msg->priv->content_handlers; 
-               while (iter) {
-                       SoupHandlerData *hd = iter->data;
-                       if (!g_strcasecmp (hd->name, "server-message"))
-                               goto REMOVE_SOURCE;
-               }
        }
 
        result = (*data->handler_cb) (msg, data->user_data);
@@ -1060,10 +1250,12 @@ soup_message_set_context (SoupMessage       *msg,
                          SoupContext       *new_ctx)
 {
        g_return_if_fail (msg != NULL);
-       g_return_if_fail (new_ctx != NULL);
 
-       soup_context_unref (msg->context);
-       soup_context_ref (new_ctx);
+       if (msg->context)
+               soup_context_unref (msg->context);
+
+       if (new_ctx)
+               soup_context_ref (new_ctx);
 
        msg->context = new_ctx;
 }
@@ -1073,7 +1265,9 @@ soup_message_get_context (SoupMessage       *msg)
 {
        g_return_val_if_fail (msg != NULL, NULL);
 
-       soup_context_ref (msg->context);
+       if (msg->context)
+               soup_context_ref (msg->context);
+
        return msg->context;
 }
 
index c93c177..98b3b22 100644 (file)
@@ -14,6 +14,7 @@
 #include <glib.h>
 #include <libsoup/soup-context.h>
 #include <libsoup/soup-error.h>
+#include <libsoup/soup-method.h>
 
 typedef enum {
        SOUP_STATUS_IDLE = 0,
@@ -36,21 +37,6 @@ typedef struct {
        guint          length;
 } SoupDataBuffer;
 
-#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_SEARCH    "SEARCH"
-
 typedef struct _SoupMessage        SoupMessage;
 typedef struct _SoupMessagePrivate SoupMessagePrivate;
 
@@ -92,6 +78,8 @@ SoupMessage   *soup_message_new_full            (SoupContext       *context,
                                                 gchar             *req_body,
                                                 gulong             req_length);
 
+SoupMessage   *soup_message_copy                (SoupMessage       *req);
+
 void           soup_message_free                (SoupMessage       *req);
 
 void           soup_message_cancel              (SoupMessage       *req);
@@ -102,6 +90,8 @@ void           soup_message_queue               (SoupMessage       *req,
                                                 SoupCallbackFn     callback, 
                                                 gpointer           user_data);
 
+void           soup_message_requeue             (SoupMessage       *req);
+
 void           soup_message_add_header          (GHashTable        *hash,
                                                 const gchar       *name,
                                                 const gchar       *value);
@@ -183,18 +173,10 @@ guint          soup_message_get_flags           (SoupMessage        *msg);
  * Handler Registration 
  */
 typedef enum {
-       /*
-        * Client-side events
-        */
        SOUP_HANDLER_PREPARE = 0,
        SOUP_HANDLER_HEADERS,
        SOUP_HANDLER_DATA,
        SOUP_HANDLER_FINISHED,
-
-       /*
-        * Server-side events
-        */
-       SOUP_HANDLER_DATA_SENT
 } SoupHandlerEvent;
 
 enum {
index 400cddc..1310fe5 100644 (file)
@@ -5,10 +5,7 @@
  * Authors:
  *      Alex Graveley (alex@ximian.com)
  *
- * soup_base64_encode() written by Chris Blizzard <blizzard@mozilla.org>,
- * and is Copyright (C) 1998 Free Software Foundation.
- *
- * All else Copyright (C) 2000, Helix Code, Inc.
+ * Copyright (C) 2000, Helix Code, Inc.
  */
 
 #include <ctype.h>
@@ -22,7 +19,7 @@
 
 gboolean soup_initialized = FALSE;
 
-static guint max_connections = 0;
+static guint max_connections = 10;
 
 static SoupContext *proxy_context = NULL;
 
@@ -149,8 +146,158 @@ soup_substring_index (gchar *str, gint len, gchar *substr)
        return -1;
 }
 
-const char base64_alphabet[65] =
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+/* Base64 utils (straight from camel-mime-utils.c) */
+#define d(x)
+
+static char *base64_alphabet =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* 
+ * call this when finished encoding everything, to
+ * flush off the last little bit 
+ */
+int
+soup_base64_encode_close (const guchar  *in, 
+                         int            inlen, 
+                         gboolean       break_lines, 
+                         guchar        *out, 
+                         int           *state, 
+                         int           *save)
+{
+       int c1, c2;
+       unsigned char *outptr = out;
+
+       if (inlen > 0)
+               outptr += soup_base64_encode_step (in, 
+                                                  inlen, 
+                                                  break_lines, 
+                                                  outptr, 
+                                                  state, 
+                                                  save);
+
+       c1 = ((unsigned char *) save) [1];
+       c2 = ((unsigned char *) save) [2];
+       
+       d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
+                (int)((char *) save) [0],
+                (int)((char *) save) [1],
+                (int)((char *) save) [2]));
+
+       switch (((char *) save) [0]) {
+       case 2:
+               outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
+               g_assert (outptr [2] != 0);
+               goto skip;
+       case 1:
+               outptr[2] = '=';
+       skip:
+               outptr [0] = base64_alphabet [ c1 >> 2 ];
+               outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
+               outptr [3] = '=';
+               outptr += 4;
+               break;
+       }
+       if (break_lines)
+               *outptr++ = '\n';
+
+       *save = 0;
+       *state = 0;
+
+       return outptr-out;
+}
+
+/*
+ * performs an 'encode step', only encodes blocks of 3 characters to the
+ * output at a time, saves left-over state in state and save (initialise to
+ * 0 on first invocation).
+ */
+int
+soup_base64_encode_step (const guchar  *in, 
+                        int            len, 
+                        gboolean       break_lines, 
+                        guchar        *out, 
+                        int           *state, 
+                        int           *save)
+{
+       register guchar *outptr;
+       register const guchar *inptr;
+
+       if (len <= 0)
+               return 0;
+
+       inptr = in;
+       outptr = out;
+
+       d (printf ("we have %d chars, and %d saved chars\n", 
+                  len, 
+                  ((char *) save) [0]));
+
+       if (len + ((char *) save) [0] > 2) {
+               const guchar *inend = in+len-2;
+               register int c1, c2, c3;
+               register int already;
+
+               already = *state;
+
+               switch (((char *) save) [0]) {
+               case 1: c1 = ((unsigned char *) save) [1]; goto skip1;
+               case 2: c1 = ((unsigned char *) save) [1];
+                       c2 = ((unsigned char *) save) [2]; goto skip2;
+               }
+               
+               /* 
+                * yes, we jump into the loop, no i'm not going to change it, 
+                * it's beautiful! 
+                */
+               while (inptr < inend) {
+                       c1 = *inptr++;
+               skip1:
+                       c2 = *inptr++;
+               skip2:
+                       c3 = *inptr++;
+                       *outptr++ = base64_alphabet [ c1 >> 2 ];
+                       *outptr++ = base64_alphabet [ c2 >> 4 | 
+                                                     ((c1&0x3) << 4) ];
+                       *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | 
+                                                     (c3 >> 6) ];
+                       *outptr++ = base64_alphabet [ c3 & 0x3f ];
+                       /* this is a bit ugly ... */
+                       if (break_lines && (++already)>=19) {
+                               *outptr++='\n';
+                               already = 0;
+                       }
+               }
+
+               ((char *)save)[0] = 0;
+               len = 2-(inptr-inend);
+               *state = already;
+       }
+
+       d(printf("state = %d, len = %d\n",
+                (int)((char *)save)[0],
+                len));
+
+       if (len>0) {
+               register char *saveout;
+
+               /* points to the slot for the next char to save */
+               saveout = & (((char *)save)[1]) + ((char *)save)[0];
+
+               /* len can only be 0 1 or 2 */
+               switch(len) {
+               case 2: *saveout++ = *inptr++;
+               case 1: *saveout++ = *inptr++;
+               }
+               ((char *)save)[0]+=len;
+       }
+
+       d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
+                (int)((char *)save)[0],
+                (int)((char *)save)[1],
+                (int)((char *)save)[2]));
+
+       return outptr-out;
+}
 
 /**
  * soup_base64_encode:
@@ -165,58 +312,117 @@ const char base64_alphabet[65] =
 gchar *
 soup_base64_encode (const gchar *text, gint inlen)
 {
-       char *buffer = NULL;
-       char *point = NULL;
-       int outlen = 0;
-
-       /* check our args */
-       if (text == NULL)
-               return NULL;
-
-       /* Use 'buffer' to store the output. Work out how big it should be...
-        * This must be a multiple of 4 bytes */
-
-       /* check our arg...avoid a pesky FPE */
-       if (inlen == 0) {
-               buffer = g_malloc (sizeof(char));
-               buffer[0] = '\0';
-               return buffer;
-       }
-
-       outlen = (inlen*4)/3;
-       if ((inlen % 3) > 0) /* got to pad */
-               outlen += 4 - (inlen % 3);
-
-       buffer = g_malloc (outlen + 1); /* +1 for the \0 */
-       memset (buffer, 0, outlen + 1); /* initialize to zero */
+        unsigned char *out;
+        int state = 0, outlen;
+        unsigned int save = 0;
+        
+        out = g_malloc (inlen * 4 / 3 + 5);
+        outlen = soup_base64_encode_close (text, 
+                                          inlen, 
+                                          FALSE,
+                                          out, 
+                                          &state, 
+                                          &save);
+        out[outlen] = '\0';
+        return (char *) out;
+}
 
-       /* now do the main stage of conversion, 3 bytes at a time,
-        * leave the trailing bytes (if there are any) for later */
+static unsigned char camel_mime_base64_rank[256] = {
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
+       255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
+       255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+};
 
-       for (point=buffer; inlen>=3; inlen-=3, text+=3) {
-               *(point++) = base64_alphabet [*text>>2];
-               *(point++) = base64_alphabet [(*text<<4 & 0x30) |
-                                            *(text+1)>>4];
-               *(point++) = base64_alphabet [(*(text+1)<<2 & 0x3c) |
-                                            *(text+2)>>6];
-               *(point++) = base64_alphabet [*(text+2) & 0x3f];
+/**
+ * base64_decode_step: decode a chunk of base64 encoded data
+ * @in: input stream
+ * @len: max length of data to decode
+ * @out: output stream
+ * @state: holds the number of bits that are stored in @save
+ * @save: leftover bits that have not yet been decoded
+ *
+ * Decodes a chunk of base64 encoded data
+ **/
+int
+soup_base64_decode_step (const guchar  *in, 
+                        int            len, 
+                        guchar        *out, 
+                        int           *state, 
+                        guint         *save)
+{
+       register const guchar *inptr;
+       register guchar *outptr;
+       const guchar *inend;
+       guchar c;
+       register unsigned int v;
+       int i;
+
+       inend = in+len;
+       outptr = out;
+
+       /* convert 4 base64 bytes to 3 normal bytes */
+       v=*save;
+       i=*state;
+       inptr = in;
+       while (inptr < inend) {
+               c = camel_mime_base64_rank [*inptr++];
+               if (c != 0xff) {
+                       v = (v<<6) | c;
+                       i++;
+                       if (i==4) {
+                               *outptr++ = v>>16;
+                               *outptr++ = v>>8;
+                               *outptr++ = v;
+                               i=0;
+                       }
+               }
        }
 
-       /* Now deal with the trailing bytes */
-       if (inlen) {
-               /* We always have one trailing byte */
-               *(point++) = base64_alphabet [*text>>2];
-               *(point++) = base64_alphabet [(*text<<4 & 0x30) |
-                                            (inlen==2?*(text+1)>>4:0)];
-               *(point++) = (inlen == 1 ?
-                             '=' :
-                             base64_alphabet [*(text+1)<<2 & 0x3c]);
-               *(point++) = '=';
+       *save = v;
+       *state = i;
+
+       /* quick scan back for '=' on the end somewhere */
+       /* fortunately we can drop 1 output char for each trailing = (upto 2) */
+       i=2;
+       while (inptr > in && i) {
+               inptr--;
+               if (camel_mime_base64_rank [*inptr] != 0xff) {
+                       if (*inptr == '=')
+                               outptr--;
+                       i--;
+               }
        }
 
-       *point = '\0';
+       /* if i!= 0 then there is a truncation error! */
+       return outptr - out;
+}
+
+gchar *
+soup_base64_decode (const gchar   *text,
+                   gint          *out_len)
+{
+       gchar *ret;
+       gint inlen, state = 0, save = 0;
+
+       inlen = strlen (text);
+       ret = g_malloc0 (inlen);
+
+       *out_len = soup_base64_decode_step (text, inlen, ret, &state, &save);
 
-       return buffer;
+       return ret; 
 }
 
 #define ALLOW_UNLESS_DENIED TRUE
index 3fb9432..be80993 100644 (file)
@@ -65,6 +65,34 @@ typedef void (*SoupAuthorizeFn) (SoupAuthType   type,
 void               soup_set_authorize_callback (SoupAuthorizeFn authfn,
                                                gpointer        user_data);
 
+/* Base64 encoding/decoding */
+
+gchar             *soup_base64_encode          (const gchar    *text,
+                                               gint            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);
+
+gchar             *soup_base64_decode          (const gchar    *text,
+                                               gint           *out_len);
+
+int                soup_base64_decode_step     (const guchar   *in, 
+                                               int             len, 
+                                               guchar         *out, 
+                                               int            *state, 
+                                               guint          *save);
+
 /* Useful debugging routines */
 
 void               soup_debug_print_headers  (SoupMessage *req);
index 1695222..64f809f 100644 (file)
 #endif
 
 #include <ctype.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "soup-ntlm.h"
-#include "soup-private.h"
-
-/* Base64 */
-static int base64_encode_close    (unsigned char       *in, 
-                                  int                  inlen, 
-                                  gboolean             break_lines, 
-                                  unsigned char       *out, 
-                                  int                 *state, 
-                                  int                 *save);
-
-static int base64_encode_step     (unsigned char       *in, 
-                                  int                  len, 
-                                  gboolean             break_lines, 
-                                  unsigned char       *out, 
-                                  int                 *state, 
-                                  int                 *save);
-
-static int base64_decode_step     (unsigned char       *in, 
-                                  int                  len, 
-                                  unsigned char       *out, 
-                                  int                 *state,
-                                  unsigned int        *save);
+#include "soup-misc.h"
 
 /* MD4 */
 static void md4sum                (const unsigned char *in, 
@@ -110,16 +90,10 @@ typedef struct {
        guchar  zero_pad[2];
 } NTLMString;
 
-#define NTLM_REQUEST_HEADER "NTLMSSP\x00\x01\x00\x00\x00\xB2\x03\x00\x00"
-
-typedef struct {
-       guchar     header[16];
-       NTLMString domain;
-       NTLMString host;
-} NTLMRequest;
-
-#define NTLM_CHALLENGE_NONCE_OFFSET 24
-#define NTLM_CHALLENGE_NONCE_LENGTH 8
+#define NTLM_CHALLENGE_NONCE_OFFSET      24
+#define NTLM_CHALLENGE_NONCE_LENGTH       8
+#define NTLM_CHALLENGE_DOMAIN_OFFSET     48
+#define NTLM_CHALLENGE_DOMAIN_LEN_OFFSET 44
 
 #define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
 #define NTLM_RESPONSE_FLAGS "\x82\x01"
@@ -155,46 +129,9 @@ ntlm_set_string (NTLMString *string, int *offset, int len)
 }
 
 char *
-soup_ntlm_request (const char *host, const char *domain)
+soup_ntlm_request (void)
 {
-       NTLMRequest req;
-       unsigned char *out, *p;
-       int hlen = strlen (host), dlen = strlen (domain), offset;
-       int state, save;
-
-       memset (&req, 0, sizeof (req));
-       memcpy (req.header, NTLM_REQUEST_HEADER, sizeof (req.header));
-
-       offset = sizeof (req);
-       ntlm_set_string (&req.host, &offset, hlen);
-       ntlm_set_string (&req.domain, &offset, dlen);
-
-       out = g_malloc (((offset + 3) * 4) / 3 + 6);
-       strncpy (out, "NTLM ", 5);
-       p = out + 5;
-
-       state = save = 0;
-       p += base64_encode_step ((guchar *) &req, 
-                                sizeof (req), 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_step ((guchar *) host, 
-                                hlen, 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_close ((guchar *) domain, 
-                                 dlen, 
-                                 FALSE, 
-                                 p, 
-                                 &state, 
-                                 &save);
-       *p = '\0';
-
-       return out;
+       return g_strdup ("NTLM TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
 }
 
 char *
@@ -205,8 +142,7 @@ soup_ntlm_response (const char *challenge,
                    const char *host, 
                    const char *domain)
 {
-       int hlen = strlen (host), dlen = strlen (domain);
-       int ulen = strlen (user), offset, decodelen;
+       int hlen, dlen, ulen, offset, decodelen;
        guchar lm_resp[24], nt_resp[24], *nonce;
        NTLMResponse resp;
        char *chall;
@@ -216,30 +152,41 @@ soup_ntlm_response (const char *challenge,
        if (strncmp (challenge, "NTLM ", 5) != 0)
                return NULL;
 
-       decodelen = strlen (challenge);
+       decodelen = strlen (challenge) - 5;
        chall = g_malloc (decodelen);
 
        state = save = 0;
-       base64_decode_step ((guchar *) challenge + 5, 
-                           decodelen,
-                           chall, 
-                           &state, 
-                           &save);
+       soup_base64_decode_step ((guchar *) challenge + 5, 
+                                decodelen,
+                                chall, 
+                                &state, 
+                                &save);
 
        nonce = chall + NTLM_CHALLENGE_NONCE_OFFSET;
        nonce [NTLM_CHALLENGE_NONCE_LENGTH] = '\0';
 
        calc_response (lm_hash, nonce, lm_resp);
        calc_response (nt_hash, nonce, nt_resp);
-       g_free (chall);
 
        memset (&resp, 0, sizeof (resp));
        memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
        memcpy (resp.flags, NTLM_RESPONSE_FLAGS, sizeof (resp.flags));
 
        offset = sizeof (resp);
+
+       if (domain)
+               dlen = strlen (domain);
+       else {
+               /* Grab the default domain from the challenge */
+               domain = chall + NTLM_CHALLENGE_DOMAIN_OFFSET;
+               dlen = atoi (chall + NTLM_CHALLENGE_DOMAIN_LEN_OFFSET);
+       }
        ntlm_set_string (&resp.domain, &offset, dlen);
+       ulen = strlen (user);
        ntlm_set_string (&resp.user, &offset, ulen);
+       if (!host)
+               host = "UNKNOWN";
+       hlen = strlen (host);
        ntlm_set_string (&resp.host, &offset, hlen);
        ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
        ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
@@ -250,43 +197,45 @@ soup_ntlm_response (const char *challenge,
 
        state = save = 0;
 
-       p += base64_encode_step ((guchar *) &resp, 
-                                sizeof (resp), 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_step ((guchar *) domain, 
-                                dlen, 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_step ((guchar *) user, 
-                                ulen, 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_step ((guchar *) host, 
-                                hlen, 
-                                FALSE, 
-                                p,
-                                &state, 
-                                &save);
-       p += base64_encode_step (lm_resp, 
-                                sizeof (lm_resp), 
-                                FALSE, 
-                                p, 
-                                &state, 
-                                &save);
-       p += base64_encode_close (nt_resp, 
-                                 sizeof (nt_resp), 
-                                 FALSE, 
-                                 p, 
-                                 &state, 
-                                 &save);
+       p += soup_base64_encode_step ((guchar *) &resp, 
+                                     sizeof (resp), 
+                                     FALSE, 
+                                     p, 
+                                     &state, 
+                                     &save);
+       p += soup_base64_encode_step ((guchar *) domain, 
+                                     dlen, 
+                                     FALSE, 
+                                     p, 
+                                     &state, 
+                                     &save);
+       p += soup_base64_encode_step ((guchar *) user, 
+                                     ulen, 
+                                     FALSE, 
+                                     p, 
+                                     &state, 
+                                     &save);
+       p += soup_base64_encode_step ((guchar *) host, 
+                                     hlen, 
+                                     FALSE, 
+                                     p,
+                                     &state, 
+                                     &save);
+       p += soup_base64_encode_step (lm_resp, 
+                                     sizeof (lm_resp), 
+                                     FALSE, 
+                                     p, 
+                                     &state, 
+                                     &save);
+
+       p += soup_base64_encode_close (nt_resp, 
+                                      sizeof (nt_resp), 
+                                      FALSE, 
+                                      p, 
+                                      &state, 
+                                      &save);
        *p = '\0';
+       g_free (chall);
 
        return out;
 }
@@ -335,240 +284,6 @@ calc_response (const guchar *key, const guchar *plaintext, guchar *results)
         des (ks, results + 16);
 }
 
-/* Base64 utils (straight from camel-mime-utils.c) */
-#define d(x)
-
-static char *base64_alphabet =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static unsigned char camel_mime_base64_rank[256] = {
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
-       255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
-       255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-       255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-};
-
-/* 
- * call this when finished encoding everything, to
- * flush off the last little bit 
- */
-static int
-base64_encode_close (unsigned char *in, 
-                    int            inlen, 
-                    gboolean       break_lines, 
-                    unsigned char *out, 
-                    int           *state, 
-                    int           *save)
-{
-       int c1, c2;
-       unsigned char *outptr = out;
-
-       if (inlen > 0)
-               outptr += base64_encode_step (in, 
-                                             inlen, 
-                                             break_lines, 
-                                             outptr, 
-                                             state, 
-                                             save);
-
-       c1 = ((unsigned char *) save) [1];
-       c2 = ((unsigned char *) save) [2];
-       
-       d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
-                (int)((char *) save) [0],
-                (int)((char *) save) [1],
-                (int)((char *) save) [2]));
-
-       switch (((char *) save) [0]) {
-       case 2:
-               outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
-               g_assert (outptr [2] != 0);
-               goto skip;
-       case 1:
-               outptr[2] = '=';
-       skip:
-               outptr [0] = base64_alphabet [ c1 >> 2 ];
-               outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
-               outptr [3] = '=';
-               outptr += 4;
-               break;
-       }
-       if (break_lines)
-               *outptr++ = '\n';
-
-       *save = 0;
-       *state = 0;
-
-       return outptr-out;
-}
-
-/*
- * performs an 'encode step', only encodes blocks of 3 characters to the
- * output at a time, saves left-over state in state and save (initialise to
- * 0 on first invocation).
- */
-static int
-base64_encode_step(unsigned char *in, 
-                  int            len, 
-                  gboolean       break_lines, 
-                  unsigned char *out, 
-                  int           *state, 
-                  int           *save)
-{
-       register unsigned char *inptr, *outptr;
-
-       if (len <= 0)
-               return 0;
-
-       inptr = in;
-       outptr = out;
-
-       d (printf ("we have %d chars, and %d saved chars\n", 
-                  len, 
-                  ((char *) save) [0]));
-
-       if (len + ((char *) save) [0] > 2) {
-               unsigned char *inend = in+len-2;
-               register int c1, c2, c3;
-               register int already;
-
-               already = *state;
-
-               switch (((char *) save) [0]) {
-               case 1: c1 = ((unsigned char *) save) [1]; goto skip1;
-               case 2: c1 = ((unsigned char *) save) [1];
-                       c2 = ((unsigned char *) save) [2]; goto skip2;
-               }
-               
-               /* 
-                * yes, we jump into the loop, no i'm not going to change it, 
-                * it's beautiful! 
-                */
-               while (inptr < inend) {
-                       c1 = *inptr++;
-               skip1:
-                       c2 = *inptr++;
-               skip2:
-                       c3 = *inptr++;
-                       *outptr++ = base64_alphabet [ c1 >> 2 ];
-                       *outptr++ = base64_alphabet [ c2 >> 4 | 
-                                                     ((c1&0x3) << 4) ];
-                       *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | 
-                                                     (c3 >> 6) ];
-                       *outptr++ = base64_alphabet [ c3 & 0x3f ];
-                       /* this is a bit ugly ... */
-                       if (break_lines && (++already)>=19) {
-                               *outptr++='\n';
-                               already = 0;
-                       }
-               }
-
-               ((char *)save)[0] = 0;
-               len = 2-(inptr-inend);
-               *state = already;
-       }
-
-       d(printf("state = %d, len = %d\n",
-                (int)((char *)save)[0],
-                len));
-
-       if (len>0) {
-               register char *saveout;
-
-               /* points to the slot for the next char to save */
-               saveout = & (((char *)save)[1]) + ((char *)save)[0];
-
-               /* len can only be 0 1 or 2 */
-               switch(len) {
-               case 2: *saveout++ = *inptr++;
-               case 1: *saveout++ = *inptr++;
-               }
-               ((char *)save)[0]+=len;
-       }
-
-       d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
-                (int)((char *)save)[0],
-                (int)((char *)save)[1],
-                (int)((char *)save)[2]));
-
-       return outptr-out;
-}
-
-
-/**
- * base64_decode_step: decode a chunk of base64 encoded data
- * @in: input stream
- * @len: max length of data to decode
- * @out: output stream
- * @state: holds the number of bits that are stored in @save
- * @save: leftover bits that have not yet been decoded
- *
- * Decodes a chunk of base64 encoded data
- **/
-static int
-base64_decode_step (unsigned char *in, 
-                   int            len, 
-                   unsigned char *out, 
-                   int           *state, 
-                   unsigned int  *save)
-{
-       register unsigned char *inptr, *outptr;
-       unsigned char *inend, c;
-       register unsigned int v;
-       int i;
-
-       inend = in+len;
-       outptr = out;
-
-       /* convert 4 base64 bytes to 3 normal bytes */
-       v=*save;
-       i=*state;
-       inptr = in;
-       while (inptr < inend) {
-               c = camel_mime_base64_rank [*inptr++];
-               if (c != 0xff) {
-                       v = (v<<6) | c;
-                       i++;
-                       if (i==4) {
-                               *outptr++ = v>>16;
-                               *outptr++ = v>>8;
-                               *outptr++ = v;
-                               i=0;
-                       }
-               }
-       }
-
-       *save = v;
-       *state = i;
-
-       /* quick scan back for '=' on the end somewhere */
-       /* fortunately we can drop 1 output char for each trailing = (upto 2) */
-       i=2;
-       while (inptr > in && i) {
-               inptr--;
-               if (camel_mime_base64_rank [*inptr] != 0xff) {
-                       if (*inptr == '=')
-                               outptr--;
-                       i--;
-               }
-       }
-
-       /* if i!= 0 then there is a truncation error! */
-       return outptr - out;
-}
-
 
 /* 
  * MD4 encoder. (The one everyone else uses is not GPL-compatible;
index 1f4eea2..3149805 100644 (file)
@@ -19,8 +19,7 @@ void  soup_ntlm_lanmanager_hash (const char *password,
 void  soup_ntlm_nt_hash         (const char *password, 
                                 char        hash[21]);
 
-char *soup_ntlm_request         (const char *host, 
-                                const char *domain);
+char *soup_ntlm_request         (void);
 
 char *soup_ntlm_response        (const char *challenge, 
                                 const char *user,
index e6ddbe8..770b1ea 100644 (file)
 
 #ifdef HAVE_OPENSSL_SSL_H
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #include <glib.h>
+#include <sys/time.h>
+
 #include <openssl/ssl.h>
+#include <openssl/rand.h>
 #include <openssl/err.h>
 
 #include "soup-openssl.h"
@@ -71,6 +78,8 @@ soup_openssl_write (GIOChannel   *channel,
 
        if (result < 0) {
                *bytes_written = 0;
+               if (SSL_get_error (chan->ssl, result) == SSL_ERROR_WANT_READ)
+                       return G_IO_ERROR_AGAIN;
                switch (errno) {
                case EINVAL:
                        return G_IO_ERROR_INVAL;
@@ -195,6 +204,48 @@ GIOFuncs soup_openssl_channel_funcs = {
 
 static SSL_CTX *ssl_context = NULL;
 
+#if SSL_LIBRARY_VERSION >= 0x00905100
+#  define CHECK_OPENSSL_SEEDED RAND_status()
+#  define CHECK_OPENSSL_SEEDED_FINAL RAND_status()
+#else
+#  define CHECK_OPENSSL_SEEDED FALSE
+#  define CHECK_OPENSSL_SEEDED_FINAL TRUE
+#endif
+
+static gboolean
+soup_openssl_seed (void) 
+{
+       pid_t pid;
+       struct timeval tv;
+       guchar stack [1024], *heap;
+
+       if (!CHECK_OPENSSL_SEEDED) {
+               /* Seed with pid */
+               pid = getpid ();
+               RAND_seed ((guchar *) &pid, sizeof (pid_t));
+
+               /* Seed with current time */
+               if (gettimeofday (&tv, NULL) == 0)
+                       RAND_seed ((guchar *) &tv, sizeof (struct timeval));
+
+               /* Seed with untouched stack (1024) */
+               RAND_seed (stack, sizeof (stack));
+
+               /* Quit now if we are adequately seeded */
+               if (CHECK_OPENSSL_SEEDED) 
+                       return TRUE;
+
+               /* Seed with untouched heap (1024) */
+               heap = g_malloc (1024);
+               if (heap) 
+                       RAND_seed (heap, 1024);
+               g_free (heap);
+
+               return CHECK_OPENSSL_SEEDED_FINAL;
+       } else
+               return TRUE;
+}
+
 GIOChannel *
 soup_openssl_get_iochannel (GIOChannel *sock)
 {
@@ -207,10 +258,15 @@ soup_openssl_get_iochannel (GIOChannel *sock)
 
         g_return_val_if_fail (sock != NULL, NULL);
 
-       if (!ssl_context && !soup_openssl_init ()) goto THROW_CREATE_ERROR;
+       if (!ssl_context && !soup_openssl_init ()) 
+               goto THROW_CREATE_ERROR;
+
+       if (!soup_openssl_seed ())
+               g_warning ("SSL random number seed failed.");
        
        sockfd = g_io_channel_unix_get_fd (sock);
-       if (!sockfd) goto THROW_CREATE_ERROR;
+       if (!sockfd) 
+               goto THROW_CREATE_ERROR;
 
        ssl = SSL_new (ssl_context);
        if (!ssl) {
index 5fc92a1..5500508 100644 (file)
@@ -37,6 +37,8 @@
 #  endif
 #endif
 
+#include <sys/types.h>
+
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
 #endif
 
 #ifdef SOUP_WIN32
-#define VERSION "Win/0.6.99"
+#define VERSION "Win/0.7.99"
 #include <windows.h>
 #include <winbase.h>
 #include <winuser.h>
 #endif
 
+#include <libsoup/soup-auth.h>
 #include <libsoup/soup-context.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
@@ -103,6 +106,7 @@ struct _SoupConnection {
        SoupContext  *context;
        GIOChannel   *channel;
        SoupSocket   *socket;
+       SoupAuth     *auth;
        guint         port;
        gboolean      in_use;
        guint         last_used_id;
@@ -110,39 +114,41 @@ struct _SoupConnection {
        guint         death_tag;
 };
 
-struct _SoupMessagePrivate {
-       SoupConnectId   connect_tag;
-       guint           read_tag;
-       guint           write_tag;
-       guint           timeout_tag;
+struct _SoupServer {
+       SoupProtocol       proto;
+       gint               port;
 
-       GString        *req_header;
+       guint              refcnt;
+       GMainLoop         *loop;
 
-       SoupCallbackFn  callback;
-       gpointer        user_data;
+       guint              accept_tag;
+       SoupSocket        *listen_sock;
 
-       guint           msg_flags;
+       GIOChannel        *cgi_read_chan;
+       GIOChannel        *cgi_write_chan;
 
-       GSList         *content_handlers;
+       GHashTable        *handlers;   /* KEY: path, VALUE: SoupServerHandler */
+       SoupServerHandler *default_handler;
+};
 
-       SoupHttpVersion http_version;
+struct _SoupMessagePrivate {
+       SoupConnectId      connect_tag;
+       guint              read_tag;
+       guint              write_tag;
+       guint              timeout_tag;
 
-       SoupServer     *server;
-       SoupSocket     *server_sock;
-};
+       SoupCallbackFn     callback;
+       gpointer           user_data;
 
-struct _SoupServer {
-       SoupProtocol       proto;
-       gint               port;
+       guint              msg_flags;
 
-       GMainLoop         *loop;
+       GSList            *content_handlers;
 
-       guint              accept_tag;
-       SoupSocket        *sock;
+       SoupHttpVersion    http_version;
 
-       GHashTable        *handlers;
-       GSList            *static_handlers;
-       SoupServerHandler  default_handler;
+       SoupServer        *server;
+       SoupSocket        *server_sock;
+       SoupServerMessage *server_msg;
 };
 
 /* from soup-message.c */
@@ -164,9 +170,6 @@ gint      soup_substring_index (gchar         *str,
                                gint           len,
                                gchar         *substr);
 
-gchar    *soup_base64_encode   (const gchar   *text,
-                               gint           len);
-
 /* from soup-socket.c */
 
 gboolean  soup_gethostbyname (const gchar         *hostname,
index 0faa268..16492b9 100644 (file)
 #include "soup-misc.h"
 #include "soup-private.h"
 #include "soup-socks.h"
+#include "soup-ssl.h"
 #include "soup-transfer.h"
 
-GSList *soup_active_requests = NULL;
+static GSList *soup_active_requests = NULL, *soup_active_request_next = NULL;
 
 static guint soup_queue_idle_tag = 0;
 
@@ -80,9 +81,7 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
                        /*
                         * FIXME: Use exponential backoff here
                         */
-                       soup_message_queue (req, 
-                                           req->priv->callback, 
-                                           req->priv->user_data);
+                       soup_message_requeue (req);
                } else {
                        soup_message_set_error (req, SOUP_ERROR_IO);
                        soup_message_issue_callback (req);
@@ -96,27 +95,6 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
        }
 }
 
-static gboolean
-soup_parse_headers (const GString   *headers, 
-                   SoupHttpVersion *version,
-                   SoupMessage     *req)
-{
-       if (!soup_headers_parse_response (headers->str, 
-                                         headers->len, 
-                                         req->response_headers,
-                                         version,
-                                         &req->errorcode,
-                                         (gchar **) &req->errorphrase)) {
-               soup_message_set_error (req, SOUP_ERROR_MALFORMED);
-               soup_message_issue_callback (req);
-               return FALSE;
-       }
-
-       req->errorclass = soup_error_get_class (req->errorcode);
-
-       return TRUE;
-}
-
 static SoupTransferDone
 soup_queue_read_headers_cb (const GString        *headers,
                             SoupTransferEncoding *encoding,
@@ -124,43 +102,65 @@ soup_queue_read_headers_cb (const GString        *headers,
                            gpointer              user_data)
 {
        SoupMessage *req = user_data;
-
        const gchar *connection, *length, *enc;
        SoupHttpVersion version;
+       GHashTable *resp_hdrs;
+       SoupMethodId meth_id;
 
-       if (!soup_parse_headers (headers, &version, req)) 
-               return SOUP_TRANSFER_END;
+       if (!soup_headers_parse_response (headers->str, 
+                                         headers->len, 
+                                         req->response_headers,
+                                         &version,
+                                         &req->errorcode,
+                                         (gchar **) &req->errorphrase)) {
+               soup_message_set_error_full (req, 
+                                            SOUP_ERROR_MALFORMED,
+                                            "Unable to parse response "
+                                            "headers");
+               goto THROW_MALFORMED_HEADER;
+       }
+               
+       meth_id   = soup_method_get_id (req->method);
+       resp_hdrs = req->response_headers;
+
+       req->errorclass = soup_error_get_class (req->errorcode);
 
        /* 
-        * Handle connection persistence 
+        * Handle connection persistence
+        * Close connection if:
+        *   - Connection header is "close"
+        *   - HTTP 1.0 and Connection header is not present
         */
-       connection = soup_message_get_header (req->response_headers,
-                                             "Connection");
+       connection = soup_message_get_header (resp_hdrs, "Connection");
 
        if ((connection && !g_strcasecmp (connection, "close")) ||
            (!connection && version == SOUP_HTTP_1_0))
                soup_connection_set_keep_alive (req->connection, FALSE);
 
+       /*
+        * Handle successful CONNECT request by keeping connection open
+        */
+       if (meth_id == SOUP_METHOD_ID_CONNECT && !SOUP_MESSAGE_IS_ERROR (req))
+               soup_connection_set_keep_alive (req->connection, TRUE);
+
        /* 
-        * Special case handling for:
+        * Special case zero body handling for:
         *   - HEAD requests (where content-length must be ignored) 
+        *   - CONNECT requests (no body expected) 
         *   - 1xx Informational responses (where no body is allowed)
         */
-       if (!g_strcasecmp (req->method, "HEAD") ||
+       if (meth_id == SOUP_METHOD_ID_HEAD ||
+           meth_id == SOUP_METHOD_ID_CONNECT ||
            req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
-                *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-                *content_len = 0;
-               goto RUN_HANDLERS;
+               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+               *content_len = 0;
+               goto SUCCESS_CONTINUE;
        }
 
        /* 
-        * Handle Content-Length or Chunked encoding 
+        * Handle Content-Length encoding 
         */
-       length = soup_message_get_header (req->response_headers, 
-                                         "Content-Length");
-       enc = soup_message_get_header (req->response_headers, 
-                                      "Transfer-Encoding");
-
+       length = soup_message_get_header (resp_hdrs, "Content-Length");
        if (length) {
                *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
                *content_len = atoi (length);
@@ -169,9 +169,15 @@ soup_queue_read_headers_cb (const GString        *headers,
                                                     SOUP_ERROR_MALFORMED,
                                                     "Invalid Content-Length");
                        goto THROW_MALFORMED_HEADER;
-               }
+               } 
+               goto SUCCESS_CONTINUE;
        }
-       else if (enc) {
+
+       /* 
+        * Handle Chunked encoding 
+        */
+       enc = soup_message_get_header (resp_hdrs, "Transfer-Encoding");
+       if (enc) {
                if (g_strcasecmp (enc, "chunked") == 0)
                        *encoding = SOUP_TRANSFER_CHUNKED;
                else {
@@ -181,15 +187,15 @@ soup_queue_read_headers_cb (const GString        *headers,
                                "Unknown Response Encoding");
                        goto THROW_MALFORMED_HEADER;
                }
+               goto SUCCESS_CONTINUE;
        }
 
- RUN_HANDLERS:
-       if (soup_message_run_handlers (req, SOUP_HANDLER_HEADERS))
-               return SOUP_TRANSFER_END;
-
+ SUCCESS_CONTINUE:
+       soup_message_run_handlers (req, SOUP_HANDLER_HEADERS);
        return SOUP_TRANSFER_CONTINUE;
 
  THROW_MALFORMED_HEADER:
+       soup_connection_set_keep_alive (req->connection, FALSE);
        soup_message_issue_callback (req);
        return SOUP_TRANSFER_END;
 }
@@ -204,8 +210,7 @@ soup_queue_read_chunk_cb (const SoupDataBuffer *data,
        req->response.length = data->length;
        req->response.body = data->body;
 
-       if (soup_message_run_handlers (req, SOUP_HANDLER_DATA))
-               return SOUP_TRANSFER_END;
+       soup_message_run_handlers (req, SOUP_HANDLER_DATA);
 
        return SOUP_TRANSFER_CONTINUE;
 }
@@ -255,7 +260,11 @@ soup_encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
 
        ctx = proxy_auth ? soup_get_proxy () : msg->context;
 
-       auth = soup_auth_lookup (ctx);
+       if (msg->connection->auth)
+               auth = msg->connection->auth;
+       else
+               auth = soup_auth_lookup (ctx);
+
        if (auth) {
                token = soup_auth_authorize (auth, msg);
                if (token) {
@@ -346,7 +355,15 @@ soup_get_request_header (SoupMessage *req)
        proxy = soup_get_proxy ();
        suri = soup_context_get_uri (req->context);
 
-       if (proxy)
+       if (!g_strcasecmp (req->method, "CONNECT")) 
+               /*
+                * CONNECT URI is hostname:port for tunnel destination
+                */
+               uri = g_strdup_printf ("%s:%d", suri->host, suri->port);
+       else if (proxy)
+               /*
+                * Proxy expects full URI to destination
+                */
                uri = soup_uri_to_string (suri, FALSE);
        else if (suri->querystring)
                uri = g_strconcat (suri->path, "?", suri->querystring, NULL);
@@ -359,7 +376,6 @@ soup_get_request_header (SoupMessage *req)
                                   "%s %s HTTP/1.0\r\n",
                           req->method,
                           uri);
-
        g_free (uri);
 
        /*
@@ -418,88 +434,176 @@ soup_queue_write_done_cb (gpointer user_data)
 }
 
 static void
-soup_queue_connect_cb (SoupContext          *ctx,
-                      SoupConnectErrorCode  err,
-                      SoupConnection       *conn,
-                      gpointer              user_data)
+start_request (SoupContext *ctx, SoupMessage *req)
 {
-       SoupMessage *req = user_data;
-       SoupProtocol proto;
        GIOChannel *channel;
        gboolean overwrt; 
 
-       req->priv->connect_tag = NULL;
+       channel = soup_connection_get_iochannel (req->connection);
+       if (!channel) {
+               SoupProtocol proto;
+               gchar *phrase;
 
-       switch (err) {
-       case SOUP_CONNECT_ERROR_NONE:
                proto = soup_context_get_uri (ctx)->protocol;
 
-               if (soup_connection_is_new (conn) &&
-                   (proto == SOUP_PROTOCOL_SOCKS4 ||
-                    proto == SOUP_PROTOCOL_SOCKS5)) {
-                       soup_connect_socks_proxy (conn, 
-                                                 req->context, 
-                                                 soup_queue_connect_cb,
-                                                 req);
-                       return;
-               }
+               if (proto == SOUP_PROTOCOL_HTTPS)
+                       phrase = "Unable to create secure data channel";
+               else
+                       phrase = "Unable to create data channel";
 
-               channel = soup_connection_get_iochannel (conn);
-               if (!channel) {
-                       gchar *phrase;
-
-                       if (proto == SOUP_PROTOCOL_HTTPS)
-                               phrase = "Unable to create secure data channel";
-                       else
-                               phrase = "Unable to create data channel";
-
-                       if (ctx != req->context)
-                               soup_message_set_error_full (
-                                       req, 
-                                       SOUP_ERROR_CANT_CONNECT_PROXY,
-                                       phrase);
-                       else 
-                               soup_message_set_error_full (
-                                       req, 
-                                       SOUP_ERROR_CANT_CONNECT,
-                                       phrase);
+               if (ctx != req->context)
+                       soup_message_set_error_full (
+                               req, 
+                               SOUP_ERROR_CANT_CONNECT_PROXY,
+                               phrase);
+               else 
+                       soup_message_set_error_full (
+                               req, 
+                               SOUP_ERROR_CANT_CONNECT,
+                               phrase);
 
-                       soup_message_issue_callback (req);
-                       return;
-               }
+               /* NOTE: Don't call soup_message_issue_callback here */
+               return;
+       }
 
-               if (req->priv->req_header) {
-                       g_string_free (req->priv->req_header, TRUE);
-                       req->priv->req_header = NULL;
-               }
+       req->priv->write_tag = 
+               soup_transfer_write_simple (channel,
+                                           soup_get_request_header (req),
+                                           &req->request,
+                                           soup_queue_write_done_cb,
+                                           soup_queue_error_cb,
+                                           req);
 
-               req->priv->req_header = soup_get_request_header (req);
+       overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
 
-               req->priv->write_tag = 
-                       soup_transfer_write (channel,
-                                            req->priv->req_header,
-                                            &req->request,
-                                            NULL,
-                                            soup_queue_write_done_cb,
-                                            soup_queue_error_cb,
-                                            req);
+       req->priv->read_tag = 
+               soup_transfer_read (channel,
+                                   overwrt,
+                                   soup_queue_read_headers_cb,
+                                   soup_queue_read_chunk_cb,
+                                   soup_queue_read_done_cb,
+                                   soup_queue_error_cb,
+                                   req);
 
-               overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
+       g_io_channel_unref (channel);
 
-               req->priv->read_tag = 
-                       soup_transfer_read (channel,
-                                           overwrt,
-                                           soup_queue_read_headers_cb,
-                                           soup_queue_read_chunk_cb,
-                                           soup_queue_read_done_cb,
-                                           soup_queue_error_cb,
-                                           req);
+       req->status = SOUP_STATUS_SENDING_REQUEST;
+}
 
-               g_io_channel_unref (channel);
+static SoupHandlerResult
+proxy_https_connect_cb (SoupMessage *msg, gpointer user_data)
+{
+       gboolean *ret = user_data;
+
+       if (!SOUP_MESSAGE_IS_ERROR (msg)) {
+               /*
+                * Bless the connection to SSL
+                */
+               msg->connection->channel = 
+                       soup_ssl_get_iochannel (msg->connection->channel);
+
+               /*
+                * Avoid releasing the connection on message free
+                */
+               msg->connection = NULL;
+               
+               *ret = TRUE;
+       }
+
+       return SOUP_HANDLER_CONTINUE;
+}
+
+static gboolean
+proxy_https_connect (SoupContext    *proxy, 
+                    SoupConnection *conn, 
+                    SoupContext    *dest_ctx)
+{
+       SoupProtocol proxy_proto;
+       SoupMessage *connect_msg;
+       gboolean ret = FALSE;
+
+       proxy_proto = soup_context_get_uri (proxy)->protocol;
+
+       if (proxy_proto != SOUP_PROTOCOL_HTTP && 
+           proxy_proto != SOUP_PROTOCOL_HTTPS) 
+               return FALSE;
+
+       connect_msg = soup_message_new (dest_ctx, SOUP_METHOD_CONNECT);
+       connect_msg->connection = conn;
+       soup_message_add_handler (connect_msg, 
+                                 SOUP_HANDLER_FINISHED,
+                                 NULL,
+                                 proxy_https_connect_cb,
+                                 &ret);
+       soup_message_send (connect_msg);
+       soup_message_free (connect_msg);
+
+       return ret;
+}
 
-               req->status = SOUP_STATUS_SENDING_REQUEST;
-               req->connection = conn;
+static gboolean
+proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn)
+{
+       SoupProtocol proto, dest_proto;
+
+       /* 
+        * Only attempt proxy connect if the connection's context is different
+        * from the requested context, and if the connection is new 
+        */
+       if (ctx == req->context || !soup_connection_is_new (conn))
+               return FALSE;
 
+       proto = soup_context_get_uri (ctx)->protocol;
+       dest_proto = soup_context_get_uri (req->context)->protocol;
+       
+       /* Handle SOCKS proxy negotiation */
+       if ((proto == SOUP_PROTOCOL_SOCKS4 || proto == SOUP_PROTOCOL_SOCKS5)) {
+               soup_connect_socks_proxy (conn, 
+                                         req->context, 
+                                         soup_queue_connect_cb,
+                                         req);
+               return TRUE;
+       } 
+       
+       /* Handle HTTPS tunnel setup via proxy CONNECT request. */
+       if (dest_proto == SOUP_PROTOCOL_HTTPS) {
+               /* Syncronously send CONNECT request */
+               if (!proxy_https_connect (ctx, conn, req->context)) {
+                       soup_message_set_error_full (
+                               req, 
+                               SOUP_ERROR_CANT_CONNECT_PROXY,
+                               "Unable to create secure data "
+                               "tunnel through proxy");
+
+                       /* NOTE: Don't call soup_message_issue_callback here */
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+void
+soup_queue_connect_cb (SoupContext          *ctx,
+                      SoupConnectErrorCode  err,
+                      SoupConnection       *conn,
+                      gpointer              user_data)
+{
+       SoupMessage *req = user_data;
+
+       req->priv->connect_tag = NULL;
+       req->connection = conn;
+
+       switch (err) {
+       case SOUP_CONNECT_ERROR_NONE:
+               /* 
+                * NOTE: proxy_connect will either set an error or call us 
+                * again after proxy negotiation.
+                */
+               if (proxy_connect (ctx, req, conn))
+                       return;
+
+               start_request (ctx, req);
                break;
 
        case SOUP_CONNECT_ERROR_ADDR_RESOLVE:
@@ -514,7 +618,7 @@ soup_queue_connect_cb (SoupContext          *ctx,
                                SOUP_ERROR_CANT_CONNECT,
                                "Unable to resolve hostname");
 
-               soup_message_issue_callback (req);
+               /* NOTE: Don't call soup_message_issue_callback here */
                break;
 
        case SOUP_CONNECT_ERROR_NETWORK:
@@ -525,20 +629,55 @@ soup_queue_connect_cb (SoupContext          *ctx,
                        soup_message_set_error (req, 
                                                SOUP_ERROR_CANT_CONNECT);
 
-               soup_message_issue_callback (req);
+               /* NOTE: Don't call soup_message_issue_callback here */
                break;
        }
 
        return;
 }
 
+void
+soup_queue_add_request (SoupMessage *req)
+{
+       soup_active_requests = g_slist_prepend (soup_active_requests, req);
+}
+
+void
+soup_queue_remove_request (SoupMessage *req)
+{
+       if (soup_active_request_next && soup_active_request_next->data == req)
+               soup_queue_next_request ();
+       soup_active_requests = g_slist_remove (soup_active_requests, req);
+}
+
+SoupMessage *
+soup_queue_first_request (void)
+{
+       if (!soup_active_requests)
+               return NULL;
+
+       soup_active_request_next = soup_active_requests->next;
+       return soup_active_requests->data;
+}
+
+SoupMessage *
+soup_queue_next_request (void)
+{
+       SoupMessage *ret;
+
+       if (!soup_active_request_next)
+               return NULL;
+       ret = soup_active_request_next->data;
+       soup_active_request_next = soup_active_request_next->next;
+       return ret;
+}
+
 static gboolean 
 soup_idle_handle_new_requests (gpointer unused)
 {
-        GSList *iter;
+       SoupMessage *req = soup_queue_first_request ();
 
-       for (iter = soup_active_requests; iter; iter = iter->next) {
-               SoupMessage *req = iter->data;
+       for (; req; req = soup_queue_next_request ()) {
                SoupContext *ctx, *proxy;
 
                if (req->status != SOUP_STATUS_QUEUED)
@@ -548,36 +687,63 @@ soup_idle_handle_new_requests (gpointer unused)
                ctx = proxy ? proxy : req->context;
 
                req->status = SOUP_STATUS_CONNECTING;
-               req->priv->connect_tag =
-                       soup_context_get_connection (ctx, 
-                                                    soup_queue_connect_cb, 
-                                                    req);
+
+               if (req->connection && 
+                   soup_connection_is_keep_alive (req->connection))
+                       start_request (ctx, req);
+               else {
+                       gpointer connect_tag;
+
+                       connect_tag = 
+                               soup_context_get_connection (
+                                       ctx, 
+                                       soup_queue_connect_cb, 
+                                       req);
+
+                       if (req->errorcode)
+                               soup_message_issue_callback (req);
+                       else if (connect_tag)
+                               req->priv->connect_tag = connect_tag;
+               }
        }
 
        soup_queue_idle_tag = 0;
        return FALSE;
 }
 
-void 
-soup_queue_message (SoupMessage    *req,
-                   SoupCallbackFn  callback, 
-                   gpointer        user_data)
+static void
+soup_queue_initialize (void)
 {
-       g_return_if_fail (req != NULL);
-
        if (!soup_initialized)
                soup_load_config (NULL);
 
        if (!soup_queue_idle_tag)
                soup_queue_idle_tag = 
                        g_idle_add (soup_idle_handle_new_requests, NULL);
+}
 
-       if (req->status != SOUP_STATUS_IDLE)
-               soup_message_cleanup (req);
+void 
+soup_queue_message (SoupMessage    *req,
+                   SoupCallbackFn  callback, 
+                   gpointer        user_data)
+{
+       g_return_if_fail (req != NULL);
 
        req->priv->callback = callback;
        req->priv->user_data = user_data;
 
+       if (!req->context) {
+               soup_message_set_error_full (req, 
+                                            SOUP_ERROR_CANCELLED,
+                                            "Attempted to queue a message "
+                                            "with no destination context");
+               soup_message_issue_callback (req);
+               return;
+       }
+
+       if (req->status != SOUP_STATUS_IDLE)
+               soup_message_cleanup (req);
+
        switch (req->response.owner) {
        case SOUP_BUFFER_USER_OWNED:
                soup_message_set_error_full (req, 
@@ -587,11 +753,9 @@ soup_queue_message (SoupMessage    *req,
                                             "buffer.");
                soup_message_issue_callback (req);
                return;
-
        case SOUP_BUFFER_SYSTEM_OWNED:
                g_free (req->response.body);
                break;
-
        case SOUP_BUFFER_STATIC:
                break;
        }
@@ -602,17 +766,19 @@ soup_queue_message (SoupMessage    *req,
 
        soup_message_clear_headers (req->response_headers);
 
+       req->errorcode = 0;
+       req->errorclass = 0;
+
        if (req->errorphrase) {
                g_free ((gchar *) req->errorphrase);
                req->errorphrase = NULL;
        }
 
-       req->errorcode = 0;
-       req->errorclass = 0;
-
        req->status = SOUP_STATUS_QUEUED;
 
-       soup_active_requests = g_slist_prepend (soup_active_requests, req);
+       soup_queue_add_request (req);
+
+       soup_queue_initialize ();
 }
 
 /**
@@ -624,11 +790,16 @@ soup_queue_message (SoupMessage    *req,
 void 
 soup_queue_shutdown (void)
 {
-        GSList *iter;
+       SoupMessage *req;
 
-       g_source_remove (soup_queue_idle_tag);
-       soup_queue_idle_tag = 0;
+       soup_initialized = FALSE;
+
+       if (soup_queue_idle_tag) {
+               g_source_remove (soup_queue_idle_tag);
+               soup_queue_idle_tag = 0;
+       }
 
-       for (iter = soup_active_requests; iter; iter = iter->next)
-               soup_message_cancel (iter->data);
+       req = soup_queue_first_request ();
+       for (; req; req = soup_queue_next_request ())
+               soup_message_cancel (req);
 }
index c6b1718..30ba5d3 100644 (file)
 
 #include <libsoup/soup-message.h>
 
-void soup_queue_message  (SoupMessage    *req,
-                         SoupCallbackFn  callback, 
-                         gpointer        user_data);
+void         soup_queue_message        (SoupMessage          *req,
+                                       SoupCallbackFn        callback, 
+                                       gpointer              user_data);
 
-void soup_queue_shutdown (void);
+void         soup_queue_connect_cb     (SoupContext          *ctx,
+                                       SoupConnectErrorCode  err,
+                                       SoupConnection       *conn,
+                                       gpointer              user_data);
+
+void         soup_queue_add_request    (SoupMessage          *req);
+void         soup_queue_remove_request (SoupMessage          *req);
+SoupMessage *soup_queue_first_request  (void);
+SoupMessage *soup_queue_next_request   (void);
+
+void         soup_queue_shutdown       (void);
 
 #endif /* SOUP_QUEUE_H */
index 81ab981..6b6087d 100644 (file)
@@ -8,10 +8,34 @@
  * Copyright (C) 2001, Ximian, Inc.
  */
 
+/*
+ * FIXME: Split into soup-server-cgi.[ch] and soup-server-dyn.[ch]
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef SOUP_WIN32
+#define ioctl ioctlsocket\r
+#define STDIN_FILENO 0\r
+#define STDOUT_FILENO 1
+#endif
+
+extern char **environ;
+
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #define SOUP_PROTOCOL_CGI 0xff
 
-SoupServer cgi_server = {
-       SOUP_PROTOCOL_CGI
-};
-
-SoupServer httpd_server = {
-       SOUP_PROTOCOL_HTTP
-};
-
-SoupServer httpd_ssl_server = {
-       SOUP_PROTOCOL_HTTPS
+struct _SoupServerMessage {
+       SoupMessage *msg;
+       GSList      *chunks;           /* CONTAINS: SoupDataBuffer* */
+       gboolean     started;
+       gboolean     finished;
 };
 
-SoupServer *SOUP_CGI_SERVER = &cgi_server;
-SoupServer *SOUP_HTTPD_SERVER = &httpd_server;
-SoupServer *SOUP_HTTPD_SSL_SERVER = &httpd_ssl_server;
-
 SoupServer *
 soup_server_new (SoupProtocol proto, guint port)
 {
        SoupServer *serv;
        SoupSocket *sock = NULL;
+       GIOChannel *read_chan = NULL, *write_chan = NULL;
 
-       if (proto != SOUP_PROTOCOL_CGI) {
+       if (proto == SOUP_PROTOCOL_CGI) {
+               read_chan = g_io_channel_unix_new (STDIN_FILENO);
+               if (!read_chan) return NULL;
+
+               write_chan = g_io_channel_unix_new (STDOUT_FILENO);
+               if (!write_chan) {
+                       g_io_channel_unref (read_chan);
+                       return NULL;
+               }
+       } else {
                sock = soup_socket_server_new (port);
                if (!sock) return NULL;
+
+               port = soup_socket_get_port (sock);
        }
 
        serv = g_new0 (SoupServer, 1);
-       serv->port = soup_socket_get_port (sock);
+       serv->refcnt = 1;
+       serv->port = port;
        serv->proto = proto;
-       serv->sock = sock;
+       serv->listen_sock = sock;
+       serv->cgi_read_chan = read_chan;
+       serv->cgi_write_chan = write_chan;
 
        return serv;
 }
 
-static gboolean 
-free_handler (char *path, SoupServerHandler *hand)
+SoupServer *
+soup_server_cgi (void)
+{
+       static SoupServer *cgi = NULL;
+
+       if (!cgi) 
+               cgi = soup_server_new (SOUP_PROTOCOL_CGI, 0);
+
+       return cgi;
+}
+
+static void 
+free_handler (SoupServer *server, SoupServerHandler *hand)
 {
-       g_free (hand->path);
+       if (hand->unregister)
+               (*hand->unregister) (server, hand, hand->user_data);
+
+       if (hand->auth_ctx) {
+               g_free ((gchar *) hand->auth_ctx->basic_info.realm);
+               g_free ((gchar *) hand->auth_ctx->digest_info.realm);
+               g_free (hand->auth_ctx);
+       }
+
+       g_free ((gchar *) hand->path);  
        g_free (hand);
+}
 
+static gboolean
+free_handler_foreach (gchar *key, SoupServerHandler *hand, SoupServer *server)
+{
+       free_handler (server, hand);
        return TRUE;
 }
 
 void
-soup_server_free (SoupServer *serv)
+soup_server_ref (SoupServer *serv)
 {
        g_return_if_fail (serv != NULL);
 
-       if (serv->sock)
-               soup_socket_unref (serv->sock);
+       ++serv->refcnt; 
+}
+
+void
+soup_server_unref (SoupServer *serv)
+{
+       g_return_if_fail (serv != NULL);
 
-       g_hash_table_foreach_remove (serv->handlers, 
-                                    (GHRFunc) free_handler, 
-                                    NULL);
-       g_hash_table_destroy (serv->handlers);
+       --serv->refcnt;
 
-       g_slist_free (serv->static_handlers);
+       if (serv->refcnt == 0) {
+               if (serv->accept_tag)
+                       g_source_remove (serv->accept_tag);
 
-       if (serv->accept_tag)
-               g_source_remove (serv->accept_tag);
+               if (serv->listen_sock)
+                       soup_socket_unref (serv->listen_sock);
 
-       g_main_destroy (serv->loop);
+               if (serv->cgi_read_chan)
+                       g_io_channel_unref (serv->cgi_read_chan);
 
-       g_free (serv);
+               if (serv->cgi_write_chan)
+                       g_io_channel_unref (serv->cgi_write_chan);
+
+               if (serv->default_handler)
+                       free_handler (serv, serv->default_handler);
+
+               g_hash_table_foreach_remove (serv->handlers, 
+                                            (GHRFunc) free_handler_foreach, 
+                                            serv);
+               g_hash_table_destroy (serv->handlers);
+
+               g_main_destroy (serv->loop);
+
+               g_free (serv);
+       }
 }
 
 gint
@@ -99,34 +173,244 @@ soup_server_get_port (SoupServer *serv)
        return serv->port;
 }
 
-static inline void
-destroy_message (SoupMessage *req)
+SoupProtocol
+soup_server_get_protocol (SoupServer *serv)
+{
+       g_return_val_if_fail (serv != NULL, 0);
+       return serv->proto;
+}
+
+static void
+free_chunk (gpointer chunk, gpointer notused)
+{
+       SoupDataBuffer *buf = chunk;
+
+       if (buf->owner == SOUP_BUFFER_SYSTEM_OWNED)
+               g_free (buf->body);
+
+       g_free (buf);
+}
+
+typedef struct {
+       SoupServer *server;
+       SoupSocket *server_sock;
+} ServerConnectData;
+
+static gboolean start_another_request (GIOChannel    *serv_chan,
+                                      GIOCondition   condition, 
+                                      gpointer       user_data);
+
+static void
+destroy_message (SoupMessage *msg)
 {
-       soup_socket_unref (req->priv->server_sock);
+       SoupServer *server = msg->priv->server;
+       SoupSocket *server_sock = msg->priv->server_sock;
+       SoupServerMessage *server_msg = msg->priv->server_msg;
+
+       if (server_sock) {
+               if (server_msg && msg->priv->http_version == SOUP_HTTP_1_0)
+                       /*
+                        * Close the socket if we are using HTTP/1.0 and 
+                        * did not specify a Content-Length response header.
+                        */
+                       soup_socket_unref (server_sock);
+               else {
+                       /*
+                        * Listen for another request on this connection
+                        */
+                       ServerConnectData *data;
+                       GIOChannel *chan;
+
+                       data = g_new0 (ServerConnectData, 1);
+                       data->server = msg->priv->server;
+                       data->server_sock = server_sock;
+
+                       chan = soup_socket_get_iochannel (server_sock);
+                       g_io_add_watch (chan,
+                                       G_IO_IN|G_IO_PRI|
+                                       G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       start_another_request,
+                                       data);
+                       g_io_channel_unref (chan);
+               }
+       }
+
+       if (server_msg) {
+               g_slist_foreach (server_msg->chunks, free_chunk, NULL);
+               g_slist_free (server_msg->chunks);
+               g_free (server_msg);
+       }
 
-       g_free ((gchar *) req->method);
+       /*
+        * If CGI, service one message and quit 
+        */
+       if (server->proto == SOUP_PROTOCOL_CGI)
+               g_main_quit (server->loop);
 
-       if (req->priv->server->proto == SOUP_PROTOCOL_CGI)
-               g_main_quit (req->priv->server->loop);
+       soup_server_unref (server);
 
-       soup_message_free (req);
+       g_free ((gchar *) msg->method);
+       soup_message_free (msg);
 }
 
 static void 
 error_cb (gboolean body_started, gpointer user_data)
 {
-       SoupMessage *req = user_data;
+       SoupMessage *msg = user_data;
 
-       destroy_message (req);
+       destroy_message (msg);
 }
 
 static void
 write_done_cb (gpointer user_data)
 {
-       SoupMessage *req = user_data;
+       SoupMessage *msg = user_data;
+
+       msg->priv->write_tag = 0;
+       destroy_message (msg);
+}
+
+static SoupTransferDone
+read_headers_cgi (SoupMessage *msg,
+                 gint        *content_len)
+{
+       SoupContext *ctx;
 
-       req->priv->write_tag = 0;
-       destroy_message (req);
+       /*
+        * Get request HTTP method
+        */
+       (gchar *) msg->method = g_strdup (g_getenv ("REQUEST_METHOD"));
+
+       /* 
+        * Get content length of request body
+        */
+       {
+               const gchar *length;
+               length = g_getenv ("CONTENT_LENGTH");
+
+               *content_len = length ? atoi (length) : 0;
+       }
+
+       /* 
+        * Determine request HTTP version
+        */
+       {
+               const gchar *proto;\r
+               proto = g_getenv ("SERVER_PROTOCOL");
+               if (proto) {
+                       if (!g_strcasecmp (proto, "HTTP/1.1"))
+                               msg->priv->http_version = SOUP_HTTP_1_1;
+                       else
+                               msg->priv->http_version = SOUP_HTTP_1_0;
+               } else
+                       msg->priv->http_version = SOUP_HTTP_1_0;
+       }
+
+       /* 
+        * Generate correct context for request 
+        */
+       {
+               const gchar *host, *https;
+               gchar *url;
+
+               host = g_getenv ("HTTP_HOST");
+               if (!host)
+                       host = g_getenv ("SERVER_ADDR");
+
+               /*
+                * MS IIS sets $HTTPS to "off" if not using HTTPS
+                */
+               https = g_getenv ("HTTPS");
+               if (https && !g_strcasecmp (https, "OFF"))
+                       https = NULL;
+
+               url = g_strconcat (https ? "https://" : "http://",
+                                  host,
+                                  ":",
+                                  g_getenv ("SERVER_PORT"),
+                                  g_getenv ("REQUEST_URI"),
+                                  NULL);
+
+               ctx = soup_context_get (url);
+               g_free (url);
+
+               if (!ctx) goto THROW_MALFORMED_HEADER;
+
+               soup_message_set_context (msg, ctx);
+               soup_context_unref (ctx);
+       }
+
+       /*
+        * Load request headers from environment. Header environment variables
+        * are of the form "HTTP_<NAME>=<VALUE>"
+        */
+       {
+               gint iter;
+               for (iter = 0; environ [iter]; iter++) {
+                       gchar *env = environ [iter];
+
+                       if (!strncmp (env, "HTTP_", 5)) {
+                               gchar *cpy, *iter;
+
+                               cpy = iter = g_strdup (env + 5);
+
+                               if (!cpy) 
+                                       continue;
+
+                               /*
+                                * Replace '_' with '-' in header names
+                                */
+                               while (*iter && *iter != '=') {
+                                       if (*iter == '_')
+                                               *iter = '-';
+                                       iter++;
+                               }
+
+                               if (*cpy && *iter) {
+                                       /* 
+                                        * Skip '=' between key and value 
+                                        */
+                                       *iter++ = '\0';
+
+                                       soup_message_add_header (
+                                               msg->request_headers,
+                                               cpy,
+                                               iter);
+                               }
+
+                               g_free (cpy);
+                       }
+               }
+       }
+
+       return SOUP_TRANSFER_CONTINUE;
+
+ THROW_MALFORMED_HEADER:
+       destroy_message (msg);
+
+       return SOUP_TRANSFER_END;
+}
+
+#define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s))
+
+static gchar *
+get_server_sockname (gint fd)
+{
+       struct sockaddr name;
+       int namelen;
+       gchar *host = NULL;
+       guchar *p;
+
+       if (getsockname (fd, &name, &namelen) == 0) {
+               p = (guchar*) &(SOUP_SOCKADDR_IN(name).sin_addr);
+               host = g_strdup_printf ("%d.%d.%d.%d",
+                                       p [0],
+                                       p [1],
+                                       p [2],
+                                       p [3]);
+       }
+
+       return host;
 }
 
 static SoupTransferDone
@@ -137,8 +421,7 @@ read_headers_cb (const GString        *headers,
 {
        SoupMessage *msg = user_data;
        SoupContext *ctx;
-       gchar *req_path = NULL, *url;
-       const gchar *connection, *length, *enc, *req_host = NULL;
+       gchar *req_path = NULL;
 
        if (!soup_headers_parse_request (headers->str, 
                                         headers->len, 
@@ -148,57 +431,98 @@ read_headers_cb (const GString        *headers,
                                         &msg->priv->http_version))
                goto THROW_MALFORMED_HEADER;
 
-       /* Handle connection persistence */
-       connection = soup_message_get_header (msg->request_headers, 
-                                             "Connection");
-
-       /* FIXME: Make this work.
-       if (connection && g_strcasecmp (connection, "close") == 0)
-               soup_connection_set_keep_alive (req->connection, FALSE);
-       */
-
-       /* Handle Content-Length or Chunked encoding */
-       length = soup_message_get_header (msg->request_headers, 
-                                         "Content-Length");
-       enc = soup_message_get_header (msg->request_headers, 
-                                      "Transfer-Encoding");
-
-       if (length) {
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-               *content_len = atoi (length);
-               if (*content_len < 0) 
-                       goto THROW_MALFORMED_HEADER;
-       } else if (enc) {
-               if (g_strcasecmp (enc, "chunked") == 0)
-                       *encoding = SOUP_TRANSFER_CHUNKED;
-               else {
-                       g_warning ("Unknown encoding type in HTTP request.");
-                       goto THROW_MALFORMED_HEADER;
+       /* 
+        * Handle request body encoding 
+        */
+       {
+               const gchar *length, *enc;
+
+               /* Handle Content-Length or Chunked encoding */
+               length = soup_message_get_header (msg->request_headers, 
+                                                 "Content-Length");
+               enc = soup_message_get_header (msg->request_headers, 
+                                              "Transfer-Encoding");
+
+               if (enc) {
+                       if (g_strcasecmp (enc, "chunked") == 0)
+                               *encoding = SOUP_TRANSFER_CHUNKED;
+                       else {
+                               g_warning ("Unknown encoding type in HTTP "
+                                          "request.");
+                               goto THROW_MALFORMED_HEADER;
+                       }
+               } else if (length) {
+                       *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+                       *content_len = atoi (length);
+                       if (*content_len < 0) 
+                               goto THROW_MALFORMED_HEADER;
+               } else {
+                       *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+                       *content_len = 0;
                }
        }
 
-       /* Generate correct context for request */
-       req_host = soup_message_get_header (msg->request_headers, "Host");
-       if (req_host) 
-               url = g_strconcat ("http://", req_host, req_path, NULL);
-       else 
-               url = g_strdup (req_path);
-
-       ctx = soup_context_get (url);
-       g_free (url);
+       /* 
+        * Generate correct context for request 
+        */
+       {
+               gchar *url = NULL;
+               const gchar *req_host = NULL;
+               SoupServer *server = msg->priv->server;
+
+               req_host = soup_message_get_header (msg->request_headers, 
+                                                   "Host");
+
+               if (req_host) {
+                       url = 
+                               g_strdup_printf (
+                                       "%s%s:%d%s",
+                                       server->proto == SOUP_PROTOCOL_HTTPS ?
+                                               "https://" :
+                                               "http://",
+                                       req_host, 
+                                       server->port,
+                                       req_path);
+               } else 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 
+                               goto THROW_MALFORMED_HEADER;
+               } else {
+                       /* 
+                        * No Host header, no AbsoluteUri
+                        */
+                       SoupSocket *server_sock = msg->priv->server_sock;
+                       gchar *host;
+
+                       host = get_server_sockname (server_sock->sockfd);
+                       url = 
+                               g_strdup_printf (
+                                       "%s%s:%d%s",
+                                       server->proto == 
+                                               SOUP_PROTOCOL_HTTPS ?
+                                                        "https://" :
+                                                        "http://",
+                                       host ? host : "localhost",
+                                       server->port,
+                                       req_path);
+               }
 
-       /* No Host, no AbsoluteUri */
-       if (!ctx) {
-               /* FIXME: Get local socket host name */
-               url = g_strconcat ("http://localhost/", req_path, NULL);
                ctx = soup_context_get (url);
                g_free (url);
-       }
 
-       if (!ctx) goto THROW_MALFORMED_HEADER;
+               if (!ctx) goto THROW_MALFORMED_HEADER;
 
-       soup_message_set_context (msg, ctx);
-       soup_context_unref (ctx);
+               soup_message_set_context (msg, ctx);
+               soup_context_unref (ctx);
+       }
 
        g_free (req_path);
 
@@ -213,34 +537,39 @@ read_headers_cb (const GString        *headers,
 }
 
 static void
-write_header (gchar *key, GSList *vals, SoupMessage *msg)
-{
-       while (vals) {
-               g_string_sprintfa (msg->priv->req_header, 
-                                  "%s: %s\r\n", 
-                                  key, 
-                                  (gchar *) vals->data);
-               vals = vals->next;
-       }
+write_header (gchar *key, gchar *value, GString *ret)
+{
+       g_string_sprintfa (ret, "%s: %s\r\n", key, value);
 }
 
 static GString *
-get_response_header (SoupMessage *req)
+get_response_header (SoupMessage          *req, 
+                    gboolean              status_line, 
+                    SoupTransferEncoding  encoding)
 {
        GString *ret = g_string_new (NULL);
 
-       g_string_sprintfa (ret, 
-                          "HTTP/1.1 %d %s\r\n", 
-                          req->errorcode, 
-                          req->errorphrase);
-
-       g_string_sprintfa (ret, 
-                          "Content-Length: %d\r\n",  
-                          req->response.length);
-
-       g_hash_table_foreach (req->response_headers, 
-                             (GHFunc) write_header,
-                             req);
+       if (status_line) 
+               g_string_sprintfa (ret, 
+                                  "HTTP/1.1 %d %s\r\n", 
+                                  req->errorcode, 
+                                  req->errorphrase);
+       else
+               g_string_sprintfa (ret, 
+                                  "Status: %d %s\r\n", 
+                                  req->errorcode, 
+                                  req->errorphrase);
+
+       if (encoding == SOUP_TRANSFER_CONTENT_LENGTH)
+               g_string_sprintfa (ret, 
+                                  "Content-Length: %d\r\n",  
+                                  req->response.length);
+       else if (encoding == SOUP_TRANSFER_CHUNKED)
+               g_string_append (ret, "Transfer-Encoding: chunked\r\n");
+
+       soup_message_foreach_header (req->response_headers,
+                                    (GHFunc) write_header,
+                                    ret);
 
        g_string_append (ret, "\r\n");
 
@@ -264,19 +593,204 @@ set_response_error (SoupMessage    *req,
 }
 
 static void
+call_handler (SoupMessage          *req,
+             const SoupDataBuffer *req_data,
+             const gchar          *handler_path)
+{
+       SoupServer *server = req->priv->server;
+       SoupServerHandler *hand;
+       SoupServerAuth *auth = NULL;
+
+       g_return_if_fail (req != NULL);
+
+       req->request.owner = req_data->owner;
+       req->request.length = req_data->length;
+       req->request.body = req_data->body;
+
+       req->status = SOUP_STATUS_FINISHED;
+
+       hand = soup_server_get_handler (server, handler_path);
+       if (!hand) {
+               set_response_error (req, SOUP_ERROR_NOT_FOUND, NULL, NULL);
+               return;
+       }
+
+       if (hand->auth_ctx) {
+               SoupServerAuthContext *auth_ctx = hand->auth_ctx;
+               const GSList *auth_hdrs;
+
+               auth_hdrs = soup_message_get_header_list (req->request_headers, 
+                                                         "Authorization");
+               auth = soup_server_auth_new (auth_ctx, auth_hdrs, req);
+
+               if (auth_ctx->callback) {
+                       gboolean ret = FALSE;
+
+                       ret = (*auth_ctx->callback) (auth_ctx, 
+                                                    auth, 
+                                                    req, 
+                                                    auth_ctx->user_data);
+                       if (!ret) {
+                               soup_server_auth_context_challenge (
+                                       auth_ctx,
+                                       req,
+                                       "WWW-Authenticate");
+
+                               if (!req->errorcode) 
+                                       soup_message_set_error (
+                                               req,
+                                               SOUP_ERROR_UNAUTHORIZED);
+
+                               return;
+                       }
+               } else if (req->errorcode) {
+                       soup_server_auth_context_challenge (
+                               auth_ctx,
+                               req,
+                               "WWW-Authenticate");
+                       return;
+               }
+       }
+
+       if (hand->callback) {
+               SoupServerContext servctx = {
+                       req,
+                       req->context->uri->path,
+                       soup_method_get_id (req->method),
+                       auth,
+                       server,
+                       hand
+               };
+
+               /* Call method handler */
+               (*hand->callback) (&servctx, req, hand->user_data);
+       }
+
+       if (auth)
+               soup_server_auth_free (auth);
+}
+
+static void
+get_header_cgi_cb (GString  **out_hdr,
+                  gpointer   user_data)
+{
+       SoupMessage *msg = user_data;
+       SoupServerMessage *server_msg = msg->priv->server_msg;
+
+       if (server_msg && server_msg->started)
+               *out_hdr = get_response_header (msg, 
+                                               FALSE, 
+                                               SOUP_TRANSFER_UNKNOWN);
+       else
+               soup_transfer_write_pause (msg->priv->write_tag);
+}
+
+static void
+get_header_cb (GString  **out_hdr,
+              gpointer   user_data)
+{
+       SoupMessage *msg = user_data;
+       SoupServerMessage *server_msg = msg->priv->server_msg;
+       SoupTransferEncoding encoding;
+
+       if (server_msg && server_msg->started) {
+               if (msg->priv->http_version == SOUP_HTTP_1_0)
+                       encoding = SOUP_TRANSFER_UNKNOWN;
+               else
+                       encoding = SOUP_TRANSFER_CHUNKED;
+               
+               *out_hdr = get_response_header (msg, TRUE, encoding);
+       } else
+               soup_transfer_write_pause (msg->priv->write_tag);
+}
+
+static SoupTransferDone
+get_chunk_cb (SoupDataBuffer *out_next, gpointer user_data)
+{
+       SoupMessage *msg = user_data;
+       SoupServerMessage *server_msg = msg->priv->server_msg;
+
+       if (server_msg->chunks) {
+               SoupDataBuffer *next = server_msg->chunks->data;
+
+               out_next->owner = next->owner;
+               out_next->body = next->body;
+               out_next->length = next->length;
+
+               server_msg->chunks = g_slist_remove (server_msg->chunks, next);
+
+               /*
+                * Caller will free the response body, so just free the
+                * SoupDataBuffer struct.
+                */
+               g_free (next);
+
+               return SOUP_TRANSFER_CONTINUE;
+       } 
+       else if (server_msg->finished) {
+               return SOUP_TRANSFER_END;
+       } 
+       else {
+               soup_transfer_write_pause (msg->priv->write_tag);
+               return SOUP_TRANSFER_CONTINUE;
+       }
+}
+
+static void
+read_done_cgi_cb (const SoupDataBuffer *data,
+                 gpointer              user_data)
+{
+       SoupMessage *req = user_data;
+       SoupServer *server = req->priv->server;
+       GIOChannel *channel;
+
+       req->priv->read_tag = 0;
+
+       call_handler (req, data, g_getenv ("PATH_INFO"));
+
+       channel = server->cgi_write_chan;
+
+       if (req->priv->server_msg) {
+               req->priv->write_tag = 
+                       soup_transfer_write (channel,
+                                            SOUP_TRANSFER_UNKNOWN,
+                                            get_header_cgi_cb,
+                                            get_chunk_cb,
+                                            write_done_cb,
+                                            error_cb,
+                                            req);
+
+               /*
+                * Pause write until soup_server_message_start()
+                */
+               if (!req->priv->server_msg->started)
+                       soup_transfer_write_pause (req->priv->write_tag);
+       } else {
+               GString *header;
+               header = get_response_header (req, 
+                                             FALSE, 
+                                             SOUP_TRANSFER_CONTENT_LENGTH);
+               req->priv->write_tag = 
+                       soup_transfer_write_simple (channel,
+                                                   header,
+                                                   &req->response,
+                                                   write_done_cb,
+                                                   error_cb,
+                                                   req);
+       }
+
+       return;
+}
+
+static void
 read_done_cb (const SoupDataBuffer *data,
              gpointer              user_data)
 {
        SoupMessage *req = user_data;
-       SoupServerHandler *hand;
+       SoupSocket *server_sock = req->priv->server_sock;
        GIOChannel *channel;
-       const gchar *path;
-
-       req->request.owner = data->owner;
-       req->request.length = data->length;
-       req->request.body = data->body;
 
-       req->status = SOUP_STATUS_FINISHED;
+       req->priv->read_tag = 0;
 
        /* FIXME: Do this in soap handler 
        action = soup_message_get_header (req->request_headers, "SOAPAction");
@@ -291,164 +805,336 @@ read_done_cb (const SoupDataBuffer *data,
        }
        */
 
-       path = soup_context_get_uri (req->context)->path;
-
-       hand = soup_server_get_handler (req->priv->server, path);
-       if (!hand) {
-               if (req->priv->server->default_handler.cb)
-                       hand = &req->priv->server->default_handler;
-               else {
-                       set_response_error (req, 404, NULL, NULL);
-                       goto START_WRITE;
-               }
+       call_handler (req, data, soup_context_get_uri (req->context)->path);
+
+       channel = soup_socket_get_iochannel (server_sock);
+
+       if (req->priv->server_msg) {
+               SoupTransferEncoding encoding;
+
+               if (req->priv->http_version == SOUP_HTTP_1_0)
+                       encoding = SOUP_TRANSFER_UNKNOWN;
+               else
+                       encoding = SOUP_TRANSFER_CHUNKED;
+
+               req->priv->write_tag = 
+                       soup_transfer_write (channel,
+                                            encoding,
+                                            get_header_cb,
+                                            get_chunk_cb,
+                                            write_done_cb,
+                                            error_cb,
+                                            req);
+
+               /*
+                * Pause write until soup_server_message_start()
+                */
+               if (!req->priv->server_msg->started)
+                       soup_transfer_write_pause (req->priv->write_tag);
+       } else {
+               GString *header;
+               header = get_response_header (req, 
+                                             TRUE, 
+                                             SOUP_TRANSFER_CONTENT_LENGTH);
+               req->priv->write_tag = 
+                       soup_transfer_write_simple (channel,
+                                                   header,
+                                                   &req->response,
+                                                   write_done_cb,
+                                                   error_cb,
+                                                   req);
        }
 
-       /* Call method handler */
-       if (hand->cb) (*hand->cb) (req, NULL, hand->user_data);
+       g_io_channel_unref (channel);
 
- START_WRITE:
-       channel = soup_socket_get_iochannel (req->priv->server_sock);
+       return;
+}
 
-       req->priv->req_header = get_response_header (req);
-       req->priv->read_tag = 0;
-       req->priv->write_tag = 
-               soup_transfer_write (channel,
-                                    req->priv->req_header,
-                                    &req->response,
-                                    NULL,
-                                    write_done_cb,
-                                    error_cb,
-                                    req);
+static SoupMessage *
+message_new (SoupServer *server)
+{
+       SoupMessage *msg;
 
-       g_io_channel_unref (channel);
+       /*
+        * Create an empty message to hold request state.
+        */
+       msg = soup_message_new (NULL, NULL);
+       if (msg) {
+               msg->priv->server = server;
+               soup_server_ref (server);
+       }
 
-       return;
+       return msg;
 }
 
-static void 
-conn_accept (GIOChannel    *chan,
+static gboolean
+start_another_request (GIOChannel    *serv_chan,
+                      GIOCondition   condition, 
+                      gpointer       user_data)
+{
+       ServerConnectData *data = user_data;
+       SoupMessage *msg;
+       int fd, cnt;
+
+       fd = g_io_channel_unix_get_fd (serv_chan);
+
+       if (!(condition & G_IO_IN) || 
+           ioctl (fd, FIONREAD, &cnt) < 0 ||
+           cnt <= 0)
+               soup_socket_unref (data->server_sock);
+       else {
+               msg = message_new (data->server);
+               if (!msg) {
+                       g_warning ("Unable to create new incoming message\n");
+                       soup_socket_unref (data->server_sock);
+               } else {
+                       msg->priv->server_sock = data->server_sock;
+                       msg->priv->read_tag = 
+                               soup_transfer_read (serv_chan,
+                                                   FALSE,
+                                                   read_headers_cb,
+                                                   NULL,
+                                                   read_done_cb,
+                                                   error_cb,
+                                                   msg);
+               }
+       }
+
+       g_free (data);
+       return FALSE;
+}
+
+static gboolean 
+conn_accept (GIOChannel    *serv_chan,
             GIOCondition   condition, 
-            SoupServer    *serv)
+            gpointer       user_data)
 {
-       GIOChannel *channel;
-       SoupSocket *sock;
-       SoupContext *ctx;
+       SoupServer *server = user_data;
        SoupMessage *msg;
-       SoupUri uri = { 
-               serv->proto,
-               NULL, 
-               NULL, 
-               NULL, 
-               "localhost", 
-               serv->port, 
-               "/",
-               NULL,
-               NULL 
-       };
-
-       sock = soup_socket_server_try_accept (serv->sock);
-       if (!sock) return;
-       
-       channel = soup_socket_get_iochannel (sock);
+       GIOChannel *chan;
+       SoupSocket *sock;
 
-       /* 
-        * Create a fake context until the request is read 
-        * and we can generate a valid one.
-        */
-       ctx = soup_context_from_uri (&uri);
-       msg = soup_message_new (ctx, NULL);
+       sock = soup_socket_server_try_accept (server->listen_sock);
+       if (!sock) return TRUE;
 
-       msg->priv->server = serv;
-       msg->priv->server_sock = sock;
+       msg = message_new (server);
+       if (!msg) {
+               g_warning ("Unable to create new incoming message\n");
+               return TRUE;
+       }
 
        chan = soup_socket_get_iochannel (sock);
 
-       if (serv->proto == SOUP_PROTOCOL_HTTPS) 
+       if (server->proto == SOUP_PROTOCOL_HTTPS) {
                chan = soup_ssl_get_iochannel (chan);
+               g_io_channel_unref (sock->iochannel);
+               g_io_channel_ref (chan);
+               sock->iochannel = chan;
+       }
 
+       msg->priv->server_sock = sock;
        msg->priv->read_tag = 
-               soup_transfer_read (
-                       chan,
-                       FALSE,
-                       read_headers_cb,
-                       NULL,
-                       read_done_cb,
-                       error_cb,
-                       msg);
+               soup_transfer_read (chan,
+                                   FALSE,
+                                   read_headers_cb,
+                                   NULL,
+                                   read_done_cb,
+                                   error_cb,
+                                   msg);
 
        g_io_channel_unref (chan);
+
+       return TRUE;
+}
+
+typedef struct {
+       SoupMessage *msg;
+       guint        content_len;
+       GByteArray  *recv_buf;
+} CgiReader;
+
+static gboolean
+cgi_read (GIOChannel    *serv_chan,
+         GIOCondition   condition, 
+         gpointer       user_data)
+{
+       CgiReader *reader = user_data;
+
+       if (!(condition & G_IO_IN))
+               goto DONE_READING;
+       else {
+               while (reader->recv_buf->len < reader->content_len) {
+                       guchar read_buf [RESPONSE_BLOCK_SIZE];
+                       gint bytes_read;
+                       GIOError error;
+
+                       error = g_io_channel_read (serv_chan,
+                                                  read_buf,
+                                                  sizeof (read_buf),
+                                                  &bytes_read);
+
+                       if (error == G_IO_ERROR_AGAIN)
+                               return TRUE;
+
+                       if (error != G_IO_ERROR_NONE)
+                               goto DONE_READING;
+
+                       if (bytes_read) 
+                               g_byte_array_append (reader->recv_buf, 
+                                                    read_buf, 
+                                                    bytes_read);
+                       else
+                               break;
+               }
+       }
+
+ DONE_READING:
+       if (reader->recv_buf->len == reader->content_len) {
+               SoupDataBuffer buf;
+
+               g_byte_array_append (reader->recv_buf, "\0", 1);
+
+               buf.owner  = SOUP_BUFFER_SYSTEM_OWNED;
+               buf.body   = reader->recv_buf->data;
+               buf.length = reader->recv_buf->len;
+
+               read_done_cgi_cb (&buf, reader->msg);
+               
+               g_byte_array_free (reader->recv_buf, FALSE);
+       } else 
+               g_byte_array_free (reader->recv_buf, TRUE);
+
+       g_free (reader);                
+
+       return FALSE;
 }
 
 void
-soup_server_run_async (SoupServer *serv)
+soup_server_run_async (SoupServer *server)
 {
-       g_return_if_fail (serv != NULL);
-       g_return_if_fail (serv->port >= 0);
+       g_return_if_fail (server != NULL);
+
+       if (server->proto == SOUP_PROTOCOL_CGI) {
+               SoupMessage *msg;
+               gint content_len = 0;
+
+               msg = message_new (server);
+               if (!msg) {
+                       g_warning ("Unable to create new incoming message\n");
+                       return;
+               }
+
+               if (read_headers_cgi (msg, &content_len) == SOUP_TRANSFER_END)
+                       goto START_ERROR;
+
+               if (content_len > 0) {
+                       CgiReader *reader;
+
+                       reader = g_new0 (CgiReader, 1);
+                       reader->msg = msg;
+                       reader->content_len = content_len;
+                       reader->recv_buf = g_byte_array_new ();
+
+                       g_io_add_watch (server->cgi_read_chan,
+                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       (GIOFunc) cgi_read, 
+                                       reader);
+               } else {
+                       SoupDataBuffer buf = {
+                               SOUP_BUFFER_STATIC,
+                               "",
+                               0
+                       };
+
+                       read_done_cgi_cb (&buf, msg);
+               }
+       } else {
+               GIOChannel *chan;
 
-       if (!serv->sock) {
-               serv->sock = soup_socket_server_new (serv->port);
-               if (!serv->sock) goto START_ERROR;
+               if (!server->listen_sock) 
+                       goto START_ERROR;
+
+               /* 
+                * Listen for new connections (if not already)
+                */
+               if (!server->accept_tag) {
+                       chan = soup_socket_get_iochannel (server->listen_sock);
+
+                       server->accept_tag = 
+                               g_io_add_watch (chan,
+                                               G_IO_IN,
+                                               (GIOFunc) conn_accept, 
+                                               server);
+
+                       g_io_channel_unref (chan);
+               }
        }
 
-       if (!serv->accept_tag) 
-               serv->accept_tag = 
-                       g_io_add_watch (soup_socket_get_iochannel (serv->sock),
-                                       G_IO_IN,
-                                       (GIOFunc) conn_accept, 
-                                       serv);
+       soup_server_ref (server);
 
        return;
 
  START_ERROR:
-       if (serv->loop) {
-               g_main_destroy (serv->loop);
-               serv->loop = NULL;
+       if (server->loop) {
+               g_main_destroy (server->loop);
+               server->loop = NULL;
        }
-       return;
 }
 
 void
-soup_server_run (SoupServer *serv)
+soup_server_run (SoupServer *server)
 {
-       g_return_if_fail (serv != NULL);
-       g_return_if_fail (serv->port >= 0);
+       g_return_if_fail (server != NULL);
 
-       serv->loop = g_main_new (TRUE);
-
-       soup_server_run_async (serv);
+       if (!server->loop) {
+               server->loop = g_main_new (TRUE);
+               soup_server_run_async (server);
+       }
 
-       if (serv->loop)
-               g_main_run (serv->loop);
+       if (server->loop)
+               g_main_run (server->loop);
 }
 
 void 
-soup_server_quit (SoupServer *serv)
+soup_server_quit (SoupServer *server)
 {
-       g_return_if_fail (serv != NULL);
+       g_return_if_fail (server != NULL);
 
-       g_main_quit (serv->loop);
+       g_main_quit (server->loop);
+       soup_server_unref (server);
 }
 
-void 
-soup_server_add_list (SoupServer        *serv,
-                     SoupServerHandler *list)
+static void
+append_handler (gpointer key, gpointer value, gpointer user_data)
 {
-       g_return_if_fail (serv != NULL);
+       GSList **ret = user_data;
+
+       *ret = g_slist_append (*ret, value);
+}
+
+GSList *
+soup_server_list_handlers (SoupServer *server)
+{
+       GSList *ret = NULL;
 
-       serv->static_handlers = g_slist_prepend (serv->static_handlers, list);
+       g_hash_table_foreach (server->handlers, append_handler, &ret);
+
+       return ret;
 }
 
 SoupServerHandler *
-soup_server_get_handler (SoupServer *serv, const gchar *path)
+soup_server_get_handler (SoupServer *server, const gchar *path)
 {
-       GSList *iter;
        gchar *mypath, *dir;
        SoupServerHandler *hand = NULL;
 
-       g_return_val_if_fail (serv != NULL, NULL);
-       g_return_val_if_fail (path != NULL, NULL);
+       g_return_val_if_fail (server != NULL, NULL);
 
-       if (!serv->handlers) return NULL;
+       if (!path)
+               return server->default_handler;
+
+       if (!server->handlers) 
+               return NULL;
 
        mypath = g_strdup (path);
 
@@ -458,85 +1144,168 @@ soup_server_get_handler (SoupServer *serv, const gchar *path)
        dir = mypath;
 
        do {
-               hand = g_hash_table_lookup (serv->handlers, mypath);
+               hand = g_hash_table_lookup (server->handlers, mypath);
                if (hand) {
                        g_free (mypath);
                        return hand;
                }
 
-               for (iter = serv->static_handlers; iter; iter = iter->next) {
-                       hand = iter->data;
-                       while (hand && hand->path) {
-                               if (!strcmp (hand->path, mypath)) {
-                                       g_free (mypath);
-                                       return hand;
-                               }
-                               hand++;
-                       }
-               }
-
                dir = strrchr (mypath, '/');
                if (dir) *dir = '\0';
        } while (dir);
 
        g_free (mypath);
-       return NULL;
+
+       return server->default_handler;
 }
 
 void  
-soup_server_register (SoupServer           *serv,
-                     const gchar          *path, 
-                     guint                 auth_types,
-                     SoupServerCallbackFn  cb,
-                     gpointer              user_data)
+soup_server_register (SoupServer            *server,
+                     const gchar           *path,
+                     SoupServerAuthContext *auth_ctx,
+                     SoupServerCallbackFn   callback,
+                     SoupServerUnregisterFn unregister,
+                     gpointer               user_data)
 {
-       SoupServerHandler *hand;
+       SoupServerHandler *new_hand;
+       SoupServerAuthContext *new_auth_ctx = NULL;
 
-       g_return_if_fail (serv != NULL);
-       g_return_if_fail (path != NULL);
+       g_return_if_fail (server != NULL);
+       g_return_if_fail (callback != NULL);
 
-       hand = g_new0 (SoupServerHandler, 1);
-       hand->path = g_strdup (path);
-       hand->auth_types = auth_types;
-       hand->cb = cb;
-       hand->user_data = user_data;
+       if (auth_ctx) {
+               new_auth_ctx = g_new0 (SoupServerAuthContext, 1);
 
-       if (!serv->handlers)
-               serv->handlers = g_hash_table_new (g_str_hash, g_str_equal);
-       else 
-               soup_server_unregister (serv, path);
+               new_auth_ctx->types = auth_ctx->types;
+               new_auth_ctx->callback = auth_ctx->callback;
+               new_auth_ctx->user_data = auth_ctx->user_data;
 
-       g_hash_table_insert (serv->handlers, hand->path, hand);
+               new_auth_ctx->basic_info.realm = 
+                       g_strdup (auth_ctx->basic_info.realm);
+
+               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;
+       }
+
+       new_hand = g_new0 (SoupServerHandler, 1);
+       new_hand->path       = g_strdup (path);
+       new_hand->auth_ctx   = new_auth_ctx;
+       new_hand->callback   = callback;
+       new_hand->unregister = unregister;
+       new_hand->user_data  = user_data;
+
+       if (path) {
+               if (!server->handlers)
+                       server->handlers = g_hash_table_new (g_str_hash, 
+                                                            g_str_equal);
+               else 
+                       soup_server_unregister (server, new_hand->path);
+
+               g_hash_table_insert (server->handlers, 
+                                    (gchar *) new_hand->path, 
+                                    new_hand);
+       } else
+               server->default_handler = new_hand;
 }
 
 void  
-soup_server_unregister (SoupServer *serv, const gchar *path)
+soup_server_unregister (SoupServer *server, const gchar *path)
 {
        SoupServerHandler *hand;
 
-       g_return_if_fail (serv != NULL);
-       g_return_if_fail (path != NULL);
+       g_return_if_fail (server != NULL);
+
+       if (!path) {
+               if (server->default_handler) {
+                       free_handler (server, server->default_handler);
+                       server->default_handler = NULL;
+               }
+               return;
+       }
 
-       if (!serv->handlers) return;
+       if (!server->handlers) return;
 
-       hand = g_hash_table_lookup (serv->handlers, path);
+       hand = g_hash_table_lookup (server->handlers, path);
        if (hand) {
-               g_hash_table_remove (serv->handlers, path);
+               g_hash_table_remove (server->handlers, path);
+               free_handler (server, hand);
+       }
+}
+
+SoupServerMessage *
+soup_server_message_new (SoupMessage *src_msg)
+{
+       SoupServerMessage *ret;
+
+       g_return_val_if_fail (src_msg != NULL, NULL);
+
+       if (src_msg->priv->server_msg) 
+               return src_msg->priv->server_msg;
+
+       ret = g_new0 (SoupServerMessage, 1);
+       ret->msg = src_msg;
+
+       src_msg->priv->server_msg = ret;
+
+       return ret;
+}
+
+void
+soup_server_message_start (SoupServerMessage *serv_msg)
+{
+       g_return_if_fail (serv_msg != NULL);
+
+       serv_msg->started = TRUE;
+
+       soup_transfer_write_unpause (serv_msg->msg->priv->write_tag);
+}
+
+void
+soup_server_message_add_data (SoupServerMessage *serv_msg,
+                             SoupOwnership      owner,
+                             gchar             *body,
+                             gulong             length)
+{
+       SoupDataBuffer *buf;
+
+       g_return_if_fail (serv_msg != NULL);
+       g_return_if_fail (body != NULL);
+       g_return_if_fail (length != 0);
+
+       buf = g_new0 (SoupDataBuffer, 1);
+       buf->length = length;
 
-               g_free (hand->path);
-               g_free (hand);
+       if (owner == SOUP_BUFFER_USER_OWNED) {
+               buf->body = g_memdup (body, length);
+               buf->owner = SOUP_BUFFER_SYSTEM_OWNED;
+       } else {
+               buf->body = body;
+               buf->owner = owner;
        }
+
+       serv_msg->chunks = g_slist_append (serv_msg->chunks, buf);
+
+       soup_transfer_write_unpause (serv_msg->msg->priv->write_tag);
 }
 
-void  
-soup_server_register_default (SoupServer           *serv,
-                             guint                 auth_types,
-                             SoupServerCallbackFn  cb,
-                             gpointer              user_data)
+void
+soup_server_message_finish  (SoupServerMessage *serv_msg)
 {
-       g_return_if_fail (serv != NULL);
+       g_return_if_fail (serv_msg != NULL);
 
-       serv->default_handler.auth_types = auth_types;
-       serv->default_handler.cb = cb;
-       serv->default_handler.user_data = user_data;
+       serv_msg->started = TRUE;
+       serv_msg->finished = TRUE;
+
+       soup_transfer_write_unpause (serv_msg->msg->priv->write_tag);
+}
+
+SoupMessage *
+soup_server_message_get_source (SoupServerMessage *serv_msg)
+{
+       g_return_val_if_fail (serv_msg != NULL, NULL);
+       return serv_msg->msg;
 }
index cac094a..9245381 100644 (file)
 
 #include <glib.h>
 #include <libsoup/soup-message.h>
+#include <libsoup/soup-method.h>
 #include <libsoup/soup-misc.h>
 #include <libsoup/soup-uri.h>
+#include <libsoup/soup-server-auth.h>
 
-typedef struct {
-       SoupAuthType  type;
-       const gchar  *realm;
-       const gchar  *username;
-       const gchar  *password;
-} SoupServerBasicToken;
+typedef struct _SoupServer SoupServer;
+typedef struct _SoupServerHandler SoupServerHandler;
 
 typedef struct {
-       SoupAuthType  type;
-       const gchar  *realm;
-       const gchar  *username;
-       const gchar  *password_hash;
-} SoupServerDigestToken;
+       SoupMessage       *msg;
+       gchar             *path;
+       SoupMethodId       method_id;
+       SoupServerAuth    *auth;
+       SoupServer        *server;
+       SoupServerHandler *handler;
+} SoupServerContext;
 
-typedef struct {
-       SoupAuthType  type;
-       const gchar  *host;
-       const gchar  *domain;
-       const gchar  *user;
-       const gchar  *lm_hash;
-       const gchar  *nt_hash;
-} SoupServerNTLMToken;
-
-typedef union {
-       SoupAuthType           type;
-       SoupServerBasicToken   basic;
-       SoupServerDigestToken  digest;
-       SoupServerNTLMToken    ntlm;
-} SoupServerAuthToken;
-
-typedef void (*SoupServerCallbackFn) (SoupMessage          *msg, 
-                                     SoupServerAuthToken  *token,
-                                     gpointer              data);
+typedef void (*SoupServerCallbackFn) (SoupServerContext    *context,
+                                     SoupMessage          *msg, 
+                                     gpointer              user_data);
 
-typedef struct {
-       gchar                *path;
-       guint                 auth_types;
-       SoupServerCallbackFn  cb;
-       gpointer              user_data;
-} SoupServerHandler;
+typedef void (*SoupServerUnregisterFn) (SoupServer        *server,
+                                       SoupServerHandler *handler,
+                                       gpointer           user_data);
 
-typedef struct _SoupServer SoupServer;
+struct _SoupServerHandler {
+       const gchar            *path;
+
+       SoupServerAuthContext  *auth_ctx;
 
-extern SoupServer *SOUP_CGI_SERVER;
-extern SoupServer *SOUP_HTTPD_SERVER;
-extern SoupServer *SOUP_HTTPD_SSL_SERVER;
+       SoupServerCallbackFn    callback;
+       SoupServerUnregisterFn  unregister;
+       gpointer                user_data;
+};
 
-SoupServer *       soup_server_new              (SoupProtocol          proto,
-                                                guint                 port);
+SoupServer        *soup_server_new           (SoupProtocol           proto,
+                                             guint                  port);
 
-void               soup_server_free             (SoupServer           *serv);
+SoupServer        *soup_server_cgi           (void);
 
-gint               soup_server_get_port         (SoupServer           *serv);
+void               soup_server_ref           (SoupServer            *serv);
 
-void               soup_server_run              (SoupServer           *serv);
+void               soup_server_unref         (SoupServer            *serv);
 
-void               soup_server_run_async        (SoupServer           *serv);
+SoupProtocol       soup_server_get_protocol  (SoupServer            *serv);
 
-void               soup_server_quit             (SoupServer           *serv);
+gint               soup_server_get_port      (SoupServer            *serv);
 
-void               soup_server_add_list         (SoupServer           *serv,
-                                                SoupServerHandler    *list);
+void               soup_server_run           (SoupServer            *serv);
 
-void               soup_server_remove_list      (SoupServer           *serv,
-                                                SoupServerHandler    *list);
+void               soup_server_run_async     (SoupServer            *serv);
 
-void               soup_server_register         (SoupServer           *serv,
-                                                const gchar          *path,
-                                                guint                 authtype,
-                                                SoupServerCallbackFn  cb,
-                                                gpointer              data);
+void               soup_server_quit          (SoupServer            *serv);
 
-void               soup_server_register_default (SoupServer           *serv,
-                                                guint                 authtype,
-                                                SoupServerCallbackFn  cb,
-                                                gpointer              data);
+void               soup_server_register      (SoupServer            *serv,
+                                             const gchar           *path,
+                                             SoupServerAuthContext *auth_ctx,
+                                             SoupServerCallbackFn   callback,
+                                             SoupServerUnregisterFn unregister,
+                                             gpointer               user_data);
+
+void               soup_server_unregister    (SoupServer            *serv,
+                                             const gchar           *path);
+
+SoupServerHandler *soup_server_get_handler   (SoupServer            *serv,
+                                             const gchar           *path);
+
+GSList            *soup_server_list_handlers (SoupServer            *serv);
+
+/* 
+ * Apache/soup-httpd module initializtion
+ * Implement soup_server_init() in your shared library. 
+ */
+extern void soup_server_init (SoupServer *server);
 
-void               soup_server_unregister       (SoupServer           *serv,
-                                                const gchar          *path);
+typedef struct _SoupServerMessage SoupServerMessage;
 
-SoupServerHandler *soup_server_get_handler      (SoupServer           *serv,
-                                                const gchar          *path);
+SoupServerMessage *soup_server_message_new        (SoupMessage       *src_msg);
 
-void               soup_server_set_auth         (SoupServer  *serv,
-                                                const gchar *path,
-                                                guint        auth_types,
-                                                const gchar *realm);
+void               soup_server_message_start      (SoupServerMessage *servmsg);
 
-void               soup_server_require_auth     (SoupMessage *message,
-                                                guint        auth_types,
-                                                const gchar *realm);
+void               soup_server_message_add_data   (SoupServerMessage *servmsg,
+                                                  SoupOwnership      owner,
+                                                  gchar             *body,
+                                                  gulong             length);
 
+void               soup_server_message_finish     (SoupServerMessage *servmsg);
 
-/* Apache module initializtion */
-/* Implement soup_server_init() in your library. */
-extern void soup_server_init      (void);
+SoupMessage       *soup_server_message_get_source (SoupServerMessage *servmsg);
 
 #endif /* SOUP_SERVER_H */
index cb7601e..a46fecc 100644 (file)
 #define socklen_t size_t
 #endif
 
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
 /*
  * Maintains a list of all currently valid SoupAddresses or active
  * SoupAddressState lookup requests.
@@ -434,7 +438,8 @@ soup_address_new_cb (GIOChannel* iochannel,
                close (state->fd);
                waitpid (state->pid, &ret, 0);
 
-               if (WIFSIGNALED (ret) || WEXITSTATUS (ret) != 1) goto ERROR;
+               if (WIFSIGNALED (ret) || WEXITSTATUS (ret) != 1) 
+                       goto ERROR;
 
                /* 
                 * Exit status of one means we are inside a debugger.
@@ -460,7 +465,8 @@ soup_address_new_cb (GIOChannel* iochannel,
                /* Return true if there's more to read */
                if ((state->len - 1) != state->buffer [0]) return TRUE;
 
-               if (state->len < 2) goto ERROR;
+               if (state->len < 2) 
+                       goto ERROR;
 
                /* Success. Copy resolved address. */
                sa_in = (struct sockaddr_in*) &state->ia.sa;
@@ -475,7 +481,7 @@ soup_address_new_cb (GIOChannel* iochannel,
        }
 
        /* Get state data before realloc */
-       cb_list = iter = state->cb_list;
+       cb_list = state->cb_list;
        cb_func = state->func;
        cb_data = state->data;
 
@@ -492,13 +498,12 @@ soup_address_new_cb (GIOChannel* iochannel,
 
        (*cb_func) (&state->ia, SOUP_ADDRESS_STATUS_OK, cb_data);
 
-       while (iter) {
+       for (iter = cb_list; iter; iter = iter->next) {
                SoupAddressCbData *cb = iter->data;
 
                (*cb->func) (&state->ia, SOUP_ADDRESS_STATUS_OK, cb->data);
 
                g_free (cb);
-               iter = iter->next;
        }
 
        g_slist_free (cb_list);
@@ -525,6 +530,60 @@ soup_address_new_cb (GIOChannel* iochannel,
        return FALSE;
 }
 
+static SoupAddress *
+lookup_in_cache_internal (const gchar       *name, 
+                         const gint         port,
+                         gboolean          *in_progress)
+{
+       SoupAddress* ia = NULL;
+
+       if (in_progress)
+               *in_progress = FALSE;
+
+       if (!active_address_hash)
+               return NULL;
+
+       ia = g_hash_table_lookup (active_address_hash, name);
+
+       if (ia && ia->ref_count >= 0) {
+               /*
+                * Existing valid request, use it.
+                */
+               if (soup_address_get_port (ia) == port) {
+                       soup_address_ref (ia);
+               } else {
+                       /* 
+                        * We can reuse the address, but we have to
+                        * change port 
+                        */
+                       SoupAddress *new_ia = soup_address_copy (ia);
+
+                       ((struct sockaddr_in*) &new_ia->sa)->sin_port = 
+                               g_htons (port);
+
+                       ia = new_ia;
+               }
+       }
+       else if (ia && in_progress)
+               *in_progress = TRUE;
+
+       return ia;
+}
+
+SoupAddress *
+soup_address_lookup_in_cache (const gchar *name, const gint port)
+{
+       SoupAddress *ia;
+       gboolean in_prog;
+
+       ia = lookup_in_cache_internal (name, port, &in_prog);
+
+       if (in_prog) 
+               return NULL;
+
+       return ia;
+}
+
 /**
  * soup_address_new:
  * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name
@@ -566,7 +625,15 @@ soup_address_new (const gchar* name,
 {
        pid_t pid = -1;
        int pipes [2];
+#ifdef HAVE_INET_PTON
+       struct in_addr inaddr;
+#else
+#  ifdef HAVE_INET_ATON
        struct in_addr inaddr;
+#  else
+       in_addr_t inaddr;
+#  endif
+#endif
        struct sockaddr_in sa;
        struct sockaddr_in* sa_in;
        SoupAddress* ia;
@@ -581,8 +648,17 @@ soup_address_new (const gchar* name,
 #ifdef HAVE_INET_PTON
        inaddr_ok = inet_pton (AF_INET, name, &inaddr) != 0;
 #else
+#  ifdef HAVE_INET_ATON
        inaddr_ok = inet_aton (name, &inaddr) != 0;
+#  else
+       inaddr = inet_addr (name);
+       if (inaddr == INADDR_NONE)
+               inaddr_ok = FALSE;
+       else
+               inaddr_ok = TRUE;
+#  endif
 #endif
+
        if (inaddr_ok) {
                ia = g_new0 (SoupAddress, 1);
                ia->ref_count = 1;
@@ -592,7 +668,7 @@ soup_address_new (const gchar* name,
                sa_in->sin_port = g_htons(port);
                memcpy (&sa_in->sin_addr,
                        (char*) &inaddr,
-                       sizeof(struct in_addr));
+                       sizeof(inaddr));
 
                (*func) (ia, SOUP_ADDRESS_STATUS_OK, data);
                return NULL;
@@ -602,29 +678,10 @@ soup_address_new (const gchar* name,
                active_address_hash = g_hash_table_new (soup_str_case_hash,
                                                        soup_str_case_equal);
        else {
-               ia = g_hash_table_lookup (active_address_hash, name);
-
-               if (ia && ia->ref_count >= 0) {
-                       /*
-                        * Existing valid request, use it.
-                        */
-                       if (soup_address_get_port (ia) == port) {
-                               soup_address_ref (ia);
-                       } else {
-                               /* 
-                                * We can reuse the address, but we have to
-                                * change port 
-                                */
-                               SoupAddress *new_ia = soup_address_copy (ia);
-                               soup_address_set_port (new_ia, port);
-                               ia = new_ia;
-                       }
+               gboolean in_prog;
 
-                       (*func) (ia, SOUP_ADDRESS_STATUS_OK, data);
-
-                       return NULL;
-               } 
-               else if (ia && soup_address_get_port (ia) == port) {
+               ia = lookup_in_cache_internal (name, port, &in_prog);
+               if (in_prog) {
                        /*
                         * Lookup currently in progress.
                         * Add func to list of callbacks in state.
@@ -646,6 +703,8 @@ soup_address_new (const gchar* name,
 
                        return state;
                }
+               else if (ia)
+                       return ia;
        }
 
        /* Check to see if we are doing synchronous DNS lookups */
index f881471..4b73320 100644 (file)
@@ -15,7 +15,6 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
-
 #include <fcntl.h>
 #include <glib.h>
 #include <string.h>
 #ifdef SOUP_WIN32
 #  define socklen_t gint32
 #  define SOUP_CLOSE_SOCKET(fd) closesocket(fd)
-#  define SOUP_SOCKET_IOCHANNEL_NEW(fd) g_io_channel_win32_new_stream_socket(fd)
-#  ifndef INET_ADDRSTRLEN
-#    define INET_ADDRSTRLEN 16
-#    define INET6_ADDRSTRLEN 46
-#  endif
+#  define SOUP_SOCKET_IOCHANNEL_NEW(fd) g_io_channel_win32_new_socket(fd)
 #else
 #  include <unistd.h>
 #  ifndef socklen_t
 #  define SOUP_SOCKET_IOCHANNEL_NEW(fd) g_io_channel_unix_new(fd)
 #endif
 
+#ifndef INET_ADDRSTRLEN
+#  define INET_ADDRSTRLEN 16
+#  define INET6_ADDRSTRLEN 46
+#endif
+
 #define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s))
 
 typedef struct {
@@ -181,18 +181,22 @@ soup_address_get_port (const SoupAddress* ia)
 }
 
 /**
- * soup_address_set_port:
- * @ia: Address to set the port number of.
- * @port: New port number
+ * soup_address_get_sockaddr:
+ * @ia: The %SoupAddress.
+ * @addrlen: Pointer to socklen_t the returned sockaddr's length is to be 
+ * placed in.
  *
- * Set the port number.
+ * Return value: const pointer to @ia's sockaddr buffer.
  **/
-void
-soup_address_set_port (const SoupAddress* ia, guint port)
+const struct sockaddr *
+soup_address_get_sockaddr (SoupAddress *ia, guint *addrlen)
 {
-       g_return_if_fail (ia != NULL);
+       g_return_val_if_fail (ia != NULL, NULL);
 
-       ((struct sockaddr_in*) &ia->sa)->sin_port = g_htons (port);
+       if (addrlen)
+               *addrlen = sizeof (struct sockaddr_in);
+
+       return &ia->sa;
 }
 
 /**
@@ -213,8 +217,11 @@ soup_address_hash (const gpointer p)
        g_assert(p != NULL);
 
        ia = (const SoupAddress*) p;
-       /* We do pay attention to network byte order just in case the hash
-          result is saved or sent to a different host.  */
+
+       /* 
+        * We do pay attention to network byte order just in case the hash
+        * result is saved or sent to a different host.  
+        */
        port = (guint32) g_ntohs (((struct sockaddr_in*) &ia->sa)->sin_port);
        addr = g_ntohl (((struct sockaddr_in*) &ia->sa)->sin_addr.s_addr);
 
@@ -236,7 +243,7 @@ soup_address_equal (const gpointer p1, const gpointer p2)
        const SoupAddress* ia1 = (const SoupAddress*) p1;
        const SoupAddress* ia2 = (const SoupAddress*) p2;
 
-       g_assert(p1 != NULL && p2 != NULL);
+       g_assert (p1 != NULL && p2 != NULL);
 
        /* Note network byte order doesn't matter */
        return ((SOUP_SOCKADDR_IN(ia1->sa).sin_addr.s_addr ==
@@ -305,17 +312,19 @@ soup_socket_connect_tcp_cb (SoupSocket* socket,
                            gpointer data)
 {
        SoupSocketConnectState* state = (SoupSocketConnectState*) data;
+       SoupSocketConnectFn func = state->func;
+       gpointer user_data = state->data;
+
+       g_free (state);
 
        if (status == SOUP_SOCKET_NEW_STATUS_OK)
-               (*state->func) (socket,
-                               SOUP_SOCKET_CONNECT_ERROR_NONE,
-                               state->data);
+               (*func) (socket,
+                        SOUP_SOCKET_CONNECT_ERROR_NONE,
+                        user_data);
        else
-               (*state->func) (NULL,
-                               SOUP_SOCKET_CONNECT_ERROR_NETWORK,
-                               state->data);
-
-       g_free (state);
+               (*func) (NULL,
+                        SOUP_SOCKET_CONNECT_ERROR_NETWORK,
+                        user_data);
 }
 
 static void
@@ -326,16 +335,30 @@ soup_socket_connect_inetaddr_cb (SoupAddress* inetaddr,
        SoupSocketConnectState* state = (SoupSocketConnectState*) data;
 
        if (status == SOUP_ADDRESS_STATUS_OK) {
+               gpointer tcp_id;
+
                state->inetaddr_id = NULL;
-               state->tcp_id = soup_socket_new (inetaddr,
-                                                soup_socket_connect_tcp_cb,
-                                                state);
+
+               tcp_id = soup_socket_new (inetaddr,
+                                         soup_socket_connect_tcp_cb,
+                                         state);
+               /* 
+                * NOTE: soup_socket_new can fail immediately and call our
+                * callback which will delete the state.  
+                */
+               if (tcp_id)
+                       state->tcp_id = tcp_id;
+
                soup_address_unref (inetaddr);
        } else {
-               (*state->func) (NULL,
-                               SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE,
-                               state->data);
+               SoupSocketConnectFn func = state->func;
+               gpointer user_data = state->data;
+
                g_free (state);
+
+               (*func) (NULL, 
+                        SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE, 
+                        user_data);
        }
 }
 
@@ -364,7 +387,8 @@ soup_socket_connect (const gchar*        hostname,
                     gpointer            data)
 {
        SoupSocketConnectState* state;
-       gpointer id;
+       SoupAddress *cached_addr;
+       gpointer addr_id, tcp_id;
 
        g_return_val_if_fail (hostname != NULL, NULL);
        g_return_val_if_fail (func != NULL, NULL);
@@ -373,20 +397,39 @@ soup_socket_connect (const gchar*        hostname,
        state->func = func;
        state->data = data;
 
-       id = soup_address_new (hostname,
-                              port,
-                              soup_socket_connect_inetaddr_cb,
-                              state);
-
-       /* Note that soup_address_new can fail immediately and call
-          our callback which will delete the state.  The users callback
-          would be called in the process. */
-
-       if (id == NULL) return NULL;
-
-       state->inetaddr_id = id;
-
-       return state;
+       /* Check if a cached version of the address already exists */
+       cached_addr = soup_address_lookup_in_cache (hostname, port);
+       if (cached_addr) {
+               tcp_id = soup_socket_new (cached_addr,
+                                         soup_socket_connect_tcp_cb,
+                                         state);
+               soup_address_unref (cached_addr);
+
+               /* 
+                * NOTE: soup_socket_new can fail immediately and call our
+                * callback which will delete the state.  
+                */
+               if (tcp_id) {
+                       state->tcp_id = tcp_id;
+                       return state;
+               } else
+                       return NULL;
+       } else {
+               addr_id = soup_address_new (hostname,
+                                           port,
+                                           soup_socket_connect_inetaddr_cb,
+                                           state);
+
+               /* 
+                * NOTE: soup_address_new can fail immediately and call our
+                * callback which will delete the state.  
+                */
+               if (addr_id) {
+                       state->inetaddr_id = addr_id;
+                       return state;
+               } else
+                       return NULL;
+       }
 }
 
 /**
@@ -607,8 +650,10 @@ soup_socket_server_new (const gint port)
        sa_in->sin_addr.s_addr = g_htonl (INADDR_ANY);
        sa_in->sin_port = g_htons (port);
 
-       /* The socket is set to non-blocking mode later in the Windows
-          version.*/
+       /* 
+        * For Unix, set REUSEADDR and NONBLOCK.
+        * For Windows, set NONBLOCK during accept.
+        */
 #ifndef SOUP_WIN32
        {
                const int on = 1;
index f34dc31..a190ecf 100644 (file)
@@ -36,8 +36,11 @@ SoupAddressNewId     soup_address_new                (const gchar*       name,
 
 void                 soup_address_new_cancel         (SoupAddressNewId   id);
 
-SoupAddress         *soup_address_new_sync           (const gchar *name, 
-                                                     const gint port);
+SoupAddress         *soup_address_new_sync           (const gchar       *name, 
+                                                     const gint         port);
+
+SoupAddress         *soup_address_lookup_in_cache    (const gchar       *name, 
+                                                     const gint         port);
 
 void                 soup_address_ref                (SoupAddress*       ia);
 
@@ -65,8 +68,9 @@ gchar*               soup_address_get_canonical_name (SoupAddress*         ia);
 
 gint                 soup_address_get_port           (const SoupAddress*   ia);
 
-void                 soup_address_set_port           (const SoupAddress*   ia, 
-                                                     guint                port);
+const struct sockaddr *
+                     soup_address_get_sockaddr       (SoupAddress         *ia,
+                                                     guint               *addrlen);
 
 guint                soup_address_hash               (const gpointer       p);
 
index 6431893..5d94066 100644 (file)
@@ -134,17 +134,27 @@ main (int argc, char** argv)
        loop = g_main_new (FALSE);
 
        env = getenv ("SOCKFD");
-       if (!env) g_error ("SOCKFD environment not set.");
+       if (!env) 
+               g_error ("SOCKFD environment not set.");
+
        sockfd = atoi (env);
+       if (sockfd <= 0)
+               g_error ("Invalid SOCKFD environment set.");
 
        env = getenv ("SECURITY_POLICY");
-       if (!env) g_error ("SECURITY_POLICY environment not set.");
-       secpol = atoi (env);
+       if (!env) 
+               g_error ("SECURITY_POLICY environment not set.");
 
+       secpol = atoi (env);
        soup_ssl_proxy_set_security_policy (secpol);
 
        read_chan = g_io_channel_unix_new (STDIN_FILENO);
+       if (!read_chan) 
+               g_error ("Unable to open STDIN");
+
        write_chan = g_io_channel_unix_new (STDOUT_FILENO);
+       if (!write_chan) 
+               g_error ("Unable to open STDOUT");
 
        /* Block on socket write */
        flags = fcntl(sockfd, F_GETFL, 0);
@@ -156,6 +166,8 @@ main (int argc, char** argv)
 
        sock_chan = g_io_channel_unix_new (sockfd);
        sock_chan = soup_ssl_proxy_get_iochannel (sock_chan);
+       if (!sock_chan)
+               g_error ("Unable to establish SSL connection");
 
        g_io_add_watch (read_chan, 
                        G_IO_IN | G_IO_HUP | G_IO_ERR, 
index f37235d..05302ca 100644 (file)
@@ -101,11 +101,11 @@ soup_ssl_get_iochannel (GIOChannel *sock)
                putenv (g_strdup_printf ("SECURITY_POLICY=%d",
                                         soup_get_security_policy ()));
 
-               execl (BINDIR G_DIR_SEPARATOR_S "soup-ssl-proxy",
-                      BINDIR G_DIR_SEPARATOR_S "soup-ssl-proxy",
+               execl (BINDIR G_DIR_SEPARATOR_S SSL_PROXY_NAME,
+                      BINDIR G_DIR_SEPARATOR_S SSL_PROXY_NAME,
                       NULL);
 
-               execlp ("soup-ssl-proxy", "soup-ssl-proxy", NULL);
+               execlp (SSL_PROXY_NAME, SSL_PROXY_NAME, NULL);
 
                g_error ("Error executing SSL Proxy\n");
        }
index 83d8a1c..332cd7c 100644 (file)
 #include "soup-transfer.h"
 #include "soup-private.h"
 
+#undef DUMP
+
+#ifdef DUMP
+static void
+DUMP_READ (guchar *data, gint bytes_read) 
+{
+       gchar *buf = alloca (bytes_read + 1);
+       memcpy (buf, data, bytes_read);
+       buf[bytes_read] = '\0';
+       
+       g_warning ("READ %d\n----------\n%s\n----------\n", bytes_read, buf);
+}
+static void
+DUMP_WRITE (guchar *data, gint bytes_written) 
+{
+       gchar *buf = alloca (bytes_written + 1);
+       memcpy (buf, data, bytes_written);
+       buf[bytes_written] = '\0';
+
+       g_warning ("WRITE %d\n----------\n%s\n----------\n", bytes_written,buf);
+}
+#else
+#  define DUMP_READ(x,y)
+#  define DUMP_WRITE(x,y)
+#endif
+
 typedef struct {
        /* 
         * Length remaining to be downloaded of the current chunk data. 
@@ -41,7 +67,7 @@ typedef struct {
        guint                  err_tag;
 
        /*
-        * If TRUE, a callback has been issed which references recv_buf.
+        * If TRUE, a callback has been issued which references recv_buf.
         * If the transfer is cancelled before a reference exists, the contents
         * of recv_buf are free'd.
         */
@@ -72,13 +98,14 @@ typedef struct {
 
        gboolean                processing;
 
-       const GString          *header;
-       const SoupDataBuffer   *src;
+       SoupTransferEncoding    encoding;
+       GByteArray             *write_buf;
 
-       guint                   write_len;
        gboolean                headers_done;
+       gint                    chunk_cnt;
 
-       SoupWriteHeadersDoneFn  headers_done_cb;
+       SoupWriteGetHeaderFn    get_header_cb;
+       SoupWriteGetChunkFn     get_chunk_cb;
        SoupWriteDoneFn         write_done_cb;
        SoupWriteErrorFn        error_cb;
        gpointer                user_data;
@@ -94,14 +121,34 @@ soup_transfer_read_cancel (guint tag)
 
        if (r->processing) return;
 
-       g_source_remove (r->read_tag);
-       g_source_remove (r->err_tag);
+       if (r->read_tag)
+               g_source_remove (r->read_tag);
+       if (r->err_tag)
+               g_source_remove (r->err_tag);
 
        g_byte_array_free (r->recv_buf, r->callback_issued ? FALSE : TRUE);
 
        g_free (r);
 }
 
+void 
+soup_transfer_read_set_callbacks (guint                   tag,
+                                 SoupReadHeadersDoneFn   headers_done_cb,
+                                 SoupReadChunkFn         read_chunk_cb,
+                                 SoupReadDoneFn          read_done_cb,
+                                 SoupReadErrorFn         error_cb,
+                                 gpointer                user_data)
+{
+       SoupReader *r = GINT_TO_POINTER (tag);
+
+       r->headers_done_cb = headers_done_cb;
+       r->read_chunk_cb = read_chunk_cb;
+       r->read_done_cb = read_done_cb;
+       r->error_cb = error_cb;
+
+       r->user_data = user_data;
+}
+
 static void
 issue_final_callback (SoupReader *r)
 {
@@ -119,6 +166,10 @@ issue_final_callback (SoupReader *r)
 
                r->callback_issued = TRUE;
 
+               g_source_remove (r->read_tag);
+               g_source_remove (r->err_tag);
+               r->read_tag = r->err_tag = 0;
+
                IGNORE_CANCEL (r);
                (*r->read_done_cb) (&buf, r->user_data);
                UNIGNORE_CANCEL (r);
@@ -134,9 +185,9 @@ soup_transfer_read_error_cb (GIOChannel* iochannel,
 
        /*
         * Closing the connection to signify EOF is valid if content length is
-        * unknown.
+        * unknown, but only if headers have been sent.
         */
-       if (r->encoding == SOUP_TRANSFER_UNKNOWN) {
+       if (r->header_len && r->encoding == SOUP_TRANSFER_UNKNOWN) {
                issue_final_callback (r);
                goto CANCELLED;
        }
@@ -404,12 +455,14 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
                                   &bytes_read);
 
        if (error == G_IO_ERROR_AGAIN) {
-               if (total_read) goto PROCESS_READ;
+               if (total_read) 
+                       goto PROCESS_READ;
                else return TRUE;
        }
 
        if (error != G_IO_ERROR_NONE) {
-               if (total_read) goto PROCESS_READ;
+               if (total_read) 
+                       goto PROCESS_READ;
                else {
                        soup_transfer_read_error_cb (iochannel, G_IO_HUP, r);
                        return FALSE;
@@ -417,29 +470,40 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
        }
 
        if (bytes_read) {
-               g_byte_array_append (r->recv_buf, read_buf, bytes_read);
+               DUMP_READ (read_buf, bytes_read);
 
+               g_byte_array_append (r->recv_buf, read_buf, bytes_read);
                total_read += bytes_read;
 
                goto READ_AGAIN;
        }
 
  PROCESS_READ:
-       if (!r->header_len) {
-               gint index = soup_substring_index (r->recv_buf->data,
-                                                  r->recv_buf->len,
-                                                  "\r\n\r\n");
-               if (index < 0) return TRUE;
+       /* 
+        * FIXME: Why are we getting a read_cb if there is no data to read? Yet
+        *        error_cb isn't being called and we get no error from
+        *        g_io_channel_read(). 
+        */
+       if (r->header_len == 0 && total_read == 0) {
+               soup_transfer_read_error_cb (iochannel, G_IO_HUP, r);
+               return FALSE;
+       }
 
-               index += 4;
+       if (r->header_len == 0) {
+               gint index;
+
+               index = soup_substring_index (r->recv_buf->data,
+                                             r->recv_buf->len,
+                                             "\r\n\r\n");
+               if (index < 0) 
+                       return TRUE;
+               else
+                       index += 4;
 
                if (r->headers_done_cb) {
                        GString str;
                        SoupTransferDone ret;
 
-                       r->encoding = SOUP_TRANSFER_UNKNOWN;
-                       r->content_length = 0;
-
                        str.len = index;
                        str.str = alloca (index + 1);
                        strncpy (str.str, r->recv_buf->data, index);
@@ -457,7 +521,6 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
                }
 
                remove_block_at_index (r->recv_buf, 0, index);
-
                r->header_len = index;
        }
 
@@ -513,6 +576,7 @@ soup_transfer_read (GIOChannel            *chan,
        reader->error_cb = error_cb;
        reader->user_data = user_data;
        reader->recv_buf = g_byte_array_new ();
+       reader->encoding = SOUP_TRANSFER_UNKNOWN;
 
        reader->read_tag =
                g_io_add_watch (chan,
@@ -536,9 +600,12 @@ soup_transfer_write_cancel (guint tag)
 
        if (w->processing) return;
 
-       g_source_remove (w->write_tag);
+       if (w->write_tag)
+               g_source_remove (w->write_tag);
        g_source_remove (w->err_tag);
 
+       g_byte_array_free (w->write_buf, TRUE);
+
        g_free (w);
 }
 
@@ -547,11 +614,9 @@ soup_transfer_write_error_cb (GIOChannel* iochannel,
                              GIOCondition condition,
                              SoupWriter *w)
 {
-       gboolean body_started = w->write_len > (guint) w->header->len;
-
        if (w->error_cb) {
                IGNORE_CANCEL (w);
-               (*w->error_cb) (body_started, w->user_data);
+               (*w->error_cb) (w->headers_done, w->user_data);
                UNIGNORE_CANCEL (w);
        }
 
@@ -560,73 +625,147 @@ soup_transfer_write_error_cb (GIOChannel* iochannel,
        return FALSE;
 }
 
+static gboolean 
+get_header (SoupWriter *w)
+{
+       GString *header = NULL;
+
+       IGNORE_CANCEL (w);
+       (*w->get_header_cb) (&header, w->user_data);
+       UNIGNORE_CANCEL (w);
+
+       if (header) {
+               g_byte_array_append (w->write_buf, header->str, header->len);
+               g_string_free (header, TRUE);
+
+               w->get_header_cb = NULL;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+write_chunk_sep (GByteArray *arr, gint len, gint chunk_cnt)
+{
+       gchar *hex;
+       gchar *end = "0\r\n\r\n";
+
+       /*
+        * Only prefix the chunk length with a \r\n if its not the first chunk
+        */
+       if (chunk_cnt)
+               g_byte_array_append (arr, "\r\n", 2);
+
+       if (len) {
+               hex = g_strdup_printf ("%x\r\n", len);
+               g_byte_array_append (arr, hex, strlen (hex));
+               g_free (hex);
+       } else
+               g_byte_array_append (arr, end, strlen (end));
+}
+
+static void
+get_next_chunk (SoupWriter *w)
+{
+       SoupTransferStatus ret = SOUP_TRANSFER_END;
+       SoupDataBuffer buf = { 0 , NULL, 0 };
+
+       IGNORE_CANCEL (w);
+       ret = (*w->get_chunk_cb) (&buf, w->user_data);
+       UNIGNORE_CANCEL (w);
+
+       if (buf.length) {
+               if (w->encoding == SOUP_TRANSFER_CHUNKED)
+                       write_chunk_sep (w->write_buf, 
+                                        buf.length, 
+                                        w->chunk_cnt++);
+
+               g_byte_array_append (w->write_buf, buf.body, buf.length);
+
+               if (buf.owner == SOUP_BUFFER_SYSTEM_OWNED)
+                       g_free (buf.body);
+       }
+
+       if (ret == SOUP_TRANSFER_END) {
+               if (w->encoding == SOUP_TRANSFER_CHUNKED)
+                       write_chunk_sep (w->write_buf, 0, w->chunk_cnt);
+
+               w->get_chunk_cb = NULL;
+       }
+}
+
+#ifdef SIGPIPE
+#  define IGNORE_PIPE(pipe_handler) pipe_handler = signal (SIGPIPE, SIG_IGN)
+#  define RESTORE_PIPE(pipe_handler) signal (SIGPIPE, pipe_handler)
+#else
+#  define IGNORE_PIPE(x)
+#  define RESTORE_PIPE(x)
+#endif
+
 static gboolean
 soup_transfer_write_cb (GIOChannel* iochannel,
                        GIOCondition condition,
                        SoupWriter *w)
 {
-       guint head_len, body_len, total_len, total_written, bytes_written;
        GIOError error;
-       gchar *write_buf;
-       guint  write_len;
-       void *pipe_handler;
+       gpointer pipe_handler;
+       guint bytes_written = 0;
 
-       head_len = w->header->len;
-       body_len = w->src->length;
-       total_len = head_len + body_len;
-       total_written = w->write_len;
+       /*
+        * Get the header and first data chunk (if available).
+        */
+       if (w->get_header_cb) {
+               if (!get_header (w)) 
+                       return TRUE;
 
-#ifdef SIGPIPE
-       pipe_handler = signal (SIGPIPE, SIG_IGN);
-#endif
+               if (w->get_chunk_cb)
+                       get_next_chunk (w);
+       }
+
+       IGNORE_PIPE (pipe_handler);
        errno = 0;
 
  WRITE_AGAIN:
-       if (total_written < head_len) {
-               /* 
-                * Send remaining headers 
-                */
-               write_buf = &w->header->str [total_written];
-               write_len = head_len - total_written;
-       } else {
-               /* 
-                * Send rest of body 
-                */
-               guint offset = total_written - head_len;
-               write_buf = &w->src->body [offset];
-               write_len = body_len - offset;
-
-               if (!w->headers_done) {
-                       if (w->headers_done_cb) {
-                               IGNORE_CANCEL (w);
-                               (*w->headers_done_cb) (w->user_data);
-                               UNIGNORE_CANCEL (w);
-                       }
-                       w->headers_done = TRUE;
+       while (w->write_buf->len) {
+               error = g_io_channel_write (iochannel,
+                                           w->write_buf->data,
+                                           w->write_buf->len,
+                                           &bytes_written);
+
+               if (error == G_IO_ERROR_AGAIN) 
+                       goto WRITE_LATER;
+
+               if (errno != 0 || error != G_IO_ERROR_NONE) {
+                       soup_transfer_write_error_cb (iochannel, G_IO_HUP, w);
+                       goto DONE_WRITING;
                }
-       }
 
-       error = g_io_channel_write (iochannel,
-                                   write_buf,
-                                   write_len,
-                                   &bytes_written);
+               if (!bytes_written) 
+                       goto WRITE_LATER;
 
-       if (error == G_IO_ERROR_AGAIN) {
-#ifdef SIGPIPE
-               signal (SIGPIPE, pipe_handler);
-#endif
-               return TRUE;
-       }
+               DUMP_WRITE (w->write_buf->data, bytes_written);
 
-       if (errno != 0 || error != G_IO_ERROR_NONE) {
-               soup_transfer_write_error_cb (iochannel, G_IO_HUP, w);
-               goto DONE_WRITING;
+               remove_block_at_index (w->write_buf, 0, bytes_written);
        }
 
-       total_written = (w->write_len += bytes_written);
+       /*
+        * When we exit the above block, we are certain that the headers have
+        * been written.  
+        */
+       w->headers_done = TRUE;
+
+       /*
+        * Get the next data chunk and try again, or quit if paused.
+        */
+       if (w->get_chunk_cb) {
+               get_next_chunk (w);
+
+               if (!w->write_tag)
+                       goto DONE_WRITING;
 
-       if (total_written != total_len)
                goto WRITE_AGAIN;
+       }
 
        if (w->write_done_cb) {
                IGNORE_CANCEL (w);
@@ -637,33 +776,31 @@ soup_transfer_write_cb (GIOChannel* iochannel,
        soup_transfer_write_cancel (GPOINTER_TO_INT (w));
 
  DONE_WRITING:
-
-#ifdef SIGPIPE
-       signal (SIGPIPE, pipe_handler);
-#endif
-
+       RESTORE_PIPE (pipe_handler);
        return FALSE;
+
+ WRITE_LATER:
+       RESTORE_PIPE (pipe_handler);
+       return TRUE;
 }
 
-guint
-soup_transfer_write (GIOChannel             *chan,
-                    const GString          *header,
-                    const SoupDataBuffer   *src,
-                    SoupWriteHeadersDoneFn  headers_done_cb,
-                    SoupWriteDoneFn         write_done_cb,
-                    SoupWriteErrorFn        error_cb,
-                    gpointer                user_data)
+static SoupWriter *
+create_writer (GIOChannel             *chan,
+              SoupTransferEncoding    encoding,
+              SoupWriteDoneFn         write_done_cb,
+              SoupWriteErrorFn        error_cb,
+              gpointer                user_data)
 {
        SoupWriter *writer;
 
        writer = g_new0 (SoupWriter, 1);
-       writer->channel = chan;
-       writer->header = header;
-       writer->src = src;
-       writer->headers_done_cb = headers_done_cb;
+
+       writer->channel       = chan;
+       writer->encoding      = encoding;
+       writer->write_buf     = g_byte_array_new ();
        writer->write_done_cb = write_done_cb;
-       writer->error_cb = error_cb;
-       writer->user_data = user_data;
+       writer->error_cb      = error_cb;
+       writer->user_data     = user_data;
 
        writer->write_tag =
                g_io_add_watch (chan,
@@ -677,5 +814,88 @@ soup_transfer_write (GIOChannel             *chan,
                                (GIOFunc) soup_transfer_write_error_cb,
                                writer);
 
+       return writer;
+}
+
+guint 
+soup_transfer_write_simple (GIOChannel             *chan,
+                           GString                *header,
+                           const SoupDataBuffer   *src,
+                           SoupWriteDoneFn         write_done_cb,
+                           SoupWriteErrorFn        error_cb,
+                           gpointer                user_data)
+{
+       SoupWriter *writer;
+
+       writer = create_writer (chan, 
+                               SOUP_TRANSFER_CONTENT_LENGTH,
+                               write_done_cb,
+                               error_cb,
+                               user_data);
+
+       if (header) {
+               g_byte_array_append (writer->write_buf, 
+                                    header->str, 
+                                    header->len);
+               g_string_free (header, TRUE);
+       }
+
+       if (src && src->length)
+               g_byte_array_append (writer->write_buf, 
+                                    src->body, 
+                                    src->length);
+
+       return GPOINTER_TO_INT (writer);
+}
+
+guint 
+soup_transfer_write (GIOChannel             *chan,
+                    SoupTransferEncoding    encoding,
+                    SoupWriteGetHeaderFn    get_header_cb,
+                    SoupWriteGetChunkFn     get_chunk_cb,
+                    SoupWriteDoneFn         write_done_cb,
+                    SoupWriteErrorFn        error_cb,
+                    gpointer                user_data)
+{
+       SoupWriter *writer;
+
+       writer = create_writer (chan, 
+                               encoding,
+                               write_done_cb,
+                               error_cb,
+                               user_data);
+
+       writer->get_header_cb = get_header_cb;
+       writer->get_chunk_cb = get_chunk_cb;
+
        return GPOINTER_TO_INT (writer);
 }
+
+void  
+soup_transfer_write_pause (guint tag)
+{
+       SoupWriter *w = GINT_TO_POINTER (tag);
+
+       g_return_if_fail (tag != 0);
+
+       if (w->write_tag) {
+               g_source_remove (w->write_tag);
+               w->write_tag = 0;
+       }
+}
+
+void  
+soup_transfer_write_unpause (guint tag)
+{
+       SoupWriter *w = GINT_TO_POINTER (tag);
+
+       g_return_if_fail (tag != 0);
+
+       if (!w->write_tag) {
+               w->write_tag =
+                       g_io_add_watch (w->channel,
+                                       G_IO_OUT,
+                                       (GIOFunc) soup_transfer_write_cb,
+                                       w);
+       }
+}
index 2f6b337..ba0d6af 100644 (file)
@@ -50,20 +50,43 @@ guint soup_transfer_read  (GIOChannel             *chan,
 
 void  soup_transfer_read_cancel (guint tag);
 
-typedef void (*SoupWriteHeadersDoneFn) (gpointer user_data);
+void  soup_transfer_read_set_callbacks (guint                   tag,
+                                       SoupReadHeadersDoneFn   headers_done_cb,
+                                       SoupReadChunkFn         read_chunk_cb,
+                                       SoupReadDoneFn          read_done_cb,
+                                       SoupReadErrorFn         error_cb,
+                                       gpointer                user_data);
+
 
 typedef void (*SoupWriteDoneFn) (gpointer user_data);
 
 typedef void (*SoupWriteErrorFn) (gboolean headers_done, gpointer user_data);
 
+guint soup_transfer_write_simple (GIOChannel             *chan,
+                                 GString                *header,
+                                 const SoupDataBuffer   *src,
+                                 SoupWriteDoneFn         write_done_cb,
+                                 SoupWriteErrorFn        error_cb,
+                                 gpointer                user_data);
+
+typedef void (*SoupWriteGetHeaderFn) (GString  **out_hdr,
+                                     gpointer   user_data);
+
+typedef SoupTransferDone (*SoupWriteGetChunkFn) (SoupDataBuffer *out_next,
+                                                gpointer        user_data);
+
 guint soup_transfer_write (GIOChannel             *chan,
-                          const GString          *header,
-                          const SoupDataBuffer   *src,
-                          SoupWriteHeadersDoneFn  headers_done_cb,
+                          SoupTransferEncoding    encoding,
+                          SoupWriteGetHeaderFn    get_header_cb,
+                          SoupWriteGetChunkFn     get_chunk_cb,
                           SoupWriteDoneFn         write_done_cb,
                           SoupWriteErrorFn        error_cb,
                           gpointer                user_data);
 
+void  soup_transfer_write_pause (guint tag);
+
+void  soup_transfer_write_unpause (guint tag);
+
 void  soup_transfer_write_cancel (guint tag);
 
 #endif /*SOUP_TRANSFER_H*/
index f22231b..970e2b4 100644 (file)
@@ -104,6 +104,191 @@ soup_uri_get_default_port (SoupProtocol proto)
        return -1;
 }
 
+/*
+ * Ripped off from libxml
+ */
+static void
+normalize_path (gchar *path)
+{
+       char *cur, *out;
+
+       /* 
+        * Skip all initial "/" chars.  We want to get to the beginning of the
+        * first non-empty segment.
+        */
+       cur = path;
+       while (cur[0] == '/')
+               ++cur;
+       if (cur[0] == '\0')
+               return;
+
+       /* Keep everything we've seen so far.  */
+       out = cur;
+
+       /*
+        * Analyze each segment in sequence for cases (c) and (d).
+        */
+       while (cur[0] != '\0') {
+               /*
+                * c) All occurrences of "./", where "." is a complete path
+                * segment, are removed from the buffer string.  
+                */
+               if ((cur[0] == '.') && (cur[1] == '/')) {
+                       cur += 2;
+                       /* 
+                        * '//' normalization should be done at this point too
+                        */
+                       while (cur[0] == '/')
+                               cur++;
+                       continue;
+               }
+
+               /*
+                * d) If the buffer string ends with "." as a complete path
+                * segment, that "." is removed.  
+                */
+               if ((cur[0] == '.') && (cur[1] == '\0'))
+                       break;
+
+               /* Otherwise keep the segment.  */
+               while (cur[0] != '/') {
+                       if (cur[0] == '\0')
+                               goto done_cd;
+                       (out++)[0] = (cur++)[0];
+               }
+               /* nomalize '//' */
+               while ((cur[0] == '/') && (cur[1] == '/'))
+                       cur++;
+               
+               (out++)[0] = (cur++)[0];
+       }
+ done_cd:
+       out[0] = '\0';
+
+       /* Reset to the beginning of the first segment for the next sequence. */
+       cur = path;
+       while (cur[0] == '/')
+               ++cur;
+       if (cur[0] == '\0')
+               return;
+
+       /*
+        * Analyze each segment in sequence for cases (e) and (f).
+        *
+        * e) All occurrences of "<segment>/../", where <segment> is a
+        *    complete path segment not equal to "..", are removed from the
+        *    buffer string.  Removal of these path segments is performed
+        *    iteratively, removing the leftmost matching pattern on each
+        *    iteration, until no matching pattern remains.
+        *
+        * f) If the buffer string ends with "<segment>/..", where <segment>
+        *    is a complete path segment not equal to "..", that
+        *    "<segment>/.." is removed.
+        *
+        * To satisfy the "iterative" clause in (e), we need to collapse the
+        * string every time we find something that needs to be removed.  Thus,
+        * we don't need to keep two pointers into the string: we only need a
+        * "current position" pointer.
+        */
+       while (1) {
+               char *segp;
+
+               /* 
+                * At the beginning of each iteration of this loop, "cur" points
+                * to the first character of the segment we want to examine.  
+                */
+
+               /* Find the end of the current segment.  */
+               segp = cur;
+               while ((segp[0] != '/') && (segp[0] != '\0'))
+                       ++segp;
+
+               /* 
+                * If this is the last segment, we're done (we need at least two
+                * segments to meet the criteria for the (e) and (f) cases).
+                */
+               if (segp[0] == '\0')
+                       break;
+
+               /* 
+                * If the first segment is "..", or if the next segment _isn't_
+                * "..", keep this segment and try the next one.  
+                */
+               ++segp;
+               if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3))
+                   || ((segp[0] != '.') || (segp[1] != '.')
+                       || ((segp[2] != '/') && (segp[2] != '\0')))) {
+                       cur = segp;
+                       continue;
+               }
+
+               /* 
+                * If we get here, remove this segment and the next one and back
+                * up to the previous segment (if there is one), to implement
+                * the "iteratively" clause.  It's pretty much impossible to
+                * back up while maintaining two pointers into the buffer, so
+                * just compact the whole buffer now.  
+                */
+
+               /* If this is the end of the buffer, we're done.  */
+               if (segp[2] == '\0') {
+                       cur[0] = '\0';
+                       break;
+               }
+               strcpy(cur, segp + 3);
+
+               /* 
+                * If there are no previous segments, then keep going from
+                * here. 
+                */
+               segp = cur;
+               while ((segp > path) && ((--segp)[0] == '/'))
+                       ;
+               if (segp == path)
+                       continue;
+
+               /* 
+                * "segp" is pointing to the end of a previous segment; find
+                * it's start.  We need to back up to the previous segment and
+                * start over with that to handle things like "foo/bar/../..".
+                * If we don't do this, then on the first pass we'll remove the
+                * "bar/..", but be pointing at the second ".." so we won't
+                * realize we can also remove the "foo/..".  
+                */
+               cur = segp;
+               while ((cur > path) && (cur[-1] != '/'))
+                       --cur;
+       }
+       out[0] = '\0';
+
+       /*
+        * g) If the resulting buffer string still begins with one or more
+        *    complete path segments of "..", then the reference is
+        *    considered to be in error. Implementations may handle this
+        *    error by retaining these components in the resolved path (i.e.,
+        *    treating them as part of the final URI), by removing them from
+        *    the resolved path (i.e., discarding relative levels above the
+        *    root), or by avoiding traversal of the reference.
+        *
+        * We discard them from the final path.
+        */
+       if (path[0] == '/') {
+               cur = path;
+               while ((cur[1] == '.') && (cur[2] == '.')
+                      && ((cur[3] == '/') || (cur[3] == '\0')))
+                       cur += 3;
+
+               if (cur != path) {
+                       out = path;
+                       while (cur[0] != '\0')
+                               (out++)[0] = (cur++)[0];
+                       out[0] = 0;
+               }
+       }
+
+       return;
+}
+
 /**
  * soup_uri_new: create a SoupUri object from a string
  * @uri_string: The string containing the URL to scan
@@ -207,13 +392,15 @@ soup_uri_new (const gchar* uri_string)
        if (path && query) {
                g_uri->path = g_strndup (path, query - path);
                g_uri->querystring = g_strdup (++query);
-               g_uri->query_elems = g_strsplit (g_uri->querystring, "&", 0);
                g_free (path);
        } else {
                g_uri->path = path;
                g_uri->querystring = NULL;
        }
 
+       if (g_uri->path)
+               normalize_path (g_uri->path);
+
        return g_uri;
 }
 
@@ -260,18 +447,40 @@ soup_uri_to_string (const SoupUri *uri, gboolean show_passwd)
 SoupUri *
 soup_uri_copy (const SoupUri* uri)
 {
-       gchar *uri_str;
        SoupUri *dup;
 
        g_return_val_if_fail (uri != NULL, NULL);
 
-       uri_str = soup_uri_to_string (uri, TRUE);
-       dup = soup_uri_new (uri_str);
-       g_free (uri_str);
+       dup = g_new0 (SoupUri, 1);
+       dup->protocol    = uri->protocol;
+       dup->user        = g_strdup (uri->user);
+       dup->authmech    = g_strdup (uri->authmech);
+       dup->passwd      = g_strdup (uri->passwd);
+       dup->host        = g_strdup (uri->host);
+       dup->port        = uri->port;
+       dup->path        = g_strdup (uri->path);
+       dup->querystring = g_strdup (uri->querystring);
 
        return dup;
 }
 
+gboolean 
+soup_uri_equal (const SoupUri *u1, 
+               const SoupUri *u2)
+{
+       if (u1->protocol == u2->protocol            &&
+           u1->port     == u2->port                &&
+           !strcmp (u1->user,        u2->user)     &&
+           !strcmp (u1->authmech,    u2->authmech) &&
+           !strcmp (u1->passwd,      u2->passwd)   &&
+           !strcmp (u1->host,        u2->host)     &&
+           !strcmp (u1->path,        u2->path)     &&
+           !strcmp (u1->querystring, u2->querystring))
+               return TRUE;
+
+       return FALSE;
+}
+
 void
 soup_uri_free (SoupUri *uri)
 {
@@ -283,7 +492,6 @@ soup_uri_free (SoupUri *uri)
        g_free (uri->host);
        g_free (uri->path);
        g_free (uri->querystring);
-       g_strfreev (uri->query_elems);
 
        g_free (uri);
 }
index e8adea0..f7d297a 100644 (file)
@@ -49,7 +49,6 @@ typedef struct {
 
        gchar              *path;
        gchar              *querystring;
-       gchar             **query_elems;
 } SoupUri;
 
 SoupUri *soup_uri_new       (const gchar   *uri_string);
@@ -59,6 +58,9 @@ gchar   *soup_uri_to_string (const SoupUri *uri,
 
 SoupUri *soup_uri_copy      (const SoupUri *uri);
 
+gboolean soup_uri_equal     (const SoupUri *uri1, 
+                            const SoupUri *uri2);
+
 void     soup_uri_free      (SoupUri       *uri);
 
 void     soup_uri_set_auth  (SoupUri       *uri,