1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

Chris Conroy brought support for RTSP transfers, and with it comes 8(!) new

libcurl options for controlling what to get and how to receive posssibly
interleaved RTP data. Initial commit.
This commit is contained in:
Daniel Stenberg 2010-01-21 13:58:30 +00:00
parent e09718d457
commit bc4582b68a
21 changed files with 1628 additions and 215 deletions

View File

@ -6,6 +6,11 @@
Changelog Changelog
Daniel Stenberg (21 Jan 2010)
- Chris Conroy brought support for RTSP transfers, and with it comes 8(!) new
libcurl options for controlling what to get and how to receive posssibly
interleaved RTP data.
Daniel Stenberg (20 Jan 2010) Daniel Stenberg (20 Jan 2010)
- As was pointed out on the http-state mailing list, the order of cookies in a - As was pointed out on the http-state mailing list, the order of cookies in a
HTTP Cookie: header _needs_ to be sorted on the path length in the cases HTTP Cookie: header _needs_ to be sorted on the path length in the cases

View File

@ -2,7 +2,7 @@ Curl and libcurl 7.20.0
Public curl releases: 114 Public curl releases: 114
Command line options: 135 Command line options: 135
curl_easy_setopt() options: 166 curl_easy_setopt() options: 174
Public functions in libcurl: 58 Public functions in libcurl: 58
Known libcurl bindings: 39 Known libcurl bindings: 39
Contributors: 761 Contributors: 761
@ -13,7 +13,8 @@ This release includes the following changes:
o curl-config can now show the arguments used when building curl o curl-config can now show the arguments used when building curl
o non-blocking TFTP o non-blocking TFTP
o send Expect: 100-continue for POSTs with unknown sizes o send Expect: 100-continue for POSTs with unknown sizes
o added support for IMAP, POP3 and SMTP transfers o added support for IMAP(S), POP3(S), SMTP(S) and RTSP
o added new curl_easy_setopt() options for SMTP and RTSP
o added --mail-from and --mail-rcpt for SMTP o added --mail-from and --mail-rcpt for SMTP
o VMS build system enhancements o VMS build system enhancements
o added support for the PRET ftp command o added support for the PRET ftp command
@ -60,6 +61,6 @@ advice from friends like these:
Marc Kleine-Budde, Jad Chamcham, Bjorn Augustsson, David Byron, Marc Kleine-Budde, Jad Chamcham, Bjorn Augustsson, David Byron,
Markus Koetter, Chad Monroe, Martin Storsjo, Siegfried Gyuricsko, Markus Koetter, Chad Monroe, Martin Storsjo, Siegfried Gyuricsko,
Jon Nelson, Julien Chaffraix, Renato Botelho, Peter Pentchev, Ingmar Runge, Jon Nelson, Julien Chaffraix, Renato Botelho, Peter Pentchev, Ingmar Runge,
Johan van Selst, Charles Kerr, Gil Weber, David McCreedy Johan van Selst, Charles Kerr, Gil Weber, David McCreedy, Chris Conroy
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@ -135,6 +135,7 @@ curl_verbose_msg="enabled (--disable-verbose)"
curl_sspi_msg="no (--enable-sspi)" curl_sspi_msg="no (--enable-sspi)"
curl_ldap_msg="no (--enable-ldap / --with-ldap-lib / --with-lber-lib)" curl_ldap_msg="no (--enable-ldap / --with-ldap-lib / --with-lber-lib)"
curl_ldaps_msg="no (--enable-ldaps)" curl_ldaps_msg="no (--enable-ldaps)"
curl_rtsp_msg="no (--enable-rtsp)"
dnl dnl
dnl Save anything in $LIBS for later dnl Save anything in $LIBS for later
@ -416,6 +417,28 @@ AC_HELP_STRING([--disable-ldaps],[Disable LDAPS support]),
AC_SUBST(CURL_DISABLE_LDAPS, [1]) AC_SUBST(CURL_DISABLE_LDAPS, [1])
) )
AC_MSG_CHECKING([whether to support rtsp])
AC_ARG_ENABLE(rtsp,
AC_HELP_STRING([--enable-rtsp],[Enable RTSP support])
AC_HELP_STRING([--disable-rtsp],[Disable RTSP support]),
[ case "$enableval" in
no)
AC_MSG_RESULT(no)
AC_DEFINE(CURL_DISABLE_RTSP, 1, [to disable RTSP])
AC_SUBST(CURL_DISABLE_RTSP, [1])
;;
*) if test x$CURL_DISABLE_HTTP = x1 ; then
AC_MSG_ERROR(HTTP support needs to be enabled in order to enable RTSP support!)
else
AC_MSG_RESULT(yes)
curl_rtsp_msg="enabled"
fi
;;
esac ],
AC_MSG_RESULT(no)
curl_rtsp_msg="disabled"
)
AC_MSG_CHECKING([whether to support proxies]) AC_MSG_CHECKING([whether to support proxies])
AC_ARG_ENABLE(proxy, AC_ARG_ENABLE(proxy,
AC_HELP_STRING([--enable-proxy],[Enable proxy support]) AC_HELP_STRING([--enable-proxy],[Enable proxy support])
@ -2579,6 +2602,9 @@ if test "x$USE_LIBSSH2" = "x1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP" SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP"
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP" SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP"
fi fi
if test "x$CURL_DISABLE_RTSP" != "x1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTSP"
fi
AC_SUBST(SUPPORT_PROTOCOLS) AC_SUBST(SUPPORT_PROTOCOLS)
@ -2653,6 +2679,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
ca cert path: ${capath} ca cert path: ${capath}
LDAP support: ${curl_ldap_msg} LDAP support: ${curl_ldap_msg}
LDAPS support: ${curl_ldaps_msg} LDAPS support: ${curl_ldaps_msg}
RTSP support: ${curl_rtsp_msg}
Protocols: ${SUPPORT_PROTOCOLS} Protocols: ${SUPPORT_PROTOCOLS}
]) ])

View File

@ -209,6 +209,28 @@ the previous request didn't match (see \fICURLOPT_TIMECONDITION\fP). Alas, if
this returns a 1 you know that the reason you didn't get data in return is this returns a 1 you know that the reason you didn't get data in return is
because it didn't fulfill the condition. The long ths argument points to will because it didn't fulfill the condition. The long ths argument points to will
get a zero stored if the condition instead was met. (Added in 7.19.4) get a zero stored if the condition instead was met. (Added in 7.19.4)
.IP CURLINFO_RTSP_SESSION_ID
Pass a pointer to a char pointer to receive a pointer to a string holding the
most recent RTSP Session ID.
Applications wishing to resume an RTSP session on another connection should
retreive this info before closing the active connection.
.IP CURLINFO_RTSP_CLIENT_CSEQ
Pass a pointer to a long to receive the next CSeq that will be used by the
application.
.IP CURLINFO_RTSP_SERVER_CSEQ
Pass a pointer to a long to receive the next server CSeq that will be expected
by the application.
\fI(NOTE: listening for server initiated requests is currently
unimplemented).\fP
Applications wishing to resume an RTSP session on another connection should
retreive this info before closing the active connection.
.IP CURLINFO_RTSP_CSEQ_RECV
Pass a pointer to a long to receive the most recently received CSeq from the
server. If your application encounters a \fICURLE_RTSP_CSEQ_ERROR\fP then you
may wish to troubleshoot and/or fix the CSeq mismatch by peeking at this value.
.SH TIMES .SH TIMES
.nf .nf
An overview of the six time values available from curl_easy_getinfo() An overview of the six time values available from curl_easy_getinfo()

View File

