1 /*
2   Copyright (c) 2008-2013 uim Project https://github.com/uim/uim
3 
4   All rights reserved.
5 
6   Redistribution and use in source and binary forms, with or without
7   modification, are permitted provided that the following conditions
8   are met:
9 
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   3. Neither the name of authors nor the names of its contributors
16      may be used to endorse or promote products derived from this software
17      without specific prior written permission.
18 
19   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
20   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
23   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29   SUCH DAMAGE.
30 
31 */
32 
33 #include <config.h>
34 
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 
40 #include <curl/curl.h>
41 
42 #include "uim.h"
43 #include "uim-scm.h"
44 #include "uim-scm-abbrev.h"
45 #include "gettext.h"
46 #include "dynlib.h"
47 
48 
49 #ifdef DEBUG
50 #define DPRINTFN(n,x)  if ((n) <= verbose_level()) fprintf x;
51 #else
52 #define DPRINTFN(n,x)
53 #endif
54 
55 static uim_lisp uim_curl_fetch_simple(uim_lisp);
56 static void *uim_curl_fetch_simple_internal(void *);
57 
58 struct uim_curl_post_args {
59   uim_lisp url;
60   uim_lisp post;
61 };
62 static uim_lisp uim_curl_post(uim_lisp, uim_lisp);
63 static void *uim_curl_post_internal(struct uim_curl_post_args *);
64 
65 static uim_lisp uim_curl_url_escape(uim_lisp);
66 static void *uim_curl_url_escape_internal(void *);
67 
68 static uim_lisp uim_curl_url_unescape(uim_lisp);
69 static void *uim_curl_url_unescape_internal(void *);
70 
71 void uim_plugin_instance_init(void);
72 void uim_plugin_instance_quit(void);
73 
74 struct curl_memory_struct {
75   char *str;
76   size_t size;
77 };
78 static size_t uim_curl_write_func(void *, size_t, size_t, void *);
79 static CURLcode uim_curl_perform(CURL *);
80 
81 static size_t
uim_curl_write_func(void * ptr,size_t size,size_t nmemb,void * data)82 uim_curl_write_func(void *ptr, size_t size, size_t nmemb, void *data)
83 {
84   struct curl_memory_struct *mem = (struct curl_memory_struct *)data;
85   size_t realsize = size * nmemb;
86 
87   /*
88    * We know that it isn't possible to overflow during multiplication if
89    * neither operand uses any of the most significant half of the bits in
90    * a size_t.
91    */
92   if((unsigned long long)((nmemb | size) &
93 	((unsigned long long)SIZE_MAX << (sizeof(size_t) << 2))) &&
94      (realsize / size != nmemb))
95     return 0;
96 
97   if(SIZE_MAX - mem->size - 1 < realsize)
98     realsize = SIZE_MAX - mem->size - 1;
99 
100   if(mem->str != NULL)
101     mem->str = uim_realloc(mem->str, mem->size + realsize + 1);
102   else
103     mem->str = uim_malloc(realsize + 1);
104 
105   if(mem->str != NULL) {
106     memcpy(&(mem->str[mem->size]), ptr, realsize);
107     mem->size += realsize;
108     mem->str[mem->size] = '\0';
109   }
110 
111   return realsize;
112 }
113 
114 static uim_lisp
uim_curl_fetch_simple(uim_lisp url_)115 uim_curl_fetch_simple(uim_lisp url_)
116 {
117   return (uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)uim_curl_fetch_simple_internal,
118 						    (void *)url_);
119 }
120 
121 static void *
uim_curl_fetch_simple_internal(void * url_)122 uim_curl_fetch_simple_internal(void *url_)
123 {
124   const char *url = REFER_C_STR((uim_lisp)url_);
125   CURL *curl;
126   CURLcode res;
127   struct curl_memory_struct chunk;
128   uim_lisp fetched_str_;
129 
130   curl = curl_easy_init();
131 
132   if(curl == NULL)
133     return uim_scm_f();
134 
135   memset(&chunk, 0, sizeof(struct curl_memory_struct));
136 
137   curl_easy_setopt(curl, CURLOPT_URL, url);
138   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, uim_curl_write_func);
139   curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
140 
141   res = uim_curl_perform(curl);
142 
143   fetched_str_ = (chunk.str != NULL) ? MAKE_STR(chunk.str) : uim_scm_f();
144 
145   curl_easy_cleanup(curl);
146   curl_global_cleanup();
147   free(chunk.str);
148 
149   return (void *)fetched_str_;
150 }
151 
152 static CURLcode
uim_curl_perform(CURL * curl)153 uim_curl_perform(CURL *curl)
154 {
155   uim_bool use_proxy;
156   char *ua;
157   char *referer;
158   char *http_proxy;
159   CURLcode res;
160 
161   ua = uim_scm_symbol_value_str("uim-curl-user-agent");
162   referer = uim_scm_symbol_value_str("uim-curl-referer");
163   use_proxy = uim_scm_symbol_value_bool("uim-curl-use-proxy?");
164   http_proxy = uim_scm_symbol_value_str("uim-curl-http-proxy");
165 
166   curl_easy_setopt(curl, CURLOPT_USERAGENT,
167 		   (ua != NULL) ? ua : "libcurl-agent/1.0");
168   curl_easy_setopt(curl, CURLOPT_REFERER,
169 		   (referer != NULL) ? referer : "");
170   if (use_proxy) {
171     curl_easy_setopt(curl, CURLOPT_PROXY,
172 		     (http_proxy != NULL) ? http_proxy : "");
173   }
174 
175   res = curl_easy_perform(curl);
176 
177   free(ua);
178   free(referer);
179   free(http_proxy);
180 
181   return res;
182 }
183 
184 static uim_lisp
uim_curl_post(uim_lisp url_,uim_lisp post_)185 uim_curl_post(uim_lisp url_, uim_lisp post_)
186 {
187   struct uim_curl_post_args args;
188 
189   args.url = url_;
190   args.post = post_;
191   return (uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)uim_curl_post_internal,
192 						    &args);
193 }
194 
195 static void *
uim_curl_post_internal(struct uim_curl_post_args * args)196 uim_curl_post_internal(struct uim_curl_post_args *args)
197 {
198   uim_lisp post_ = args->post;
199   uim_lisp post_car_, post_cdr_;
200   uim_lisp fetched_str_;
201   const char *url = REFER_C_STR(args->url);
202   CURL *curl;
203   CURLcode res;
204   struct curl_memory_struct chunk;
205   struct curl_httppost* post_first = NULL;
206   struct curl_httppost* post_last = NULL;
207 
208   curl = curl_easy_init();
209 
210   if(curl == NULL)
211     return uim_scm_f();
212 
213   memset(&chunk, 0, sizeof(struct curl_memory_struct));
214 
215   curl_easy_setopt(curl, CURLOPT_URL, url);
216   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, uim_curl_write_func);
217   curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
218 
219   for(post_cdr_ = post_;
220       !NULLP(post_cdr_);
221       post_cdr_ = CDR(post_cdr_)) {
222       const char *name, *value;
223       post_car_ = CAR(post_cdr_);
224 
225       name = REFER_C_STR(CAR(post_car_));
226       value = REFER_C_STR(CDR(post_car_));
227 
228       curl_formadd(&post_first, &post_last,
229 		   CURLFORM_COPYNAME, name,
230 		   CURLFORM_COPYCONTENTS, value,
231 		   CURLFORM_END);
232   }
233 
234   curl_easy_setopt(curl, CURLOPT_HTTPPOST, post_first);
235 
236   res = uim_curl_perform(curl);
237 
238   fetched_str_ = (chunk.str != NULL) ? MAKE_STR(chunk.str) : uim_scm_f();
239 
240   curl_easy_cleanup(curl);
241   curl_formfree(post_first);
242   curl_global_cleanup();
243   free(chunk.str);
244 
245   return (void *)fetched_str_;
246 }
247 
248 static uim_lisp
uim_curl_url_escape(uim_lisp url_)249 uim_curl_url_escape(uim_lisp url_)
250 {
251   return (uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)uim_curl_url_escape_internal,
252 						    (void *)url_);
253 }
254 static void *
uim_curl_url_escape_internal(void * url_)255 uim_curl_url_escape_internal(void *url_)
256 {
257   uim_lisp escaped_url_;
258   const char *unescaped_url = REFER_C_STR((uim_lisp)url_);
259   char *escaped_url;
260   CURL *curl;
261 
262   curl = curl_easy_init();
263 
264   if(curl == NULL)
265     return uim_scm_f();
266 
267   escaped_url = curl_easy_escape(curl, unescaped_url, strlen(unescaped_url));
268   escaped_url_ = (escaped_url != NULL) ? MAKE_STR(escaped_url) : uim_scm_f();
269 
270   curl_free(escaped_url);
271   curl_easy_cleanup(curl);
272   curl_global_cleanup();
273 
274   return (void *)escaped_url_;
275 }
276 
277 static uim_lisp
uim_curl_url_unescape(uim_lisp url_)278 uim_curl_url_unescape(uim_lisp url_)
279 {
280   return (uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)uim_curl_url_unescape_internal,
281 						    (void *)url_);
282 }
283 
284 static void *
uim_curl_url_unescape_internal(void * url_)285 uim_curl_url_unescape_internal(void *url_)
286 {
287   uim_lisp unescaped_url_;
288   const char *escaped_url = REFER_C_STR((uim_lisp)url_);
289   char *unescaped_url;
290   int len; /* curl_easy_unescape uses int, not size_t */
291   CURL *curl;
292 
293   curl = curl_easy_init();
294 
295   if(curl == NULL)
296     return uim_scm_f();
297 
298   unescaped_url = curl_easy_unescape(curl, escaped_url,
299 				     strlen(escaped_url), &len);
300   unescaped_url_ = (len > 0) ? MAKE_STR(unescaped_url) : uim_scm_f();
301 
302   curl_free(unescaped_url);
303   curl_easy_cleanup(curl);
304   curl_global_cleanup();
305 
306   return (void *)unescaped_url_;
307 }
308 
uim_plugin_instance_init(void)309 void uim_plugin_instance_init(void)
310 {
311   uim_scm_init_proc1("curl-fetch-simple", uim_curl_fetch_simple);
312   uim_scm_init_proc1("curl-url-escape", uim_curl_url_escape);
313   uim_scm_init_proc1("curl-url-unescape", uim_curl_url_unescape);
314   uim_scm_init_proc2("curl-post", uim_curl_post);
315 }
316 
uim_plugin_instance_quit(void)317 void uim_plugin_instance_quit(void)
318 {
319   return;
320 }
321