=== modified file 'lib/rtsp.c'
Index: curl/lib/rtsp.c
===================================================================
--- curl.orig/lib/rtsp.c	2010-01-27 16:25:37.000000000 -0500
+++ curl/lib/rtsp.c	2010-01-27 17:19:28.000000000 -0500
@@ -127,6 +127,10 @@
   long CSeq_sent;
   long CSeq_recv;
 
+  /* Bypass HTTP empty-reply checks on receive */
+  if(data->set.rtspreq == RTSPREQ_RECEIVE)
+    premature = TRUE;
+
   httpStatus = Curl_http_done(conn, status, premature);
 
   /* Check the sequence numbers */
@@ -139,7 +143,7 @@
   }
   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);
+    infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
     /* TODO CPC: Server -> Client logic here */
   }
 
@@ -376,7 +380,7 @@
   result =
     Curl_add_bufferf(req_buffer,
                      "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
-                     "CSeq: %d \r\n", /* CSeq */
+                     "CSeq: %d\r\n", /* CSeq */
                      (p_request ? p_request : ""), p_stream_uri,
                      rtsp->CSeq_sent);
   if(result)
@@ -387,7 +391,7 @@
    * to make comparison easier
    */
   if(p_session_id) {
-    result = Curl_add_bufferf(req_buffer, "Session: %s \r\n", p_session_id);
+    result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
     if(result)
       return result;
   }
@@ -512,8 +516,7 @@
 CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
                                  struct connectdata *conn,
                                  ssize_t *nread,
-                                 bool *readmore,
-                                 bool *done) {
+                                 bool *readmore) {
   struct SingleRequest *k = &data->req;
   struct rtsp_conn *rtspc = &(conn->proto.rtspc);
 
@@ -538,10 +541,6 @@
     rtp_dataleft = *nread;
   }
 
-  if(rtp_dataleft == 0 || rtp[0] != '$') {
-    return  CURLE_OK;
-  }
-
   while((rtp_dataleft > 0) &&
         (rtp[0] == '$')) {
     if(rtp_dataleft > 4) {
@@ -564,22 +563,18 @@
       else {
         /* We have the full RTP interleaved packet
          * Write out the header but strip the leading '$' */
-        infof(data, "CPCDEBUG: RTP write channel %d rtp_length %d\n",
-              rtspc->rtp_channel, rtp_length);
+        DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
+              rtspc->rtp_channel, rtp_length));
         result = rtp_client_write(conn, &rtp[1], rtp_length + 3);
         if(result) {
-            failf(data, "Got an error writing an RTP packet");
-            *done = TRUE;
-            *readmore = FALSE;
-            return result;
+          failf(data, "Got an error writing an RTP packet");
+          *readmore = FALSE;
+          Curl_safefree(rtspc->rtp_buf);
+          rtspc->rtp_buf = NULL;
+          rtspc->rtp_bufsize = 0;
+          return result;
         }
 
-        /* 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;
@@ -587,11 +582,8 @@
         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;
         }
       }
     }
@@ -602,41 +594,36 @@
     }
   }
 
-  if(*done || *readmore) {
-    if(rtp_dataleft != 0 && rtp[0] == '$') {
-      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;
-    }
+  if(rtp_dataleft != 0 && rtp[0] == '$') {
+    DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft,
+          *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;
+
+    /* As far as the transfer is concerned, this data is consumed */
+    *nread = 0;
+    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;
+    /* 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. */
+    /* either all of the data has been read or...
+     * rtp now points at the next byte to parse
+     */
+    if(rtp_dataleft > 0)
       DEBUGASSERT(k->str[0] == rtp[0]);
 
-      DEBUGASSERT(rtp_dataleft < *nread); /* sanity check */
+    DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
 
-      *nread = rtp_dataleft;
-    }
+    *nread = rtp_dataleft;
   }
 
   /* If we get here, we have finished with the leftover/merge buffer */
@@ -644,9 +631,6 @@
   rtspc->rtp_buf = NULL;
   rtspc->rtp_bufsize = 0;
 
-  /* TODO CPC: Could implement parsing logic for Server->Client requests
-     here */
-
   return CURLE_OK;
 }
 
@@ -701,7 +685,7 @@
       failf(data, "Unable to read the CSeq header: [%s]", header);
       return CURLE_RTSP_CSEQ_ERROR;
     }
-    }
+  }
   else if(checkprefix("Session:", header)) {
     char *start;
 
Index: curl/lib/rtsp.h
===================================================================
--- curl.orig/lib/rtsp.h	2010-01-27 16:25:37.000000000 -0500
+++ curl/lib/rtsp.h	2010-01-27 16:47:18.000000000 -0500
@@ -27,11 +27,17 @@
 
 extern const struct Curl_handler Curl_handler_rtsp;
 
+/*
+ * Parse and write out any available RTP data.
+ *
+ * nread: amount of data left after k->str. will be modified if RTP
+ *        data is parsed and k->str is moved up
+ * readmore: whether or not the RTP parser needs more data right away
+ */
 CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
                                  struct connectdata *conn,
                                  ssize_t *nread,
-                                 bool *readmore,
-                                 bool *done);
+                                 bool *readmore);
 
 
 /* protocol-specific functions set up to be called by the main engine */
Index: curl/lib/transfer.c
===================================================================
--- curl.orig/lib/transfer.c	2010-01-27 16:25:37.000000000 -0500
+++ curl/lib/transfer.c	2010-01-27 17:24:08.000000000 -0500
@@ -368,7 +368,9 @@
 {
   CURLcode result = CURLE_OK;
   ssize_t nread; /* number of bytes read */
+  size_t excess = 0; /* excess bytes read */
   bool is_empty_data = FALSE;
+  bool readmore = FALSE; /* used by RTP to signal for more data */
 
   *done = FALSE;
 
@@ -437,15 +439,13 @@
     k->str = k->buf;
 
 #ifndef CURL_DISABLE_RTSP
+    /* Check for RTP at the beginning of the data */
     if(conn->protocol & PROT_RTSP) {
-      bool readmore = FALSE;
-      result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore, done);
+      result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
       if(result)
         return result;
       if(readmore)
         break;
-      if(*done)
-        return CURLE_OK;
     }
 #endif
 