@ -401,6 +401,34 @@ follows:
You will need to override these definitions if they are different on your You will need to override these definitions if they are different on your
system. system.
.IP CURLOPT_RTPFUNCTION
Function pointer that should match the following prototype: \fIsize_t
function( void *ptr, size_t size, size_t nmemb, void *stream)\fP. This
function gets called by libcurl as soon as it has received interleaved RTP
data. This function gets called for each $ block and therefore contains
exactly one upper-layer protocol unit (e.g. one RTP packet). Curl writes the
interleaved header as well as the included data for each call. The first byte
is always an ASCII dollar sign. The dollar sign is followed by a one byte
channel identifier and then a 2 byte integer length in network byte order. See
\fIRFC 2326 Section 10.12\fP for more information on how RTP interleaving
behaves.
Interleaved RTP poses some challeneges for the client application. Since the
stream data is sharing the RTSP control connection, it is critical to service
the RTP in a timely fashion. If the RTP data is not handled quickly,
subsequent response processing may become unreasonably delayed and the
connection may close. The application may use \fICURL_RTSPREQ_RECEIVE\fP to
service RTP data when no requests are desired. If the application makes a
request, (e.g. \fICURL_RTSPREQ_PAUSE\fP) then the response handler will
process any pending RTP data before marking the request as finished. (Added
in 7.20.0)
.IP CURLOPT_RTPDATA
This is the stream that will be passed to \fICURLOPT_RTPFUNCTION\fP when
interleaved RTP data is received. Since the application is required to provide
a custom function for RTP data, there is no requirement that the stream
pointer be a valid FILE pointer. An application may wish to pass a cookie
containing information about many streams to assist in demultiplexing the
different RTP channels. (Added in 7.20.0)
.SH ERROR OPTIONS .SH ERROR OPTIONS
.IP CURLOPT_ERRORBUFFER .IP CURLOPT_ERRORBUFFER
Pass a char * to a buffer that the libcurl may store human readable error Pass a char * to a buffer that the libcurl may store human readable error
@ -1275,6 +1303,107 @@ file \&"normally" (like in the multicwd case). This is somewhat more standards
compliant than 'nocwd' but without the full penalty of 'multicwd'. compliant than 'nocwd' but without the full penalty of 'multicwd'.
.RE .RE
(Added in 7.15.1) (Added in 7.15.1)
.SH RTSP OPTIONS
.IP CURLOPT_RTSP_REQUEST
Tell libcurl what kind of RTSP request to make. Pass one of the following RTSP
enum values. Unless noted otherwise, commands require the Session ID to be
initialized. (Added in 7.20.0)
.RS
.IP CURL_RTSPREQ_OPTIONS
Used to retrieve the available methods of the server. The application is
responsbile for parsing and obeying the response. \fB(The session ID is not
needed for this method.)\fP (Added in 7.20.0)
.IP CURL_RTSPREQ_DESCRIBE
Used to get the low level description of a stream. The application should note
what formats it understands in the \fI'Accept:'\fP header. Unless set
manually, libcurl will automatically fill in \fI'Accept:
application/sdp'\fP. Time-condition headers will be added to Describe requests
if the \fICURLOPT_TIMECONDITION\fP option is active. \fB(The session ID is not
needed for this method)\fP (Added in 7.20.0)
.IP CURL_RTSPREQ_ANNOUNCE
When sent by a client, this method changes the description of the session. For
example, if a client is using the server to record a meeting, the client can
use Announce to inform the server of all the meta-information about the
session. ANNOUNCE acts like an HTTP PUT or POST just like
\fICURL_RTSPREQ_SET_PARAMETER\fP (Added in 7.20.0)
.IP CURL_RTSPREQ_SETUP
Setup is used to initialize the transport layer for the session. The
application must set the desired Transport options for a session by using the
\fICURLOPT_RTSP_TRANSPORT\fP option prior to calling setup. If no session ID
is currently set with \fICURLOPT_RTSP_SESSION_ID\fP, libcurl will extract and
use the session ID in the response to this request. \fB(The session ID is not
needed for this method).\fP (Added in 7.20.0)
.IP CURL_RTSPREQ_PLAY
Send a Play command to the server. Use the \fICURLOPT_RANGE\fP option to
modify the playback time (e.g. 'npt=10-15'). (Added in 7.20.0)
.IP CURL_RTSPREQ_PAUSE
Send a Pause command to the server. Use the \fICURLOPT_RANGE\fP option with a
single value to indicate when the stream should be halted. (e.g. npt='25')
(Added in 7.20.0)
.IP CURL_RTSPREQ_TEARDOWN
This command terminates an RTSP session. Note that simply closing a connection
does not terminate the RTSP session since it is valid to control an RTSP
session over different connections. (Added in 7.20.0)
.IP CURL_RTSPREQ_GET_PARAMETER
Retrieve a parameter from the server. By default, libcurl will automatically
include an \fIAccept: text/parameters\fP header unless a custom one is set.
Applications wishing to send a heartbeat message (e.g. in the presence of a
server-specified timeout) should send use an empty GET_PARAMETER request.
(Added in 7.20.0)
.IP CURL_RTSPREQ_SET_PARAMETER
Set a parameter on the server. By default, libcurl will automatically include
a \fIContent-Type: text/parameters\fP header unless a custom one is set. The
interaction with SET_PARAMTER is much like an HTTP PUT or POST. An application
may either use \fICURLOPT_UPLOAD\fP with \fICURLOPT_READDATA\fP like an HTTP
PUT, or it may use \fICURLOPT_POSTFIELDS\fP like an HTTP POST. Note that no
chunked transfers are allowed, so the application must set the
\fICURLOPT_INFILESIZE\fP in the former and \fICURLOPT_POSTFIELDSIZE\fP in the
latter. Also, there is no use of multi-part POSTs within RTSP. (Added in
7.20.0)
.IP CURL_RTSPREQ_RECORD
Used to tell the server to record a session. Use the \fICURLOPT_RANGE\fP
option to modify the record time. (Added in 7.20.0)
.IP CURL_RTSPREQ_RECEIVE
This is a special request because it does not send any data to the server. The
application may call this function in order to receive interleaved RTP
data. It will return after processing one read buffer of data in order to give
the application a chance to run. (Added in 7.20.0)
.RE
.IP CURLOPT_RTSP_SESSION_ID
Pass a char * as a parameter to set the value of the current RTSP Session ID
for the handle. Useful for resuming an in-progress session. Once this value is
set to any non-NULL value, libcurl will return \fICURLE_RTSP_SESSION_ERROR\fP
if ID received from the server does not match. If unset (or set to NULL),
libcurl will automatically set the ID the first time the server sets it in a
response. (Added in 7.20.0)
.IP CURLOPT_RTSP_STREAM_URI
Set the stream URI to operate on by passing a char * . For example, a single
session may be controlling \fIrtsp://foo/twister/audio\fP and
\fIrtsp://foo/twister/video\fP and the application can switch to the
appropriate stream using this option. If unset, libcurl will default to
operating on generic server options by passing '*' in the place of the RTSP
Stream URI. Note that this option is distinct from \fICURLOPT_URL\fP. When
working with RTSP, the \fICURLOPT_STREAM_URI\fP indicates what URL to send to
the server in the request header while the \fICURLOPT_URL\fP indicates where
to make the connection to. (e.g. the \fICURLOPT_URL\fP for the above examples
might be set to \fIrtsp://foo/twister\fP (Added in 7.20.0)
.IP CURLOPT_RTSP_TRANSPORT
Pass a char * to tell libcurl what to pass for the Transport: header for this
RTSP session. This is mainly a convenience method to avoid needing to set a
custom Transport: header for every SETUP request. The application must set a
Transport: header before issuing a SETUP request. (Added in 7.20.0)
.IP CURLOPT_RTSP_HEADER
This option is simply an alias for \fICURLOPT_HTTP_HEADER\fP. Use this to
replace the standard headers that RTSP and HTTP share. It is also valid to use
the shortcuts such as \fICURLOPT_USERAGENT\fP. (Added in 7.20.0)
.IP CURLOPT_RTSP_CLIENT_CSEQ
Manually set the the CSEQ number to issue for the next RTSP request. Useful if
the application is resuming a previously broken connection. The CSEQ will
increment from this new number henceforth. (Added in 7.20.0)
.IP CURLOPT_RTSP_SERVER_CSEQ
Manually set the CSEQ number to expect for the next RTSP Server->Client
request. At the moment, this feature (listening for Server requests) is
unimplemented. (Added in 7.20.0)
.SH PROTOCOL OPTIONS .SH PROTOCOL OPTIONS
.IP CURLOPT_TRANSFERTEXT .IP CURLOPT_TRANSFERTEXT
A parameter set to 1 tells the library to use ASCII mode for FTP transfers, A parameter set to 1 tells the library to use ASCII mode for FTP transfers,
@ -1301,9 +1430,14 @@ want. It should be in the format "X-Y", where X or Y may be left out. HTTP
transfers also support several intervals, separated with commas as in transfers also support several intervals, separated with commas as in
\fI"X-Y,N-M"\fP. Using this kind of multiple intervals will cause the HTTP \fI"X-Y,N-M"\fP. Using this kind of multiple intervals will cause the HTTP
server to send the response document in pieces (using standard MIME separation server to send the response document in pieces (using standard MIME separation
techniques). Pass a NULL to this option to disable the use of ranges. techniques). For RTSP, the formatting of a range should follow RFC 2326 Section
12.29. Note that for RTSP, byte ranges are \fBnot\fP permitted. Instead, ranges
should be given in npt, utc, or smpte formats.
Ranges work on HTTP, FTP and FILE (since 7.18.0) transfers only. Pass a NULL to this option to disable the use of ranges.
Ranges work on HTTP, FTP, FILE (since 7.18.0), and RTSP (since 7.20.0)
transfers only.
.IP CURLOPT_RESUME_FROM .IP CURLOPT_RESUME_FROM
Pass a long as parameter. It contains the offset in number of bytes that you Pass a long as parameter. It contains the offset in number of bytes that you
want the transfer to start from. Set this option to 0 to make the transfer want the transfer to start from. Set this option to 0 to make the transfer
@ -1408,7 +1542,8 @@ given limit. This concerns both FTP and HTTP transfers.
.IP CURLOPT_TIMECONDITION .IP CURLOPT_TIMECONDITION
Pass a long as parameter. This defines how the \fICURLOPT_TIMEVALUE\fP time Pass a long as parameter. This defines how the \fICURLOPT_TIMEVALUE\fP time
value is treated. You can set this parameter to \fICURL_TIMECOND_IFMODSINCE\fP value is treated. You can set this parameter to \fICURL_TIMECOND_IFMODSINCE\fP
or \fICURL_TIMECOND_IFUNMODSINCE\fP. This feature applies to HTTP and FTP. or \fICURL_TIMECOND_IFUNMODSINCE\fP. This feature applies to HTTP, FTP, and
RTSP.
The last modification time of a file is not always known and in such instances The last modification time of a file is not always known and in such instances
this feature will have no effect even if the given time condition would not this feature will have no effect even if the given time condition would not
@ -1629,8 +1764,8 @@ Pass a char * to a zero terminated string naming a file holding a CA
certificate in PEM format. If the option is set, an additional check against certificate in PEM format. If the option is set, an additional check against
the peer certificate is performed to verify the issuer is indeed the one the peer certificate is performed to verify the issuer is indeed the one
associated with the certificate provided by the option. This additional check associated with the certificate provided by the option. This additional check
is useful in multi-level PKI where one needs to enforce that the peer certificate is is useful in multi-level PKI where one needs to enforce that the peer
from a specific branch of the tree. certificate is from a specific branch of the tree.
This option makes sense only when used in combination with the This option makes sense only when used in combination with the
\fICURLOPT_SSL_VERIFYPEER\fP option. Otherwise, the result of the check is not \fICURLOPT_SSL_VERIFYPEER\fP option. Otherwise, the result of the check is not

View File

@ -55,6 +55,7 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG],
AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP]) AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT]) AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP]) AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
AC_ARG_WITH(libcurl, AC_ARG_WITH(libcurl,
AC_HELP_STRING([--with-libcurl=DIR],[look for the curl library in DIR]), AC_HELP_STRING([--with-libcurl=DIR],[look for the curl library in DIR]),
@ -194,7 +195,7 @@ x=CURLOPT_VERBOSE;
# We don't have --protocols, so just assume that all # We don't have --protocols, so just assume that all
# protocols are available # protocols are available
_libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT" _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP RTSP"
if test x$libcurl_feature_SSL = xyes ; then if test x$libcurl_feature_SSL = xyes ; then
_libcurl_protocols="$_libcurl_protocols HTTPS" _libcurl_protocols="$_libcurl_protocols HTTPS"

View File

@ -197,6 +197,12 @@ typedef size_t (*curl_write_callback)(char *buffer,
size_t nitems, size_t nitems,
void *outstream); void *outstream);
typedef size_t (*curl_rtp_write_callback)(char *buffer,
size_t size,
size_t nitems,
void *outstream,
int channel);
/* These are the return codes for the seek callbacks */ /* These are the return codes for the seek callbacks */
#define CURL_SEEKFUNC_OK 0 #define CURL_SEEKFUNC_OK 0
#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ #define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */
@ -406,6 +412,9 @@ typedef enum {
CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in
7.19.0) */ 7.19.0) */
CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */
CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */
CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Identifiers */
CURL_LAST /* never use! */ CURL_LAST /* never use! */
} CURLcode; } CURLcode;
@ -618,6 +627,7 @@ typedef enum {
#define CURLPROTO_POP3S (1<<15) #define CURLPROTO_POP3S (1<<15)
#define CURLPROTO_SMTP (1<<16) #define CURLPROTO_SMTP (1<<16)
#define CURLPROTO_SMTPS (1<<17) #define CURLPROTO_SMTPS (1<<17)
#define CURLPROTO_RTSP (1<<18)
#define CURLPROTO_ALL (~0) /* enable everything */ #define CURLPROTO_ALL (~0) /* enable everything */
/* long may be 32 or 64 bits, but we should never depend on anything else /* long may be 32 or 64 bits, but we should never depend on anything else
@ -1287,6 +1297,30 @@ typedef enum {
/* FTP: send PRET before PASV */ /* FTP: send PRET before PASV */
CINIT(FTP_USE_PRET, LONG, 188), CINIT(FTP_USE_PRET, LONG, 188),
/* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */
CINIT(RTSP_REQUEST, LONG, 189),
/* The RTSP session identifier */
CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190),
/* The RTSP stream URI */
CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191),
/* The Transport: header to use in RTSP requests */
CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192),
/* Manually initialize the client RTSP CSeq for this handle */
CINIT(RTSP_CLIENT_CSEQ, LONG, 193),
/* Manually initialize the server RTSP CSeq for this handle */
CINIT(RTSP_SERVER_CSEQ, LONG, 194),
/* The stream to pass to RTPFUNCTION. */
CINIT(RTPDATA, OBJECTPOINT, 195),
/* Let the application define a custom write method for RTP data */
CINIT(RTPFUNCTION, FUNCTIONPOINT, 196),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;
@ -1330,6 +1364,7 @@ typedef enum {
#define CURLOPT_WRITEDATA CURLOPT_FILE #define CURLOPT_WRITEDATA CURLOPT_FILE
#define CURLOPT_READDATA CURLOPT_INFILE #define CURLOPT_READDATA CURLOPT_INFILE
#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER #define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER
#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
/* These enums are for use with the CURLOPT_HTTP_VERSION option. */ /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
enum { enum {
@ -1342,6 +1377,25 @@ enum {
CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
}; };
/*
* Public API enums for RTSP requests
*/
enum {
CURL_RTSPREQ_NONE, /* first in list */
CURL_RTSPREQ_OPTIONS,
CURL_RTSPREQ_DESCRIBE,
CURL_RTSPREQ_ANNOUNCE,
CURL_RTSPREQ_SETUP,
CURL_RTSPREQ_PLAY,
CURL_RTSPREQ_PAUSE,
CURL_RTSPREQ_TEARDOWN,
CURL_RTSPREQ_GET_PARAMETER,
CURL_RTSPREQ_SET_PARAMETER,
CURL_RTSPREQ_RECORD,
CURL_RTSPREQ_RECEIVE,
CURL_RTSPREQ_LAST /* last in list */
};
/* These enums are for use with the CURLOPT_NETRC option. */ /* These enums are for use with the CURLOPT_NETRC option. */
enum CURL_NETRC_OPTION { enum CURL_NETRC_OPTION {
CURL_NETRC_IGNORED, /* The .netrc will never be read. CURL_NETRC_IGNORED, /* The .netrc will never be read.
@ -1711,9 +1765,13 @@ typedef enum {
CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33,
CURLINFO_CERTINFO = CURLINFO_SLIST + 34, CURLINFO_CERTINFO = CURLINFO_SLIST + 34,
CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35,
CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36,
CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37,
CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38,
CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39,
/* Fill in new entries below here! */ /* Fill in new entries below here! */
CURLINFO_LASTONE = 35 CURLINFO_LASTONE = 39
} CURLINFO; } CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

View File

@ -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 \ 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 \ 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 \ 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 \ 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 \ 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 \ 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 \ 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_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

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * 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 */ /* return if the condition prevented the document to get transfered */
*param_longp = data->info.timecond; *param_longp = data->info.timecond;
break; 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: default:
return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_BAD_FUNCTION_ARGUMENT;
} }

