1 /* libquvi
2 * Copyright (C) 2009-2011 Toni Gundogdu <legatvs@gmail.com>
3 *
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.
8 *
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.
13 *
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
18 */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <memory.h>
24 #include <assert.h>
25
26 #include "quvi/quvi.h"
27 #include "quvi/net.h"
28
29 #include "internal.h"
30 #include "util.h"
31 #include "lua_wrap.h"
32 #include "curl_wrap.h"
33
34 /* curl_init */
35
curl_init(_quvi_t q)36 QUVIcode curl_init(_quvi_t q)
37 {
38 curl_global_init(CURL_GLOBAL_ALL);
39
40 q->curl = curl_easy_init();
41 if (!q->curl)
42 return (QUVI_CURLINIT);
43
44 /* Set quvi defaults with libcurl. */
45 curl_easy_setopt(q->curl, CURLOPT_USERAGENT, "Mozilla/5.0");
46 curl_easy_setopt(q->curl, CURLOPT_FOLLOWLOCATION, 1L);
47 curl_easy_setopt(q->curl, CURLOPT_NOBODY, 0L);
48
49 return (QUVI_OK);
50 }
51
52 /* curl_close */
53
curl_close(_quvi_t q)54 void curl_close(_quvi_t q)
55 {
56 curl_easy_cleanup(q->curl);
57 q->curl = NULL;
58
59 curl_global_cleanup();
60 }
61
_realloc(void * p,const size_t size)62 static void *_realloc(void *p, const size_t size)
63 {
64 if (p) return realloc(p, size);
65 return malloc(size);
66 }
67
68 struct content_s
69 {
70 size_t size;
71 char *p;
72 };
73
74 typedef struct content_s *content_t;
75
quvi_write_callback_default(void * p,size_t size,size_t nmemb,void * data)76 size_t quvi_write_callback_default(void *p,
77 size_t size,
78 size_t nmemb,
79 void *data)
80 {
81 content_t m = (content_t)data;
82 const size_t rsize = size * nmemb;
83 void *tmp = _realloc(m->p, m->size + rsize + 1);
84 if (tmp)
85 {
86 m->p = (char *)tmp;
87 memcpy(&(m->p[m->size]), p, rsize);
88 m->size += rsize;
89 m->p[m->size] = '\0';
90 }
91 return (rsize);
92 }
93
set_feat(_quvi_t q,_quvi_net_t n,QUVInetPropertyFeatureName feature,CURLoption copt)94 static void set_feat(_quvi_t q, _quvi_net_t n,
95 QUVInetPropertyFeatureName feature,
96 CURLoption copt)
97
98 {
99 char *s = quvi_net_get_one_prop_feat(n, feature);
100 if (s)
101 curl_easy_setopt(q->curl, copt, s);
102 }
103
set_opts_from_lua_script(_quvi_t q,_quvi_net_t n)104 static void set_opts_from_lua_script(_quvi_t q, _quvi_net_t n)
105 {
106 set_feat(q, n, QUVI_NET_PROPERTY_FEATURE_ARBITRARYCOOKIE, CURLOPT_COOKIE);
107 set_feat(q, n, QUVI_NET_PROPERTY_FEATURE_USERAGENT, CURLOPT_USERAGENT);
108 }
109
110 /* curl_fetch */
111
curl_fetch(_quvi_t q,_quvi_net_t n)112 QUVIcode curl_fetch(_quvi_t q, _quvi_net_t n)
113 {
114 struct content_s content;
115 CURLcode curlcode;
116 long conncode;
117 QUVIcode rc;
118
119 curl_easy_setopt(q->curl, CURLOPT_URL, n->url);
120 curl_easy_setopt(q->curl, CURLOPT_ENCODING, "");
121
122 memset(&content, 0, sizeof(struct content_s));
123 curl_easy_setopt(q->curl, CURLOPT_WRITEDATA, &content);
124
125 curl_easy_setopt(q->curl, CURLOPT_WRITEFUNCTION,
126 (curl_write_callback) quvi_write_callback_default);
127
128 set_opts_from_lua_script(q,n);
129
130 curlcode = curl_easy_perform(q->curl);
131
132 curl_easy_getinfo(q->curl, CURLINFO_RESPONSE_CODE, &n->resp_code);
133 curl_easy_getinfo(q->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
134
135 if (curlcode == CURLE_OK && n->resp_code == 200)
136 rc = QUVI_OK;
137 else
138 {
139 if (curlcode == CURLE_OK)
140 {
141 freprintf(&n->errmsg, "server response code %ld (conncode=%ld)",
142 n->resp_code, conncode);
143 }
144 else
145 {
146 freprintf(&n->errmsg, "%s (http/%ld, conn/%ld, curl/%ld)",
147 curl_easy_strerror(curlcode), n->resp_code,
148 conncode, curlcode);
149 }
150
151 rc = QUVI_CALLBACK;
152 }
153
154 n->fetch.content = content.p;
155
156 return (rc);
157 }
158
set_redirect_opts(_quvi_t q,content_t m,const char * url)159 static void set_redirect_opts(_quvi_t q, content_t m, const char *url)
160 {
161 curl_easy_setopt(q->curl, CURLOPT_WRITEDATA, m);
162
163 curl_easy_setopt(q->curl, CURLOPT_WRITEFUNCTION,
164 (curl_write_callback) quvi_write_callback_default);
165
166 /* GET -> HEAD */
167 curl_easy_setopt(q->curl, CURLOPT_URL, url);
168 curl_easy_setopt(q->curl, CURLOPT_FOLLOWLOCATION, 0L);
169 }
170
restore_opts(_quvi_t q)171 static void restore_opts(_quvi_t q)
172 {
173 curl_easy_setopt(q->curl, CURLOPT_FOLLOWLOCATION, 1L);
174 curl_easy_setopt(q->curl, CURLOPT_HTTPGET, 1L); /* HEAD -> GET */
175 }
176
177 /* curl_resolve */
178
curl_resolve(_quvi_t q,_quvi_net_t n)179 QUVIcode curl_resolve(_quvi_t q, _quvi_net_t n)
180 {
181 struct content_s tmp;
182 CURLcode curlcode;
183 long conncode;
184 QUVIcode rc;
185
186 memset(&tmp, 0, sizeof(tmp));
187
188 set_redirect_opts(q, &tmp, n->url);
189 set_opts_from_lua_script(q,n);
190
191 curlcode = curl_easy_perform(q->curl);
192 rc = QUVI_OK;
193
194 restore_opts(q);
195
196 curl_easy_getinfo(q->curl, CURLINFO_RESPONSE_CODE, &n->resp_code);
197 curl_easy_getinfo(q->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
198
199 if (curlcode == CURLE_OK)
200 {
201 if (n->resp_code >= 301 && n->resp_code <= 303) /* New location */
202 {
203 char *r_url = NULL;
204
205 curl_easy_getinfo(q->curl, CURLINFO_REDIRECT_URL, &r_url);
206 assert(r_url != NULL);
207
208 freprintf(&n->redirect.url, "%s", r_url);
209 }
210 }
211 else
212 {
213 freprintf(&n->errmsg, "%s (http/%ld, conn/%ld, curl/%ld)",
214 curl_easy_strerror(curlcode), n->resp_code,
215 conncode, curlcode);
216
217 rc = QUVI_CALLBACK;
218 }
219
220 if (tmp.p)
221 _free(tmp.p);
222
223 return (rc);
224 }
225
226 /* curl_verify */
227
curl_verify(_quvi_t q,_quvi_net_t n)228 QUVIcode curl_verify(_quvi_t q, _quvi_net_t n)
229 {
230 struct content_s tmp;
231 CURLcode curlcode;
232 long conncode;
233 QUVIcode rc;
234
235 rc = QUVI_OK;
236
237 curl_easy_setopt(q->curl, CURLOPT_URL, n->url);
238 curl_easy_setopt(q->curl, CURLOPT_NOBODY, 1L); /* get -> head */
239
240 memset(&tmp, 0, sizeof(tmp));
241 curl_easy_setopt(q->curl, CURLOPT_WRITEDATA, &tmp);
242
243 curl_easy_setopt(q->curl, CURLOPT_WRITEFUNCTION,
244 (curl_write_callback) quvi_write_callback_default);
245
246 set_opts_from_lua_script(q,n);
247
248 curlcode = curl_easy_perform(q->curl);
249
250 curl_easy_setopt(q->curl, CURLOPT_HTTPGET, 1L); /* reset: head -> get */
251
252 curl_easy_getinfo(q->curl, CURLINFO_RESPONSE_CODE, &n->resp_code);
253 curl_easy_getinfo(q->curl, CURLINFO_HTTP_CONNECTCODE, &conncode);
254
255 if (curlcode == CURLE_OK)
256 {
257 if (n->resp_code == 200 || n->resp_code == 206)
258 {
259 const char *ct;
260
261 curl_easy_getinfo(q->curl, CURLINFO_CONTENT_TYPE, &ct);
262 assert(ct != NULL);
263
264 freprintf(&n->verify.content_type, "%s", ct);
265
266 curl_easy_getinfo(q->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD,
267 &n->verify.content_length);
268 }
269 else
270 {
271 freprintf(&n->errmsg, "server response code %ld (conncode=%ld)",
272 n->resp_code, conncode);
273
274 rc = QUVI_CALLBACK;
275 }
276 }
277 else
278 {
279 freprintf(&n->errmsg, "%s (curlcode=%d, conncode=%ld)",
280 curl_easy_strerror(curlcode), curlcode, conncode);
281
282 rc = QUVI_CALLBACK;
283 }
284
285 if (tmp.p)
286 _free(tmp.p);
287
288 return (rc);
289 }
290
curl_unescape_url(_quvi_t q,char * s)291 char *curl_unescape_url(_quvi_t q, char *s)
292 {
293 char *tmp, *r;
294
295 assert(q!= 0);
296 assert(q->curl != 0);
297
298 tmp = curl_easy_unescape(q->curl, s, 0, NULL);
299 assert(tmp != 0);
300
301 r = strdup(tmp);
302 curl_free(tmp);
303
304 _free(s);
305 return (r);
306 }
307
308 /* vim: set ts=2 sw=2 tw=72 expandtab: */
309