@@ -458,6 +458,18 @@
       result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
       if(result)
         return result;
+
+#ifndef CURL_DISABLE_RTSP
+      /* Check for RTP after the headers if there is no Content */
+      if(k->maxdownload <= 0 && nread > 0 && (conn->protocol & PROT_RTSP)) {
+        result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
+        if(result)
+          return result;
+        if(readmore)
+          break;
+      }
+#endif
+
       if(stop_reading)
         /* We've stopped dealing with input, get out of the do-while loop */
         break;
@@ -594,13 +606,19 @@
       }
 #endif   /* CURL_DISABLE_HTTP */
 
+      /* Account for body content stored in the header buffer */
+      if(k->badheader && !k->ignorebody) {
+        DEBUGF(infof(data, "Increasing bytecount by %" FORMAT_OFF_T" from hbuflen\n", k->hbuflen));
+        k->bytecount += k->hbuflen;
+      }
+
       if((-1 != k->maxdownload) &&
          (k->bytecount + nread >= k->maxdownload)) {
 
+        excess = (size_t)(k->bytecount + nread - k->maxdownload);
         if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
           /* The 'excess' amount below can't be more than BUFSIZE which
              always will fit in a size_t */
-          size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
           if(excess > 0 && !k->ignorebody) {
             infof(data,
                   "Rewinding stream by : %d"
@@ -611,6 +629,13 @@
                   k->size, k->maxdownload, k->bytecount, nread);
             read_rewind(conn, excess);
           }
+        } else {
+          infof(data, "Excess found in a non pipelined read:"
+                      " excess=%" FORMAT_OFF_T
+                      ", size=%" FORMAT_OFF_T
+                      ", maxdownload=%" FORMAT_OFF_T
+                      ",bytecount=%" FORMAT_OFF_T "\n",
+                      excess, k->size, k->maxdownload, k->bytecount);
         }
 
         nread = (ssize_t) (k->maxdownload - k->bytecount);
@@ -630,9 +655,17 @@
         if(k->badheader && !k->ignorebody) {
           /* we parsed a piece of data wrongly assuming it was a header
              and now we output it as body instead */
-          result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                     data->state.headerbuff,
-                                     k->hbuflen);
+
+          /* Don't let excess data pollute body writes */
+          if(k->maxdownload == -1 || k->hbuflen <= (size_t)k->maxdownload)
+            result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                data->state.headerbuff,
+                k->hbuflen);
+          else
+            result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                data->state.headerbuff,
+                k->maxdownload);
+
           if(result)
             return result;
         }
@@ -694,6 +727,25 @@
 
     } /* if(! header and data to read ) */
 
