aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.inc4
-rw-r--r--lib/getinfo.c15
-rw-r--r--lib/http.c429
-rw-r--r--lib/http.h32
-rw-r--r--lib/rtsp.c753
-rw-r--r--lib/rtsp.h77
-rw-r--r--lib/sendf.c3
-rw-r--r--lib/sendf.h8
-rw-r--r--lib/setup.h1
-rw-r--r--lib/strerror.c6
-rw-r--r--lib/transfer.c33
-rw-r--r--lib/url.c161
-rw-r--r--lib/urldata.h47
-rw-r--r--lib/version.c5
14 files changed, 1369 insertions, 205 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index ff9269e63..b7b10d3e1 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -11,7 +11,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
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 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
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
diff --git a/lib/getinfo.c b/lib/getinfo.c
index be88f5091..f295a503e 100644
--- a/lib/getinfo.c
+++ b/lib/getinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -235,6 +235,19 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
/* 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;
}
diff --git a/lib/http.c b/lib/http.c
index 2cc1154fc..deade9183 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -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 @@ const struct Curl_handler Curl_handler_https = {
*
* 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 @@ output_auth_headers(struct connectdata *conn,
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 @@ static size_t readmoredata(char *buffer,
}
/* ------------------------------------------------------------------------- */
-/*
- * 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 functions */
/*
- * add_buffer_init() sets up and returns a fine buffer struct
+ * Curl_add_buffer_init() sets up and returns a fine buffer struct
*/
-static
-send_buffer *add_buffer_init(void)
+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 @@ CURLcode add_buffer_send(send_buffer *in,
/*
* 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 @@ CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
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 @@ CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
/*
* 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 @@ CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
/* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */
+
+
/*
* Curl_compareheader()
*
@@ -1320,7 +1303,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
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 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
}
/* 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 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
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 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
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 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
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 @@ CURLcode Curl_http_done(struct connectdata *conn,
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);
@@ -1962,9 +1945,9 @@ CURLcode Curl_http_done(struct connectdata *conn,
/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it
-are if the user specifically requested HTTP 1.0, if the server we are
-connected to only supports 1.0, or if any server previously contacted to
-handle this request only supports 1.0. */
+ are if the user specifically requested HTTP 1.0, if the server we are
+ connected to only supports 1.0, or if any server previously contacted to
+ handle this request only supports 1.0. */
static bool use_http_1_1(const struct SessionHandle *data,
const struct connectdata *conn)
{
@@ -1978,7 +1961,7 @@ static bool use_http_1_1(const struct SessionHandle *data,
/* 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;
@@ -1988,14 +1971,14 @@ static CURLcode expect100(struct SessionHandle *data,
/* 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");
+ result = Curl_add_bufferf(req_buffer,
+ "Expect: 100-continue\r\n");
if(result == CURLE_OK)
data->state.expect100header = TRUE;
}
@@ -2003,8 +1986,8 @@ static CURLcode expect100(struct SessionHandle *data,
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,8 @@ static CURLcode add_custom_headers(struct connectdata *conn,
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 +2031,58 @@ static CURLcode add_custom_headers(struct connectdata *conn,
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 +2091,6 @@ static CURLcode add_custom_headers(struct connectdata *conn,
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 +2104,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2175,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2196,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2213,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 =
@@ -2191,7 +2226,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if (use_http_1_1(data, conn)) {
/* HTTP, upload, unknown file size and not HTTP 1.0 */
data->req.upload_chunky = TRUE;
- } else {
+ }
+ else {
failf(data, "Chunky upload is not supported by HTTP 1.0");
return CURLE_UPLOAD_FAILED;
}
@@ -2207,7 +2243,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2370,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* 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 +2380,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
- 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 +2463,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
* 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 +2471,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2514,27 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2568,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2604,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2621,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
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 +2638,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#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 +2659,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2695,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2719,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2733,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2767,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2778,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2786,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2816,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2827,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
}
- 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 +2838,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2862,25 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2901,20 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* 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 +2935,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
}
/* 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 +2949,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
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 +2993,11 @@ checkhttpprefix(struct SessionHandle *data,
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 +3024,51 @@ checkhttpprefix(struct SessionHandle *data,
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,9 +3117,9 @@ static CURLcode header_append(struct SessionHandle *data,
* 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;
@@ -3106,9 +3144,9 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
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 +3178,8 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
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 +3236,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
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 +3348,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
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 +3380,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
/* 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 +3405,53 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
#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 us, 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 +3629,8 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
*/
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,7 +3766,13 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
}
}
}
-
+#ifndef CURL_DISABLE_RTSP
+ else if(conn->protocol & PROT_RTSP) {
+ result = Curl_rtsp_parseheader(conn, k->p);
+ if(result)
+ return result;
+ }
+#endif
/*
* End of header-checks. Write them to the client.
*/
@@ -3741,4 +3805,3 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
return CURLE_OK;
}
-#endif
diff --git a/lib/http.h b/lib/http.h
index 155027d99..b2cbdae35 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -35,8 +35,40 @@ bool Curl_compareheader(const char *headerline, /* line to check */
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,
diff --git a/lib/rtsp.c b/lib/rtsp.c
new file mode 100644
index 000000000..b527fe985
--- /dev/null
+++ b/lib/rtsp.c
@@ -0,0 +1,753 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#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 "multiif.h"
+#include "http.h"
+#include "url.h"
+#include "progress.h"
+#include "rtsp.h"
+#include "rawstr.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?
+ */
+
+
+static int rtsp_getsock_do(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks);
+
+/* this returns the socket to wait for in the DO and DOING state for the multi
+ interface and then we're always _sending_ a request and thus we wait for
+ the single socket to become writable only */
+static int rtsp_getsock_do(struct connectdata *conn,
+ curl_socket_t *socks,
+ int numsocks)
+{
+ /* write mode */
+ (void)numsocks; /* unused, we trust it to be at least 1 */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+}
+
+static
+CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
+
+
+/*
+ * 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 */
+ rtsp_getsock_do, /* 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,
+ (size_t)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);
+
+ if(result) {
+ failf(data, "Failed RTSP transfer");
+ return result;
+ }
+
+ /* 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;
+ CURLcode result;
+
+ 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] != '$') {
+ return CURLE_OK;
+ }
+
+ while((rtp_dataleft > 0) &&
+ (rtp[0] == '$')) {
+ 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
+ * Write out the header but strip the leading '$' */
+ infof(data, "CPCDEBUG: 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;
+ }
+
+ /* 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] == '$') {
+ 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;
+}
+
+CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
+{
+ struct SessionHandle *data = conn->data;
+ size_t wrote;
+ curl_write_callback writeit;
+
+ if(len == 0) {
+ failf (data, "Cannot write a 0 size RTP packet.");
+ return CURLE_WRITE_ERROR;
+ }
+
+ writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func;
+ wrote = writeit(ptr, 1, len, data->set.rtp_out);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote) {
+ failf (data, "Cannot pause RTP");
+ return CURLE_WRITE_ERROR;
+ }
+
+ if(wrote != len) {
+ failf (data, "Failed writing RTP data");
+ return CURLE_WRITE_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
+ char *header)
+{
+ struct SessionHandle *data = conn->data;
+ long CSeq = 0;
+
+ if(checkprefix("CSeq:", header)) {
+ /* Store the received CSeq. Match is verified in rtsp_done */
+ int nc;
+ char *temp = strdup(header);
+ 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]", header);
+ return CURLE_RTSP_CSEQ_ERROR;
+ }
+ }
+ else if(checkprefix("Session:", header)) {
+ char *start;
+
+ /* Find the first non-space letter */
+ start = header + 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';
+ }
+ }
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_RTSP */
diff --git a/lib/rtsp.h b/lib/rtsp.h
new file mode 100644
index 000000000..dcc8d392e
--- /dev/null
+++ b/lib/rtsp.h
@@ -0,0 +1,77 @@
+#ifndef __RTSP_H_
+#define __RTSP_H_
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+#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);
+
+CURLcode Curl_rtsp_parseheader(struct connectdata *conn, char *header);
+
+#endif /* CURL_DISABLE_RTSP */
+#endif /* __RTSP_H_ */
diff --git a/lib/sendf.c b/lib/sendf.c
index e80da04c7..9b360be79 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -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>
diff --git a/lib/sendf.h b/lib/sendf.h
index dab91433d..2ed975ec7 100644
--- a/lib/sendf.h
+++ b/lib/sendf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,13 +48,15 @@ void Curl_failf(struct SessionHandle *, const char *fmt, ...);
#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)
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr,
size_t len);
+CURLcode Curl_rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
+
/* internal read-function, does plain socket only */
int Curl_read_plain(curl_socket_t sockfd,
char *buf,
diff --git a/lib/setup.h b/lib/setup.h
index 8620f83b9..b8d62d417 100644
--- a/lib/setup.h
+++ b/lib/setup.h
@@ -169,6 +169,7 @@
# define CURL_DISABLE_TELNET
# define CURL_DISABLE_DICT
# define CURL_DISABLE_FILE
+# define CURL_DISABLE_RTSP
#endif
/* ================================================================ */
diff --git a/lib/strerror.c b/lib/strerror.c
index 540886919..b16e04b82 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -270,6 +270,12 @@ curl_easy_strerror(CURLcode error)
case CURLE_AGAIN:
return "Socket not ready for send/recv";
+ case CURLE_RTSP_CSEQ_ERROR:
+ return "RTSP CSeq mismatch or invalid CSeq";
+
+ case CURLE_RTSP_SESSION_ERROR:
+ return "RTSP session error";
+
/* error codes not used by current libcurl */
case CURLE_OBSOLETE4:
case CURLE_OBSOLETE10:
diff --git a/lib/transfer.c b/lib/transfer.c
index 3c5821692..e6b2259f8 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -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 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
#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 @@ static CURLcode readwrite_data(struct SessionHandle *data,
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 @@ static CURLcode readwrite_data(struct SessionHandle *data,
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 @@ static CURLcode readwrite_data(struct SessionHandle *data,
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 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
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 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
/* 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 @@ CURLcode Curl_retry_request(struct connectdata *conn,
/* 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
diff --git a/lib/url.c b/lib/url.c
index 5c7699d88..d9802dba7 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -136,6 +136,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#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 @@ static const struct Curl_handler * const protocols[] = {
#endif
#endif
+#ifndef CURL_DISABLE_RTSP
+ &Curl_handler_rtsp,
+#endif
+
(struct Curl_handler *) NULL
};
@@ -699,6 +704,7 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
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,114 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
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_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_write_callback);
+ break;
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_FAILED_INIT; /* correct this */
@@ -2360,6 +2474,7 @@ static void conn_free(struct connectdata *conn)
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 */
@@ -2500,6 +2615,42 @@ static bool SocketIsDead(curl_socket_t sock)
return ret_val;
}
+#ifndef CURL_DISABLE_RTSP
+/*
+ * 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;
+}
+#endif /* CURL_DISABLE_RTSP */
+
static bool IsPipeliningPossible(const struct SessionHandle *handle)
{
if(handle->multi && Curl_multi_canPipeline(handle->multi) &&
@@ -2664,7 +2815,15 @@ ConnectionExists(struct SessionHandle *data,
/* 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
+#endif /*CURL_DISABLE_RTSP*/
+ dead = SocketIsDead(check->sock[FIRSTSOCKET]);
+
if(dead) {
check->data = data;
infof(data, "Connection #%d seems to be dead!\n", i);
diff --git a/lib/urldata.h b/lib/urldata.h
index 278fd4667..8e3f612ce 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -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,8 @@ struct SingleRequest {
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 +675,9 @@ struct connectdata {
#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
@@ -718,7 +722,8 @@ struct connectdata {
char *proxypasswd; /* proxy password string, allocated */
curl_proxytype proxytype; /* what kind of proxy that is in use */
- int httpversion; /* the HTTP version*10 reported by the server */
+ 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 +755,7 @@ struct connectdata {
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 +831,7 @@ struct connectdata {
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 +851,7 @@ struct connectdata {
* 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 +922,22 @@ typedef enum {
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 +1088,11 @@ struct UrlState {
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 +1103,7 @@ struct UrlState {
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 +1154,7 @@ enum dupstring {
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 +1185,9 @@ enum dupstring {
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 +1213,7 @@ struct UserDefined {
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 +1235,7 @@ struct UserDefined {
'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_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 +1372,9 @@ struct UserDefined {
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 {
diff --git a/lib/version.c b/lib/version.c
index 19eb3d4aa..2b9ebf893 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -143,6 +143,9 @@ static const char * const protocols[] = {
#ifndef CURL_DISABLE_FILE
"file",
#endif
+#ifndef CURL_DISABLE_RTSP
+ "rtsp",
+#endif
#ifdef USE_SSL
#ifndef CURL_DISABLE_HTTP