Extend quvi.fetch to allow setting user-agent [11]
[quvi.git] / lib / curl_wrap.c
blobfe422ee2127c58a0c9724d5277e46e27ae2a305c
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 {
42 size_t size;
43 char *p;
46 size_t
47 quvi_write_callback_default(void *p, size_t size, size_t nmemb, void *data)
49 struct mem_s *m = (struct mem_s *)data;
50 const size_t rsize = size * nmemb;
51 void *tmp = _realloc(m->p, m->size + rsize + 1);
52 if (tmp) {
53 m->p = (char *)tmp;
54 memcpy(&(m->p[m->size]), p, rsize);
55 m->size += rsize;
56 m->p[m->size] = '\0';
58 return (rsize);
61 /* lua_wrap.c */
62 extern char *lua_get_field_s(lua_State *, const char *);
64 QUVIcode
65 fetch_to_mem(_quvi_video_t video, const char *url, lua_State * l, char **dst)
67 char *fetch_type, *arbitrary_cookie, *user_agent;
68 QUVIstatusType fetch_type_n;
69 long respcode, conncode;
70 CURLcode curlcode;
71 struct mem_s mem;
72 _quvi_t quvi;
73 QUVIcode rc;
75 if (!video)
76 return (QUVI_BADHANDLE);
78 quvi = video->quvi;
80 if (!quvi)
81 return (QUVI_BADHANDLE);
83 if (!dst)
84 return (QUVI_INVARG);
86 *dst = NULL;
87 fetch_type = NULL;
88 user_agent = NULL;
89 arbitrary_cookie = NULL;
90 fetch_type_n = QUVISTATUSTYPE_PAGE; /* default */
92 /* Additional settings from LUA table */
94 if (lua_istable(l, 2)) {
95 fetch_type = lua_get_field_s(l, "fetch_type");
96 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 (quvi->status_func) {
107 if (quvi->status_func(makelong(QUVISTATUS_FETCH, fetch_type_n),
108 (void *)url) != QUVI_OK) {
109 return (QUVI_ABORTEDBYCALLBACK);
113 csetopt(CURLOPT_URL, url);
114 csetopt(CURLOPT_ENCODING, "");
116 memset(&mem, 0, sizeof(mem));
118 csetopt(CURLOPT_WRITEDATA, &mem);
120 if (quvi->write_func)
121 csetopt(CURLOPT_WRITEFUNCTION, (curl_write_callback) quvi->write_func);
122 else
123 csetopt(CURLOPT_WRITEFUNCTION, quvi_write_callback_default);
125 if (arbitrary_cookie != NULL && *arbitrary_cookie != '\0')
126 csetopt(CURLOPT_COOKIE, arbitrary_cookie);
128 if (user_agent != NULL && *user_agent != '\0')
129 csetopt(CURLOPT_USERAGENT, user_agent);
131 curlcode = curl_easy_perform(quvi->curl);
132 respcode = 0;
133 conncode = 0;
134 rc = QUVI_OK;
136 curl_easy_getinfo(quvi->curl, CURLINFO_RESPONSE_CODE, &respcode);
138 curl_easy_getinfo(quvi->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
140 if (curlcode == CURLE_OK && respcode == 200) {
142 if (quvi->status_func) {
144 if (quvi->status_func(makelong(QUVISTATUS_FETCH, QUVISTATUSTYPE_DONE),
145 0) != QUVI_OK) {
146 rc = QUVI_ABORTEDBYCALLBACK;
153 else {
155 if (curlcode == CURLE_OK) {
156 seterr("server response code %ld (conncode=%ld)", respcode, conncode);
159 else {
160 seterr("%s (curlcode=%d, conncode=%ld)",
161 curl_easy_strerror(curlcode), curlcode, conncode);
164 rc = QUVI_CURL;
167 if (mem.p) {
168 *dst = mem.p;
169 if (rc == QUVI_OK && !video->charset) /* charset */
170 run_lua_charset_func(video, mem.p);
173 video->quvi->httpcode = respcode;
174 video->quvi->curlcode = curlcode;
176 return (rc);
179 QUVIcode query_file_length(_quvi_t quvi, llst_node_t lnk)
181 long respcode, conncode;
182 _quvi_video_link_t qvl;
183 CURLcode curlcode;
184 struct mem_s mem;
185 QUVIcode rc;
187 if (!quvi)
188 return (QUVI_BADHANDLE);
190 if (!lnk)
191 return (QUVI_BADHANDLE);
193 qvl = (_quvi_video_link_t) lnk->data;
194 assert(qvl != NULL);
196 if (!qvl)
197 return (QUVI_BADHANDLE);
199 if (quvi->status_func) {
200 if (quvi->status_func(makelong(QUVISTATUS_VERIFY, 0), 0) != QUVI_OK) {
201 return (QUVI_ABORTEDBYCALLBACK);
205 memset(&mem, 0, sizeof(mem));
207 csetopt(CURLOPT_WRITEDATA, &mem);
209 if (quvi->write_func)
210 csetopt(CURLOPT_WRITEFUNCTION, (curl_write_callback) quvi->write_func);
211 else
212 csetopt(CURLOPT_WRITEFUNCTION, quvi_write_callback_default);
214 qvl->url = from_html_entities(qvl->url);
216 csetopt(CURLOPT_URL, qvl->url);
217 csetopt(CURLOPT_NOBODY, 1L); /* get -> head */
219 curlcode = curl_easy_perform(quvi->curl);
221 csetopt(CURLOPT_HTTPGET, 1L); /* reset: head -> get */
223 respcode = 0;
224 conncode = 0;
225 rc = QUVI_OK;
227 curl_easy_getinfo(quvi->curl, CURLINFO_RESPONSE_CODE, &respcode);
229 curl_easy_getinfo(quvi->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
231 if (curlcode == CURLE_OK) {
233 if (respcode == 200 || respcode == 206) {
235 const char *ct;
237 curl_easy_getinfo(quvi->curl, CURLINFO_CONTENT_TYPE, &ct);
239 _free(qvl->content_type);
240 asprintf(&qvl->content_type, "%s", ct);
242 curl_easy_getinfo(quvi->curl,
243 CURLINFO_CONTENT_LENGTH_DOWNLOAD, &qvl->length);
245 if (quvi->status_func) {
246 if (quvi->status_func(makelong(QUVISTATUS_VERIFY, QUVISTATUSTYPE_DONE),
247 0) != QUVI_OK) {
248 rc = QUVI_ABORTEDBYCALLBACK;
252 if (rc == QUVI_OK) {
253 /* Content-Type -> suffix. */
254 rc = run_lua_suffix_func(quvi, qvl);
256 } else {
257 seterr("server response code %ld (conncode=%ld)", respcode, conncode);
258 rc = QUVI_CURL;
260 } else {
261 seterr("%s (curlcode=%d, conncode=%ld)",
262 curl_easy_strerror(curlcode), curlcode, conncode);
263 rc = QUVI_CURL;
266 quvi->httpcode = respcode;
267 quvi->curlcode = curlcode;
269 if (mem.p)
270 _free(mem.p);
272 return (rc);
276 * Check if URL is a shortened URL (e.g. dai.ly, goo.gl, etc.) and
277 * replace the (shortened) video page URL with the redirect URL.
279 QUVIcode is_shortened_url(_quvi_video_t video)
281 long respcode, conncode;
282 CURLcode curlcode;
283 struct mem_s mem;
284 _quvi_t quvi;
285 QUVIcode rc;
287 assert(video != NULL);
288 quvi = video->quvi;
289 assert(quvi != NULL);
291 if (quvi->status_func) {
293 const QUVIcode r = quvi->status_func(makelong(QUVISTATUS_SHORTENED, 0), 0);
295 if (r != QUVI_OK)
296 return (QUVI_ABORTEDBYCALLBACK);
300 memset(&mem, 0, sizeof(mem));
302 csetopt(CURLOPT_WRITEDATA, &mem);
304 if (quvi->write_func)
305 csetopt(CURLOPT_WRITEFUNCTION, (curl_write_callback) quvi->write_func);
306 else
307 csetopt(CURLOPT_WRITEFUNCTION, quvi_write_callback_default);
309 csetopt(CURLOPT_URL, video->page_link);
310 csetopt(CURLOPT_FOLLOWLOCATION, 0L);
311 csetopt(CURLOPT_NOBODY, 1L); /* get -> head */
313 curlcode = curl_easy_perform(quvi->curl);
315 csetopt(CURLOPT_FOLLOWLOCATION, 1L); /* reset */
316 csetopt(CURLOPT_HTTPGET, 1L); /* reset: head -> get */
318 respcode = 0;
319 conncode = 0;
320 rc = QUVI_OK;
322 curl_easy_getinfo(quvi->curl, CURLINFO_RESPONSE_CODE, &respcode);
323 curl_easy_getinfo(quvi->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
325 if (curlcode == CURLE_OK) {
327 if (respcode >= 301 && respcode <= 303) {
329 /* A redirect. */
331 char *url = NULL;
333 curl_easy_getinfo(quvi->curl, CURLINFO_REDIRECT_URL, &url);
334 setvid(video->page_link, "%s", url);
336 rc = QUVI_OK;
339 /* respcode >= 301 && respcode <= 303 */
340 else {
341 /* Most likely not a shortened URL redirect. Let it pass. */
342 rc = QUVI_OK;
345 if (quvi->status_func) {
347 rc = quvi->status_func(makelong
348 (QUVISTATUS_SHORTENED, QUVISTATUSTYPE_DONE), 0);
352 } else {
354 seterr("%s (curlcode=%d, conncode=%ld)",
355 curl_easy_strerror(curlcode), curlcode, conncode);
357 rc = QUVI_CURL;
360 if (mem.p)
361 _free(mem.p);
363 quvi->httpcode = respcode;
364 quvi->curlcode = curlcode;
366 return (rc);