View File

@ -98,6 +98,7 @@
#include "multiif.h" #include "multiif.h"
#include "rawstr.h" #include "rawstr.h"
#include "content_encoding.h" #include "content_encoding.h"
#include "rtsp.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #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. * 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; struct curl_slist *head;
size_t thislen = strlen(thisheader); size_t thislen = strlen(thisheader);
@ -565,9 +566,9 @@ output_auth_headers(struct connectdata *conn,
if(authstatus->picked == CURLAUTH_BASIC) { if(authstatus->picked == CURLAUTH_BASIC) {
/* Basic */ /* Basic */
if((proxy && conn->bits.proxy_user_passwd && if((proxy && conn->bits.proxy_user_passwd &&
!checkheaders(data, "Proxy-authorization:")) || !Curl_checkheaders(data, "Proxy-authorization:")) ||
(!proxy && conn->bits.user_passwd && (!proxy && conn->bits.user_passwd &&
!checkheaders(data, "Authorization:"))) { !Curl_checkheaders(data, "Authorization:"))) {
auth="Basic"; auth="Basic";
result = http_output_basic(conn, proxy); result = http_output_basic(conn, proxy);
if(result) if(result)
@ -960,41 +961,23 @@ static size_t readmoredata(char *buffer,
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* /* add_buffer functions */
* The add_buffer series of functions are used to build one large memory chunk
* from repeated function invokes. Used so that the entire HTTP request can
* be sent in one go.
*/
struct send_buffer {
char *buffer;
size_t size_max;
size_t size_used;
};
typedef struct send_buffer send_buffer;
static CURLcode add_custom_headers(struct connectdata *conn,
send_buffer *req_buffer);
static CURLcode
add_buffer(send_buffer *in, const void *inptr, size_t size);
/* /*
* add_buffer_init() sets up and returns a fine buffer struct * Curl_add_buffer_init() sets up and returns a fine buffer struct
*/ */
static Curl_send_buffer *Curl_add_buffer_init(void)
send_buffer *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. * Body data may be appended to the header data if desired.
* *
* Returns CURLcode * Returns CURLcode
*/ */
static CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
CURLcode add_buffer_send(send_buffer *in,
struct connectdata *conn, struct connectdata *conn,
long *bytes_written, /* add the number of sent bytes long *bytes_written, /* add the number of sent bytes
to this counter */ to this counter */
@ -1144,8 +1127,7 @@ CURLcode add_buffer_send(send_buffer *in,
/* /*
* add_bufferf() add the formatted input to the buffer. * add_bufferf() add the formatted input to the buffer.
*/ */
static CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
{ {
char *s; char *s;
va_list ap; va_list ap;
@ -1154,7 +1136,7 @@ CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
va_end(ap); va_end(ap);
if(s) { if(s) {
CURLcode result = add_buffer(in, s, strlen(s)); CURLcode result = Curl_add_buffer(in, s, strlen(s));
free(s); free(s);
return result; 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 * add_buffer() appends a memory chunk to the existing buffer
*/ */
static CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
{ {
char *new_rb; char *new_rb;
size_t new_size; 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 */ /* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* /*
* Curl_compareheader() * Curl_compareheader()
* *
@ -1320,7 +1303,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
do { do {
if(!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */ if(!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
char *host_port; char *host_port;
send_buffer *req_buffer; Curl_send_buffer *req_buffer;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n", infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port); hostname, remote_port);
@ -1334,7 +1317,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
} }
/* initialize a dynamic send-buffer */ /* initialize a dynamic send-buffer */
req_buffer = add_buffer_init(); req_buffer = Curl_add_buffer_init();
if(!req_buffer) if(!req_buffer)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
@ -1355,7 +1338,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ? const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
"1.0" : "1.1"; "1.0" : "1.1";
if(!checkheaders(data, "Host:")) { if(!Curl_checkheaders(data, "Host:")) {
host = aprintf("Host: %s\r\n", host_port); host = aprintf("Host: %s\r\n", host_port);
if(!host) { if(!host) {
free(req_buffer); free(req_buffer);
@ -1363,17 +1346,17 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
} }
} }
if(!checkheaders(data, "Proxy-Connection:")) if(!Curl_checkheaders(data, "Proxy-Connection:"))
proxyconn = "Proxy-Connection: Keep-Alive\r\n"; proxyconn = "Proxy-Connection: Keep-Alive\r\n";
if(!checkheaders(data, "User-Agent:") && if(!Curl_checkheaders(data, "User-Agent:") &&
data->set.str[STRING_USERAGENT]) data->set.str[STRING_USERAGENT])
useragent = conn->allocptr.uagent; useragent = conn->allocptr.uagent;
/* Send the connect request to the proxy */ /* Send the connect request to the proxy */
/* BLOCKING */ /* BLOCKING */
result = result =
add_bufferf(req_buffer, Curl_add_bufferf(req_buffer,
"CONNECT %s:%d HTTP/%s\r\n" "CONNECT %s:%d HTTP/%s\r\n"
"%s" /* Host: */ "%s" /* Host: */
"%s" /* Proxy-Authorization */ "%s" /* Proxy-Authorization */
@ -1390,15 +1373,15 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
free(host); free(host);
if(CURLE_OK == result) if(CURLE_OK == result)
result = add_custom_headers(conn, req_buffer); result = Curl_add_custom_headers(conn, req_buffer);
if(CURLE_OK == result) if(CURLE_OK == result)
/* CRLF terminate the request */ /* CRLF terminate the request */
result = add_bufferf(req_buffer, "\r\n"); result = Curl_add_bufferf(req_buffer, "\r\n");
if(CURLE_OK == result) { if(CURLE_OK == result) {
/* Now send off the request */ /* 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); &data->info.request_size, 0, sockindex);
} }
req_buffer = NULL; req_buffer = NULL;
@ -1921,7 +1904,7 @@ CURLcode Curl_http_done(struct connectdata *conn,
return CURLE_OK; return CURLE_OK;
if(http->send_buffer) { if(http->send_buffer) {
send_buffer *buff = http->send_buffer; Curl_send_buffer *buff = http->send_buffer;
free(buff->buffer); free(buff->buffer);
free(buff); free(buff);
@ -1978,7 +1961,7 @@ static bool use_http_1_1(const struct SessionHandle *data,
/* check and possibly add an Expect: header */ /* check and possibly add an Expect: header */
static CURLcode expect100(struct SessionHandle *data, static CURLcode expect100(struct SessionHandle *data,
struct connectdata *conn, struct connectdata *conn,
send_buffer *req_buffer) Curl_send_buffer *req_buffer)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
const char *ptr; const char *ptr;
@ -1988,13 +1971,13 @@ static CURLcode expect100(struct SessionHandle *data,
/* if not doing HTTP 1.0 or disabled explicitly, we add a Expect: /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
100-continue to the headers which actually speeds up post operations 100-continue to the headers which actually speeds up post operations
(as there is one packet coming back from the web server) */ (as there is one packet coming back from the web server) */
ptr = checkheaders(data, "Expect:"); ptr = Curl_checkheaders(data, "Expect:");
if (ptr) { if (ptr) {
data->state.expect100header = data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue"); Curl_compareheader(ptr, "Expect:", "100-continue");
} }
else { else {
result = add_bufferf(req_buffer, result = Curl_add_bufferf(req_buffer,
"Expect: 100-continue\r\n"); "Expect: 100-continue\r\n");
if(result == CURLE_OK) if(result == CURLE_OK)
data->state.expect100header = TRUE; data->state.expect100header = TRUE;
@ -2003,8 +1986,8 @@ static CURLcode expect100(struct SessionHandle *data,
return result; return result;
} }
static CURLcode add_custom_headers(struct connectdata *conn, CURLcode Curl_add_custom_headers(struct connectdata *conn,
send_buffer *req_buffer) Curl_send_buffer *req_buffer)
{ {
char *ptr; char *ptr;
struct curl_slist *headers=conn->data->set.headers; 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)) checkprefix("Content-Length", headers->data))
; ;
else { 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) if(result)
return result; return result;
} }
@ -2047,6 +2031,58 @@ static CURLcode add_custom_headers(struct connectdata *conn,
return CURLE_OK; 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 * 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 * 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) CURLcode Curl_http(struct connectdata *conn, bool *done)
{ {
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is a short cut to the buffer */
CURLcode result=CURLE_OK; CURLcode result=CURLE_OK;
struct HTTP *http; struct HTTP *http;
const char *ppath = data->state.path; const char *ppath = data->state.path;
@ -2069,7 +2104,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
char *addcookies = NULL; char *addcookies = NULL;
curl_off_t included_body = 0; curl_off_t included_body = 0;
const char *httpstring; 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 */ curl_off_t postsize; /* off_t type to be able to hold a large file size */
int seekerr = CURL_SEEKFUNC_OK; 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 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 with the user-agent string specified, we erase the previously made string
here. */ here. */
if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent); free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL; conn->allocptr.uagent=NULL;
} }
@ -2161,15 +2196,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
conn->bits.authneg = FALSE; conn->bits.authneg = FALSE;
Curl_safefree(conn->allocptr.ref); 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); conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
else else
conn->allocptr.ref = NULL; 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]; addcookies = data->set.str[STRING_COOKIE];
if(!checkheaders(data, "Accept-Encoding:") && if(!Curl_checkheaders(data, "Accept-Encoding:") &&
data->set.str[STRING_ENCODING]) { data->set.str[STRING_ENCODING]) {
Curl_safefree(conn->allocptr.accept_encoding); Curl_safefree(conn->allocptr.accept_encoding);
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; return CURLE_OUT_OF_MEMORY;
} }
ptr = checkheaders(data, "Transfer-Encoding:"); ptr = Curl_checkheaders(data, "Transfer-Encoding:");
if(ptr) { if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */ /* Some kind of TE is requested, check if 'chunked' is chosen */
data->req.upload_chunky = data->req.upload_chunky =
@ -2191,7 +2226,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if (use_http_1_1(data, conn)) { if (use_http_1_1(data, conn)) {
/* HTTP, upload, unknown file size and not HTTP 1.0 */ /* HTTP, upload, unknown file size and not HTTP 1.0 */
data->req.upload_chunky = TRUE; data->req.upload_chunky = TRUE;
} else { }
else {
failf(data, "Chunky upload is not supported by HTTP 1.0"); failf(data, "Chunky upload is not supported by HTTP 1.0");
return CURLE_UPLOAD_FAILED; return CURLE_UPLOAD_FAILED;
} }
@ -2207,7 +2243,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
Curl_safefree(conn->allocptr.host); Curl_safefree(conn->allocptr.host);
ptr = checkheaders(data, "Host:"); ptr = Curl_checkheaders(data, "Host:");
if(ptr && (!data->state.this_is_a_follow || if(ptr && (!data->state.this_is_a_follow ||
Curl_raw_equal(data->state.first_host, conn->host.name))) { Curl_raw_equal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES) #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 /* 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 */ a size of the whole shebang before we start to send it */
result = Curl_getFormData(&http->sendit, data->set.httppost, result = Curl_getFormData(&http->sendit, data->set.httppost,
checkheaders(data, "Content-Type:"), Curl_checkheaders(data, "Content-Type:"),
&http->postsize); &http->postsize);
if(CURLE_OK != result) { if(CURLE_OK != result) {
/* Curl_getFormData() doesn't use failf() */ /* 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) || if(( (HTTPREQ_POST == httpreq) ||
(HTTPREQ_POST_FORM == httpreq) || (HTTPREQ_POST_FORM == httpreq) ||
@ -2427,7 +2463,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
* ones if any such are specified. * ones if any such are specified.
*/ */
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && 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 a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline) if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline); free(conn->allocptr.rangeline);
@ -2435,7 +2471,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
data->state.range); data->state.range);
} }
else if((httpreq != HTTPREQ_GET) && 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 a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline) 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"; httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
/* initialize a dynamic send-buffer */ /* initialize a dynamic send-buffer */
req_buffer = add_buffer_init(); req_buffer = Curl_add_buffer_init();
if(!req_buffer) if(!req_buffer)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
/* add the main request stuff */ /* add the main request stuff */
/* GET/HEAD/POST/PUT */ /* GET/HEAD/POST/PUT */
result = add_bufferf(req_buffer, "%s ", request); result = Curl_add_bufferf(req_buffer, "%s ", request);
if (result) if (result)
return result; return result;
/* url */ /* url */
if (paste_ftp_userpwd) 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); conn->user, conn->passwd, ppath + sizeof("ftp://") - 1);
else else
result = add_buffer(req_buffer, ppath, strlen(ppath)); result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
if (result) if (result)
return result; return result;
result = add_bufferf(req_buffer, result = Curl_add_bufferf(req_buffer,
"%s" /* ftp typecode (;type=x) */ "%s" /* ftp typecode (;type=x) */
" HTTP/%s\r\n" /* HTTP version */ " HTTP/%s\r\n" /* HTTP version */
"%s" /* proxyuserpwd */ "%s" /* proxyuserpwd */
@ -2532,7 +2568,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
conn->allocptr.ref:"" /* Referer: <data> */, conn->allocptr.ref:"" /* Referer: <data> */,
(conn->bits.httpproxy && (conn->bits.httpproxy &&
!conn->bits.tunnel_proxy && !conn->bits.tunnel_proxy &&
!checkheaders(data, "Proxy-Connection:"))? !Curl_checkheaders(data, "Proxy-Connection:"))?
"Proxy-Connection: Keep-Alive\r\n":"", "Proxy-Connection: Keep-Alive\r\n":"",
te te
); );
@ -2568,11 +2604,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
while(co) { while(co) {
if(co->value) { if(co->value) {
if(0 == count) { if(0 == count) {
result = add_bufferf(req_buffer, "Cookie: "); result = Curl_add_bufferf(req_buffer, "Cookie: ");
if(result) if(result)
break; break;
} }
result = add_bufferf(req_buffer, result = Curl_add_bufferf(req_buffer,
"%s%s=%s", count?"; ":"", "%s%s=%s", count?"; ":"",
co->name, co->value); co->name, co->value);
if(result) if(result)
@ -2585,16 +2621,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
} }
if(addcookies && (CURLE_OK == result)) { if(addcookies && (CURLE_OK == result)) {
if(!count) if(!count)
result = add_bufferf(req_buffer, "Cookie: "); result = Curl_add_bufferf(req_buffer, "Cookie: ");
if(CURLE_OK == result) { if(CURLE_OK == result) {
result = add_bufferf(req_buffer, "%s%s", result = Curl_add_bufferf(req_buffer, "%s%s",
count?"; ":"", count?"; ":"",
addcookies); addcookies);
count++; count++;
} }
} }
if(count && (CURLE_OK == result)) if(count && (CURLE_OK == result))
result = add_buffer(req_buffer, "\r\n", 2); result = Curl_add_buffer(req_buffer, "\r\n", 2);
if(result) if(result)
return result; return result;
@ -2602,54 +2638,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#endif #endif
if(data->set.timecondition) { if(data->set.timecondition) {
struct tm *tm; result = Curl_add_timecondition(data, req_buffer);
/* 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;
}
if(result) if(result)
return result; return result;
} }
result = add_custom_headers(conn, req_buffer); result = Curl_add_custom_headers(conn, req_buffer);
if(result) if(result)
return result; return result;
@ -2665,11 +2659,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
case HTTPREQ_POST_FORM: case HTTPREQ_POST_FORM:
if(!http->sendit || conn->bits.authneg) { if(!http->sendit || conn->bits.authneg) {
/* nothing to post! */ /* 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) if(result)
return result; return result;
result = add_buffer_send(req_buffer, conn, result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET); &data->info.request_size, 0, FIRSTSOCKET);
if(result) if(result)
failf(data, "Failed sending POST request"); failf(data, "Failed sending POST request");
@ -2701,7 +2695,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(!data->req.upload_chunky) { if(!data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */ /* 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", "Content-Length: %" FORMAT_OFF_T "\r\n",
http->postsize); http->postsize);
if(result) if(result)
@ -2725,13 +2719,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return CURLE_HTTP_POST_ERROR; return CURLE_HTTP_POST_ERROR;
} }
result = add_buffer(req_buffer, contentType, linelength); result = Curl_add_buffer(req_buffer, contentType, linelength);
if(result) if(result)
return result; return result;
} }
/* make the request end in a true CRLF */ /* 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) if(result)
return result; return result;
@ -2739,7 +2733,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
Curl_pgrsSetUploadSize(data, http->postsize); Curl_pgrsSetUploadSize(data, http->postsize);
/* fire away the whole request to the server */ /* 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); &data->info.request_size, 0, FIRSTSOCKET);
if(result) if(result)
failf(data, "Failed sending POST request"); 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) { if((postsize != -1) && !data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */ /* 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", "Content-Length: %" FORMAT_OFF_T "\r\n",
postsize ); postsize );
if(result) if(result)
@ -2784,7 +2778,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(result) if(result)
return 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) if(result)
return result; return result;
@ -2792,7 +2786,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
Curl_pgrsSetUploadSize(data, postsize); Curl_pgrsSetUploadSize(data, postsize);
/* this sends the buffer and frees all the buffer resources */ /* 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); &data->info.request_size, 0, FIRSTSOCKET);
if(result) if(result)
failf(data, "Failed sending PUT request"); 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 we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */ 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, /* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */ 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", "Content-Length: %" FORMAT_OFF_T"\r\n",
postsize); postsize);
if(result) if(result)
@ -2833,8 +2827,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
} }
} }
if(!checkheaders(data, "Content-Type:")) { if(!Curl_checkheaders(data, "Content-Type:")) {
result = add_bufferf(req_buffer, result = Curl_add_bufferf(req_buffer,
"Content-Type: application/x-www-form-urlencoded\r\n"); "Content-Type: application/x-www-form-urlencoded\r\n");
if(result) if(result)
return 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 the somewhat bigger ones we allow the app to disable it. Just make
sure that the expect100header is always set to the preferred value sure that the expect100header is always set to the preferred value
here. */ here. */
ptr = checkheaders(data, "Expect:"); ptr = Curl_checkheaders(data, "Expect:");
if(ptr) { if(ptr) {
data->state.expect100header = data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue"); 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 is no magic limit but only set to prevent really huge POSTs to
get the data duplicated with malloc() and family. */ 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) if(result)
return result; return result;
if(!data->req.upload_chunky) { if(!data->req.upload_chunky) {
/* We're not sending it 'chunked', append it to the request /* We're not sending it 'chunked', append it to the request
already now to reduce the number if send() calls */ 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); (size_t)postsize);
included_body = postsize; included_body = postsize;
} }
else { else {
/* Append the POST data chunky-style */ /* 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) if(CURLE_OK == result)
result = add_buffer(req_buffer, data->set.postfields, result = Curl_add_buffer(req_buffer, data->set.postfields,
(size_t)postsize); (size_t)postsize);
if(CURLE_OK == result) if(CURLE_OK == result)
result = add_buffer(req_buffer, result = Curl_add_buffer(req_buffer,
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7); "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
/* CR LF 0 CR LF CR LF */ /* CR LF 0 CR LF CR LF */
included_body = postsize + 7; included_body = postsize + 7;
@ -2907,20 +2901,20 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* set the upload size to the progress meter */ /* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize); 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) if(result)
return result; return result;
} }
} }
else { 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) if(result)
return result; return result;
if(data->req.upload_chunky && conn->bits.authneg) { if(data->req.upload_chunky && conn->bits.authneg) {
/* Chunky upload is selected and we're negotiating auth still, send /* Chunky upload is selected and we're negotiating auth still, send
end-of-data only */ end-of-data only */
result = add_buffer(req_buffer, result = Curl_add_buffer(req_buffer,
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7); "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
/* CR LF 0 CR LF CR LF */ /* CR LF 0 CR LF CR LF */
if(result) if(result)
@ -2941,7 +2935,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
} }
} }
/* issue the request */ /* 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); (size_t)included_body, FIRSTSOCKET);
if(result) if(result)
@ -2955,12 +2949,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
break; break;
default: default:
result = add_buffer(req_buffer, "\r\n", 2); result = Curl_add_buffer(req_buffer, "\r\n", 2);
if(result) if(result)
return result; return result;
/* issue the request */ /* issue the request */
result = add_buffer_send(req_buffer, conn, result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET); &data->info.request_size, 0, FIRSTSOCKET);
if(result) if(result)
@ -2999,12 +2993,11 @@ checkhttpprefix(struct SessionHandle *data,
bool rc = FALSE; bool rc = FALSE;
#ifdef CURL_DOES_CONVERSIONS #ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding using a scratch area */ /* convert from the network encoding using a scratch area */
char *scratch = calloc(1, strlen(s)+1); char *scratch = strdup(s);
if(NULL == scratch) { 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 */ 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)) { if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
/* Curl_convert_from_network calls failf if unsuccessful */ /* Curl_convert_from_network calls failf if unsuccessful */
free(scratch); free(scratch);
@ -3031,6 +3024,51 @@ checkhttpprefix(struct SessionHandle *data,
return rc; 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_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 * header. We make sure that the full string fit in the allocated header
@ -3106,9 +3144,9 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
return result; return result;
if(!k->headerline && (k->hbuflen>5)) { if(!k->headerline && (k->hbuflen>5)) {
/* make a first check that this looks like a HTTP header */ /* make a first check that this looks like a protocol header */
if(!checkhttpprefix(data, data->state.headerbuff)) { if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */ /* this is not the beginning of a protocol first header line */
k->header = FALSE; k->header = FALSE;
k->badheader = HEADER_ALLBAD; k->badheader = HEADER_ALLBAD;
break; break;
@ -3140,8 +3178,8 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
if(!k->headerline) { if(!k->headerline) {
/* the first read header */ /* the first read header */
if((k->hbuflen>5) && if((k->hbuflen>5) &&
!checkhttpprefix(data, data->state.headerbuff)) { !checkprotoprefix(data, conn, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */ /* this is not the beginning of a protocol first header line */
k->header = FALSE; k->header = FALSE;
if(*nread) if(*nread)
/* since there's more, this is a partial bad header */ /* 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! */ k->header = FALSE; /* no more header to parse! */
if((k->size == -1) && !k->chunk && !conn->bits.close && 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 /* On HTTP 1.1, when connection is not to get closed, but no
Content-Length nor Content-Encoding chunked have been Content-Length nor Content-Encoding chunked have been
received, according to RFC2616 section 4.4 point 5, we 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); Curl_pgrsSetDownloadSize(data, k->size);
k->maxdownload = k->size; k->maxdownload = k->size;
} }
/* If max download size is *zero* (nothing) we already /* If max download size is *zero* (nothing) we already
have nothing and can safely return ok now! */ have nothing and can safely return ok now! */
if(0 == k->maxdownload) 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 /* This is the first header, it MUST be the error code line
or else we consider this to be the body right away! */ or else we consider this to be the body right away! */
int httpversion_major; int httpversion_major;
int rtspversion_major;
int nc; int nc;
#ifdef CURL_DOES_CONVERSIONS #ifdef CURL_DOES_CONVERSIONS
#define HEADER1 scratch #define HEADER1 scratch
@ -3365,6 +3405,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
#define HEADER1 k->p /* no conversion needed, just use k->p */ #define HEADER1 k->p /* no conversion needed, just use k->p */
#endif /* CURL_DOES_CONVERSIONS */ #endif /* CURL_DOES_CONVERSIONS */
if(conn->protocol & PROT_HTTP) {
nc = sscanf(HEADER1, nc = sscanf(HEADER1,
" HTTP/%d.%d %3d", " HTTP/%d.%d %3d",
&httpversion_major, &httpversion_major,
@ -3391,9 +3432,26 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
} }
} }
} }
}
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) { if(nc) {
data->info.httpcode = k->httpcode; data->info.httpcode = k->httpcode;
data->info.httpversion = conn->httpversion; data->info.httpversion = conn->httpversion;
if (!data->state.httpversion || if (!data->state.httpversion ||
data->state.httpversion > conn->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 */ conn->bits.close = TRUE; /* close when done */
} }
else if(Curl_compareheader(k->p, else if(Curl_compareheader(k->p, "Transfer-Encoding:", "chunked") &&
"Transfer-Encoding:", "chunked")) { !(conn->protocol & PROT_RTSP)) {
/* /*
* [RFC 2616, section 3.6.1] A 'chunked' transfer encoding * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
* means that the server will send a series of "chunks". Each * 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. * End of header-checks. Write them to the client.
*/ */
@ -3741,4 +3805,3 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
return CURLE_OK; return CURLE_OK;
} }
#endif

View File

@ -35,8 +35,40 @@ bool Curl_compareheader(const char *headerline, /* line to check */
const char *header, /* header keyword _with_ colon */ const char *header, /* header keyword _with_ colon */
const char *content); /* content string to find */ const char *content); /* content string to find */
char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader);
char *Curl_copy_header_value(const char *h); 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 */ /* ftp can use this as well */
CURLcode Curl_proxyCONNECT(struct connectdata *conn, CURLcode Curl_proxyCONNECT(struct connectdata *conn,
int tunnelsocket, int tunnelsocket,

753
lib/rtsp.c Normal file
View File

@ -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 */

77
lib/rtsp.h Normal file
View File

@ -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_ */

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -43,6 +43,7 @@
#include "sslgen.h" #include "sslgen.h"
#include "ssh.h" #include "ssh.h"
#include "multiif.h" #include "multiif.h"
#include "rtsp.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * 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 failf Curl_failf
#define CLIENTWRITE_BODY 1 #define CLIENTWRITE_BODY (1<<0)
#define CLIENTWRITE_HEADER 2 #define CLIENTWRITE_HEADER (1<<1)
#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) #define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr,
size_t len); size_t len);
CURLcode Curl_rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
/* internal read-function, does plain socket only */ /* internal read-function, does plain socket only */
int Curl_read_plain(curl_socket_t sockfd, int Curl_read_plain(curl_socket_t sockfd,
char *buf, char *buf,

View File

@ -169,6 +169,7 @@
# define CURL_DISABLE_TELNET # define CURL_DISABLE_TELNET
# define CURL_DISABLE_DICT # define CURL_DISABLE_DICT
# define CURL_DISABLE_FILE # define CURL_DISABLE_FILE
# define CURL_DISABLE_RTSP
#endif #endif
/* ================================================================ */ /* ================================================================ */

View File

@ -270,6 +270,12 @@ curl_easy_strerror(CURLcode error)
case CURLE_AGAIN: case CURLE_AGAIN:
return "Socket not ready for send/recv"; 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 */ /* error codes not used by current libcurl */
case CURLE_OBSOLETE4: case CURLE_OBSOLETE4:
case CURLE_OBSOLETE10: case CURLE_OBSOLETE10:

View File

@ -103,6 +103,7 @@
#include "select.h" #include "select.h"
#include "multiif.h" #include "multiif.h"
#include "easyif.h" /* for Curl_convert_to_network prototype */ #include "easyif.h" /* for Curl_convert_to_network prototype */
#include "rtsp.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -124,7 +125,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
#ifdef CURL_DOES_CONVERSIONS #ifdef CURL_DOES_CONVERSIONS
bool sending_http_headers = FALSE; bool sending_http_headers = FALSE;
if((conn->protocol&PROT_HTTP) && if((conn->protocol&(PROT_HTTP|PROT_RTSP)) &&
(data->state.proto.http->sending == HTTPSEND_REQUEST)) { (data->state.proto.http->sending == HTTPSEND_REQUEST)) {
/* We're sending the HTTP request headers, not the data. /* We're sending the HTTP request headers, not the data.
Remember that so we don't re-translate them into garbage. */ 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; CURLcode result = CURLE_OK;
ssize_t nread; /* number of bytes read */ ssize_t nread; /* number of bytes read */
bool is_empty_data = FALSE; bool is_empty_data = FALSE;
bool readmore = FALSE; /* used by RTP to signal for more data */
*done = FALSE; *done = FALSE;
@ -435,6 +437,19 @@ static CURLcode readwrite_data(struct SessionHandle *data,
in the flow below before the actual storing is done. */ in the flow below before the actual storing is done. */
k->str = k->buf; 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 #ifndef CURL_DISABLE_HTTP
/* Since this is a two-state thing, we check if we are parsing /* Since this is a two-state thing, we check if we are parsing
headers at the moment or not. */ headers at the moment or not. */
@ -456,11 +471,12 @@ static CURLcode readwrite_data(struct SessionHandle *data,
is non-headers. */ is non-headers. */
if(k->str && !k->header && (nread > 0 || is_empty_data)) { if(k->str && !k->header && (nread > 0 || is_empty_data)) {
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
if(0 == k->bodywrites && !is_empty_data) { if(0 == k->bodywrites && !is_empty_data) {
/* These checks are only made the first time we are about to /* These checks are only made the first time we are about to
write a piece of the body */ write a piece of the body */
if(conn->protocol&PROT_HTTP) { if(conn->protocol&(PROT_HTTP|PROT_RTSP)) {
/* HTTP-only checks */ /* HTTP-only checks */
if(data->req.newurl) { if(data->req.newurl) {
@ -747,7 +763,7 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
break; break;
} }
if(conn->protocol&PROT_HTTP) { if(conn->protocol&(PROT_HTTP|PROT_RTSP)) {
if(data->state.proto.http->sending == HTTPSEND_REQUEST) if(data->state.proto.http->sending == HTTPSEND_REQUEST)
/* We're sending the HTTP request headers, not the data. /* We're sending the HTTP request headers, not the data.
Remember that so we don't change the line endings. */ Remember that so we don't change the line endings. */
@ -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 /* 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 */ 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; return CURLE_OK;
if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry || if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
((data->req.bytecount + ((data->req.bytecount +
data->req.headerbytecount == 0) && data->req.headerbytecount == 0) &&
conn->bits.reuse && conn->bits.reuse &&
!data->set.opt_no_body)) { !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 /* 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 "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 done using it before, but that was closed when we wanted to read from

161
lib/url.c
View File

@ -136,6 +136,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#include "inet_ntop.h" #include "inet_ntop.h"
#include "http_ntlm.h" #include "http_ntlm.h"
#include "socks.h" #include "socks.h"
#include "rtsp.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -226,6 +227,10 @@ static const struct Curl_handler * const protocols[] = {
#endif #endif
#endif #endif
#ifndef CURL_DISABLE_RTSP
&Curl_handler_rtsp,
#endif
(struct Curl_handler *) NULL (struct Curl_handler *) NULL
}; };
@ -699,6 +704,7 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
set->maxredirs = -1; /* allow any amount by default */ set->maxredirs = -1; /* allow any amount by default */
set->httpreq = HTTPREQ_GET; /* Default HTTP request */ 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_epsv = TRUE; /* FTP defaults to EPSV operations */
set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ 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 *); data->set.mail_rcpt = va_arg(param, struct curl_slist *);
break; 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: default:
/* unknown tag and its companion, just ignore: */ /* unknown tag and its companion, just ignore: */
result = CURLE_FAILED_INIT; /* correct this */ 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.ref);
Curl_safefree(conn->allocptr.host); Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost); Curl_safefree(conn->allocptr.cookiehost);
Curl_safefree(conn->allocptr.rtsp_transport);
Curl_safefree(conn->trailer); Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc); /* host name buffer */ Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
@ -2500,6 +2615,42 @@ static bool SocketIsDead(curl_socket_t sock)
return ret_val; 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) static bool IsPipeliningPossible(const struct SessionHandle *handle)
{ {
if(handle->multi && Curl_multi_canPipeline(handle->multi) && 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 /* The check for a dead socket makes sense only if there are no
handles in pipeline and the connection isn't already marked in handles in pipeline and the connection isn't already marked in
use */ 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) { if(dead) {
check->data = data; check->data = data;
infof(data, "Connection #%d seems to be dead!\n", i); infof(data, "Connection #%d seems to be dead!\n", i);

View File

@ -43,6 +43,7 @@
#define PORT_POP3S 995 #define PORT_POP3S 995
#define PORT_SMTP 25 #define PORT_SMTP 25
#define PORT_SMTPS 465 /* sometimes called SSMTP */ #define PORT_SMTPS 465 /* sometimes called SSMTP */
#define PORT_RTSP 554
#define DICT_MATCH "/MATCH:" #define DICT_MATCH "/MATCH:"
#define DICT_MATCH2 "/M:" #define DICT_MATCH2 "/M:"
@ -147,6 +148,7 @@
#include "file.h" #include "file.h"
#include "ssh.h" #include "ssh.h"
#include "http.h" #include "http.h"
#include "rtsp.h"
#ifdef HAVE_GSSAPI #ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU # ifdef HAVE_GSSGNU
@ -510,7 +512,8 @@ struct SingleRequest {
bool content_range; /* set TRUE if Content-Range: was found */ bool content_range; /* set TRUE if Content-Range: was found */
curl_off_t offset; /* possible resume offset read from the curl_off_t offset; /* possible resume offset read from the
Content-Range: header */ 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 */ struct timeval start100; /* time stamp to wait for the 100 code from */
enum expect100 exp100; /* expect 100 continue state */ enum expect100 exp100; /* expect 100 continue state */
@ -672,8 +675,9 @@ struct connectdata {
#define PROT_POP3S CURLPROTO_POP3S #define PROT_POP3S CURLPROTO_POP3S
#define PROT_SMTP CURLPROTO_SMTP #define PROT_SMTP CURLPROTO_SMTP
#define PROT_SMTPS CURLPROTO_SMTPS #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. */ sure we use "private bits" above the public ones to make things easier. */
#define PROT_EXTMASK 0xfffff #define PROT_EXTMASK 0xfffff
@ -719,6 +723,7 @@ struct connectdata {
curl_proxytype proxytype; /* what kind of proxy that is in use */ 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 now; /* "current" time */
struct timeval created; /* creation time */ struct timeval created; /* creation time */
@ -750,6 +755,7 @@ struct connectdata {
char *ref; /* free later if not NULL! */ char *ref; /* free later if not NULL! */
char *host; /* free later if not NULL */ char *host; /* free later if not NULL */
char *cookiehost; /* free later if not NULL */ char *cookiehost; /* free later if not NULL */
char *rtsp_transport; /* free later if not NULL */
} allocptr; } allocptr;
int sec_complete; /* if kerberos is enabled for this connection */ int sec_complete; /* if kerberos is enabled for this connection */
@ -825,6 +831,7 @@ struct connectdata {
struct imap_conn imapc; struct imap_conn imapc;
struct pop3_conn pop3c; struct pop3_conn pop3c;
struct smtp_conn smtpc; struct smtp_conn smtpc;
struct rtsp_conn rtspc;
} proto; } proto;
int cselect_bits; /* bitmask of socket events */ int cselect_bits; /* bitmask of socket events */
@ -844,7 +851,7 @@ struct connectdata {
* Struct to keep statistical and informational data. * Struct to keep statistical and informational data.
*/ */
struct PureInfo { 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 httpproxycode; /* response code from proxy when received separate */
int httpversion; /* the http version number X.Y = X*10+Y */ 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 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 */ HTTPREQ_LAST /* last in list */
} Curl_HttpReq; } 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 * Values that are generated, temporary or calculated internally for a
* "session handle" must be defined within the 'struct UrlState'. This struct * "session handle" must be defined within the 'struct UrlState'. This struct
@ -1065,6 +1088,11 @@ struct UrlState {
this syntax. */ this syntax. */
curl_off_t resume_from; /* continue [ftp] transfer from here */ 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. /* Protocol specific data.
* *
************************************************************************* *************************************************************************
@ -1075,6 +1103,7 @@ struct UrlState {
union { union {
struct HTTP *http; struct HTTP *http;
struct HTTP *https; /* alias, just for the sake of being more readable */ struct HTTP *https; /* alias, just for the sake of being more readable */
struct RTSP *rtsp;
struct FTP *ftp; struct FTP *ftp;
/* void *tftp; not used */ /* void *tftp; not used */
struct FILEPROTO *file; struct FILEPROTO *file;
@ -1125,7 +1154,7 @@ enum dupstring {
STRING_CERT_TYPE, /* format for certificate (default: PEM)*/ STRING_CERT_TYPE, /* format for certificate (default: PEM)*/
STRING_COOKIE, /* HTTP cookie string to send */ STRING_COOKIE, /* HTTP cookie string to send */
STRING_COOKIEJAR, /* dump all cookies to this file */ 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_DEVICE, /* local network interface/address to use */
STRING_ENCODING, /* Accept-Encoding string */ STRING_ENCODING, /* Accept-Encoding string */
STRING_FTP_ACCOUNT, /* ftp account data */ STRING_FTP_ACCOUNT, /* ftp account data */
@ -1156,6 +1185,9 @@ enum dupstring {
STRING_PROXYPASSWORD, /* Proxy <password>, if used */ STRING_PROXYPASSWORD, /* Proxy <password>, if used */
STRING_NOPROXY, /* List of hosts which should not use the proxy, if STRING_NOPROXY, /* List of hosts which should not use the proxy, if
used */ 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 #ifdef USE_LIBSSH2
STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
STRING_SSH_PUBLIC_KEY, /* path to the public 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 *out; /* the fetched file goes here */
void *in; /* the uploaded file is read from here */ void *in; /* the uploaded file is read from here */
void *writeheader; /* write the header to this if non-NULL */ 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 use_port; /* which port to use (when not using default) */
long httpauth; /* what kind of HTTP authentication to use (bitmask) */ long httpauth; /* what kind of HTTP authentication to use (bitmask) */
long proxyauth; /* what kind of proxy 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 */ 'localport' one can't be bind()ed */
curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_func; /* function that stores the output */
curl_write_callback fwrite_header; /* function that stores headers */ 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_read_callback fread_func; /* function that reads the input */
curl_progress_callback fprogress; /* function for progress information */ curl_progress_callback fprogress; /* function for progress information */
curl_debug_callback fdebug; /* function that write informational data */ 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 */ long socks5_gssapi_nec; /* flag to support nec socks5 server */
#endif #endif
struct curl_slist *mail_rcpt; /* linked list of mail recipients */ 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 { struct Names {

View File

@ -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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -143,6 +143,9 @@ static const char * const protocols[] = {
#ifndef CURL_DISABLE_FILE #ifndef CURL_DISABLE_FILE
"file", "file",
#endif #endif
#ifndef CURL_DISABLE_RTSP
"rtsp",
#endif
#ifdef USE_SSL #ifdef USE_SSL
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP