Manual: Add stdin note, example
[quvi.git] / lib / curl_wrap.c
blob6420f8d6286526cc1dd885e7980fbf0a409ccbd9
1 /* quvi
2 * Copyright (C) 2009,2010 Toni Gundogdu <legatvs@gmail.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 #include "config.h"
22 #include <stdlib.h>
23 #include <memory.h>
24 #include <assert.h>
26 #include <curl/curl.h>
28 #include "quvi/quvi.h"
29 #include "internal.h"
30 #include "util.h"
31 #include "lua_wrap.h"
32 #include "curl_wrap.h"
34 static void *_realloc(void *p, const size_t size)
36 if (p)
37 return realloc(p, size);
38 return malloc(size);
41 struct mem_s
43 size_t size;
44 char *p;
47 size_t
48 quvi_write_callback_default(void *p, size_t size, size_t nmemb,
49 void *data)
51 struct mem_s *m = (struct mem_s *)data;
52 const size_t rsize = size * nmemb;
53 void *tmp = _realloc(m->p, m->size + rsize + 1);
54 if (tmp)
56 m->p = (char *)tmp;
57 memcpy(&(m->p[m->size]), p, rsize);
58 m->size += rsize;
59 m->p[m->size] = '\0';
61 return (rsize);
64 /* lua_wrap.c */
65 extern char *lua_get_field_s(lua_State *, const char *);
67 QUVIcode
68 fetch_to_mem(_quvi_media_t video, const char *url, lua_State * l,
69 char **dst)
71 char *fetch_type, *arbitrary_cookie, *user_agent;
72 QUVIstatusType fetch_type_n;
73 long respcode, conncode;
74 CURLcode curlcode;
75 struct mem_s mem;
76 QUVIcode rc;
78 if (!video)
79 return (QUVI_BADHANDLE);
81 if (!dst)
82 return (QUVI_INVARG);
84 *dst = NULL;
85 fetch_type = NULL;
86 user_agent = NULL;
87 arbitrary_cookie = NULL;
88 fetch_type_n = QUVISTATUSTYPE_PAGE; /* default */
90 /* Additional settings from LUA table */
92 if (lua_istable(l, 2))
94 fetch_type = lua_get_field_s(l, "fetch_type");
95 if (fetch_type)
97 if (strcmp(fetch_type, "config") == 0)
98 fetch_type_n = QUVISTATUSTYPE_CONFIG;
99 else if (strcmp(fetch_type, "playlist") == 0)
100 fetch_type_n = QUVISTATUSTYPE_PLAYLIST;
102 arbitrary_cookie = lua_get_field_s(l, "arbitrary_cookie");
103 user_agent = lua_get_field_s(l, "user_agent");
106 if (video->quvi->status_func)
108 if (video->quvi->
109 status_func(makelong(QUVISTATUS_FETCH, fetch_type_n),
110 (void *)url) != QUVI_OK)
112 return (QUVI_ABORTEDBYCALLBACK);
116 curl_easy_setopt(video->quvi->curl, CURLOPT_URL, url);
117 curl_easy_setopt(video->quvi->curl, CURLOPT_ENCODING, "");
119 memset(&mem, 0, sizeof(mem));
120 curl_easy_setopt(video->quvi->curl, CURLOPT_WRITEDATA, &mem);
122 curl_easy_setopt(video->quvi->curl, CURLOPT_WRITEFUNCTION,
123 video->quvi->write_func
124 ? (curl_write_callback) video->quvi->write_func
125 : (curl_write_callback) quvi_write_callback_default);
127 if (arbitrary_cookie != NULL && *arbitrary_cookie != '\0')
129 curl_easy_setopt(video->quvi->curl, CURLOPT_COOKIE,
130 arbitrary_cookie);
133 if (user_agent != NULL && *user_agent != '\0')
135 curl_easy_setopt(video->quvi->curl, CURLOPT_USERAGENT, user_agent);
138 curlcode = curl_easy_perform(video->quvi->curl);
139 respcode = 0;
140 conncode = 0;
141 rc = QUVI_OK;
143 curl_easy_getinfo(video->quvi->curl, CURLINFO_RESPONSE_CODE,
144 &respcode);
145 curl_easy_getinfo(video->quvi->curl, CURLINFO_HTTP_CONNECTCODE,
146 &conncode);
148 if (curlcode == CURLE_OK && respcode == 200)
150 if (video->quvi->status_func)
152 if (video->quvi->status_func(makelong(QUVISTATUS_FETCH,
153 QUVISTATUSTYPE_DONE),
154 0) != QUVI_OK)
156 rc = QUVI_ABORTEDBYCALLBACK;
160 else
162 if (curlcode == CURLE_OK)
164 freprintf(&video->quvi->errmsg,
165 "server response code %ld (conncode=%ld)", respcode,
166 conncode);
168 else
170 freprintf(&video->quvi->errmsg,
171 "%s (curlcode=%d, conncode=%ld)",
172 curl_easy_strerror(curlcode), curlcode, conncode);
175 rc = QUVI_CURL;
178 if (mem.p)
180 *dst = mem.p;
181 if (rc == QUVI_OK && !video->charset) /* charset */
182 run_lua_charset_func(video, mem.p);
185 video->quvi->httpcode = respcode;
186 video->quvi->curlcode = curlcode;
188 return (rc);
191 QUVIcode query_file_length(_quvi_t quvi, llst_node_t lnk)
193 static const char *scheme = "http://";
194 long respcode, conncode;
195 _quvi_video_link_t qvl;
196 CURLcode curlcode;
197 struct mem_s mem;
198 QUVIcode rc;
199 char buf[8];
201 if (!quvi)
202 return (QUVI_BADHANDLE);
204 if (!lnk)
205 return (QUVI_BADHANDLE);
207 qvl = (_quvi_video_link_t) lnk->data;
208 assert(qvl != NULL);
210 if (!qvl)
211 return (QUVI_BADHANDLE);
213 qvl->url = from_html_entities(qvl->url);
215 /* We can currently check HTTP URLs only */
216 memset(&buf, 0, sizeof(buf));
217 if (strcmp(strncpy(buf, qvl->url, strlen(scheme)), scheme) != 0)
218 return (QUVI_OK); /* Skip video URL verification discreetly. */
220 if (quvi->status_func)
222 if (quvi->status_func(makelong(QUVISTATUS_VERIFY, 0), 0) != QUVI_OK)
224 return (QUVI_ABORTEDBYCALLBACK);
228 curl_easy_setopt(quvi->curl, CURLOPT_URL, qvl->url);
229 curl_easy_setopt(quvi->curl, CURLOPT_NOBODY, 1L); /* get -> head */
231 memset(&mem, 0, sizeof(mem));
232 curl_easy_setopt(quvi->curl, CURLOPT_WRITEDATA, &mem);
234 curl_easy_setopt(quvi->curl, CURLOPT_WRITEFUNCTION, (quvi->write_func)
235 ? (curl_write_callback) quvi->write_func
236 : (curl_write_callback) quvi_write_callback_default);
238 curlcode = curl_easy_perform(quvi->curl);
240 curl_easy_setopt(quvi->curl, CURLOPT_HTTPGET, 1L); /* reset: head -> get */
242 respcode = 0;
243 conncode = 0;
244 rc = QUVI_OK;
246 curl_easy_getinfo(quvi->curl, CURLINFO_RESPONSE_CODE, &respcode);
247 curl_easy_getinfo(quvi->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
249 if (curlcode == CURLE_OK)
251 if (respcode == 200 || respcode == 206)
253 const char *ct;
255 curl_easy_getinfo(quvi->curl, CURLINFO_CONTENT_TYPE, &ct);
257 _free(qvl->content_type);
258 asprintf(&qvl->content_type, "%s", ct);
260 curl_easy_getinfo(quvi->curl,
261 CURLINFO_CONTENT_LENGTH_DOWNLOAD, &qvl->length);
263 if (quvi->status_func)
265 if (quvi->status_func(makelong
266 (QUVISTATUS_VERIFY, QUVISTATUSTYPE_DONE),
267 0) != QUVI_OK)
269 rc = QUVI_ABORTEDBYCALLBACK;
273 if (rc == QUVI_OK)
275 /* Content-Type -> suffix. */
276 rc = run_lua_suffix_func(quvi, qvl);
279 else
281 freprintf(&quvi->errmsg,
282 "server response code %ld (conncode=%ld)", respcode,
283 conncode);
284 rc = QUVI_CURL;
287 else
289 freprintf(&quvi->errmsg, "%s (curlcode=%d, conncode=%ld)",
290 curl_easy_strerror(curlcode), curlcode, conncode);
291 rc = QUVI_CURL;
294 quvi->httpcode = respcode;
295 quvi->curlcode = curlcode;
297 if (mem.p)
298 _free(mem.p);
300 return (rc);
304 * Check if URL is a shortened URL (e.g. dai.ly, goo.gl, etc.) and
305 * replace the (shortened) video page URL with the redirect URL.
307 QUVIcode is_shortened_url(_quvi_media_t video)
309 long respcode, conncode;
310 CURLcode curlcode;
311 struct mem_s mem;
312 QUVIcode rc;
314 assert(video != NULL);
316 if (video->quvi->status_func)
318 if (video->quvi->status_func(QUVISTATUS_SHORTENED, 0) != QUVI_OK)
319 return (QUVI_ABORTEDBYCALLBACK);
322 memset(&mem, 0, sizeof(mem));
323 curl_easy_setopt(video->quvi->curl, CURLOPT_WRITEDATA, &mem);
325 curl_easy_setopt(video->quvi->curl, CURLOPT_WRITEFUNCTION,
326 (video->quvi->write_func)
327 ? (curl_write_callback) video->quvi->write_func
328 : (curl_write_callback) quvi_write_callback_default);
330 curl_easy_setopt(video->quvi->curl, CURLOPT_URL, video->page_link);
331 curl_easy_setopt(video->quvi->curl, CURLOPT_FOLLOWLOCATION, 0L);
332 curl_easy_setopt(video->quvi->curl, CURLOPT_NOBODY, 1L); /* get -> head */
334 curlcode = curl_easy_perform(video->quvi->curl);
336 curl_easy_setopt(video->quvi->curl, CURLOPT_FOLLOWLOCATION, 1L); /* reset */
338 curl_easy_setopt(video->quvi->curl, CURLOPT_HTTPGET, 1L); /* reset: head -> get */
340 respcode = 0;
341 conncode = 0;
342 rc = QUVI_OK;
344 curl_easy_getinfo(video->quvi->curl, CURLINFO_RESPONSE_CODE,
345 &respcode);
346 curl_easy_getinfo(video->quvi->curl, CURLINFO_HTTP_CONNECTCODE,
347 &conncode);
349 if (curlcode == CURLE_OK)
351 if (respcode >= 301 && respcode <= 303)
353 /* A redirect. */
355 char *url = NULL;
357 curl_easy_getinfo(video->quvi->curl, CURLINFO_REDIRECT_URL, &url);
358 freprintf(&video->page_link, "%s", url);
360 rc = QUVI_OK;
362 /* respcode >= 301 && respcode <= 303 */
363 else
365 /* Most likely not a shortened URL redirect. Let it pass. */
366 rc = QUVI_OK;
369 if (video->quvi->status_func)
371 const long param =
372 makelong(QUVISTATUS_SHORTENED, QUVISTATUSTYPE_DONE);
374 rc = video->quvi->status_func(param, 0);
377 else
379 freprintf(&video->quvi->errmsg, "%s (curlcode=%d, conncode=%ld)",
380 curl_easy_strerror(curlcode), curlcode, conncode);
382 rc = QUVI_CURL;
385 if (mem.p)
386 _free(mem.p);
388 video->quvi->httpcode = respcode;
389 video->quvi->curlcode = curlcode;
391 return (rc);
394 /* vim: set ts=2 sw=2 tw=72 expandtab: */