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