1/***************************************************************************
2 *                      _   _ ____  _
3 *  Project         ___| | | |  _ \| |
4 *                 / __| | | | |_) | |
5 *                | (__| |_| |  _ <| |___
6 *                 \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com>
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "setup.h"
24
25#ifdef USE_LIBRTMP
26
27#include "urldata.h"
28#include "nonblock.h" /* for curlx_nonblock */
29#include "progress.h" /* for Curl_pgrsSetUploadSize */
30#include "transfer.h"
31#include <curl/curl.h>
32#include <librtmp/rtmp.h>
33
34#define _MPRINTF_REPLACE /* use our functions only */
35#include <curl/mprintf.h>
36
37#include "curl_memory.h"
38/* The last #include file should be: */
39#include "memdebug.h"
40
41#ifdef _WIN32
42#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
43#define SET_RCVTIMEO(tv,s)   int tv = s*1000
44#else
45#define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
46#endif
47
48#define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
49
50static CURLcode rtmp_setup(struct connectdata *conn);
51static CURLcode rtmp_do(struct connectdata *conn, bool *done);
52static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature);
53static CURLcode rtmp_connect(struct connectdata *conn, bool *done);
54static CURLcode rtmp_disconnect(struct connectdata *conn, bool dead);
55
56static Curl_recv rtmp_recv;
57static Curl_send rtmp_send;
58
59/*
60 * RTMP protocol handler.h, based on http://rtmpdump.mplayerhq.hu
61 */
62
63const struct Curl_handler Curl_handler_rtmp = {
64  "RTMP",                               /* scheme */
65  rtmp_setup,                           /* setup_connection */
66  rtmp_do,                              /* do_it */
67  rtmp_done,                            /* done */
68  ZERO_NULL,                            /* do_more */
69  rtmp_connect,                         /* connect_it */
70  ZERO_NULL,                            /* connecting */
71  ZERO_NULL,                            /* doing */
72  ZERO_NULL,                            /* proto_getsock */
73  ZERO_NULL,                            /* doing_getsock */
74  ZERO_NULL,                            /* perform_getsock */
75  rtmp_disconnect,                      /* disconnect */
76  ZERO_NULL,                            /* readwrite */
77  PORT_RTMP,                            /* defport */
78  CURLPROTO_RTMP,                       /* protocol */
79  PROTOPT_NONE                          /* flags*/
80};
81
82const struct Curl_handler Curl_handler_rtmpt = {
83  "RTMPT",                              /* scheme */
84  rtmp_setup,                           /* setup_connection */
85  rtmp_do,                              /* do_it */
86  rtmp_done,                            /* done */
87  ZERO_NULL,                            /* do_more */
88  rtmp_connect,                         /* connect_it */
89  ZERO_NULL,                            /* connecting */
90  ZERO_NULL,                            /* doing */
91  ZERO_NULL,                            /* proto_getsock */
92  ZERO_NULL,                            /* doing_getsock */
93  ZERO_NULL,                            /* perform_getsock */
94  rtmp_disconnect,                      /* disconnect */
95  ZERO_NULL,                            /* readwrite */
96  PORT_RTMPT,                           /* defport */
97  CURLPROTO_RTMPT,                      /* protocol */
98  PROTOPT_NONE                          /* flags*/
99};
100
101const struct Curl_handler Curl_handler_rtmpe = {
102  "RTMPE",                              /* scheme */
103  rtmp_setup,                           /* setup_connection */
104  rtmp_do,                              /* do_it */
105  rtmp_done,                            /* done */
106  ZERO_NULL,                            /* do_more */
107  rtmp_connect,                         /* connect_it */
108  ZERO_NULL,                            /* connecting */
109  ZERO_NULL,                            /* doing */
110  ZERO_NULL,                            /* proto_getsock */
111  ZERO_NULL,                            /* doing_getsock */
112  ZERO_NULL,                            /* perform_getsock */
113  rtmp_disconnect,                      /* disconnect */
114  ZERO_NULL,                            /* readwrite */
115  PORT_RTMP,                            /* defport */
116  CURLPROTO_RTMPE,                      /* protocol */
117  PROTOPT_NONE                          /* flags*/
118};
119
120const struct Curl_handler Curl_handler_rtmpte = {
121  "RTMPTE",                             /* scheme */
122  rtmp_setup,                           /* setup_connection */
123  rtmp_do,                              /* do_it */
124  rtmp_done,                            /* done */
125  ZERO_NULL,                            /* do_more */
126  rtmp_connect,                         /* connect_it */
127  ZERO_NULL,                            /* connecting */
128  ZERO_NULL,                            /* doing */
129  ZERO_NULL,                            /* proto_getsock */
130  ZERO_NULL,                            /* doing_getsock */
131  ZERO_NULL,                            /* perform_getsock */
132  rtmp_disconnect,                      /* disconnect */
133  ZERO_NULL,                            /* readwrite */
134  PORT_RTMPT,                           /* defport */
135  CURLPROTO_RTMPTE,                     /* protocol */
136  PROTOPT_NONE                          /* flags*/
137};
138
139const struct Curl_handler Curl_handler_rtmps = {
140  "RTMPS",                              /* scheme */
141  rtmp_setup,                           /* setup_connection */
142  rtmp_do,                              /* do_it */
143  rtmp_done,                            /* done */
144  ZERO_NULL,                            /* do_more */
145  rtmp_connect,                         /* connect_it */
146  ZERO_NULL,                            /* connecting */
147  ZERO_NULL,                            /* doing */
148  ZERO_NULL,                            /* proto_getsock */
149  ZERO_NULL,                            /* doing_getsock */
150  ZERO_NULL,                            /* perform_getsock */
151  rtmp_disconnect,                      /* disconnect */
152  ZERO_NULL,                            /* readwrite */
153  PORT_RTMPS,                           /* defport */
154  CURLPROTO_RTMPS,                      /* protocol */
155  PROTOPT_NONE                          /* flags*/
156};
157
158const struct Curl_handler Curl_handler_rtmpts = {
159  "RTMPTS",                             /* scheme */
160  rtmp_setup,                           /* setup_connection */
161  rtmp_do,                              /* do_it */
162  rtmp_done,                            /* done */
163  ZERO_NULL,                            /* do_more */
164  rtmp_connect,                         /* connect_it */
165  ZERO_NULL,                            /* connecting */
166  ZERO_NULL,                            /* doing */
167  ZERO_NULL,                            /* proto_getsock */
168  ZERO_NULL,                            /* doing_getsock */
169  ZERO_NULL,                            /* perform_getsock */
170  rtmp_disconnect,                      /* disconnect */
171  ZERO_NULL,                            /* readwrite */
172  PORT_RTMPS,                           /* defport */
173  CURLPROTO_RTMPTS,                     /* protocol */
174  PROTOPT_NONE                          /* flags*/
175};
176
177static CURLcode rtmp_setup(struct connectdata *conn)
178{
179  RTMP *r = RTMP_Alloc();
180
181  if(!r)
182    return CURLE_OUT_OF_MEMORY;
183
184  RTMP_Init(r);
185  RTMP_SetBufferMS(r, DEF_BUFTIME);
186  if(!RTMP_SetupURL(r, conn->data->change.url)) {
187    RTMP_Free(r);
188    return CURLE_URL_MALFORMAT;
189  }
190  conn->proto.generic = r;
191  return CURLE_OK;
192}
193
194static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
195{
196  RTMP *r = conn->proto.generic;
197  SET_RCVTIMEO(tv,10);
198
199  r->m_sb.sb_socket = conn->sock[FIRSTSOCKET];
200
201  /* We have to know if it's a write before we send the
202   * connect request packet
203   */
204  if(conn->data->set.upload)
205    r->Link.protocol |= RTMP_FEATURE_WRITE;
206
207  /* For plain streams, use the buffer toggle trick to keep data flowing */
208  if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
209     !(r->Link.protocol & RTMP_FEATURE_HTTP))
210    r->Link.lFlags |= RTMP_LF_BUFX;
211
212  curlx_nonblock(r->m_sb.sb_socket, FALSE);
213  setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
214             (char *)&tv, sizeof(tv));
215
216  if(!RTMP_Connect1(r, NULL))
217    return CURLE_FAILED_INIT;
218
219  /* Clients must send a periodic BytesReceived report to the server */
220  r->m_bSendCounter = true;
221
222  *done = TRUE;
223  conn->recv[FIRSTSOCKET] = rtmp_recv;
224  conn->send[FIRSTSOCKET] = rtmp_send;
225  return CURLE_OK;
226}
227
228static CURLcode rtmp_do(struct connectdata *conn, bool *done)
229{
230  RTMP *r = conn->proto.generic;
231
232  if(!RTMP_ConnectStream(r, 0))
233    return CURLE_FAILED_INIT;
234
235  if(conn->data->set.upload) {
236    Curl_pgrsSetUploadSize(conn->data, conn->data->set.infilesize);
237    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
238  }
239  else
240    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
241  *done = TRUE;
242  return CURLE_OK;
243}
244
245static CURLcode rtmp_done(struct connectdata *conn, CURLcode status,
246                          bool premature)
247{
248  (void)conn; /* unused */
249  (void)status; /* unused */
250  (void)premature; /* unused */
251
252  return CURLE_OK;
253}
254
255static CURLcode rtmp_disconnect(struct connectdata *conn,
256                                bool dead_connection)
257{
258  RTMP *r = conn->proto.generic;
259  (void)dead_connection;
260  if(r) {
261    conn->proto.generic = NULL;
262    RTMP_Close(r);
263    RTMP_Free(r);
264  }
265  return CURLE_OK;
266}
267
268static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf,
269                         size_t len, CURLcode *err)
270{
271  RTMP *r = conn->proto.generic;
272  ssize_t nread;
273
274  (void)sockindex; /* unused */
275
276  nread = RTMP_Read(r, buf, len);
277  if(nread < 0) {
278    if(r->m_read.status == RTMP_READ_COMPLETE ||
279        r->m_read.status == RTMP_READ_EOF) {
280      conn->data->req.size = conn->data->req.bytecount;
281      nread = 0;
282    }
283    else
284      *err = CURLE_RECV_ERROR;
285  }
286  return nread;
287}
288
289static ssize_t rtmp_send(struct connectdata *conn, int sockindex,
290                         const void *buf, size_t len, CURLcode *err)
291{
292  RTMP *r = conn->proto.generic;
293  ssize_t num;
294
295  (void)sockindex; /* unused */
296
297  num = RTMP_Write(r, (char *)buf, len);
298  if(num < 0)
299    *err = CURLE_SEND_ERROR;
300
301  return num;
302}
303#endif  /* USE_LIBRTMP */
304