+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.
* 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
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
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
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)
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,
[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],
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,
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
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
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 \
soup-queue.c \
soup-serializer.c \
soup-server.c \
+ soup-server-auth.c \
soup-socket.c \
soup-socket-unix.c \
soup-socks.h \
#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
*/
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
{
int i;
+ if (!name)
+ return 0;
+
for (i = 0; dtype[i].name; i++) {
if (!g_strcasecmp (dtype[i].name, name))
return dtype[i].type;
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);
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;
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
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);
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];
uri->user,
(gchar *) &lm_hash,
(gchar *) &nt_hash,
- host ? host : "UNKNOWN",
- domain ? domain : "UNKNOWN");
+ host,
+ domain);
auth->completed = TRUE;
}
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);
-}
};
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);
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 =
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;
}
/**
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;
}
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,
#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"
}
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)
*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);
+}
/* 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*/
{
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,
ret->priv->http_version = SOUP_HTTP_1_1;
- soup_context_ref (context);
+ soup_message_set_context (ret, context);
return ret;
}
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.
{
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;
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
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);
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.
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;
*/
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);
}
}
- 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;
SoupHandlerData *data = user_data;
SoupMessage *msg = data->msg;
SoupHandlerResult result;
- GSList *iter;
switch (data->type) {
case SOUP_HANDLER_PREPARE:
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);
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;
}
{
g_return_val_if_fail (msg != NULL, NULL);
- soup_context_ref (msg->context);
+ if (msg->context)
+ soup_context_ref (msg->context);
+
return msg->context;
}
#include <glib.h>
#include <libsoup/soup-context.h>
#include <libsoup/soup-error.h>
+#include <libsoup/soup-method.h>
typedef enum {
SOUP_STATUS_IDLE = 0,
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;
gchar *req_body,
gulong req_length);
+SoupMessage *soup_message_copy (SoupMessage *req);
+
void soup_message_free (SoupMessage *req);
void soup_message_cancel (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);
* 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 {
* 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>
gboolean soup_initialized = FALSE;
-static guint max_connections = 0;
+static guint max_connections = 10;
static SoupContext *proxy_context = NULL;
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:
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
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);
#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,
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"
}
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 *
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;
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));
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;
}
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;
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,
#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"
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;
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)
{
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) {
# 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>
SoupContext *context;
GIOChannel *channel;
SoupSocket *socket;
+ SoupAuth *auth;
guint port;
gboolean in_use;
guint last_used_id;
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 */
gint len,
gchar *substr);
-gchar *soup_base64_encode (const gchar *text,
- gint len);
-
/* from soup-socket.c */
gboolean soup_gethostbyname (const gchar *hostname,
#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;
/*
* 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);
}
}
-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,
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);
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 {
"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;
}
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;
}
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) {
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);
"%s %s HTTP/1.0\r\n",
req->method,
uri);
-
g_free (uri);
/*
}
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:
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:
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)
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,
"buffer.");
soup_message_issue_callback (req);
return;
-
case SOUP_BUFFER_SYSTEM_OWNED:
g_free (req->response.body);
break;
-
case SOUP_BUFFER_STATIC:
break;
}
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 ();
}
/**
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);
}
#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 */
* 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
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
{
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,
&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);
}
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");
}
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");
}
*/
- 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);
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;
}
#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 */
#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.
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.
/* 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;
}
/* Get state data before realloc */
- cb_list = iter = state->cb_list;
+ cb_list = state->cb_list;
cb_func = state->func;
cb_data = state->data;
(*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);
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
{
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;
#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;
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;
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.
return state;
}
+ else if (ia)
+ return ia;
}
/* Check to see if we are doing synchronous DNS lookups */
#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 {
}
/**
- * 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;
}
/**
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);
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 ==
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
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);
}
}
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);
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;
+ }
}
/**
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;
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);
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);
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);
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,
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");
}
#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.
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.
*/
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;
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)
{
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);
/*
* 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;
}
&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;
}
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);
}
remove_block_at_index (r->recv_buf, 0, index);
-
r->header_len = index;
}
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,
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);
}
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);
}
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);
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,
(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);
+ }
+}
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*/
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
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;
}
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)
{
g_free (uri->host);
g_free (uri->path);
g_free (uri->querystring);
- g_strfreev (uri->query_elems);
g_free (uri);
}
gchar *path;
gchar *querystring;
- gchar **query_elems;
} SoupUri;
SoupUri *soup_uri_new (const gchar *uri_string);
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,