+#ifndef CURL_DISABLE_RTSP
+    if(excess > 0 && !conn->bits.stream_was_rewound &&
+        (conn->protocol & PROT_RTSP)) {
+      /* Check for RTP after the content if there is unrewound excess */
+
+      /* Parse the excess data */
+      k->str += nread;
+      nread = excess;
+
+      result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
+      if(result)
+        return result;
+
+      if(readmore)
+        k->keepon |= KEEP_RECV; /* we're not done reading */
+        break;
+    }
+#endif
+
     if(is_empty_data) {
       /* if we received nothing, the server closed the connection and we
          are done */
Index: curl/tests/FILEFORMAT
===================================================================
--- curl.orig/tests/FILEFORMAT	2009-05-30 00:34:11.000000000 -0400
+++ curl/tests/FILEFORMAT	2010-01-27 16:47:18.000000000 -0500
@@ -128,6 +128,10 @@
                 sending back anything, to allow pipelining tests
 skip: [num]     instructs the server to ignore reading this many bytes from a PUT
                 or POST request
+
+rtp: part [num] channel [num] size [num]
+               stream a fake RTP packet for the given part on a chosen channel
+               with the given payload size
 </servercmd>
 </reply>
 
Index: curl/tests/data/test567
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test567	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,50 @@
+<testcase>
+
+#Informational
+<info>
+<keywords>
+RTSP
+OPTIONS
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 1
+Public: DESCRIBE, OPTIONS, SETUP, TEARDOWN, PLAY, PAUSE
+Curl-Private: swsclose
+</data>
+</reply>
+
+# Client-Side
+<client>
+<server>
+rtsp
+</server>
+<tool>
+lib567
+</tool>
+
+<name>
+simple RTSP OPTIONS command
+</name>
+<command>
+rtsp://%HOSTIP:%RTSPPORT/567
+</command>
+</client>
+
+<verify>
+<protocol>
+OPTIONS rtsp://%HOSTIP:%RTSPPORT/567 RTSP/1.0
+CSeq: 1
+User-Agent: test567
+Test-Number: 567
+
+</protocol>
+</verify>
+
+</testcase>
+
Index: curl/tests/data/test568
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test568	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,103 @@
+<testcase>
+
+#Informational
+<info>
+<keywords>
+RTSP
+ANNOUNCE
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Cseq: 1
+
+</data1>
+
+<data2>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Cseq: 2
+Content-Length: 70
+Content-Type: application/sdp
+
+v=0
+s=sws SDP
+i=A fake SDP reply
+u=http://www.curl.haxx.se/fakesdp.ps
+</data2>
+<data3>
+RTSP/1.0 200 Okie Dokie
+Server: SWS/libcurl-test
+Cseq: 3
+
+</data3>
+<data4>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 4
+Curl-private: swsclose
+Informational: Empty Options Response
+
+</data4>
+</reply>
+
+# Client-Side
+<client>
+<server>
+rtsp
+</server>
+<tool>
+lib568
+</tool>
+
+<name>
+RTSP Announce (PUT and POST style) test
+</name>
+<command>
+rtsp://%HOSTIP:%RTSPPORT/568
+</command>
+</client>
+
+<verify>
+<strip>
+^If-Modified-Since:.*
+</strip>
+<protocol>
+ANNOUNCE rtsp://%HOSTIP:%RTSPPORT/5680001 RTSP/1.0
+CSeq: 1
+Content-Length: 306
+Content-Type: application/sdp
+
+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
+DESCRIBE rtsp://%HOSTIP:%RTSPPORT/5680002 RTSP/1.0
+CSeq: 2
+Accept: application/sdp
+
+ANNOUNCE rtsp://%HOSTIP:%RTSPPORT/5680003 RTSP/1.0
+CSeq: 3
+Content-Type: posty goodness
+Content-Length: 35
+
+postyfield=postystuff&project=curl
+OPTIONS rtsp://%HOSTIP:%RTSPPORT/5680004 RTSP/1.0
+CSeq: 4
+
+</protocol>
+</verify>
+
+</testcase>
+
Index: curl/tests/data/test568.sdp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test568.sdp	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,11 @@
+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
Index: curl/tests/data/test569
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test569	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,110 @@
+<testcase>
+
+#Informational
+<info>
+<keywords>
+RTSP
+SESSION_ID
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 1
+Session: 00.+1-am-aSe55ion_id\$yes-i-am\$;timeout=60
+
+</data1>
+<data2>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 2
+
+</data2>
+<data3>
+RTSP/1.0 200 OK
+Session:        \$extraspaces ignore-this-part-------;foo=bar
+CSeq: 3
+Server: SWS/libcurl-test
+
+</data3>
+<data4>
+RTSP/1.0 200 OK
+CSeq: 4
+Server: SWS/libcurl-test
+
+</data4>
+<data5>
+RTSP/1.0 200 OK
+Session: A
+Server: SWS/libcurl-test
+CSeq: 5
+Informational: Single Character
+
+</data5>
+<data6>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Curl-Private: swsclose
+CSeq: 6
+
+</data6>
+
+# Client-Side
+<client>
+<server>
+rtsp
+</server>
+<tool>
+lib569
+</tool>
+
+<name>
+RTSP Session ID parsing
+</name>
+<command>
+rtsp://%HOSTIP:%RTSPPORT/569 log/idfile.txt
+</command>
+</client>
+
+<verify>
+<strip>
+^If-Modified-Since:.*
+</strip>
+<protocol>
+SETUP rtsp://%HOSTIP:%RTSPPORT/5690001 RTSP/1.0
+CSeq: 1
+Transport: Fake/NotReal/JustATest;foo=baz
+
+TEARDOWN rtsp://%HOSTIP:%RTSPPORT/5690002 RTSP/1.0
+CSeq: 2
+Session: 00.+1-am-aSe55ion_id\$yes-i-am\$
+
+SETUP rtsp://%HOSTIP:%RTSPPORT/5690003 RTSP/1.0
+CSeq: 3
+Transport: Fake/NotReal/JustATest;foo=baz
+
+TEARDOWN rtsp://%HOSTIP:%RTSPPORT/5690004 RTSP/1.0
+CSeq: 4
+Session: \$extraspaces
+
+SETUP rtsp://%HOSTIP:%RTSPPORT/5690005 RTSP/1.0
+CSeq: 5
+Transport: Fake/NotReal/JustATest;foo=baz
+
+TEARDOWN rtsp://%HOSTIP:%RTSPPORT/5690006 RTSP/1.0
+CSeq: 6
+Session: A
+
+</protocol>
+<file name="log/idfile.txt">
+Got Session ID: [00.+1-am-aSe55ion_id\$yes-i-am\$]
+Got Session ID: [\$extraspaces]
+Got Session ID: [A]
+</file>
+</verify>
+
+</testcase>
+
Index: curl/tests/data/test570
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test570	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,77 @@
+<testcase>
+
+#Informational
+<info>
+<keywords>
+RTSP
+Mismatch checking
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 2
+Public: DESCRIBE, OPTIONS, SETUP, TEARDOWN, PLAY, PAUSE
+Informational: CSeq Mismatch
+
+</data1>
+<data2>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Session: asdf
+CSeq: 999
+
+</data2>
+<data3>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Session: asdfWRONG
+Informational: Session ID mismatch
+Curl-Private: swsclose
+CSeq: 1000
+
+</data3>
+
+</reply>
+
+# Client-Side
+<client>
+<server>
+rtsp
+</server>
+<tool>
+lib570
+</tool>
+
+<name>
+RTSP CSeq and Session Mismatch checks
+</name>
+<command>
+rtsp://%HOSTIP:%RTSPPORT/570
+</command>
+</client>
+
+<verify>
+<strip>
+^If-Modified-Since:.*
+</strip>
+<protocol>
+OPTIONS rtsp://%HOSTIP:%RTSPPORT/5700001 RTSP/1.0
+CSeq: 1
+
+SETUP rtsp://%HOSTIP:%RTSPPORT/5700002 RTSP/1.0
+CSeq: 999
+Transport: RAW/RAW/UDP;unicast;client_port=3056-3057
+
+PLAY rtsp://%HOSTIP:%RTSPPORT/5700003 RTSP/1.0
+CSeq: 1000
+Session: asdf
+
+</protocol>
+</verify>
+
+</testcase>
+
Index: curl/tests/data/test571
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/data/test571	2010-01-27 17:19:39.000000000 -0500
@@ -0,0 +1,108 @@
+<testcase>
+
+# Informational
+# Bang on RTP by
+#   1) giving an evil payload (with $)
+#   2) Giving evil content (SDP starting with $)
+#   3) packing rtp after headers, after content, and at the start
+<info>
+<keywords>
+RTSP
+RTP
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Session: asdf
+CSeq: 1
+
+</data1>
+
+<data2>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+CSeq: 2
+Session: asdf
+
+</data2>
+
+<data3>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Cseq: 3
+Content-Length: 4
+Content-Type: fake/evil
+
+$99
+</data3>
+
+<data4>
+RTSP/1.0 200 OK
+Server: SWS/libcurl-test
+Session: asdf
+CSeq: 4
+
+</data4>
+
+<servercmd>
+rtp: part 2 channel 1 size 10
+rtp: part 2 channel 0 size 500
+rtp: part 2 channel 0 size 196
+rtp: part 2 channel 0 size 124
+rtp: part 2 channel 0 size 824
+rtp: part 3 channel 1 size 10
+rtp: part 3 channel 0 size 50
+rtp: part 4 channel 0 size 798
+rtp: part 4 channel 0 size 42
+rtp: part 4 channel 1 size 30
+rtp: part 4 channel 0 size 2048
+rtp: part 4 channel 0 size 85
+rtp: part 4 channel 1 size 24
+</servercmd>
+</reply>
+
+# Client-Side
+<client>
+<server>
+rtsp
+</server>
+<tool>
+lib571
+</tool>
+
+<name>
+RTSP RTP Interleaving Test
+</name>
+<command>
+rtsp://%HOSTIP:%RTSPPORT/571 log/protofile.txt
+</command>
+</client>
+
+<verify>
+<stdout>
+RTP: message size 10, channel 1
+RTP: message size 500, channel 0
+RTP: message size 196, channel 0
+RTP: message size 124, channel 0
+RTP: message size 824, channel 0
+RTP: message size 10, channel 1
+RTP: message size 50, channel 0
+RTP: message size 798, channel 0
+RTP: message size 42, channel 0
+RTP: message size 30, channel 1
+RTP: message size 2048, channel 0
+RTP: message size 85, channel 0
+RTP: message size 24, channel 1
+</stdout>
+
+<file name="log/protofile.txt">
+$99
+</file>
+</verify>
+
+</testcase>
+
Index: curl/tests/libtest/Makefile.inc
===================================================================
--- curl.orig/tests/libtest/Makefile.inc	2009-10-30 18:38:21.000000000 -0400
+++ curl/tests/libtest/Makefile.inc	2010-01-27 17:20:43.000000000 -0500
@@ -10,7 +10,8 @@
   lib517 lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527	\
   lib529 lib530 lib532 lib533 lib536 lib537 lib540 lib541 lib542 lib543 \
   lib544 lib545 lib547 lib548 lib549 lib552 lib553 lib554 lib555 lib556 \
-  lib539 lib557 lib558 lib559 lib560 lib562 lib564 lib565 lib566
+  lib539 lib557 lib558 lib559 lib560 lib562 lib564 lib565 lib566 lib567 \
+  lib568 lib569 lib570 lib571
 
 lib500_SOURCES = lib500.c $(SUPPORTFILES)
 
@@ -131,3 +132,10 @@
 lib565_CFLAGS = -DLIB565
 
 lib566_SOURCES = lib566.c $(SUPPORTFILES)
+
+lib567_SOURCES = lib567.c $(SUPPORTFILES)
+lib568_SOURCES = lib568.c $(SUPPORTFILES)
+lib569_SOURCES = lib569.c $(SUPPORTFILES)
+lib570_SOURCES = lib570.c $(SUPPORTFILES)
+lib571_SOURCES = lib571.c $(SUPPORTFILES)
+
Index: curl/tests/libtest/lib567.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/libtest/lib567.c	2010-01-27 17:38:22.000000000 -0500
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ */
+
+#include "test.h"
+#include "memdebug.h"
+
+/*
+ * Test a simple OPTIONS request with a custom header
+ */
+int test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  struct curl_slist *custom_headers=NULL;
+
+  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if ((curl = curl_easy_init()) == NULL) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  /* Dump data to stdout for protocol verification */
+  curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
+
+  curl_easy_setopt(curl, CURLOPT_URL, URL);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL);
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
+  curl_easy_setopt(curl, CURLOPT_USERAGENT, "test567");
+
+  custom_headers = curl_slist_append(custom_headers, "Test-Number: 567");
+  curl_easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers);
+
+  res = curl_easy_perform(curl);
+
+  curl_slist_free_all(custom_headers);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return (int)res;
+}
+
Index: curl/tests/libtest/lib568.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/libtest/lib568.c	2010-01-27 16:47:18.000000000 -0500
@@ -0,0 +1,124 @@
+
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ */
+
+#include "test.h"
+#include "memdebug.h"
+#include <curl/mprintf.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+/* build request url */
+static char *suburl(const char *base, int i)
+{
+  return curl_maprintf("%s%.4d", base, i);
+}
+
+/*
+ * Test the Client->Server ANNOUNCE functionality (PUT style)
+ */
+int test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  int sdp;
+  FILE *sdpf;
+  struct stat file_info;
+  char *stream_uri;
+  int request=1;
+  struct curl_slist *custom_headers=NULL;
+
+  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if ((curl = curl_easy_init()) == NULL) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
+
+  curl_easy_setopt(curl, CURLOPT_URL, URL);
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  sdp = open("data/test568.sdp", O_RDONLY);
+  fstat(sdp, &file_info);
+  sdpf = fdopen(sdp, "rb");
+  if(sdpf == NULL) {
+    fprintf(stderr, "can't open data/test568.sdp\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+  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);
+
+  /* Do the ANNOUNCE */
+  res = curl_easy_perform(curl);
+  if(res)
+    return res;
+
+  curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);
+  fclose(sdpf);
+  close(sdp);
+
+  /* Make sure we can do a normal request now */
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
+  res = curl_easy_perform(curl);
+  if(res)
+    return res;
+
+  /* Now do a POST style one */
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  custom_headers = curl_slist_append(custom_headers, "Content-Type: posty goodness");
+  curl_easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers);
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE);
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "postyfield=postystuff&project=curl\n");
+  res = curl_easy_perform(curl);
+  if(res)
+    return res;
+
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
+  curl_easy_setopt(curl, CURLOPT_RTSPHEADER, NULL);
+  curl_slist_free_all(custom_headers);
+
+  /* Make sure we can do a normal request now */
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
+  res = curl_easy_perform(curl);
+  if(res)
+    return res;
+
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}
+
Index: curl/tests/libtest/lib569.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/libtest/lib569.c	2010-01-27 17:40:08.000000000 -0500
@@ -0,0 +1,98 @@
+
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ */
+
+#include "test.h"
+#include "memdebug.h"
+#include <curl/mprintf.h>
+
+/* build request url */
+static char *suburl(const char *base, int i)
+{
+  return curl_maprintf("%s%.4d", base, i);
+}
+
+/*
+ * Test Session ID capture
+ */
+int test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  char *stream_uri;
+  char *rtsp_session_id;
+  int request=1;
+  int i;
+  FILE *idfile;
+
+  idfile = fopen(libtest_arg2, "w");
+  if(idfile == NULL) {
+    fprintf(stderr, "couldn't open the Session ID File\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if ((curl = curl_easy_init()) == NULL) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+  curl_easy_setopt(curl, CURLOPT_URL, URL);
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
+  res = curl_easy_perform(curl);
+  if(res != CURLE_BAD_FUNCTION_ARGUMENT) {
+    fprintf(stderr, "This should have failed. Cannot setup without a Transport: header");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  /* Go through the various Session IDs */
+  for(i = 0; i < 3; i++) {
+    stream_uri = suburl(URL, request++);
+    curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+    free(stream_uri);
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
+    curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Fake/NotReal/JustATest;foo=baz");
+    res = curl_easy_perform(curl);
+    if(res)
+      return res;
+
+    curl_easy_getinfo(curl, CURLINFO_RTSP_SESSION_ID, &rtsp_session_id);
+    fprintf(idfile, "Got Session ID: [%s]\n", rtsp_session_id);
+    rtsp_session_id = NULL;
+
+    stream_uri = suburl(URL, request++);
+    curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+    free(stream_uri);
+
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN);
+    curl_easy_perform(curl);
+
+    /* Clear for the next go-round */
+    curl_easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, NULL);
+  }
+
+  fclose(idfile);
+
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return (int)res;
+}
+
Index: curl/tests/libtest/lib570.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/libtest/lib570.c	2010-01-27 17:19:19.000000000 -0500
@@ -0,0 +1,84 @@
+
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ */
+
+#include "test.h"
+#include "memdebug.h"
+#include <curl/mprintf.h>
+
+/* build request url */
+static char *suburl(const char *base, int i)
+{
+  return curl_maprintf("%s%.4d", base, i);
+}
+
+int test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  int request=1;
+  char *stream_uri;
+
+  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if ((curl = curl_easy_init()) == NULL) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
+  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+  curl_easy_setopt(curl, CURLOPT_URL, URL);
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  res = curl_easy_perform(curl);
+  if(res != CURLE_RTSP_CSEQ_ERROR) {
+    fprintf(stderr, "Failed to detect CSeq mismatch");
+    return res;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999);
+  curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RAW/RAW/UDP;unicast;client_port=3056-3057");
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  res = curl_easy_perform(curl);
+  if(res)
+    return res;
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  res = curl_easy_perform(curl);
+  if(res != CURLE_RTSP_SESSION_ERROR) {
+    fprintf(stderr, "Failed to detect a Session ID mismatch");
+  }
+
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return (int)res;
+}
+
Index: curl/tests/libtest/lib571.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ curl/tests/libtest/lib571.c	2010-01-27 17:26:17.000000000 -0500
@@ -0,0 +1,133 @@
+
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ */
+
+#include "test.h"
+#include "memdebug.h"
+#include <curl/mprintf.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define RTP_DATA_SIZE 12
+static const char *RTP_DATA = "$_1234\n\0asdf";
+
+static int rtp_packet_count = 0;
+
+static size_t rtp_write(void *ptr, size_t size, size_t nmemb, void *stream) {
+  char *data = (char *)ptr;
+  int channel = (int)data[0];
+  int message_size = (int)(size * nmemb - 3);
+  int i;
+  (void)stream;
+
+  printf("RTP: message size %d, channel %d\n", message_size, channel);
+
+  data += 3;
+  for(i = 0; i < message_size; i+= RTP_DATA_SIZE) {
+    if(message_size - i > RTP_DATA_SIZE) {
+      if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE) != 0) {
+        printf("RTP PAYLOAD CORRUPTED [%s]\n", data + i);
+      }
+    } else {
+      if (memcmp(RTP_DATA, data + i, message_size - i) != 0) {
+        printf("RTP PAYLOAD END CORRUPTED (%d), [%s]\n", message_size - i, data + i);
+      }
+    }
+  }
+
+  rtp_packet_count++;
+  fprintf(stderr, "packet count is %d\n", rtp_packet_count);
+
+  return size * nmemb;
+}
+
+/* build request url */
+static char *suburl(const char *base, int i)
+{
+  return curl_maprintf("%s%.4d", base, i);
+}
+
+int test(char *URL)
+{
+  CURLcode res;
+  CURL *curl;
+  char *stream_uri;
+  int request=1;
+  FILE *protofile;
+
+  protofile = fopen(libtest_arg2, "w");
+  if(protofile == NULL) {
+    fprintf(stderr, "Couldn't open the protocol dump file\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  if ((curl = curl_easy_init()) == NULL) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+  curl_easy_setopt(curl, CURLOPT_URL, URL);
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+
+  curl_easy_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write);
+  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
+  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, protofile);
+
+  curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1");
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
+  res = curl_easy_perform(curl);
+
+  /* This PLAY starts the interleave */
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
+  res = curl_easy_perform(curl);
+
+  /* The DESCRIBE request will try to consume data after the Content */
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
+
+  res = curl_easy_perform(curl);
+
+  stream_uri = suburl(URL, request++);
+  curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI,stream_uri);
+  free(stream_uri);
+  curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
+  res = curl_easy_perform(curl);
+
+  fprintf(stderr, "PLAY COMPLETE\n");
+
+  /* Use Receive to get the rest of the data */
+  while(!res && rtp_packet_count < 13) {
+    fprintf(stderr, "LOOPY LOOP!\n");
+    curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
+    res = curl_easy_perform(curl);
+  }
+
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+  fclose(protofile);
+
+  return (int)res;
+}
+
Index: curl/tests/runtests.pl
===================================================================
--- curl.orig/tests/runtests.pl	2010-01-21 10:16:38.000000000 -0500
+++ curl/tests/runtests.pl	2010-01-27 17:30:54.000000000 -0500
@@ -132,6 +132,8 @@
 my $IMAP6PORT; # IMAP IPv6 server port
 my $SMTPPORT; # SMTP
 my $SMTP6PORT; # SMTP IPv6 server port
