=== modified file 'configure.ac'
--- old/configure.ac	2010-01-11 16:59:42 +0000
+++ new/configure.ac	2010-01-11 21:13:59 +0000
@@ -135,6 +135,7 @@
    curl_sspi_msg="no      (--enable-sspi)"
    curl_ldap_msg="no      (--enable-ldap / --with-ldap-lib / --with-lber-lib)"
   curl_ldaps_msg="no      (--enable-ldaps)"
+  curl_rtsp_msg="no       (--enable-rtsp)"
 
 dnl
 dnl Save anything in $LIBS for later
@@ -416,6 +417,28 @@
        AC_SUBST(CURL_DISABLE_LDAPS, [1])
 )
 
+AC_MSG_CHECKING([whether to support rtsp])
+AC_ARG_ENABLE(rtsp,
+AC_HELP_STRING([--enable-rtsp],[Enable RTSP support])
+AC_HELP_STRING([--disable-rtsp],[Disable RTSP support]),
+[ case "$enableval" in
+  no)
+       AC_MSG_RESULT(no)
+       AC_DEFINE(CURL_DISABLE_RTSP, 1, [to disable RTSP])
+       AC_SUBST(CURL_DISABLE_RTSP, [1])
+       ;;
+       *)   if test x$CURL_DISABLE_HTTP = x1 ; then
+                    AC_MSG_ERROR(HTTP support needs to be enabled in order to enable RTSP support!)
+            else
+                    AC_MSG_RESULT(yes)
+                    curl_rtsp_msg="enabled"
+            fi
+       ;;
+  esac ],
+       AC_MSG_RESULT(no)
+       curl_rtsp_msg="disabled"
+)
+
 AC_MSG_CHECKING([whether to support proxies])
 AC_ARG_ENABLE(proxy,
 AC_HELP_STRING([--enable-proxy],[Enable proxy support])
@@ -2580,6 +2603,9 @@
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP"
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP"
 fi
+if test "x$CURL_DISABLE_RTSP" != "x1"; then
+  SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTSP"
+fi
 
 AC_SUBST(SUPPORT_PROTOCOLS)
 
@@ -2654,6 +2680,7 @@
   ca cert path:    ${capath}
   LDAP support:    ${curl_ldap_msg}
   LDAPS support:   ${curl_ldaps_msg}
+  RTSP support:    ${curl_rtsp_msg}
   Protocols:       ${SUPPORT_PROTOCOLS}
 ])
 

=== modified file 'docs/libcurl/curl_easy_getinfo.3'
--- old/docs/libcurl/curl_easy_getinfo.3	2009-11-19 15:52:21 +0000
+++ new/docs/libcurl/curl_easy_getinfo.3	2010-01-11 21:13:59 +0000
@@ -209,6 +209,28 @@
 this returns a 1 you know that the reason you didn't get data in return is
 because it didn't fulfill the condition. The long ths argument points to will
 get a zero stored if the condition instead was met. (Added in 7.19.4)
+.IP CURLINFO_RTSP_SESSION_ID
+Pass a pointer to a char pointer to receive a pointer to a string holding the
+most recent RTSP Session ID.
+
+Applications wishing to resume an RTSP session on another connection should
+retreive this info before closing the active connection.
+.IP CURLINFO_RTSP_CLIENT_CSEQ
+Pass a pointer to a long to receive the next CSeq that will be used by the
+application. 
+.IP CURLINFO_RTSP_SERVER_CSEQ
+Pass a pointer to a long to receive the next server CSeq that will be expected
+by the application. 
+
+\fI(NOTE: listening for server initiated requests is currently
+unimplemented).\fP
+
+Applications wishing to resume an RTSP session on another connection should
+retreive this info before closing the active connection.
+.IP CURLINFO_RTSP_CSEQ_RECV
+Pass a pointer to a long to receive the most recently received CSeq from the
+server. If your application encounters a \fICURLE_RTSP_CSEQ_ERROR\fP then you
+may wish to troubleshoot and/or fix the CSeq mismatch by peeking at this value.
 .SH TIMES
 .nf
 An overview of the six time values available from curl_easy_getinfo()

=== modified file 'docs/libcurl/curl_easy_setopt.3'
--- old/docs/libcurl/curl_easy_setopt.3	2010-01-11 16:59:42 +0000
+++ new/docs/libcurl/curl_easy_setopt.3	2010-01-12 22:50:31 +0000
@@ -1275,6 +1275,138 @@
 compliant than 'nocwd' but without the full penalty of 'multicwd'.
 .RE
 (Added in 7.15.1)
+.SH RTSP OPTIONS
+.IP CURLOPT_RTSP_REQUEST
+Tell libcurl what kind of RTSP request to make. Pass one of the following RTSP
+enum values. Unless noted otherwise, commands require the Session ID to be
+initialized.
+.RS
+.IP CURL_RTSPREQ_OPTIONS
+Used to retrieve the available methods of the server. The application is
+responsbile for parsing and obeying the response. \fB(The session ID is not
+needed for this method.)\fP
+.IP CURL_RTSPREQ_DESCRIBE
+Used to get the low level description of a stream. The application should note
+what formats it understands in the \fI'Accept:'\fP header. Unless set manually,
+libcurl will automatically fill in \fI'Accept: application/sdp'\fP. Time-condition
+headers will be added to Describe requests if the \fICURLOPT_TIMECONDITION\fP
+option is active. \fB(The session ID is not needed for this method)\fP
+.IP CURL_RTSPREQ_ANNOUNCE
+When sent by a client, this method changes the description of the session. For
+example, if a client is using the server to record a meeting, the client can use
+Announce to inform the server of all the meta-information about the session.
+ANNOUNCE acts like an HTTP PUT or POST just like
+\fICURL_RTSPREQ_SET_PARAMETER\fP
+.IP CURL_RTSPREQ_SETUP
+Setup is used to initialize the transport layer for the session. The application
+must set the desired Transport options for a session by using the
+\fICURLOPT_RTSP_TRANSPORT\fP option prior to calling setup. If no session ID is
+currently set with \fICURLOPT_RTSP_SESSION_ID\fP, libcurl will extract and use the
+session ID in the response to this request. \fB(The session ID is not needed for
+this method).\fP
+.IP CURL_RTSPREQ_PLAY
+Send a Play command to the server. Use the \fICURLOPT_RANGE\fP option to modify
+the playback time (e.g. 'npt=10-15').
+.IP CURL_RTSPREQ_PAUSE
+Send a Pause command to the server. Use the \fICURLOPT_RANGE\fP option with a
+single value to indicate when the stream should be halted. (e.g. npt='25')
+.IP CURL_RTSPREQ_TEARDOWN
+This command terminates an RTSP session. Note that simply closing a connection
+does not terminate the RTSP session since it is valid to control an RTSP session
+over different connections.
+.IP CURL_RTSPREQ_GET_PARAMETER
+Retrieve a parameter from the server. By default, libcurl will automatically
+include an \fIAccept: text/parameters\fP header unless a custom one is set.
+Applications wishing to send a heartbeat message (e.g. in the presence of a
+server-specified timeout) should send use an empty GET_PARAMETER request.
+.IP CURL_RTSPREQ_SET_PARAMETER
+Set a parameter on the server. By default, libcurl will automatically include a
+\fIContent-Type: text/parameters\fP header unless a custom one is set. The
+interaction with SET_PARAMTER is much like an HTTP PUT or POST. An application
+may either use \fICURLOPT_UPLOAD\fP with \fICURLOPT_READDATA\fP like an HTTP
+PUT, or it may use \fICURLOPT_POSTFIELDS\fP like an HTTP POST. Note that no
+chunked transfers are allowed, so the application must set the
+\fICURLOPT_INFILESIZE\fP in the former and \fICURLOPT_POSTFIELDSIZE\fP in the
+latter. Also, there is no use of multi-part POSTs within RTSP.
+.IP CURL_RTSPREQ_RECORD
+Used to tell the server to record a session. Use the \fICURLOPT_RANGE\fP option
+to modify the record time.
+.IP CURL_RTSPREQ_RECEIVE
+This is a special request because it does not send any data to the server. The
+application may call this function in order to receive interleaved RTP data. It
+will return after processing one read buffer of data in order to give the
+application a chance to run.
+.RE
+.IP CURLOPT_RTSP_SESSION_ID
+Pass a char * as a parameter to set the value of the current RTSP Session ID for
+the handle. Useful for resuming an in-progress session. Once this value is set
+to any non-NULL value, libcurl will return \fICURLE_RTSP_SESSION_ERROR\fP if ID
+received from the server does not match. If unset (or set to NULL), libcurl will
+automatically set the ID the first time the server sets it in a response.
+.IP CURLOPT_RTSP_STREAM_URI
+Set the stream URI to operate on by passing a char * . For example, a single
+session may be controlling \fIrtsp://foo/twister/audio\fP and
+\fIrtsp://foo/twister/video\fP and the application can switch to the appropriate
+stream using this option. If unset, libcurl will default to operating on generic
+server options by passing '*' in the place of the RTSP Stream URI. Note that
+this option is distinct from \fICURLOPT_URL\fP. When working with RTSP, the
+\fICURLOPT_STREAM_URI\fP indicates what URL to send to the server in the request
+header while the \fICURLOPT_URL\fP indicates where to make the connection to.
+(e.g. the \fICURLOPT_URL\fP for the above examples might be set to
+\fIrtsp://foo/twister\fP
+.IP CURLOPT_RTSP_TRANSPORT
+Pass a char * to tell libcurl what to pass for the Transport: header for this
+RTSP session. This is mainly a convenience method to avoid needing to set a
+custom Transport: header for every SETUP request. The application must set a
+Transport: header before issuing a SETUP request.
+.IP CURLOPT_RTSP_VERSION
+Currently, there is only one version of the RTSP protocol (1.0). So at the
+moment, this option is inactive. Its function is similar to the
+\fICURLOPT_HTTP_VERSION\fP.
+.IP CURLOPT_RTSP_HEADER
+This option is simply an alias for \fICURLOPT_HTTP_HEADER\fP. Use this to
+replace the standard headers that RTSP and HTTP share. It is also valid to use
+the shortcuts such as \fICURLOPT_USERAGENT\fP.
+.RS
+.IP CURL_RTSP_VERSION_NONE
+We don't care about what version the library uses. libcurl will use whatever
+it thinks fit.
+.IP CURL_RTSP_VERSION_1_0
+Enforce RTSP 1.0 requests.
+.RE
+.IP CURLOPT_RTSP_CLIENT_CSEQ
+Manually set the the CSEQ number to issue for the next RTSP request. Useful if
+the application is resuming a previously broken connection. The CSEQ will
+increment from this new number henceforth.
+.IP CURLOPT_RTSP_SERVER_CSEQ
+Manually set the CSEQ number to expect for the next RTSP Server->Client request.
+At the moment, this feature (listening for Server requests) is unimplemented.
+.IP CURLOPT_RTPDATA
+This is the stream that will be passed to \fICURLOPT_RTPFUNCTION\fP when
+interleaved RTP data is received. Since the application is required to provide a
+custom function for RTP data, there is no requirement that the stream pointer be
+a valid FILE pointer. An application may wish to pass a cookie containing
+information about many streams to assist in demultiplexing the different RTP
+channels.
+.IP CURLOPT_RTPFUNCTION
+Function pointer that should match the following prototype: \fIsize_t
+function( void *ptr, size_t size, size_t nmemb, void *stream, int channel);\fP.
+\fBThis does not match the prototype of fwrite()\fP. This function gets called
+by libcurl as soon as it has received interleaved RTP data. It operates like the
+other write callbacks however it requires a user implementation since the
+application must demux the different RTP channels. This function gets called for
+each $ block and therefore contains exactly one upper-layer protocol unit (e.g.
+one RTP packet). See \fIRFC 2326 Section 10.12\fP for more information on how
+RTP interleaving behaves.
+
+Interleaved RTP poses some challeneges for the client application. Since the
+stream data is sharing the RTSP control connection, it is critical to service
+the RTP in a timely fashion. If the RTP data is not handled quickly, subsequent
+response processing may become unreasonably delayed and the connection may
+close. The application may use \fICURL_RTSPREQ_RECEIVE\fP to service RTP data
+when no requests are desired. If the application makes a request, (e.g.
+\fICURL_RTSPREQ_PAUSE\fP) then the response handler will process any pending RTP
+data before marking the request as finished.
 .SH PROTOCOL OPTIONS
 .IP CURLOPT_TRANSFERTEXT
 A parameter set to 1 tells the library to use ASCII mode for FTP transfers,
@@ -1301,9 +1433,13 @@
 transfers also support several intervals, separated with commas as in
 \fI"X-Y,N-M"\fP. Using this kind of multiple intervals will cause the HTTP
 server to send the response document in pieces (using standard MIME separation
-techniques). Pass a NULL to this option to disable the use of ranges.
-
-Ranges work on HTTP, FTP and FILE (since 7.18.0) transfers only.
+techniques). For RTSP, the formatting of a range should follow RFC 2326 Section
+12.29. Note that for RTSP, byte ranges are \fBnot\fP permitted. Instead, ranges
+should be given in npt, utc, or smpte formats.
+
+Pass a NULL to this option to disable the use of ranges.
+
+Ranges work on HTTP, FTP, FILE (since 7.18.0), and RTSP (since 7.x.x?) transfers only.
 .IP CURLOPT_RESUME_FROM
 Pass a long as parameter. It contains the offset in number of bytes that you
 want the transfer to start from. Set this option to 0 to make the transfer
@@ -1408,7 +1544,8 @@
 .IP CURLOPT_TIMECONDITION
 Pass a long as parameter. This defines how the \fICURLOPT_TIMEVALUE\fP time
 value is treated. You can set this parameter to \fICURL_TIMECOND_IFMODSINCE\fP
-or \fICURL_TIMECOND_IFUNMODSINCE\fP. This feature applies to HTTP and FTP.
+or \fICURL_TIMECOND_IFUNMODSINCE\fP. This feature applies to HTTP, FTP, and
+RTSP.
 
 The last modification time of a file is not always known and in such instances
 this feature will have no effect even if the given time condition would not

=== modified file 'docs/libcurl/libcurl.m4'
--- old/docs/libcurl/libcurl.m4	2009-11-19 15:52:21 +0000
+++ new/docs/libcurl/libcurl.m4	2010-01-11 21:13:59 +0000
@@ -55,6 +55,7 @@
   AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
   AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
   AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
+  AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
 
   AC_ARG_WITH(libcurl,
      AC_HELP_STRING([--with-libcurl=DIR],[look for the curl library in DIR]),
@@ -194,7 +195,7 @@
 
 	      # We don't have --protocols, so just assume that all
 	      # protocols are available
-	      _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT"
+	      _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP RTSP"
 
 	      if test x$libcurl_feature_SSL = xyes ; then
 	         _libcurl_protocols="$_libcurl_protocols HTTPS"

=== modified file 'include/curl/curl.h'
--- old/include/curl/curl.h	2010-01-11 16:59:42 +0000
+++ new/include/curl/curl.h	2010-01-11 21:13:59 +0000
@@ -197,6 +197,12 @@
                                       size_t nitems,
                                       void *outstream);
 
+typedef size_t (*curl_rtp_write_callback)(char *buffer,
+                                      size_t size,
+                                      size_t nitems,
+                                      void *outstream,
+                                      int channel);
+
 /* These are the return codes for the seek callbacks */
 #define CURL_SEEKFUNC_OK       0
 #define CURL_SEEKFUNC_FAIL     1 /* fail the entire transfer */
@@ -414,6 +420,10 @@
   CURLE_SSL_ISSUER_ERROR,        /* 83 - Issuer check failed.  (Added in
                                     7.19.0) */
   CURLE_FTP_PRET_FAILED,         /* 84 - a PRET command failed */
+  CURLE_RTSP_RETURNED_ERROR,     /* 85 - RTSP return code failure */
+  CURLE_RTSP_CSEQ_ERROR,         /* 86 - mismatch of RTSP CSeq numbers */
+  CURLE_RTSP_SESSION_ERROR,      /* 87 - mismatch of RTSP Sessiion Identifiers */
+  
   CURL_LAST /* never use! */
 } CURLcode;
 
@@ -626,6 +636,7 @@
 #define CURLPROTO_POP3S  (1<<15)
 #define CURLPROTO_SMTP   (1<<16)
 #define CURLPROTO_SMTPS  (1<<17)
+#define CURLPROTO_RTSP   (1<<18)
 #define CURLPROTO_ALL    (~0) /* enable everything */
 
 /* long may be 32 or 64 bits, but we should never depend on anything else
@@ -1295,6 +1306,39 @@
   /* FTP: send PRET before PASV */
   CINIT(FTP_USE_PRET, LONG, 188),
 
+  /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */
+  CINIT(RTSP_REQUEST, LONG, 189),
+
+  /* The RTSP session identifier */
+  CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190),
+
+  /* The RTSP stream URI */
+  CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191),
+
+  /* The Transport: header to use in RTSP requests */
+  CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192),
+
+  /* 
+   * The RTSP protocol verison. 
+   * Currently only CURL_RTSP_VERSION_1_0 is supported
+   */ 
+  CINIT(RTSP_VERSION, LONG, 193),
+
+  /* Manually initialize the client RTSP CSeq for this handle */
+  CINIT(RTSP_CLIENT_CSEQ, LONG, 194),
+
+  /* Manually initialize the server RTSP CSeq for this handle */
+  CINIT(RTSP_SERVER_CSEQ, LONG, 195),
+
+  /* The stream to pass to RTP_FUNCTION. */
+  CINIT(RTPDATA, OBJECTPOINT, 196),
+
+  /* Let the application define a custom write method for RTP data 
+   * This is like the other write callbacks with an extra parameter
+   * for the RTP channel.
+   */
+  CINIT(RTPFUNCTION, FUNCTIONPOINT, 197),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -1338,6 +1382,7 @@
 #define CURLOPT_WRITEDATA CURLOPT_FILE
 #define CURLOPT_READDATA  CURLOPT_INFILE
 #define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER
+#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
 
   /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
 enum {
@@ -1350,6 +1395,37 @@
   CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
 };
 
+  /* 
+   * These enums are for use with the CURLOPT_RTSP_VERSION option.
+   * However, currently there is only one version of the protocol.
+   * Application developers wishing to safeguard against a future protocol
+   * spec should specify CURL_RTSP_VERSION_1_0 explicitly
+   */
+enum {
+  CURL_RTSP_VERSION_NONE, /* let the library choose */
+  CURL_RTSP_VERSION_1_0,  /* use RTSP 1.0 in the request */
+  CURL_RTSP_VERSION_LAST  /* *ILLEGAL* rtsp versoin */
+};
+
+/*
+ * Public API enums for RTSP requests
+ */
+enum {
+    CURL_RTSPREQ_NONE, /* first in list */
+    CURL_RTSPREQ_OPTIONS,
+    CURL_RTSPREQ_DESCRIBE,
+    CURL_RTSPREQ_ANNOUNCE,
+    CURL_RTSPREQ_SETUP,
+    CURL_RTSPREQ_PLAY,
+    CURL_RTSPREQ_PAUSE,
+    CURL_RTSPREQ_TEARDOWN,
+    CURL_RTSPREQ_GET_PARAMETER,
+    CURL_RTSPREQ_SET_PARAMETER,
+    CURL_RTSPREQ_RECORD,
+    CURL_RTSPREQ_RECEIVE,
+    CURL_RTSPREQ_LAST /* last in list */
+};
+
   /* These enums are for use with the CURLOPT_NETRC option. */
 enum CURL_NETRC_OPTION {
   CURL_NETRC_IGNORED,     /* The .netrc will never be read.
@@ -1719,9 +1795,13 @@
   CURLINFO_APPCONNECT_TIME  = CURLINFO_DOUBLE + 33,
   CURLINFO_CERTINFO         = CURLINFO_SLIST  + 34,
   CURLINFO_CONDITION_UNMET  = CURLINFO_LONG   + 35,
+  CURLINFO_RTSP_SESSION_ID  = CURLINFO_STRING + 36,
+  CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG   + 37,
+  CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG   + 38,
+  CURLINFO_RTSP_CSEQ_RECV   = CURLINFO_LONG   + 39,
   /* Fill in new entries below here! */
 
-  CURLINFO_LASTONE          = 35
+  CURLINFO_LASTONE          = 39
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

=== modified file 'lib/Makefile.inc'
--- old/lib/Makefile.inc	2010-01-11 16:59:42 +0000
+++ new/lib/Makefile.inc	2010-01-11 21:13:59 +0000
@@ -11,7 +11,7 @@
   inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c	\
   strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c          \
   socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c		\
-  curl_memrchr.c imap.c pop3.c smtp.c pingpong.c
+  curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c
 
 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h	\
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h	\
@@ -23,4 +23,4 @@
   transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h gtls.h	\
   tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h	\
   curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h slist.h nonblock.h	\
-  curl_memrchr.h imap.h pop3.h smtp.h pingpong.h
+  curl_memrchr.h imap.h pop3.h smtp.h pingpong.h rtsp.h

=== modified file 'lib/getinfo.c'
--- old/lib/getinfo.c	2009-11-19 15:52:21 +0000
+++ new/lib/getinfo.c	2010-01-11 21:13:59 +0000
@@ -235,6 +235,19 @@
     /* return if the condition prevented the document to get transfered */
     *param_longp = data->info.timecond;
     break;
+  case CURLINFO_RTSP_SESSION_ID:
+    *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
+    break;
+  case CURLINFO_RTSP_CLIENT_CSEQ:
+    *param_longp = data->state.rtsp_next_client_CSeq;
+    break;
+  case CURLINFO_RTSP_SERVER_CSEQ:
+    *param_longp = data->state.rtsp_next_server_CSeq;
+    break;
+  case CURLINFO_RTSP_CSEQ_RECV:
+    *param_longp = data->state.rtsp_CSeq_recv;
+    break;
+
   default:
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }

=== modified file 'lib/http.c'
--- old/lib/http.c	2010-01-11 16:59:42 +0000
+++ new/lib/http.c	2010-01-12 22:32:03 +0000
@@ -98,6 +98,7 @@
 #include "multiif.h"
 #include "rawstr.h"
 #include "content_encoding.h"
+#include "rtsp.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -173,7 +174,7 @@
  *
  * Returns a pointer to the first matching header or NULL if none matched.
  */
-static char *checkheaders(struct SessionHandle *data, const char *thisheader)
+char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
 {
   struct curl_slist *head;
   size_t thislen = strlen(thisheader);
@@ -565,9 +566,9 @@
   if(authstatus->picked == CURLAUTH_BASIC) {
     /* Basic */
     if((proxy && conn->bits.proxy_user_passwd &&
-       !checkheaders(data, "Proxy-authorization:")) ||
+       !Curl_checkheaders(data, "Proxy-authorization:")) ||
        (!proxy && conn->bits.user_passwd &&
-       !checkheaders(data, "Authorization:"))) {
+       !Curl_checkheaders(data, "Authorization:"))) {
       auth="Basic";
       result = http_output_basic(conn, proxy);
       if(result)
@@ -960,41 +961,23 @@
 }
 
 /* ------------------------------------------------------------------------- */
-/*
- * The add_buffer series of functions are used to build one large memory chunk
- * from repeated function invokes. Used so that the entire HTTP request can
- * be sent in one go.
- */
-
-struct send_buffer {
-  char *buffer;
-  size_t size_max;
-  size_t size_used;
-};
-typedef struct send_buffer send_buffer;
-
-static CURLcode add_custom_headers(struct connectdata *conn,
-                                   send_buffer *req_buffer);
-static CURLcode
- add_buffer(send_buffer *in, const void *inptr, size_t size);
-
-/*
- * add_buffer_init() sets up and returns a fine buffer struct
- */
-static
-send_buffer *add_buffer_init(void)
+/* add_buffer functions */
+
+/*
+ * Curl_add_buffer_init() sets up and returns a fine buffer struct
+ */
+Curl_send_buffer *Curl_add_buffer_init(void)
 {
-  return calloc(1, sizeof(send_buffer));
+  return calloc(1, sizeof(Curl_send_buffer));
 }
 
 /*
- * add_buffer_send() sends a header buffer and frees all associated memory.
+ * Curl_add_buffer_send() sends a header buffer and frees all associated memory.
  * Body data may be appended to the header data if desired.
  *
  * Returns CURLcode
  */
-static
-CURLcode add_buffer_send(send_buffer *in,
+CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
                          struct connectdata *conn,
                          long *bytes_written, /* add the number of sent bytes
                                                  to this counter */
@@ -1144,8 +1127,7 @@
 /*
  * add_bufferf() add the formatted input to the buffer.
  */
-static
-CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
+CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
 {
   char *s;
   va_list ap;
@@ -1154,7 +1136,7 @@
   va_end(ap);
 
   if(s) {
-    CURLcode result = add_buffer(in, s, strlen(s));
+    CURLcode result = Curl_add_buffer(in, s, strlen(s));
     free(s);
     return result;
   }
@@ -1168,8 +1150,7 @@
 /*
  * add_buffer() appends a memory chunk to the existing buffer
  */
-static
-CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
+CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
 {
   char *new_rb;
   size_t new_size;
@@ -1223,6 +1204,8 @@
 /* end of the add_buffer functions */
 /* ------------------------------------------------------------------------- */
 
+
+
 /*
  * Curl_compareheader()
  *
@@ -1320,7 +1303,7 @@
   do {
     if(!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
       char *host_port;
-      send_buffer *req_buffer;
+      Curl_send_buffer *req_buffer;
 
       infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
             hostname, remote_port);
@@ -1334,7 +1317,7 @@
       }
 
       /* initialize a dynamic send-buffer */
-      req_buffer = add_buffer_init();
+      req_buffer = Curl_add_buffer_init();
 
       if(!req_buffer)
         return CURLE_OUT_OF_MEMORY;
@@ -1355,7 +1338,7 @@
         const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
           "1.0" : "1.1";
 
-        if(!checkheaders(data, "Host:")) {
+        if(!Curl_checkheaders(data, "Host:")) {
           host = aprintf("Host: %s\r\n", host_port);
           if(!host) {
             free(req_buffer);
@@ -1363,17 +1346,17 @@
             return CURLE_OUT_OF_MEMORY;
           }
         }
-        if(!checkheaders(data, "Proxy-Connection:"))
+        if(!Curl_checkheaders(data, "Proxy-Connection:"))
           proxyconn = "Proxy-Connection: Keep-Alive\r\n";
 
-        if(!checkheaders(data, "User-Agent:") &&
+        if(!Curl_checkheaders(data, "User-Agent:") &&
            data->set.str[STRING_USERAGENT])
           useragent = conn->allocptr.uagent;
 
         /* Send the connect request to the proxy */
         /* BLOCKING */
         result =
-          add_bufferf(req_buffer,
+          Curl_add_bufferf(req_buffer,
                       "CONNECT %s:%d HTTP/%s\r\n"
                       "%s"  /* Host: */
                       "%s"  /* Proxy-Authorization */
@@ -1390,15 +1373,15 @@
           free(host);
 
         if(CURLE_OK == result)
-          result = add_custom_headers(conn, req_buffer);
+          result = Curl_add_custom_headers(conn, req_buffer);
 
         if(CURLE_OK == result)
           /* CRLF terminate the request */
-          result = add_bufferf(req_buffer, "\r\n");
+          result = Curl_add_bufferf(req_buffer, "\r\n");
 
         if(CURLE_OK == result) {
           /* Now send off the request */
-          result = add_buffer_send(req_buffer, conn,
+          result = Curl_add_buffer_send(req_buffer, conn,
                                    &data->info.request_size, 0, sockindex);
         }
         req_buffer = NULL;
@@ -1921,7 +1904,7 @@
     return CURLE_OK;
 
   if(http->send_buffer) {
-    send_buffer *buff = http->send_buffer;
+    Curl_send_buffer *buff = http->send_buffer;
 
     free(buff->buffer);
     free(buff);
@@ -1978,33 +1961,33 @@
 /* check and possibly add an Expect: header */
 static CURLcode expect100(struct SessionHandle *data,
                           struct connectdata *conn,
-                          send_buffer *req_buffer)
+                          Curl_send_buffer *req_buffer)
 {
   CURLcode result = CURLE_OK;
   const char *ptr;
   data->state.expect100header = FALSE; /* default to false unless it is set
                                           to TRUE below */
-  if(use_http_1_1(data, conn)) {
+  if(use_http_1_1(data, conn) && !Curl_checkheaders(data, "Expect:")) {
     /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
        100-continue to the headers which actually speeds up post operations
        (as there is one packet coming back from the web server) */
-    ptr = checkheaders(data, "Expect:");
+    ptr = Curl_checkheaders(data, "Expect:");
     if (ptr) {
       data->state.expect100header =
         Curl_compareheader(ptr, "Expect:", "100-continue");
     }
     else {
-      result = add_bufferf(req_buffer,
-                           "Expect: 100-continue\r\n");
-      if(result == CURLE_OK)
-        data->state.expect100header = TRUE;
-    }
+    result = Curl_add_bufferf(req_buffer,
+                         "Expect: 100-continue\r\n");
+    if(result == CURLE_OK)
+      data->state.expect100header = TRUE;
+  }
   }
   return result;
 }
 
-static CURLcode add_custom_headers(struct connectdata *conn,
-                                   send_buffer *req_buffer)
+CURLcode Curl_add_custom_headers(struct connectdata *conn,
+                                   Curl_send_buffer *req_buffer)
 {
   char *ptr;
   struct curl_slist *headers=conn->data->set.headers;
@@ -2036,7 +2019,7 @@
                 checkprefix("Content-Length", headers->data))
           ;
         else {
-          CURLcode result = add_bufferf(req_buffer, "%s\r\n", headers->data);
+          CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
           if(result)
             return result;
         }
@@ -2047,6 +2030,56 @@
   return CURLE_OK;
 }
 
+CURLcode Curl_add_timecondition(struct SessionHandle *data, Curl_send_buffer *req_buffer) {
+    struct tm *tm;
+    char *buf = data->state.buffer;
+    CURLcode result = CURLE_OK;
+
+    /* The If-Modified-Since header family should have their times set in
+     * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
+     * represented in Greenwich Mean Time (GMT), without exception. For the
+     * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
+     * Time)." (see page 20 of RFC2616).
+     */
+
+#ifdef HAVE_GMTIME_R
+    /* thread-safe version */
+    struct tm keeptime;
+    tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
+#else
+    tm = gmtime(&data->set.timevalue);
+#endif
+
+    /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+    snprintf(buf, BUFSIZE-1,
+             "%s, %02d %s %4d %02d:%02d:%02d GMT",
+             Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+             tm->tm_mday,
+             Curl_month[tm->tm_mon],
+             tm->tm_year + 1900,
+             tm->tm_hour,
+             tm->tm_min,
+             tm->tm_sec);
+
+    switch(data->set.timecondition) {
+    case CURL_TIMECOND_IFMODSINCE:
+    default:
+      result = Curl_add_bufferf(req_buffer,
+                           "If-Modified-Since: %s\r\n", buf);
+      break;
+    case CURL_TIMECOND_IFUNMODSINCE:
+      result = Curl_add_bufferf(req_buffer,
+                           "If-Unmodified-Since: %s\r\n", buf);
+      break;
+    case CURL_TIMECOND_LASTMOD:
+      result = Curl_add_bufferf(req_buffer,
+                           "Last-Modified: %s\r\n", buf);
+      break;
+    }
+
+    return result;
+}
+
 /*
  * Curl_http() gets called from the generic Curl_do() function when a HTTP
  * request is to be performed. This creates and sends a properly constructed
@@ -2055,7 +2088,6 @@
 CURLcode Curl_http(struct connectdata *conn, bool *done)
 {
   struct SessionHandle *data=conn->data;
-  char *buf = data->state.buffer; /* this is a short cut to the buffer */
   CURLcode result=CURLE_OK;
   struct HTTP *http;
   const char *ppath = data->state.path;
@@ -2069,7 +2101,7 @@
   char *addcookies = NULL;
   curl_off_t included_body = 0;
   const char *httpstring;
-  send_buffer *req_buffer;
+  Curl_send_buffer *req_buffer;
   curl_off_t postsize; /* off_t type to be able to hold a large file size */
   int seekerr = CURL_SEEKFUNC_OK;
 
@@ -2140,7 +2172,7 @@
      it might have been used in the proxy connect, but if we have got a header
      with the user-agent string specified, we erase the previously made string
      here. */
-  if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
     free(conn->allocptr.uagent);
     conn->allocptr.uagent=NULL;
   }
@@ -2161,15 +2193,15 @@
     conn->bits.authneg = FALSE;
 
   Curl_safefree(conn->allocptr.ref);
-  if(data->change.referer && !checkheaders(data, "Referer:"))
+  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
     conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
   else
     conn->allocptr.ref = NULL;
 
-  if(data->set.str[STRING_COOKIE] && !checkheaders(data, "Cookie:"))
+  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
     addcookies = data->set.str[STRING_COOKIE];
 
-  if(!checkheaders(data, "Accept-Encoding:") &&
+  if(!Curl_checkheaders(data, "Accept-Encoding:") &&
      data->set.str[STRING_ENCODING]) {
     Curl_safefree(conn->allocptr.accept_encoding);
     conn->allocptr.accept_encoding =
@@ -2178,7 +2210,7 @@
       return CURLE_OUT_OF_MEMORY;
   }
 
-  ptr = checkheaders(data, "Transfer-Encoding:");
+  ptr = Curl_checkheaders(data, "Transfer-Encoding:");
   if(ptr) {
     /* Some kind of TE is requested, check if 'chunked' is chosen */
     data->req.upload_chunky =
@@ -2207,7 +2239,7 @@
 
   Curl_safefree(conn->allocptr.host);
 
-  ptr = checkheaders(data, "Host:");
+  ptr = Curl_checkheaders(data, "Host:");
   if(ptr && (!data->state.this_is_a_follow ||
              Curl_raw_equal(data->state.first_host, conn->host.name))) {
 #if !defined(CURL_DISABLE_COOKIES)
@@ -2334,7 +2366,7 @@
     /* we must build the whole darned post sequence first, so that we have
        a size of the whole shebang before we start to send it */
      result = Curl_getFormData(&http->sendit, data->set.httppost,
-                               checkheaders(data, "Content-Type:"),
+                               Curl_checkheaders(data, "Content-Type:"),
                                &http->postsize);
      if(CURLE_OK != result) {
        /* Curl_getFormData() doesn't use failf() */
@@ -2344,7 +2376,7 @@
   }
 
 
-  http->p_accept = checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
+  http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
 
   if(( (HTTPREQ_POST == httpreq) ||
        (HTTPREQ_POST_FORM == httpreq) ||
@@ -2427,7 +2459,7 @@
      * ones if any such are specified.
      */
     if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
-       !checkheaders(data, "Range:")) {
+       !Curl_checkheaders(data, "Range:")) {
       /* if a line like this was already allocated, free the previous one */
       if(conn->allocptr.rangeline)
         free(conn->allocptr.rangeline);
@@ -2435,7 +2467,7 @@
                                          data->state.range);
     }
     else if((httpreq != HTTPREQ_GET) &&
-            !checkheaders(data, "Content-Range:")) {
+            !Curl_checkheaders(data, "Content-Range:")) {
 
       /* if a line like this was already allocated, free the previous one */
       if(conn->allocptr.rangeline)
@@ -2478,27 +2510,27 @@
   httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
 
   /* initialize a dynamic send-buffer */
-  req_buffer = add_buffer_init();
+  req_buffer = Curl_add_buffer_init();
 
   if(!req_buffer)
     return CURLE_OUT_OF_MEMORY;
 
   /* add the main request stuff */
   /* GET/HEAD/POST/PUT */
-  result = add_bufferf(req_buffer, "%s ", request);
+  result = Curl_add_bufferf(req_buffer, "%s ", request);
   if (result)
     return result;
 
   /* url */
   if (paste_ftp_userpwd)
-    result = add_bufferf(req_buffer, "ftp://%s:%s@%s",
+    result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
         conn->user, conn->passwd, ppath + sizeof("ftp://") - 1);
   else
-    result = add_buffer(req_buffer, ppath, strlen(ppath));
+    result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
   if (result)
     return result;
 
-  result = add_bufferf(req_buffer,
+  result = Curl_add_bufferf(req_buffer,
                 "%s" /* ftp typecode (;type=x) */
                 " HTTP/%s\r\n" /* HTTP version */
                 "%s" /* proxyuserpwd */
@@ -2532,7 +2564,7 @@
                 conn->allocptr.ref:"" /* Referer: <data> */,
                 (conn->bits.httpproxy &&
                  !conn->bits.tunnel_proxy &&
-                 !checkheaders(data, "Proxy-Connection:"))?
+                 !Curl_checkheaders(data, "Proxy-Connection:"))?
                 "Proxy-Connection: Keep-Alive\r\n":"",
                 te
       );
@@ -2568,11 +2600,11 @@
       while(co) {
         if(co->value) {
           if(0 == count) {
-            result = add_bufferf(req_buffer, "Cookie: ");
+            result = Curl_add_bufferf(req_buffer, "Cookie: ");
             if(result)
               break;
           }
-          result = add_bufferf(req_buffer,
+          result = Curl_add_bufferf(req_buffer,
                                "%s%s=%s", count?"; ":"",
                                co->name, co->value);
           if(result)
@@ -2585,16 +2617,16 @@
     }
     if(addcookies && (CURLE_OK == result)) {
       if(!count)
-        result = add_bufferf(req_buffer, "Cookie: ");
+        result = Curl_add_bufferf(req_buffer, "Cookie: ");
       if(CURLE_OK == result) {
-        result = add_bufferf(req_buffer, "%s%s",
+        result = Curl_add_bufferf(req_buffer, "%s%s",
                              count?"; ":"",
                              addcookies);
         count++;
       }
     }
     if(count && (CURLE_OK == result))
-      result = add_buffer(req_buffer, "\r\n", 2);
+      result = Curl_add_buffer(req_buffer, "\r\n", 2);
 
     if(result)
       return result;
@@ -2602,54 +2634,12 @@
 #endif
 
   if(data->set.timecondition) {
-    struct tm *tm;
-
-    /* The If-Modified-Since header family should have their times set in
-     * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
-     * represented in Greenwich Mean Time (GMT), without exception. For the
-     * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
-     * Time)." (see page 20 of RFC2616).
-     */
-
-#ifdef HAVE_GMTIME_R
-    /* thread-safe version */
-    struct tm keeptime;
-    tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
-#else
-    tm = gmtime(&data->set.timevalue);
-#endif
-
-    /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
-    snprintf(buf, BUFSIZE-1,
-             "%s, %02d %s %4d %02d:%02d:%02d GMT",
-             Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
-             tm->tm_mday,
-             Curl_month[tm->tm_mon],
-             tm->tm_year + 1900,
-             tm->tm_hour,
-             tm->tm_min,
-             tm->tm_sec);
-
-    switch(data->set.timecondition) {
-    case CURL_TIMECOND_IFMODSINCE:
-    default:
-      result = add_bufferf(req_buffer,
-                           "If-Modified-Since: %s\r\n", buf);
-      break;
-    case CURL_TIMECOND_IFUNMODSINCE:
-      result = add_bufferf(req_buffer,
-                           "If-Unmodified-Since: %s\r\n", buf);
-      break;
-    case CURL_TIMECOND_LASTMOD:
-      result = add_bufferf(req_buffer,
-                           "Last-Modified: %s\r\n", buf);
-      break;
-    }
+      result = Curl_add_timecondition(data, req_buffer);
     if(result)
       return result;
   }
 
-  result = add_custom_headers(conn, req_buffer);
+  result = Curl_add_custom_headers(conn, req_buffer);
   if(result)
     return result;
 
@@ -2665,11 +2655,11 @@
   case HTTPREQ_POST_FORM:
     if(!http->sendit || conn->bits.authneg) {
       /* nothing to post! */
-      result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
+      result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
       if(result)
         return result;
 
-      result = add_buffer_send(req_buffer, conn,
+      result = Curl_add_buffer_send(req_buffer, conn,
                                &data->info.request_size, 0, FIRSTSOCKET);
       if(result)
         failf(data, "Failed sending POST request");
@@ -2701,7 +2691,7 @@
 
     if(!data->req.upload_chunky) {
       /* only add Content-Length if not uploading chunked */
-      result = add_bufferf(req_buffer,
+      result = Curl_add_bufferf(req_buffer,
                            "Content-Length: %" FORMAT_OFF_T "\r\n",
                            http->postsize);
       if(result)
@@ -2725,13 +2715,13 @@
         return CURLE_HTTP_POST_ERROR;
       }
 
-      result = add_buffer(req_buffer, contentType, linelength);
+      result = Curl_add_buffer(req_buffer, contentType, linelength);
       if(result)
         return result;
     }
 
     /* make the request end in a true CRLF */
-    result = add_buffer(req_buffer, "\r\n", 2);
+    result = Curl_add_buffer(req_buffer, "\r\n", 2);
     if(result)
       return result;
 
@@ -2739,7 +2729,7 @@
     Curl_pgrsSetUploadSize(data, http->postsize);
 
     /* fire away the whole request to the server */
-    result = add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(req_buffer, conn,
                              &data->info.request_size, 0, FIRSTSOCKET);
     if(result)
       failf(data, "Failed sending POST request");
@@ -2773,7 +2763,7 @@
 
     if((postsize != -1) && !data->req.upload_chunky) {
       /* only add Content-Length if not uploading chunked */
-      result = add_bufferf(req_buffer,
+      result = Curl_add_bufferf(req_buffer,
                            "Content-Length: %" FORMAT_OFF_T "\r\n",
                            postsize );
       if(result)
@@ -2784,7 +2774,7 @@
     if(result)
       return result;
 
-    result = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+    result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
     if(result)
       return result;
 
@@ -2792,7 +2782,7 @@
     Curl_pgrsSetUploadSize(data, postsize);
 
     /* this sends the buffer and frees all the buffer resources */
-    result = add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(req_buffer, conn,
                              &data->info.request_size, 0, FIRSTSOCKET);
     if(result)
       failf(data, "Failed sending PUT request");
@@ -2822,10 +2812,10 @@
          we don't upload data chunked, as RFC2616 forbids us to set both
          kinds of headers (Transfer-Encoding: chunked and Content-Length) */
 
-      if(conn->bits.authneg || !checkheaders(data, "Content-Length:")) {
+      if(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
         /* we allow replacing this header if not during auth negotiation,
            although it isn't very wise to actually set your own */
-        result = add_bufferf(req_buffer,
+        result = Curl_add_bufferf(req_buffer,
                              "Content-Length: %" FORMAT_OFF_T"\r\n",
                              postsize);
         if(result)
@@ -2833,8 +2823,8 @@
       }
     }
 
-    if(!checkheaders(data, "Content-Type:")) {
-      result = add_bufferf(req_buffer,
+    if(!Curl_checkheaders(data, "Content-Type:")) {
+      result = Curl_add_bufferf(req_buffer,
                            "Content-Type: application/x-www-form-urlencoded\r\n");
       if(result)
         return result;
@@ -2844,7 +2834,7 @@
        the somewhat bigger ones we allow the app to disable it. Just make
        sure that the expect100header is always set to the preferred value
        here. */
-    ptr = checkheaders(data, "Expect:");
+    ptr = Curl_checkheaders(data, "Expect:");
     if(ptr) {
       data->state.expect100header =
         Curl_compareheader(ptr, "Expect:", "100-continue");
@@ -2868,25 +2858,25 @@
            is no magic limit but only set to prevent really huge POSTs to
            get the data duplicated with malloc() and family. */
 
-        result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
         if(result)
           return result;
 
         if(!data->req.upload_chunky) {
           /* We're not sending it 'chunked', append it to the request
              already now to reduce the number if send() calls */
-          result = add_buffer(req_buffer, data->set.postfields,
+          result = Curl_add_buffer(req_buffer, data->set.postfields,
                               (size_t)postsize);
           included_body = postsize;
         }
         else {
           /* Append the POST data chunky-style */
-          result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
+          result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
           if(CURLE_OK == result)
-            result = add_buffer(req_buffer, data->set.postfields,
+            result = Curl_add_buffer(req_buffer, data->set.postfields,
                                 (size_t)postsize);
           if(CURLE_OK == result)
-            result = add_buffer(req_buffer,
+            result = Curl_add_buffer(req_buffer,
                                 "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
           /* CR  LF   0  CR  LF  CR  LF */
           included_body = postsize + 7;
@@ -2907,20 +2897,20 @@
         /* set the upload size to the progress meter */
         Curl_pgrsSetUploadSize(data, http->postsize);
 
-        result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
         if(result)
           return result;
       }
     }
     else {
-      result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+      result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
       if(result)
         return result;
 
       if(data->req.upload_chunky && conn->bits.authneg) {
         /* Chunky upload is selected and we're negotiating auth still, send
            end-of-data only */
-        result = add_buffer(req_buffer,
+        result = Curl_add_buffer(req_buffer,
                             "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
         /* CR  LF   0  CR  LF  CR  LF */
         if(result)
@@ -2941,7 +2931,7 @@
       }
     }
     /* issue the request */
-    result = add_buffer_send(req_buffer, conn, &data->info.request_size,
+    result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size,
                              (size_t)included_body, FIRSTSOCKET);
 
     if(result)
@@ -2955,12 +2945,12 @@
     break;
 
   default:
-    result = add_buffer(req_buffer, "\r\n", 2);
+    result = Curl_add_buffer(req_buffer, "\r\n", 2);
     if(result)
       return result;
 
     /* issue the request */
-    result = add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(req_buffer, conn,
                              &data->info.request_size, 0, FIRSTSOCKET);
 
     if(result)
@@ -2999,12 +2989,11 @@
   bool rc = FALSE;
 #ifdef CURL_DOES_CONVERSIONS
   /* convert from the network encoding using a scratch area */
-  char *scratch = calloc(1, strlen(s)+1);
+  char *scratch = strdup(s);
   if(NULL == scratch) {
-     failf (data, "Failed to calloc memory for conversion!");
+     failf (data, "Failed to allocate memory for conversion!");
      return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
   }
-  strcpy(scratch, s);
   if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
     /* Curl_convert_from_network calls failf if unsuccessful */
      free(scratch);
@@ -3031,6 +3020,50 @@
   return rc;
 }
 
+#ifndef CURL_DISABLE_RTSP
+static bool
+checkrtspprefix(struct SessionHandle *data,
+        const char *s)
+{
+
+#ifdef CURL_DOES_CONVERSIONS
+  /* convert from the network encoding using a scratch area */
+  char *scratch = strdup(s);
+  if(NULL == scratch) {
+     failf (data, "Failed to allocate memory for conversion!");
+     return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
+  }
+  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
+    /* Curl_convert_from_network calls failf if unsuccessful */
+     free(scratch);
+     return FALSE; /* can't return CURLE_foobar so return FALSE */
+  }
+  s = scratch;
+#else
+  (void)data; /* unused */
+#endif /* CURL_DOES_CONVERSIONS */
+  if(checkprefix("RTSP/", s))
+    return TRUE;
+  else
+    return FALSE;
+}
+#endif /* CURL_DISABLE_RTSP */
+
+static bool
+checkprotoprefix(struct SessionHandle *data, struct connectdata *conn, const char *s)
+{
+#ifndef CURL_DISABLE_RTSP
+  if(conn->protocol & PROT_RTSP)
+    return checkrtspprefix(data, s);
+#endif /* CURL_DISABLE_RTSP */
+
+  return checkhttpprefix(data, s);
+}
+
+#endif
+
+
+
 /*
  * header_append() copies a chunk of data to the end of the already received
  * header. We make sure that the full string fit in the allocated header
@@ -3079,12 +3112,13 @@
  * Read any HTTP header lines from the server and pass them to the client app.
  */
 CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
-                                     struct connectdata *conn,
-                                     ssize_t *nread,
-                                     bool *stop_reading)
+                                       struct connectdata *conn,
+                                       ssize_t *nread,
+                                       bool *stop_reading)
 {
   CURLcode result;
   struct SingleRequest *k = &data->req;
+  long CSeq = 0; 
 
   /* header line within buffer loop */
   do {
@@ -3106,9 +3140,9 @@
         return result;
 
       if(!k->headerline && (k->hbuflen>5)) {
-        /* make a first check that this looks like a HTTP header */
-        if(!checkhttpprefix(data, data->state.headerbuff)) {
-          /* this is not the beginning of a HTTP first header line */
+        /* make a first check that this looks like a protocol header */
+        if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
+          /* this is not the beginning of a protocol first header line */
           k->header = FALSE;
           k->badheader = HEADER_ALLBAD;
           break;
@@ -3140,8 +3174,8 @@
     if(!k->headerline) {
       /* the first read header */
       if((k->hbuflen>5) &&
-         !checkhttpprefix(data, data->state.headerbuff)) {
-        /* this is not the beginning of a HTTP first header line */
+         !checkprotoprefix(data, conn, data->state.headerbuff)) {
+        /* this is not the beginning of a protocol first header line */
         k->header = FALSE;
         if(*nread)
           /* since there's more, this is a partial bad header */
@@ -3198,7 +3232,7 @@
         k->header = FALSE; /* no more header to parse! */
 
         if((k->size == -1) && !k->chunk && !conn->bits.close &&
-           (conn->httpversion >= 11) ) {
+           (conn->httpversion >= 11) && !(conn->protocol & PROT_RTSP)) {
           /* On HTTP 1.1, when connection is not to get closed, but no
              Content-Length nor Content-Encoding chunked have been
              received, according to RFC2616 section 4.4 point 5, we
@@ -3310,6 +3344,7 @@
           Curl_pgrsSetDownloadSize(data, k->size);
           k->maxdownload = k->size;
         }
+
         /* If max download size is *zero* (nothing) we already
            have nothing and can safely return ok now! */
         if(0 == k->maxdownload)
@@ -3341,6 +3376,7 @@
       /* This is the first header, it MUST be the error code line
          or else we consider this to be the body right away! */
       int httpversion_major;
+      int rtspversion_major;
       int nc;
 #ifdef CURL_DOES_CONVERSIONS
 #define HEADER1 scratch
@@ -3365,35 +3401,51 @@
 #define HEADER1 k->p /* no conversion needed, just use k->p */
 #endif /* CURL_DOES_CONVERSIONS */
 
-      nc = sscanf(HEADER1,
-                  " HTTP/%d.%d %3d",
-                  &httpversion_major,
-                  &conn->httpversion,
-                  &k->httpcode);
-      if(nc==3) {
-        conn->httpversion += 10 * httpversion_major;
-      }
-      else {
-        /* this is the real world, not a Nirvana
-           NCSA 1.5.x returns this crap when asked for HTTP/1.1
-        */
-        nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
-        conn->httpversion = 10;
+      if(conn->protocol & PROT_HTTP) {
+        nc = sscanf(HEADER1,
+            " HTTP/%d.%d %3d",
+            &httpversion_major,
+            &conn->httpversion,
+            &k->httpcode);
+        if(nc==3) {
+          conn->httpversion += 10 * httpversion_major;
+        }
+        else {
+          /* this is the real world, not a Nirvana
+             NCSA 1.5.x returns this crap when asked for HTTP/1.1
+             */
+          nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
+          conn->httpversion = 10;
 
-        /* If user has set option HTTP200ALIASES,
-           compare header line against list of aliases
-        */
-        if(!nc) {
-          if(checkhttpprefix(data, k->p)) {
-            nc = 1;
-            k->httpcode = 200;
-            conn->httpversion = 10;
+          /* If user has set option HTTP200ALIASES,
+             compare header line against list of aliases
+             */
+          if(!nc) {
+            if(checkhttpprefix(data, k->p)) {
+              nc = 1;
+              k->httpcode = 200;
+              conn->httpversion = 10;
+            }
           }
         }
+      } else if(conn->protocol & PROT_RTSP) {
+        nc = sscanf(HEADER1,
+            " RTSP/%d.%d %3d",
+            &rtspversion_major,
+            &conn->rtspversion,
+            &k->httpcode);
+        if(nc==3) {
+          conn->rtspversion += 10 * rtspversion_major;
+          conn->httpversion = 11; /* For our purposes, RTSP acts like HTTP 1.1 */
+        } else {
+          /* TODO: do we care about the other cases here? */
+          nc = 0;
+        }
       }
 
       if(nc) {
         data->info.httpcode = k->httpcode;
+
         data->info.httpversion = conn->httpversion;
         if (!data->state.httpversion ||
             data->state.httpversion > conn->httpversion)
@@ -3571,8 +3623,8 @@
        */
       conn->bits.close = TRUE; /* close when done */
     }
-    else if(Curl_compareheader(k->p,
-                               "Transfer-Encoding:", "chunked")) {
+    else if(Curl_compareheader(k->p, "Transfer-Encoding:", "chunked") &&
+            !(conn->protocol & PROT_RTSP)) {
       /*
        * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
        * means that the server will send a series of "chunks". Each
@@ -3708,6 +3760,59 @@
         }
       }
     }
+    else if((conn->protocol & PROT_RTSP) && checkprefix("CSeq:", k->p)) {
+      /* Store the received CSeq. Match is verified in rtsp_done */
+      int nc;
+      char *temp = strdup(k->p);
+      if(!temp)
+        return CURLE_OUT_OF_MEMORY;
+      Curl_strntoupper(temp, temp, sizeof(temp));
+      nc = sscanf(temp, "CSEQ: %ld", &CSeq);
+      free(temp);
+      if(nc == 1) {
+        data->state.proto.rtsp->CSeq_recv = CSeq; /* mark the request */
+        data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
+      } else {
+        failf(data, "Unable to read the CSeq header: [%s]", k->p);
+        return CURLE_RTSP_CSEQ_ERROR;
+      }
+    }
+    else if((conn->protocol & PROT_RTSP) && checkprefix("Session:", k->p)) {
+      char *start;
+
+      /* Find the first non-space letter */
+      start = k->p + 9;
+      while(*start && ISSPACE(*start))
+        start++;
+
+      if(!start) {
+        failf(data, "Got a blank Session ID");
+      } else if(data->set.str[STRING_RTSP_SESSION_ID]) {
+        /* If the Session ID is set, then compare */
+        if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], strlen(data->set.str[STRING_RTSP_SESSION_ID]))  != 0) {
+          failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", start, data->set.str[STRING_RTSP_SESSION_ID]);
+          return CURLE_RTSP_SESSION_ERROR;
+        }
+      } else {
+        /* If the Session ID is not set, and we find it in a response, then set it */ 
+
+        /* The session ID can be an alphanumeric or a 'safe' character
+         *
+         * RFC 2326 15.1 Base Syntax:
+         * safe =  "\$" | "-" | "_" | "." | "+"
+         * */
+        char *end = start;
+        while(*end && 
+            (ISALNUM(*end) || *end == '-' || *end == '_' || *end == '.' || *end == '+' ||
+            (*end == '\\' && *(end + 1) && *(end + 1) == '$' && (++end, 1))))
+          end++;
+
+        /* Copy the id substring into a new buffer */
+        data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
+        memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
+        (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
+      }
+    }
 
     /*
      * End of header-checks. Write them to the client.
@@ -3741,4 +3846,3 @@
   return CURLE_OK;
 }
 
-#endif

=== modified file 'lib/http.h'
--- old/lib/http.h	2010-01-11 16:59:42 +0000
+++ new/lib/http.h	2010-01-11 21:13:59 +0000
@@ -35,8 +35,39 @@
                         const char *header,   /* header keyword _with_ colon */
                         const char *content); /* content string to find */
 
+char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader);
+
 char *Curl_copy_header_value(const char *h);
 
+
+/* ------------------------------------------------------------------------- */
+/*
+ * The add_buffer series of functions are used to build one large memory chunk
+ * from repeated function invokes. Used so that the entire HTTP request can
+ * be sent in one go.
+ */
+struct Curl_send_buffer {
+  char *buffer;
+  size_t size_max;
+  size_t size_used;
+};
+typedef struct Curl_send_buffer Curl_send_buffer;
+
+Curl_send_buffer *Curl_add_buffer_init(void);
+CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...);
+CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size);
+CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
+                         struct connectdata *conn,
+                         long *bytes_written,
+                         size_t included_body_bytes,
+                         int socketindex);
+ 
+
+CURLcode Curl_add_timecondition(struct SessionHandle *data, Curl_send_buffer *buf);
+CURLcode Curl_add_custom_headers(struct connectdata *conn,
+                                   Curl_send_buffer *req_buffer);
+
+
 /* ftp can use this as well */
 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
                            int tunnelsocket,

=== added file 'lib/rtsp.c'
--- old/lib/rtsp.c	1970-01-01 00:00:00 +0000
+++ new/lib/rtsp.c	2010-01-12 22:32:06 +0000
@@ -0,0 +1,580 @@
+#include "setup.h"
+
+#ifndef CURL_DISABLE_RTSP
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "easyif.h" /* for Curl_convert_... prototypes */
+#include "http.h"
+#include "url.h"
+#include "progress.h"
+#include "rtsp.h"
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * TODO (general)
+ *  -incoming server requests
+ *      -server CSeq counter
+ *  -digest authentication
+ *  -connect thru proxy
+ *  -pipelining?
+ */
+
+/*
+ * RTSP handler interface.
+ */
+const struct Curl_handler Curl_handler_rtsp = {
+  "RTSP",                               /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_rtsp,                            /* do_it */
+  Curl_rtsp_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_rtsp_connect,                    /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  Curl_rtsp_disconnect,                 /* disconnect */
+  PORT_RTSP,                            /* defport */
+  PROT_RTSP,                            /* protocol */
+};
+
+CURLcode Curl_rtsp_connect(struct connectdata *conn, bool *done)
+{
+  CURLcode httpStatus;
+  struct SessionHandle *data = conn->data;
+
+  httpStatus = Curl_http_connect(conn, done);
+
+  /* Initialize the CSeq if not already done */
+  if(data->state.rtsp_next_client_CSeq == 0)
+    data->state.rtsp_next_client_CSeq = 1;
+  if(data->state.rtsp_next_server_CSeq == 0)
+    data->state.rtsp_next_server_CSeq = 1;
+
+  conn->proto.rtspc.rtp_channel = -1;
+
+  return httpStatus;
+}
+
+CURLcode Curl_rtsp_disconnect(struct connectdata *conn) {
+  Curl_safefree(conn->proto.rtspc.rtp_buf);
+  return CURLE_OK;
+}
+
+
+CURLcode Curl_rtsp_done(struct connectdata *conn,
+    CURLcode status, bool premature)
+{
+  CURLcode httpStatus;
+  struct SessionHandle *data = conn->data;
+  long CSeq_sent;
+  long CSeq_recv;
+
+  httpStatus = Curl_http_done(conn, status, premature);
+
+  /* Check the sequence numbers */
+  CSeq_sent = data->state.proto.rtsp->CSeq_sent;
+  CSeq_recv = data->state.proto.rtsp->CSeq_recv;
+  if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
+    failf(data, "The CSeq of this request %ld did not match the response %ld",
+        CSeq_sent, CSeq_recv);
+    return CURLE_RTSP_CSEQ_ERROR;
+  } else if (data->set.rtspreq == RTSPREQ_RECEIVE && 
+      (conn->proto.rtspc.rtp_channel == -1)) {
+    infof(data, "Got a non RTP Receive with a CSeq of %ld\n", CSeq_recv);
+    /* TODO CPC: Server -> Client logic here */
+  }
+
+  return httpStatus;
+}
+
+CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
+{
+  struct SessionHandle *data = conn->data;
+  CURLcode result=CURLE_OK;
+  Curl_RtspReq rtspreq = data->set.rtspreq;
+  struct RTSP *rtsp;
+  struct HTTP *http;
+  Curl_send_buffer *req_buffer;
+  curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+  curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+
+  const char *p_request = NULL;
+  const char *p_session_id = NULL;
+  const char *p_accept = NULL;
+  const char *p_accept_encoding = NULL;
+  const char *p_range = NULL;
+  const char *p_referrer = NULL;
+  const char *p_stream_uri = NULL;
+  const char *p_transport = NULL;
+  const char *p_uagent = NULL;
+
+  *done = TRUE;
+
+  Curl_reset_reqproto(conn);
+
+  if(!data->state.proto.rtsp) {
+    /* Only allocate this struct if we don't already have it! */
+
+    rtsp = calloc(sizeof(struct RTSP), 1);
+    if(!rtsp)
+      return CURLE_OUT_OF_MEMORY;
+    data->state.proto.rtsp = rtsp;
+  }
+  else {
+    rtsp = data->state.proto.rtsp;
+  }
+
+  http = &(rtsp->http_wrapper);
+  /* Assert that no one has changed the RTSP struct in an evil way */
+  DEBUGASSERT((void *)http == (void *)rtsp);
+
+  rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
+  rtsp->CSeq_recv = 0;
+
+  /* Setup the 'p_request' pointer to the proper p_request string
+   * Since all RTSP requests are included here, there is no need to
+   * support custom requests like HTTP.
+   **/
+  DEBUGASSERT((rtspreq > RTSPREQ_NONE && rtspreq < RTSPREQ_LAST));
+  data->set.opt_no_body = TRUE; /* most requests don't contain a body */
+  switch(rtspreq) {
+    case RTSPREQ_NONE:
+      failf(data, "Got invalid RTSP request: RTSPREQ_NONE");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+      break;
+    case RTSPREQ_OPTIONS:
+      p_request = "OPTIONS";
+      break;
+    case RTSPREQ_DESCRIBE:
+      p_request = "DESCRIBE";
+      data->set.opt_no_body = FALSE;
+      break;
+    case RTSPREQ_ANNOUNCE:
+      p_request = "ANNOUNCE";
+      break;
+    case RTSPREQ_SETUP:
+      p_request = "SETUP";
+      break;
+    case RTSPREQ_PLAY:
+      p_request = "PLAY";
+      break;
+    case RTSPREQ_PAUSE:
+      p_request = "PAUSE";
+      break;
+    case RTSPREQ_TEARDOWN:
+      p_request = "TEARDOWN";
+      break;
+    case RTSPREQ_GET_PARAMETER:
+      p_request = "GET_PARAMETER";
+      data->set.opt_no_body = FALSE;
+      break;
+    case RTSPREQ_SET_PARAMETER:
+      p_request = "SET_PARAMETER";
+      break;
+    case RTSPREQ_RECORD:
+      p_request = "RECORD";
+      break;
+    case RTSPREQ_RECEIVE:
+      p_request = "";
+      /* Treat interleaved RTP as body*/
+      data->set.opt_no_body = FALSE;
+      break;
+    case RTSPREQ_LAST:
+      failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+      break;
+  }
+
+  if(rtspreq == RTSPREQ_RECEIVE) {
+    result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, -1, NULL);
+
+    return result;
+  }
+
+  p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
+  if(!p_session_id &&
+      (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
+    failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
+        p_request ? p_request : "");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  /* TODO: auth? */
+  /* TODO: proxy? */
+
+  /* Stream URI. Default to server '*' if not specified */
+  if(data->set.str[STRING_RTSP_STREAM_URI]) {
+    p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
+  } else {
+    p_stream_uri = "*";
+  }
+
+  /* Transport Header for SETUP requests */
+  p_transport = Curl_checkheaders(data, "Transport:");
+  if(rtspreq == RTSPREQ_SETUP && !p_transport) {
+    /* New Transport: setting? */
+    if(data->set.str[STRING_RTSP_TRANSPORT]) {
+      Curl_safefree(conn->allocptr.rtsp_transport);
+
+      conn->allocptr.rtsp_transport = aprintf("Transport: %s\r\n",
+          data->set.str[STRING_RTSP_TRANSPORT]);
+      if(!conn->allocptr.rtsp_transport)
+        return CURLE_OUT_OF_MEMORY;
+    } else {
+      failf(data, "Refusing to issue an RTSP SETUP without a Transport: header.");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+
+    p_transport = conn->allocptr.rtsp_transport;
+  }
+
+  /* Accept Headers for DESCRIBE requests */
+  if(rtspreq == RTSPREQ_DESCRIBE) {
+    /* Accept Header */
+    p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: application/sdp\r\n";
+
+    /* Accept-Encoding header */
+    if(!Curl_checkheaders(data, "Accept-Encoding:") &&
+        data->set.str[STRING_ENCODING]) {
+      Curl_safefree(conn->allocptr.accept_encoding);
+      conn->allocptr.accept_encoding =
+        aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+
+      if(!conn->allocptr.accept_encoding)
+        return CURLE_OUT_OF_MEMORY;
+
+      p_accept_encoding = conn->allocptr.accept_encoding;
+    }
+  }
+
+  /* Default to text/parameters for GET_PARAMETER */
+  if(rtspreq == RTSPREQ_GET_PARAMETER) {
+    p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: text/parameters\r\n";
+  }
+
+  /* The User-Agent string might have been allocated in url.c already, because
+     it might have been used in the proxy connect, but if we have got a header
+     with the user-agent string specified, we erase the previously made string
+     here. */
+  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+    Curl_safefree(conn->allocptr.uagent);
+    conn->allocptr.uagent = NULL;
+  } else if(!Curl_checkheaders(data, "User-Agent:") &&
+      data->set.str[STRING_USERAGENT]) {
+    p_uagent = conn->allocptr.uagent;
+  }
+
+  /* Referrer */
+  Curl_safefree(conn->allocptr.ref);
+  if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
+    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
+  else
+    conn->allocptr.ref = NULL;
+
+  p_referrer = conn->allocptr.ref;
+
+  /* 
+   * Range Header
+   * Only applies to PLAY, PAUSE, RECORD
+   *
+   * Go ahead and use the Range stuff supplied for HTTP
+   */
+  if(data->state.use_range && 
+      (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
+
+    /* Check to see if there is a range set in the custom headers */
+    if(!Curl_checkheaders(data, "Range:") && data->state.range) {
+      Curl_safefree(conn->allocptr.rangeline);
+      conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
+      p_range = conn->allocptr.rangeline;
+    }
+  }
+
+  /*
+   * Sanity check the custom headers
+   */
+  if(Curl_checkheaders(data, "CSeq:")) {
+    failf(data, "CSeq cannot be set as a custom header.");
+    return CURLE_RTSP_CSEQ_ERROR;
+  }
+  if(Curl_checkheaders(data, "Session:")) {
+    failf(data, "Session ID cannot be set as a custom header.");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  /* Initialize a dynamic send buffer */
+  req_buffer = Curl_add_buffer_init();
+
+  if(!req_buffer)
+    return CURLE_OUT_OF_MEMORY;
+
+  result = Curl_add_bufferf(req_buffer,
+      "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
+      "CSeq: %d \r\n", /* CSeq */
+      (p_request ? p_request : ""), p_stream_uri,
+      rtsp->CSeq_sent);
+  if(result)
+    return result;
+
+  /* 
+   * Rather than do a normal alloc line, keep the session_id unformatted
+   * to make comparison easier
+   */
+  if(p_session_id) {
+    result = Curl_add_bufferf(req_buffer, "Session: %s \r\n", p_session_id);
+    if(result)
+      return result;
+  }
+
+  /*
+   * Shared HTTP-like options
+   */
+  result = Curl_add_bufferf(req_buffer,  
+      "%s" /* transport */
+      "%s" /* accept */
+      "%s" /* accept-encoding */
+      "%s" /* range */
+      "%s" /* referrer */
+      "%s" /* user-agent */
+      ,
+      p_transport ? p_transport : "",
+      p_accept ? p_accept : "",
+      p_accept_encoding ? p_accept_encoding : "",
+      p_range ? p_range : "",
+      p_referrer ? p_referrer : "",
+      p_uagent ? p_uagent : "");
+
+  if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
+    result = Curl_add_timecondition(data, req_buffer);
+    if(result)
+      return result;
+  }
+
+  result = Curl_add_custom_headers(conn, req_buffer);
+  if(result)
+    return result;
+
+  if(rtspreq == RTSPREQ_ANNOUNCE || rtspreq == RTSPREQ_SET_PARAMETER) {
+    if(data->set.upload) {
+      putsize = data->set.infilesize;
+      data->set.httpreq = HTTPREQ_PUT;
+
+    } else {
+      postsize = (data->set.postfieldsize != -1)?
+        data->set.postfieldsize:
+        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
+      data->set.httpreq = HTTPREQ_POST;
+    }
+
+    /* As stated in the http comments, it is probably not wise to
+     * actually set a custom Content-Length in the headers */
+    if(!Curl_checkheaders(data, "Content-Length:")) {
+      result = Curl_add_bufferf(req_buffer,
+          "Content-Length: %" FORMAT_OFF_T"\r\n",
+          (data->set.upload ? putsize : postsize));
+      if(result)
+        return result;
+    }
+
+    if(rtspreq == RTSPREQ_SET_PARAMETER) {
+      if(!Curl_checkheaders(data, "Content-Type:")) {
+        result = Curl_add_bufferf(req_buffer,
+            "Content-Type: text/parameters\r\n");
+      }
+      if(result)
+        return result;
+    }
+
+    if(rtspreq == RTSPREQ_ANNOUNCE) {
+      if(!Curl_checkheaders(data, "Content-Type:")) {
+        result = Curl_add_bufferf(req_buffer,
+            "Content-Type: application/sdp\r\n");
+      }
+    }
+
+    data->state.expect100header = FALSE; /* RTSP posts are simple/small */
+  }
+
+
+  /* RTSP never allows chunked transfer */
+  data->req.forbidchunk = TRUE; 
+  /* Finish the request buffer */
+  result = Curl_add_buffer(req_buffer, "\r\n", 2);
+  if(result)
+    return result;
+
+  if(postsize > 0) {
+    result = Curl_add_buffer(req_buffer, data->set.postfields, postsize);
+    if(result)
+      return result;
+  }
+
+  /* issue the request */
+  result = Curl_add_buffer_send(req_buffer, conn,
+      &data->info.request_size, 0, FIRSTSOCKET);
+  if(result) {
+    failf(data, "Failed sending RTSP request");
+    return result;
+  }
+
+  result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
+      &http->readbytecount,
+      putsize?FIRSTSOCKET:-1,
+      putsize?&http->writebytecount:NULL);
+
+  /* Increment the CSeq on success */
+  data->state.rtsp_next_client_CSeq++;
+
+  if(http->writebytecount) {
+    /* if a request-body has been sent off, we make sure this progress is noted
+       properly */
+    Curl_pgrsSetUploadCounter(data, http->writebytecount);
+    if(Curl_pgrsUpdate(conn))
+      result = CURLE_ABORTED_BY_CALLBACK;
+  }
+
+  return result;
+
+  return CURLE_OK;
+
+}
+
+CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
+    struct connectdata *conn,
+    ssize_t *nread,
+    bool *readmore,
+    bool *done) {
+  struct SingleRequest *k = &data->req;
+  struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+
+  char *rtp; /* moving pointer to rtp data */
+  ssize_t rtp_dataleft; /* how much data left to parse in this round */
+  char *scratch;
+
+  if(rtspc->rtp_buf) {
+    /* There was some leftover data the last time. Merge buffers */
+    rtspc->rtp_buf = realloc(rtspc->rtp_buf, rtspc->rtp_bufsize + *nread);
+    if(!rtspc->rtp_buf)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
+    rtspc->rtp_bufsize += *nread;
+    rtp = rtspc->rtp_buf;
+    rtp_dataleft = rtspc->rtp_bufsize;
+  } else {
+    /* Just parse the request buffer directly */
+    rtp = k->str;
+    rtp_dataleft = *nread;
+  }
+
+  if(rtp_dataleft == 0 || rtp[0] != 0x24) {
+    return  CURLE_OK;
+  }
+
+  while((rtp_dataleft > 0) &&
+      (rtp[0] == 0x24)) {
+    if(rtp_dataleft > 4) {
+      char channel;
+      int rtp_length;
+
+      /* Parse the header */
+      /* The channel identifier immediately follows and is 1 byte */
+      channel = rtp[1];
+      rtspc->rtp_channel = (int) channel;
+
+      /* The length is two bytes */
+      rtp_length = ntohs( *((unsigned short *)(&rtp[2])) );
+
+      if(rtp_dataleft < rtp_length + 4) {
+        /* Need more - incomplete payload*/
+        *readmore = TRUE;
+        break;
+      } else {
+        /* We have the full RTP interleaved packet */
+        Curl_client_write(conn, CLIENTWRITE_RTP,
+            &rtp[4],
+            rtp_length);
+
+        /* Update progress */
+        k->bytecount += rtp_length + 4;
+        Curl_pgrsSetDownloadCounter(data, k->bytecount);
+        if(k->bytecountp)
+          *k->bytecountp = k->bytecount;
+
+        /* Move forward in the buffer */
+        rtp_dataleft -= rtp_length + 4;
+        rtp += rtp_length + 4;
+
+        if(data->set.rtspreq == RTSPREQ_RECEIVE) {
+          /* If we are in a passive receive, give control back
+           * to the app as often as we can.
+           *
+           * Otherwise, keep chugging along until we get RTSP data
+           */
+          k->keepon &= ~KEEP_RECV;
+          *done = TRUE;
+        }
+      } 
+    } else {
+      /* Need more - incomplete header */
+      *readmore = TRUE;
+      break;
+    }
+  }
+
+  if(*done || *readmore) {
+    if(rtp_dataleft != 0 && rtp[0] == 0x24) {
+      infof(data, "RTP Rewinding %zu %s %s\n", rtp_dataleft,
+          *done ? "DONE " : "",
+          *readmore ? "READMORE" : "");
+
+      /* Store the incomplete RTP packet for a "rewind" */
+      scratch = malloc(rtp_dataleft);
+      if(!scratch)
+        return CURLE_OUT_OF_MEMORY;
+      memcpy(scratch, rtp, rtp_dataleft);
+      Curl_safefree(rtspc->rtp_buf);
+      rtspc->rtp_buf = scratch;
+      rtspc->rtp_bufsize = rtp_dataleft;
+      return CURLE_OK;
+    }
+  } else {
+    /* RTP followed by RTSP */
+    if(rtp_dataleft == 0) {
+      /* Need more */
+      *readmore = TRUE;
+    } else {
+      /* Fix up k->str to point just after the last RTP packet */
+      k->str += *nread - rtp_dataleft;
+
+      /* rtp may point into the leftover buffer, but at this point
+       * it is somewhere in the merged data from k->str. */
+      DEBUGASSERT(k->str[0] == rtp[0]);
+
+      DEBUGASSERT(rtp_dataleft < *nread); /* sanity check */
+
+      *nread = rtp_dataleft;
+    }
+  }
+
+  /* If we get here, we have finished with the leftover/merge buffer */
+  Curl_safefree(rtspc->rtp_buf);
+  rtspc->rtp_buf = NULL;
+  rtspc->rtp_bufsize = 0;
+
+  /* TODO CPC: Could implement parsing logic for Server->Client requests here */
+
+  return CURLE_OK;
+}
+
+
+#endif /* CURL_DISABLE_RTSP */
+

=== added file 'lib/rtsp.h'
--- old/lib/rtsp.h	1970-01-01 00:00:00 +0000
+++ new/lib/rtsp.h	2010-01-12 21:34:34 +0000
@@ -0,0 +1,53 @@
+#ifndef __RTSP_H_
+#define __RTSP_H_
+
+#ifndef CURL_DISABLE_RTSP
+
+
+extern const struct Curl_handler Curl_handler_rtsp;
+
+/*
+ * RTSP Connection data
+ *
+ * Currently, only used for tracking incomplete RTP data reads
+ */
+struct rtsp_conn {
+    char *rtp_buf;
+    ssize_t rtp_bufsize;
+    int rtp_channel;
+};
+
+/****************************************************************************
+ * RTSP unique setup
+ ***************************************************************************/
+struct RTSP {
+    /* 
+     * http_wrapper MUST be the first element of this structure for the wrap
+     * logic to work. In this way, we get a cheap polymorphism because
+     * &(data->state.proto.rtsp) == &(data->state.proto.http) per the C spec
+     *
+     * HTTP functions can safely treat this as an HTTP struct, but RTSP aware
+     * functions can also index into the later elements.
+     */
+    struct HTTP http_wrapper; /*wrap HTTP to do the heavy lifting */
+
+    long CSeq_sent; /* CSeq of this request */
+    long CSeq_recv; /* CSeq received */
+};
+
+
+CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
+                                 struct connectdata *conn,
+                                 ssize_t *nread,
+                                 bool *readmore,
+                                 bool *done);
+
+
+/* protocol-specific functions set up to be called by the main engine */
+CURLcode Curl_rtsp(struct connectdata *conn, bool *done);
+CURLcode Curl_rtsp_done(struct connectdata *conn, CURLcode, bool premature);
+CURLcode Curl_rtsp_connect(struct connectdata *conn, bool *done);
+CURLcode Curl_rtsp_disconnect(struct connectdata *conn);
+
+#endif /* CURL_DISABLE_RTSP */
+#endif /* __RTSP_H_ */

=== modified file 'lib/sendf.c'
--- old/lib/sendf.c	2009-11-19 15:52:21 +0000
+++ new/lib/sendf.c	2010-01-12 21:34:02 +0000
@@ -43,6 +43,7 @@
 #include "sslgen.h"
 #include "ssh.h"
 #include "multiif.h"
+#include "rtsp.h"
 
 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
 #include <curl/mprintf.h>
@@ -455,6 +456,25 @@
     }
   }
 
+  if(type & CLIENTWRITE_RTP) {
+    if((conn->protocol & PROT_RTSP) && data->set.fwrite_rtp) {
+      /* Write the RTP data to user supplied channel pointer */
+      wrote = data->set.fwrite_rtp(ptr, 1, len, data->set.rtp_out, conn->proto.rtspc.rtp_channel);
+
+      if(CURL_WRITEFUNC_PAUSE == wrote) {
+        return pausewrite(data, type, ptr, len);
+      }
+
+      if(wrote != len) {
+        failf (data, "Failed writing RTP data");
+        return CURLE_WRITE_ERROR;
+      }
+    } else {
+      failf (data, "Got RTP data without an RTP write callback.");
+      return CURLE_WRITE_ERROR;
+    }
+  }
+
   return CURLE_OK;
 }
 

=== modified file 'lib/sendf.h'
--- old/lib/sendf.h	2010-01-11 16:59:42 +0000
+++ new/lib/sendf.h	2010-01-11 21:13:59 +0000
@@ -48,9 +48,10 @@
 
 #define failf Curl_failf
 
-#define CLIENTWRITE_BODY   1
-#define CLIENTWRITE_HEADER 2
+#define CLIENTWRITE_BODY   (1<<0)
+#define CLIENTWRITE_HEADER (1<<1)
 #define CLIENTWRITE_BOTH   (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+#define CLIENTWRITE_RTP    (1<<2)
 
 CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr,
                            size_t len);

=== modified file 'lib/setup.h'
--- old/lib/setup.h	2010-01-11 16:59:42 +0000
+++ new/lib/setup.h	2010-01-11 21:13:59 +0000
@@ -169,6 +169,7 @@
 #  define CURL_DISABLE_TELNET
 #  define CURL_DISABLE_DICT
 #  define CURL_DISABLE_FILE
+#  define CURL_DISABLE_RTSP
 #endif
 
 /* ================================================================ */

=== modified file 'lib/strerror.c'
--- old/lib/strerror.c	2010-01-11 16:59:42 +0000
+++ new/lib/strerror.c	2010-01-11 21:14:00 +0000
@@ -270,6 +270,12 @@
   case CURLE_AGAIN:
     return "Socket not ready for send/recv";
 
+  case CURLE_RTSP_RETURNED_ERROR:
+    return "RTSP response code said error";
+
+  case CURLE_RTSP_CSEQ_ERROR:
+    return "RTSP CSeq mismatch or invalid CSeq";
+
     /* error codes not used by current libcurl */
   case CURLE_OBSOLETE4:
   case CURLE_OBSOLETE10:

=== modified file 'lib/transfer.c'
--- old/lib/transfer.c	2010-01-11 16:59:42 +0000
+++ new/lib/transfer.c	2010-01-12 21:40:43 +0000
@@ -103,6 +103,7 @@
 #include "select.h"
 #include "multiif.h"
 #include "easyif.h" /* for Curl_convert_to_network prototype */
+#include "rtsp.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -124,7 +125,7 @@
 #ifdef CURL_DOES_CONVERSIONS
   bool sending_http_headers = FALSE;
 
-  if((conn->protocol&PROT_HTTP) &&
+  if((conn->protocol&(PROT_HTTP|PROT_RTSP)) &&
      (data->state.proto.http->sending == HTTPSEND_REQUEST)) {
     /* We're sending the HTTP request headers, not the data.
        Remember that so we don't re-translate them into garbage. */
@@ -368,6 +369,7 @@
   CURLcode result = CURLE_OK;
   ssize_t nread; /* number of bytes read */
   bool is_empty_data = FALSE;
+  bool readmore = FALSE; /* used by RTP to signal for more data */
 
   *done = FALSE;
 
@@ -435,6 +437,19 @@
        in the flow below before the actual storing is done. */
     k->str = k->buf;
 
+#ifndef CURL_DISABLE_RTSP
+    if(conn->protocol & PROT_RTSP) {
+        readmore = FALSE;
+        result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore, done);
+        if(result)
+            return result;
+        if(readmore)
+            break;
+        if(*done)
+            return CURLE_OK;
+    }
+#endif
+
 #ifndef CURL_DISABLE_HTTP
     /* Since this is a two-state thing, we check if we are parsing
        headers at the moment or not. */
@@ -456,11 +471,12 @@
        is non-headers. */
     if(k->str && !k->header && (nread > 0 || is_empty_data)) {
 
+
 #ifndef CURL_DISABLE_HTTP
       if(0 == k->bodywrites && !is_empty_data) {
         /* These checks are only made the first time we are about to
            write a piece of the body */
-        if(conn->protocol&PROT_HTTP) {
+        if(conn->protocol&(PROT_HTTP|PROT_RTSP)) {
           /* HTTP-only checks */
 
           if(data->req.newurl) {
@@ -747,7 +763,7 @@
           break;
         }
 
-        if(conn->protocol&PROT_HTTP) {
+        if(conn->protocol&(PROT_HTTP|PROT_RTSP)) {
           if(data->state.proto.http->sending == HTTPSEND_REQUEST)
             /* We're sending the HTTP request headers, not the data.
                Remember that so we don't change the line endings. */
@@ -846,7 +862,7 @@
 
     /* write to socket (send away data) */
     result = Curl_write(conn,
-                        conn->writesockfd,         /* socket to send to */
+                        conn->writesockfd,     /* socket to send to */
                         data->req.upload_fromhere, /* buffer pointer */
                         data->req.upload_present,  /* buffer size */
                         &bytes_written);           /* actually sent */
@@ -1825,14 +1841,15 @@
 
   /* if we're talking upload, we can't do the checks below, unless the protocol
      is HTTP as when uploading over HTTP we will still get a response */
-  if(data->set.upload && !(conn->protocol&PROT_HTTP))
+  if(data->set.upload && !(conn->protocol&(PROT_HTTP|PROT_RTSP)))
     return CURLE_OK;
 
   if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
       ((data->req.bytecount +
-      data->req.headerbytecount == 0) &&
-     conn->bits.reuse &&
-     !data->set.opt_no_body)) {
+        data->req.headerbytecount == 0) &&
+        conn->bits.reuse &&
+        !data->set.opt_no_body &&
+        data->set.rtspreq != RTSPREQ_RECEIVE)) {
     /* We got no data, we attempted to re-use a connection and yet we want a
        "body". This might happen if the connection was left alive when we were
        done using it before, but that was closed when we wanted to read from

=== modified file 'lib/url.c'
--- old/lib/url.c	2010-01-11 16:59:42 +0000
+++ new/lib/url.c	2010-01-12 21:35:39 +0000
@@ -136,6 +136,7 @@
 #include "inet_ntop.h"
 #include "http_ntlm.h"
 #include "socks.h"
+#include "rtsp.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -226,6 +227,10 @@
 #endif
 #endif
 
+#ifndef CURL_DISABLE_RTSP
+  &Curl_handler_rtsp,
+#endif
+
   (struct Curl_handler *) NULL
 };
 
@@ -699,6 +704,7 @@
   set->maxredirs = -1;       /* allow any amount by default */
 
   set->httpreq = HTTPREQ_GET; /* Default HTTP request */
+  set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
   set->ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
   set->ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
   set->ftp_use_pret = FALSE;  /* mainly useful for drftpd servers */
@@ -2323,6 +2329,121 @@
     data->set.mail_rcpt = va_arg(param, struct curl_slist *);
     break;
 
+  case CURLOPT_RTSP_REQUEST:
+    {
+      /* 
+       * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
+       * Would this be better if the RTSPREQ_* were just moved into here?
+       */
+      long curl_rtspreq = va_arg(param, long);
+      long rtspreq = RTSPREQ_NONE;
+      switch(curl_rtspreq) {
+        case CURL_RTSPREQ_OPTIONS:
+          rtspreq = RTSPREQ_OPTIONS;
+          break;
+
+        case CURL_RTSPREQ_DESCRIBE:
+          rtspreq = RTSPREQ_DESCRIBE;
+          break;
+
+        case CURL_RTSPREQ_ANNOUNCE:
+          rtspreq = RTSPREQ_ANNOUNCE;
+          break;
+
+        case CURL_RTSPREQ_SETUP:
+          rtspreq = RTSPREQ_SETUP;
+          break;
+
+        case CURL_RTSPREQ_PLAY:
+          rtspreq = RTSPREQ_PLAY;
+          break;
+
+        case CURL_RTSPREQ_PAUSE:
+          rtspreq = RTSPREQ_PAUSE;
+          break;
+
+        case CURL_RTSPREQ_TEARDOWN:
+          rtspreq = RTSPREQ_TEARDOWN;
+          break;
+
+        case CURL_RTSPREQ_GET_PARAMETER:
+          rtspreq = RTSPREQ_GET_PARAMETER;
+          break;
+
+        case CURL_RTSPREQ_SET_PARAMETER:
+          rtspreq = RTSPREQ_SET_PARAMETER;
+          break;
+
+        case CURL_RTSPREQ_RECORD:
+          rtspreq = RTSPREQ_RECORD;
+          break;
+
+        case CURL_RTSPREQ_RECEIVE:
+          rtspreq = RTSPREQ_RECEIVE;
+          break;
+        default:
+          rtspreq = RTSPREQ_NONE;
+      }
+
+      data->set.rtspreq = rtspreq;
+    break;
+    }
+
+
+  case CURLOPT_RTSP_SESSION_ID:
+    /*
+     * Set the RTSP Session ID manually. Useful if the application is
+     * resuming a previously established RTSP session
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_STREAM_URI:
+    /*
+     * Set the Stream URI for the RTSP request. Unless the request is
+     * for generic server options, the application will need to set this.
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_TRANSPORT:
+    /*
+     * The content of the Transport: header for the RTSP request
+     */
+    result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
+                       va_arg(param, char *));
+    break;
+
+  case CURLOPT_RTSP_VERSION:
+    /*
+     * Change the RTSP protocol version
+     */
+    data->set.rtspversion = va_arg(param, long);
+    break;
+  
+  case CURLOPT_RTSP_CLIENT_CSEQ:
+    /*
+     * Set the CSEQ number to issue for the next RTSP request. Useful if the
+     * application is resuming a previously broken connection. The CSEQ
+     * will increment from this new number henceforth.
+     */
+    data->state.rtsp_next_client_CSeq = va_arg(param, long);
+    break;
+
+  case CURLOPT_RTSP_SERVER_CSEQ:
+    /* Same as the above, but for server-initiated requests */
+    data->state.rtsp_next_client_CSeq = va_arg(param, long);
+    break;
+
+  case CURLOPT_RTPDATA:
+    data->set.rtp_out = va_arg(param, void *);
+    break;
+  case CURLOPT_RTPFUNCTION:
+    /* Set the user defined RTP write function */
+    data->set.fwrite_rtp = va_arg(param, curl_rtp_write_callback);
+    break;
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_FAILED_INIT; /* correct this */
@@ -2360,6 +2481,7 @@
   Curl_safefree(conn->allocptr.ref);
   Curl_safefree(conn->allocptr.host);
   Curl_safefree(conn->allocptr.cookiehost);
+  Curl_safefree(conn->allocptr.rtsp_transport);
   Curl_safefree(conn->trailer);
   Curl_safefree(conn->host.rawalloc); /* host name buffer */
   Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
@@ -2499,6 +2621,37 @@
 
   return ret_val;
 }
+/*
+ * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
+ * want to block the application forever while receiving a stream. Therefore, 
+ * we cannot assume that an RTSP socket is dead just because it is readable.
+ * 
+ * Instead, if it is readable, run Curl_getconnectinfo() to peek at the socket
+ * and distinguish between closed and data.
+ */
+static bool RTSPConnIsDead(struct connectdata *check)
+{
+  int sval;
+  bool ret_val = TRUE;
+
+  sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0);
+  if(sval == 0) {
+    /* timeout */
+    ret_val = FALSE;
+  } else if (sval & CURL_CSELECT_ERR) {
+    /* socket is in an error state */
+    ret_val = TRUE;
+  } else if (sval & CURL_CSELECT_IN) {
+    /* readable with no error. could be closed or could be alive */
+    long connectinfo = 0;
+    Curl_getconnectinfo(check->data, &connectinfo, &check);
+    if(connectinfo != -1) {
+      ret_val = FALSE;
+    }
+  }
+
+  return ret_val;
+}
 
 static bool IsPipeliningPossible(const struct SessionHandle *handle)
 {
@@ -2664,7 +2817,18 @@
       /* The check for a dead socket makes sense only if there are no
          handles in pipeline and the connection isn't already marked in
          use */
-      bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+      bool dead;
+#ifndef CURL_DISABLE_RTSP
+      if(check->protocol & PROT_RTSP) {
+        /* RTSP is a special case due to RTP interleaving */
+        dead = RTSPConnIsDead(check);
+      } else  {
+        dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+      }
+#else
+        dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+#endif /*CURL_DISABLE_RTSP*/
+
       if(dead) {
         check->data = data;
         infof(data, "Connection #%d seems to be dead!\n", i);

=== modified file 'lib/urldata.h'
--- old/lib/urldata.h	2010-01-11 16:59:42 +0000
+++ new/lib/urldata.h	2010-01-12 21:35:17 +0000
@@ -43,6 +43,7 @@
 #define PORT_POP3S 995
 #define PORT_SMTP 25
 #define PORT_SMTPS 465 /* sometimes called SSMTP */
+#define PORT_RTSP 554
 
 #define DICT_MATCH "/MATCH:"
 #define DICT_MATCH2 "/M:"
@@ -147,6 +148,7 @@
 #include "file.h"
 #include "ssh.h"
 #include "http.h"
+#include "rtsp.h"
 
 #ifdef HAVE_GSSAPI
 # ifdef HAVE_GSSGNU
@@ -510,7 +512,7 @@
   bool content_range;           /* set TRUE if Content-Range: was found */
   curl_off_t offset;            /* possible resume offset read from the
                                    Content-Range: header */
-  int httpcode;                 /* error code from the 'HTTP/1.? XXX' line */
+  int httpcode;                 /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */
   struct timeval start100;      /* time stamp to wait for the 100 code from */
   enum expect100 exp100;        /* expect 100 continue state */
 
@@ -672,8 +674,9 @@
 #define PROT_POP3S   CURLPROTO_POP3S
 #define PROT_SMTP    CURLPROTO_SMTP
 #define PROT_SMTPS   CURLPROTO_SMTPS
+#define PROT_RTSP    CURLPROTO_RTSP
 
-/* (1<<17) is currently the highest used bit in the public bitmask. We make
+/* (1<<18) is currently the highest used bit in the public bitmask. We make
    sure we use "private bits" above the public ones to make things easier. */
 
 #define PROT_EXTMASK 0xfffff
@@ -719,6 +722,7 @@
   curl_proxytype proxytype; /* what kind of proxy that is in use */
 
   int httpversion;              /* the HTTP version*10 reported by the server */
+  int rtspversion;              /* the RTSP version*10 reported by the server */
 
   struct timeval now;     /* "current" time */
   struct timeval created; /* creation time */
@@ -750,6 +754,7 @@
     char *ref; /* free later if not NULL! */
     char *host; /* free later if not NULL */
     char *cookiehost; /* free later if not NULL */
+    char *rtsp_transport; /* free later if not NULL */
   } allocptr;
 
   int sec_complete; /* if kerberos is enabled for this connection */
@@ -825,6 +830,7 @@
     struct imap_conn imapc;
     struct pop3_conn pop3c;
     struct smtp_conn smtpc;
+    struct rtsp_conn rtspc;
   } proto;
 
   int cselect_bits; /* bitmask of socket events */
@@ -844,7 +850,7 @@
  * Struct to keep statistical and informational data.
  */
 struct PureInfo {
-  int httpcode;  /* Recent HTTP or FTP response code */
+  int httpcode;  /* Recent HTTP, FTP, or RTSP response code */
   int httpproxycode; /* response code from proxy when received separate */
   int httpversion; /* the http version number X.Y = X*10+Y */
   long filetime; /* If requested, this is might get set. Set to -1 if the time
@@ -915,6 +921,22 @@
   HTTPREQ_LAST /* last in list */
 } Curl_HttpReq;
 
+typedef enum {
+    RTSPREQ_NONE, /* first in list */
+    RTSPREQ_OPTIONS,
+    RTSPREQ_DESCRIBE,
+    RTSPREQ_ANNOUNCE,
+    RTSPREQ_SETUP,
+    RTSPREQ_PLAY,
+    RTSPREQ_PAUSE,
+    RTSPREQ_TEARDOWN,
+    RTSPREQ_GET_PARAMETER,
+    RTSPREQ_SET_PARAMETER,
+    RTSPREQ_RECORD,
+    RTSPREQ_RECEIVE,
+    RTSPREQ_LAST /* last in list */
+} Curl_RtspReq;
+
 /*
  * Values that are generated, temporary or calculated internally for a
  * "session handle" must be defined within the 'struct UrlState'.  This struct
@@ -1065,6 +1087,11 @@
                   this syntax. */
   curl_off_t resume_from; /* continue [ftp] transfer from here */
 
+  /* This RTSP state information survives requests and connections */
+  long rtsp_next_client_CSeq; /* the session's next client CSeq */
+  long rtsp_next_server_CSeq; /* the session's next server CSeq */
+  long rtsp_CSeq_recv; /* most recent CSeq received */
+
   /* Protocol specific data.
    *
    *************************************************************************
@@ -1075,6 +1102,7 @@
   union {
     struct HTTP *http;
     struct HTTP *https;  /* alias, just for the sake of being more readable */
+    struct RTSP *rtsp;
     struct FTP *ftp;
     /* void *tftp;    not used */
     struct FILEPROTO *file;
@@ -1125,7 +1153,7 @@
   STRING_CERT_TYPE,       /* format for certificate (default: PEM)*/
   STRING_COOKIE,          /* HTTP cookie string to send */
   STRING_COOKIEJAR,       /* dump all cookies to this file */
-  STRING_CUSTOMREQUEST,   /* HTTP/FTP request/method to use */
+  STRING_CUSTOMREQUEST,   /* HTTP/FTP/RTSP request/method to use */
   STRING_DEVICE,          /* local network interface/address to use */
   STRING_ENCODING,        /* Accept-Encoding string */
   STRING_FTP_ACCOUNT,     /* ftp account data */
@@ -1156,6 +1184,9 @@
   STRING_PROXYPASSWORD,   /* Proxy <password>, if used */
   STRING_NOPROXY,         /* List of hosts which should not use the proxy, if
                              used */
+  STRING_RTSP_SESSION_ID, /* Session ID to use */
+  STRING_RTSP_STREAM_URI, /* Stream URI for this request */
+  STRING_RTSP_TRANSPORT,  /* Transport for this session */
 #ifdef USE_LIBSSH2
   STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
   STRING_SSH_PUBLIC_KEY,  /* path to the public key file for auth */
@@ -1181,6 +1212,7 @@
   void *out;         /* the fetched file goes here */
   void *in;          /* the uploaded file is read from here */
   void *writeheader; /* write the header to this if non-NULL */
+  void *rtp_out;     /* write RTP to this if non-NULL */
   long use_port;     /* which port to use (when not using default) */
   long httpauth;     /* what kind of HTTP authentication to use (bitmask) */
   long proxyauth;    /* what kind of proxy authentication to use (bitmask) */
@@ -1202,6 +1234,7 @@
                          'localport' one can't be bind()ed */
   curl_write_callback fwrite_func;   /* function that stores the output */
   curl_write_callback fwrite_header; /* function that stores headers */
+  curl_rtp_write_callback fwrite_rtp;    /* function that stores interleaved RTP */
   curl_read_callback fread_func;     /* function that reads the input */
   curl_progress_callback fprogress;  /* function for progress information */
   curl_debug_callback fdebug;      /* function that write informational data */
@@ -1338,6 +1371,9 @@
   long socks5_gssapi_nec; /* flag to support nec socks5 server */
 #endif
   struct curl_slist *mail_rcpt; /* linked list of mail recipients */
+  /* Common RTSP header options */
+  Curl_RtspReq rtspreq; /* RTSP request type */
+  long rtspversion; /* like httpversion, for RTSP */
 };
 
 struct Names {

=== modified file 'lib/version.c'
--- old/lib/version.c	2010-01-11 16:59:42 +0000
+++ new/lib/version.c	2010-01-11 21:14:00 +0000
@@ -143,6 +143,9 @@
 #ifndef CURL_DISABLE_FILE
   "file",
 #endif
+#ifndef CURL_DISABLE_RTSP
+  "rtsp",
+#endif
 
 #ifdef USE_SSL
 #ifndef CURL_DISABLE_HTTP

=== added directory 'rtsp-wip'
=== added file 'rtsp-wip/Makefile'
--- old/rtsp-wip/Makefile	1970-01-01 00:00:00 +0000
+++ new/rtsp-wip/Makefile	2010-01-11 21:14:00 +0000
@@ -0,0 +1,12 @@
+all: rtsp-test
+CFLAGS=-Wall -g -I../include/
+LIBS += -lcurl
+CURL_LIBDIR=../lib/.libs/
+LDFLAGS = -L$(CURL_LIBDIR) -Wl,-rpath-link,$(CURL_LIBDIR) -Wl,-rpath,$(CURL_LIBDIR)
+
+rtsp-test: rtsp-test.c
+	$(CC) $(CFLAGS) $(LDFLAGS) rtsp-test.c -o rtsp-test $(LIBS)
+
+clean:
+	rm rtsp-test
+

=== added file 'rtsp-wip/rtsp-test.c'
--- old/rtsp-wip/rtsp-test.c	1970-01-01 00:00:00 +0000
+++ new/rtsp-wip/rtsp-test.c	2010-01-12 20:22:49 +0000
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <curl/curl.h>
+#include <curl/easy.h>
+
+static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  size_t retcode;
+ 
+  /* in real-world cases, this would probably get this data differently
+     as this fread() stuff is exactly what the library already would do
+     by default internally */ 
+  if(stream == NULL) {
+        fprintf(stderr, "**********got NULL stream\n");
+        abort();
+  }
+  retcode = fread(ptr, size, nmemb, stream);
+ 
+  fprintf(stderr, "*** We read %zu bytes from file\n", retcode);
+ 
+  return retcode;
+}
+
+static size_t rtp_write(void *ptr, size_t size, size_t nmemb, void *stream, int channel) {
+  fprintf(stderr, "######Got RTP callback with total size %zu on channel %d\n", size * nmemb, channel);
+  fwrite(ptr, size, nmemb, stream);
+  fwrite("\n\n", 1, 2, stream);
+  return size * nmemb;
+}
+
+
+int main(void) {
+  CURL *curl;
+  CURLcode res;
+ 
+  curl_global_init(CURL_GLOBAL_ALL);
+  curl = curl_easy_init();
+  if(curl) {
+    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //turn on debug
+    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 8);
+    curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
+
+    curl_easy_setopt(curl, CURLOPT_URL,             "rtsp://10.9.5.74:554");
+    curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, "rtsp://10.9.5.74:554/sample_300kbit.mp4");
+
+    struct curl_slist *custom_headers=NULL;
+    custom_headers = curl_slist_append(custom_headers, "Awesomeness: 42");
+    curl_easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers);
+
+    //curl_easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, "fake session");
+
+    curl_easy_setopt(curl, CURLOPT_USERAGENT, "Agent 007");
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
+    res = curl_easy_perform(curl); //OPTIONS
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
+    custom_headers = curl_slist_append(custom_headers, "Accept: application/sdp");
+    curl_easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers);
+
+    res = curl_easy_perform(curl); //DESCRIBE
+
+    curl_easy_setopt(curl, CURLOPT_RTSPHEADER, NULL);
+    curl_slist_free_all(custom_headers);
+
+    FILE *rtp = fopen("rtp.out", "w+");
+    curl_easy_setopt(curl, CURLOPT_RTPDATA, rtp); 
+    curl_easy_setopt(curl, CURLOPT_RTPFUNCTION, rtp_write);
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1");
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, "rtsp://10.9.5.74/sample_300kbit.mp4/trackid=3");
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
+
+    res = curl_easy_perform(curl); //SETUP
+
+    //curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
+    char *rtsp_session_id;
+    long cseq;
+    curl_easy_getinfo(curl, CURLINFO_RTSP_SESSION_ID, &rtsp_session_id);
+    curl_easy_getinfo(curl, CURLINFO_RTSP_CLIENT_CSEQ, &cseq);
+
+    fprintf(stderr, "CSeq: %ld Got RTSP Session ID %s\n", cseq, rtsp_session_id);
+    rtsp_session_id = NULL; //not valid after next curl call
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SET_PARAMETER);
+    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "fooparam: foostuff\r\n");
+    res = curl_easy_perform(curl); //SET_PARAMETER
+
+
+#if 0
+    int sdp = open("sdp.txt", O_RDONLY);
+    struct stat file_info;
+    fstat(sdp, &file_info);
+    FILE *sdpf = fdopen(sdp, "rb");
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE);
+
+    curl_easy_setopt(curl, CURLOPT_READDATA, sdpf);
+    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) file_info.st_size);
+    res = curl_easy_perform(curl); //ANNOUNCE
+    curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);
+    curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
+    fclose(sdpf);
+    close(sdp);
+#endif
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
+    res = curl_easy_perform(curl); //PLAY
+
+#if 1
+    int i;
+    for(i = 0; i  < 5; i++) {
+      fprintf(stderr, "\n\n LOOPY LOOP!\n\n");
+      curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
+      res = curl_easy_perform(curl);
+    }
+#else
+    sleep(1);
+#endif
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PAUSE);
+    res = curl_easy_perform(curl); //PAUSE
+
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
+    res = curl_easy_perform(curl); //PLAY
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SET_PARAMETER);
+    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "barparam: barstuff\r\n");
+    res = curl_easy_perform(curl); //SET_PARAMETER
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999);
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PAUSE);
+    res = curl_easy_perform(curl); //PAUSE
+
+    curl_easy_getinfo(curl, CURLINFO_RTSP_CSEQ_RECV, &cseq);
+    fprintf(stderr, "This server %s us travel in time!\n", 
+            (cseq == 999 ? "let's" : "does NOT let"));
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN);
+    res = curl_easy_perform(curl); //TEARDOWN
+ 
+    /* always cleanup */ 
+    curl_easy_cleanup(curl);
+  }
+
+  curl_global_cleanup();
+  return 0;
+}
+

=== added file 'rtsp-wip/sdp.txt'
--- old/rtsp-wip/sdp.txt	1970-01-01 00:00:00 +0000
+++ new/rtsp-wip/sdp.txt	2010-01-11 21:14:00 +0000
@@ -0,0 +1,12 @@
+v=0
+o=mhandley 2890844526 2890845468 IN IP4 126.16.64.4
+s=SDP Seminar
+i=A Seminar on the session description protocol
+u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
+e=mjh@isi.edu (Mark Handley)
+c=IN IP4 224.2.17.12/127
+t=2873397496 2873404696
+a=recvonly
+m=audio 3456 RTP/AVP 0
+m=video 2232 RTP/AVP 31
+

