diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile.inc | 4 | ||||
| -rw-r--r-- | lib/getinfo.c | 15 | ||||
| -rw-r--r-- | lib/http.c | 429 | ||||
| -rw-r--r-- | lib/http.h | 32 | ||||
| -rw-r--r-- | lib/rtsp.c | 753 | ||||
| -rw-r--r-- | lib/rtsp.h | 77 | ||||
| -rw-r--r-- | lib/sendf.c | 3 | ||||
| -rw-r--r-- | lib/sendf.h | 8 | ||||
| -rw-r--r-- | lib/setup.h | 1 | ||||
| -rw-r--r-- | lib/strerror.c | 6 | ||||
| -rw-r--r-- | lib/transfer.c | 33 | ||||
| -rw-r--r-- | lib/url.c | 161 | ||||
| -rw-r--r-- | lib/urldata.h | 47 | ||||
| -rw-r--r-- | lib/version.c | 5 | 
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 @@ -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 | 