+my $RTSPPORT; # RTSP
+my $RTSP6PORT; # RTSP IPv6 server port
 
 my $srcdir = $ENV{'srcdir'} || '.';
 my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests
@@ -323,7 +325,7 @@
       }
     }
   }
-  for my $proto (('tftp', 'sftp', 'socks', 'ssh')) {
+  for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp')) {
     for my $ipvnum ((4, 6)) {
       for my $idnum ((1, 2)) {
         my $serv = servername_id($proto, $ipvnum, $idnum);
@@ -664,6 +666,11 @@
 
 sub verifyhttp {
     my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
+    if($proto eq "rtsp") {
+        #Use HTTP to make the /verified request
+        $proto = "http";
+    }
+
     my $server = servername_id($proto, $ipvnum, $idnum);
     my $pid = 0;
 
@@ -901,6 +908,7 @@
 
 my %protofunc = ('http' => \&verifyhttp,
                  'https' => \&verifyhttp,
+                 'rtsp' => \&verifyhttp,
                  'ftp' => \&verifyftp,
                  'pop3' => \&verifyftp,
                  'imap' => \&verifyftp,
@@ -939,7 +947,7 @@
 # start the http server
 #
 sub runhttpserver {
-    my ($verbose, $ipv6) = @_;
+    my ($verbose, $ipv6, $rtsp) = @_;
     my $port = $HTTPPORT;
     my $ip = $HOSTIP;
     my $proto = 'http';
@@ -957,6 +965,10 @@
         $port = $HTTP6PORT;
         $ip = $HOST6IP;
     }
+    if($rtsp) {
+        $proto = 'rtsp';
+        $port = $ipv6 ? $RTSP6PORT : $RTSPPORT;
+    }
 
     $server = servername_id($proto, $ipvnum, $idnum);
 
@@ -1912,6 +1924,7 @@
     logmsg sprintf("*   HTTP/%d ", $HTTPPORT);
     logmsg sprintf("FTP/%d ", $FTPPORT);
     logmsg sprintf("FTP2/%d ", $FTP2PORT);
+    logmsg sprintf("RTSP/%d ", $RTSPPORT);
     if($stunnel) {
         logmsg sprintf("FTPS/%d ", $FTPSPORT);
         logmsg sprintf("HTTPS/%d ", $HTTPSPORT);
@@ -1919,6 +1932,7 @@
     logmsg sprintf("\n*   TFTP/%d ", $TFTPPORT);
     if($http_ipv6) {
         logmsg sprintf("HTTP-IPv6/%d ", $HTTP6PORT);
+        logmsg sprintf("RTSP-IPv6/%d ", $RTSP6PORT);
     }
     if($ftp_ipv6) {
         logmsg sprintf("FTP-IPv6/%d ", $FTP6PORT);
@@ -1973,6 +1987,8 @@
   $$thing =~ s/%USER/$USER/g;
   $$thing =~ s/%CLIENTIP/$CLIENTIP/g;
   $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g;
+  $$thing =~ s/%RTSPPORT/$RTSPPORT/g;
+  $$thing =~ s/%RTSP6PORT/$RTSP6PORT/g;
 
   # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
   # used for time-out tests and that whould work on most hosts as these
@@ -3034,7 +3050,7 @@
         }
         elsif($what eq "http") {
             if(!$run{'http'}) {
-                ($pid, $pid2) = runhttpserver($verbose);
+                ($pid, $pid2) = runhttpserver($verbose, "");
                 if($pid <= 0) {
                     return "failed starting HTTP server";
                 }
@@ -3053,6 +3069,28 @@
                 $run{'http-ipv6'}="$pid $pid2";
             }
         }
+        elsif($what eq "rtsp") {
+            if(!$run{'rtsp'}) {
+                ($pid, $pid2) = runhttpserver($verbose, "", "RTSP");
+                if($pid <= 0) {
+                    return "failed starting RTSP server";
+                }
+                printf ("* pid rtsp => %d %d\n", $pid, $pid2) if($verbose);
+                $run{'rtsp'}="$pid $pid2";
+            }
+        }
+        elsif($what eq "rtsp-ipv6") {
+            if(!$run{'rtsp-ipv6'}) {
+                ($pid, $pid2) = runhttpserver($verbose, "IPv6", "RTSP");
+                if($pid <= 0) {
+                    return "failed starting RTSP-IPv6 server";
+                }
+                logmsg sprintf("* pid RTSP-ipv6 => %d %d\n", $pid, $pid2)
+                    if($verbose);
+                $run{'rtsp-ipv6'}="$pid $pid2";
+            }
+        }
+
         elsif($what eq "ftps") {
             if(!$stunnel) {
                 # we can't run ftps tests without stunnel
@@ -3587,6 +3625,8 @@
 $IMAP6PORT = $base++;
 $SMTPPORT =  $base++;
 $SMTP6PORT = $base++;
+$RTSPPORT =  $base++;
+$RTSP6PORT = $base++;
 
 #######################################################################
 # clear and create logging directory:
Index: curl/tests/server/sws.c
===================================================================
--- curl.orig/tests/server/sws.c	2010-01-11 11:41:39.000000000 -0500
+++ curl/tests/server/sws.c	2010-01-27 17:19:46.000000000 -0500
@@ -89,6 +89,13 @@
 #define RCMD_IDLE      1 /* told to sit idle */
 #define RCMD_STREAM    2 /* told to stream */
 
+typedef enum {
+    PROT_NONE = 0,
+    PROT_HTTP = 1,
+    PROT_RTSP = 2,
+    PROT_LAST = 3
+} proto_t;
+
 struct httprequest {
   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
   size_t checkindex; /* where to start checking of the request */
@@ -110,7 +117,11 @@
                      - skip bytes. */
   int rcmd;       /* doing a special command, see defines above */
   int prot_version;  /* HTTP version * 10 */
+
+  char *rtp_buffer;
+  size_t rtp_buffersize;
   bool pipelining;   /* true if request is pipelined */
+  proto_t prot;   /* HTTP or RTSP? */
 };
 
 static int ProcessRequest(struct httprequest *req);
@@ -184,6 +195,14 @@
     "The requested URL was not found on this server.\n"
     "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
 
+static const char *doc404_RTSP = "RTSP/1.0 404 Not Found\r\n"
+    "Server: " SWSVERSION "\r\n"
+    END_OF_HEADERS;
+
+/* Default size to send away fake RTP data */
+#define RTP_DATA_SIZE 12
+static const char *RTP_DATA = "$_1234\n\0asdf";
+
 /* do-nothing macro replacement for systems which lack siginterrupt() */
 
 #ifndef HAVE_SIGINTERRUPT
@@ -299,27 +318,39 @@
 {
   char *line=&req->reqbuf[req->checkindex];
   bool chunked = FALSE;
-  static char request[REQUEST_KEYWORD_SIZE];
-  static char doc[MAXDOCNAMELEN];
+  static char request[REQUEST_KEYWORD_SIZE + 1]; /* sscanf will NULL terminate */
+  static char doc[MAXDOCNAMELEN + 1];
+  static char prot_str[5];
   char logbuf[256];
   int prot_major, prot_minor;
   char *end;
   int error;
   end = strstr(line, END_OF_HEADERS);
 
-  logmsg("ProcessRequest() called");
+  logmsg("ProcessRequest() called with testno %ld and line [%s]", req->testno, line);
 
   /* try to figure out the request characteristics as soon as possible, but
      only once! */
   if((req->testno == DOCNUMBER_NOTHING) &&
      sscanf(line,
-            "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
+            "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s %4s/%d.%d",
             request,
             doc,
+            prot_str,
             &prot_major,
-            &prot_minor) == 4) {
+            &prot_minor) == 5) {
     char *ptr;
 
+    if(!strcmp(prot_str, "HTTP")) {
+        req->prot = PROT_HTTP;
+    }
+    else if(!strcmp(prot_str, "RTSP")) {
+        req->prot = PROT_RTSP;
+    } else {
+        logmsg("got unknown protocol %s", prot_str);
+        return 1;
+    }
+
     req->prot_version = prot_major*10 + prot_minor;
 
     /* find the last slash */
@@ -331,11 +362,11 @@
       char *filename;
 
       if((strlen(doc) + strlen(request)) < 200)
-        sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
-                request, doc, prot_major, prot_minor);
+        sprintf(logbuf, "Got request: %s %s %s/%d.%d",
+                request, doc, prot_str, prot_major, prot_minor);
       else
-        sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
-                prot_major, prot_minor);
+        sprintf(logbuf, "Got a *HUGE* request %s/%d.%d",
+                prot_str, prot_major, prot_minor);
       logmsg("%s", logbuf);
 
       if(!strncmp("/verifiedserver", ptr, 15)) {
@@ -385,42 +416,91 @@
         size_t cmdsize = 0;
         int num=0;
 
+        int rtp_channel = 0;
+        int rtp_size = 0;
+        int rtp_partno = -1;
+        int i = 0;
+        char *rtp_scratch = NULL;
+
         /* get the custom server control "commands" */
         cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize);
+        ptr = cmd;
         fclose(stream);
 
         if(cmdsize) {
           logmsg("Found a reply-servercmd section!");
+          do {
+            if(!strncmp(CMD_AUTH_REQUIRED, ptr, strlen(CMD_AUTH_REQUIRED))) {
+              logmsg("instructed to require authorization header");
+              req->auth_req = TRUE;
+            }
+            else if(!strncmp(CMD_IDLE, ptr, strlen(CMD_IDLE))) {
+              logmsg("instructed to idle");
+              req->rcmd = RCMD_IDLE;
+              req->open = TRUE;
+            }
+            else if(!strncmp(CMD_STREAM, ptr, strlen(CMD_STREAM))) {
+              logmsg("instructed to stream");
+              req->rcmd = RCMD_STREAM;
+            }
+            else if(1 == sscanf(ptr, "pipe: %d", &num)) {
+              logmsg("instructed to allow a pipe size of %d", num);
+              if(num < 0)
+                logmsg("negative pipe size ignored");
+              else if(num > 0)
+                req->pipe = num-1; /* decrease by one since we don't count the
+                                      first request in this number */
+            }
+            else if(1 == sscanf(ptr, "skip: %d", &num)) {
+              logmsg("instructed to skip this number of bytes %d", num);
+              req->skip = num;
+            }
+            else if(3 == sscanf(ptr, "rtp: part %d channel %d size %d", &rtp_partno, &rtp_channel, &rtp_size)) {
+              if(rtp_partno == req->partno) {
+                logmsg("RTP: part %d channel %d size %d", rtp_partno, rtp_channel, rtp_size);
+
+                /* Make our scratch buffer enough to fit all the
+                 * desired data and one for padding */
+                rtp_scratch = malloc(rtp_size + 4 + RTP_DATA_SIZE);
+
+                /* RTP is signalled with a $ */
+                rtp_scratch[0] = '$';
+
+                /* The channel follows and is one byte */
+                rtp_scratch[1] = (char)(rtp_channel & 0xFF);
+
+                /* The length follows and is a two byte short in network order */
+                *((unsigned short *)(&rtp_scratch[2])) = htons((unsigned short)rtp_size);
+
+                /* Fill it with junk data */
+                for(i = 0; i < rtp_size; i+= RTP_DATA_SIZE) {
+                  memcpy(rtp_scratch + 4 + i, RTP_DATA, RTP_DATA_SIZE);
+                }
+
+                if(req->rtp_buffer == NULL) {
+                  req->rtp_buffer = rtp_scratch;
+                  req->rtp_buffersize = rtp_size + 4;
+                } else {
+                  req->rtp_buffer = realloc(req->rtp_buffer, req->rtp_buffersize + rtp_size + 4);
+                  memcpy(req->rtp_buffer + req->rtp_buffersize, rtp_scratch, rtp_size + 4);
+                  req->rtp_buffersize += rtp_size + 4;
+                  free(rtp_scratch);
+                }
+                logmsg("rtp_buffersize is %zd, rtp_size is %d.", req->rtp_buffersize, rtp_size);
 
-          if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
-            logmsg("instructed to require authorization header");
-            req->auth_req = TRUE;
-          }
-          else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
-            logmsg("instructed to idle");
-            req->rcmd = RCMD_IDLE;
-            req->open = TRUE;
-          }
-          else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
-            logmsg("instructed to stream");
-            req->rcmd = RCMD_STREAM;
-          }
-          else if(1 == sscanf(cmd, "pipe: %d", &num)) {
-            logmsg("instructed to allow a pipe size of %d", num);
-            if(num < 0)
-              logmsg("negative pipe size ignored");
-            else if(num > 0)
-              req->pipe = num-1; /* decrease by one since we don't count the
-                                    first request in this number */
-          }
-          else if(1 == sscanf(cmd, "skip: %d", &num)) {
-            logmsg("instructed to skip this number of bytes %d", num);
-            req->skip = num;
-          }
-          else {
-            logmsg("funny instruction found: %s", cmd);
-          }
-          free(cmd);
+              }
+            }
+            else {
+              logmsg("funny instruction found: %s", ptr);
+            }
+
+            ptr = strchr(ptr, '\n');
+            if(ptr)
+              ptr++;
+            else
+              ptr = NULL;
+          } while(ptr && *ptr);
+          logmsg("Done parsing server commands");
         }
       }
     }
@@ -686,6 +766,8 @@
   req->rcmd = RCMD_NORMALREQ;
   req->prot_version = 0;
   req->pipelining = FALSE;
+  req->rtp_buffer = NULL;
+  req->rtp_buffersize = 0;
 
   /*** end of httprequest init ***/
 
@@ -839,7 +921,11 @@
     case DOCNUMBER_404:
     default:
       logmsg("Replying to with a 404");
-      buffer = doc404;
+      if(req->prot == PROT_HTTP) {
+          buffer = doc404;
+      } else {
+          buffer = doc404_RTSP;
+      }
       break;
     }
 
@@ -935,6 +1021,27 @@
     buffer += written;
   } while(count>0);
 
+  /* Send out any RTP data */
+  if(req->rtp_buffer) {
+    logmsg("About to write %zd RTP bytes", req->rtp_buffersize);
+    count = req->rtp_buffersize;
+    do {
+      size_t num = count;
+      if(num > 200)
+        num = 200;
+      written = swrite(sock, req->rtp_buffer + (req->rtp_buffersize - count), num);
+      if(written < 0) {
+        sendfailure = TRUE;
+        break;
+      }
+      count -= written;
+    } while(count > 0);
+
+    free(req->rtp_buffer);
+    req->rtp_buffersize = 0;
+  }
+
+
   do {
     res = fclose(dump);
   } while(res && ((error = ERRNO) == EINTR));
Index: curl/tests/serverhelp.pm
===================================================================
--- curl.orig/tests/serverhelp.pm	2010-01-21 10:16:38.000000000 -0500
+++ curl/tests/serverhelp.pm	2010-01-27 17:27:28.000000000 -0500
@@ -77,7 +77,7 @@
         $idnum  = ($3 && ($3 > 1)) ? $3 : 1;
         $ipvnum = ($4 && ($4 =~ /6$/)) ? 6 : 4;
     }
-    elsif($server =~ /^(tftp|sftp|socks|ssh)(\d*)(-ipv6|)$/) {
+    elsif($server =~ /^(tftp|sftp|socks|ssh|rtsp)(\d*)(-ipv6|)$/) {
         $proto  = $1;
         $idnum  = ($2 && ($2 > 1)) ? $2 : 1;
         $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4;
@@ -97,7 +97,7 @@
 
     $proto = uc($proto) if($proto);
     die "unsupported protocol: $proto" unless($proto &&
-        ($proto =~ /^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH))$/));
+        ($proto =~ /^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP))$/));
 
     $ipver = (not $ipver) ? 'ipv4' : lc($ipver);
     die "unsupported IP version: $ipver" unless($ipver &&
Index: curl/docs/libcurl/curl_easy_setopt.3
===================================================================
--- curl.orig/docs/libcurl/curl_easy_setopt.3	2010-01-27 17:44:42.000000000 -0500
+++ curl/docs/libcurl/curl_easy_setopt.3	2010-01-27 17:44:57.000000000 -0500
@@ -411,7 +411,7 @@
 is always an ASCII dollar sign. The dollar sign is followed by a one byte
 channel identifier and then a 2 byte integer length in network byte order. See
 \fIRFC 2326 Section 10.12\fP for more information on how RTP interleaving
-behaves.
+behaves. If unset or set to NULL, curl will use the default write function.
 
 Interleaved RTP poses some challeneges for the client application. Since the
 stream data is sharing the RTSP control connection, it is critical to service
@@ -424,11 +424,7 @@
 in 7.20.0)
 .IP CURLOPT_INTERLEAVEDATA
 This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION\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. (Added in 7.20.0)
+interleaved RTP data is received. (Added in 7.20.0)
 .SH ERROR OPTIONS
 .IP CURLOPT_ERRORBUFFER
 Pass a char * to a buffer that the libcurl may store human readable error
