update opam file
[ocurl.git] / curl-helper.c
blob2f1b0e82eb77460f095875570f67b1fcc85a9d4b
1 /***
2 *** curl-helper.c
3 ***
4 *** Copyright (c) 2003-2008, Lars Nilsson, <lars@quantumchamaeleon.com>
5 *** Copyright (c) 2009, ygrek, <ygrek@autistici.org>
6 ***/
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <caml/config.h>
13 #ifdef HAS_UNISTD
14 #include <unistd.h>
15 #endif
16 /* suppress false gcc warning on seekFunction */
17 #define CURL_DISABLE_TYPECHECK
18 #include <curl/curl.h>
20 #ifndef CAML_NAME_SPACE
21 #define CAML_NAME_SPACE
22 #endif
23 #include <caml/alloc.h>
24 #include <caml/memory.h>
25 #include <caml/mlvalues.h>
26 #include <caml/callback.h>
27 #include <caml/fail.h>
28 #include <caml/unixsupport.h>
29 #include <caml/custom.h>
30 #include <caml/threads.h>
32 #ifndef CAMLdrop
33 #define CAMLdrop caml_local_roots = caml__frame
34 #endif
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #else
39 #pragma message("No config file given.")
40 #endif
42 #ifdef __cplusplus
43 extern "C" {
44 #endif
46 #define Val_none Val_int(0)
48 static __inline value
49 Val_some( value v )
51 CAMLparam1( v );
52 CAMLlocal1( some );
53 some = caml_alloc(1, 0);
54 Store_field( some, 0, v );
55 CAMLreturn( some );
58 static value Val_pair(value v1, value v2)
60 CAMLparam2(v1,v2);
61 CAMLlocal1(pair);
62 pair = caml_alloc_small(2,0);
63 Field(pair,0) = v1;
64 Field(pair,1) = v2;
65 CAMLreturn(pair);
68 static value Val_cons(value list, value v) { return Val_pair(v,list); }
70 typedef struct Connection Connection;
71 typedef struct ConnectionList ConnectionList;
73 #define Connection_val(v) (*(Connection**)Data_custom_val(v))
75 typedef enum OcamlValues
77 Ocaml_WRITEFUNCTION,
78 Ocaml_READFUNCTION,
79 Ocaml_ERRORBUFFER,
80 Ocaml_POSTFIELDS,
81 Ocaml_HTTPHEADER,
82 Ocaml_HTTPPOST,
83 Ocaml_QUOTE,
84 Ocaml_POSTQUOTE,
85 Ocaml_HEADERFUNCTION,
86 Ocaml_PROGRESSFUNCTION,
87 Ocaml_DEBUGFUNCTION,
88 Ocaml_HTTP200ALIASES,
89 Ocaml_IOCTLFUNCTION,
90 Ocaml_SEEKFUNCTION,
91 Ocaml_OPENSOCKETFUNCTION,
93 Ocaml_URL,
94 Ocaml_PROXY,
95 Ocaml_USERPWD,
96 Ocaml_PROXYUSERPWD,
97 Ocaml_RANGE,
98 Ocaml_REFERER,
99 Ocaml_USERAGENT,
100 Ocaml_FTPPORT,
101 Ocaml_COOKIE,
102 Ocaml_HTTPPOSTSTRINGS,
103 Ocaml_SSLCERT,
104 Ocaml_SSLCERTTYPE,
105 Ocaml_SSLCERTPASSWD,
106 Ocaml_SSLKEY,
107 Ocaml_SSLKEYTYPE,
108 Ocaml_SSLKEYPASSWD,
109 Ocaml_SSLENGINE,
110 Ocaml_COOKIEFILE,
111 Ocaml_CUSTOMREQUEST,
112 Ocaml_INTERFACE,
113 Ocaml_CAINFO,
114 Ocaml_CAPATH,
115 Ocaml_RANDOM_FILE,
116 Ocaml_EGDSOCKET,
117 Ocaml_COOKIEJAR,
118 Ocaml_SSL_CIPHER_LIST,
119 Ocaml_PRIVATE,
120 Ocaml_NETRC_FILE,
121 Ocaml_FTP_ACCOUNT,
122 Ocaml_COOKIELIST,
123 Ocaml_FTP_ALTERNATIVE_TO_USER,
124 Ocaml_SSH_PUBLIC_KEYFILE,
125 Ocaml_SSH_PRIVATE_KEYFILE,
126 Ocaml_SSH_HOST_PUBLIC_KEY_MD5,
127 Ocaml_COPYPOSTFIELDS,
128 Ocaml_DNS_SERVERS,
129 Ocaml_MAIL_FROM,
130 Ocaml_MAIL_RCPT,
131 Ocaml_RESOLVE,
132 Ocaml_USERNAME,
133 Ocaml_PASSWORD,
134 Ocaml_LOGIN_OPTIONS,
135 Ocaml_CONNECT_TO,
137 /* Not used, last for size */
138 OcamlValuesSize
139 } OcamlValue;
141 struct Connection
143 CURL *handle;
145 value ocamlValues;
147 size_t refcount; /* number of references to this structure */
149 char *curl_ERRORBUFFER;
150 char *curl_POSTFIELDS;
151 struct curl_slist *curl_HTTPHEADER;
152 struct curl_slist *httpPostBuffers;
153 struct curl_httppost *httpPostFirst;
154 struct curl_httppost *httpPostLast;
155 struct curl_slist *curl_RESOLVE;
156 struct curl_slist *curl_QUOTE;
157 struct curl_slist *curl_POSTQUOTE;
158 struct curl_slist *curl_HTTP200ALIASES;
159 struct curl_slist *curl_MAIL_RCPT;
160 struct curl_slist *curl_CONNECT_TO;
163 typedef struct CURLErrorMapping CURLErrorMapping;
165 struct CURLErrorMapping
167 char *name;
168 CURLcode error;
171 CURLErrorMapping errorMap[] =
173 {"CURLE_OK", CURLE_OK},
174 #if HAVE_DECL_CURLE_UNSUPPORTED_PROTOCOL
175 {"CURLE_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL},
176 #else
177 {"CURLE_UNSUPPORTED_PROTOCOL", -1},
178 #endif
179 #if HAVE_DECL_CURLE_FAILED_INIT
180 {"CURLE_FAILED_INIT", CURLE_FAILED_INIT},
181 #else
182 {"CURLE_FAILED_INIT", -1},
183 #endif
184 #if HAVE_DECL_CURLE_URL_MALFORMAT
185 {"CURLE_URL_MALFORMAT", CURLE_URL_MALFORMAT},
186 #else
187 {"CURLE_URL_MALFORMAT", -1},
188 #endif
189 #if HAVE_DECL_CURLE_URL_MALFORMAT_USER
190 {"CURLE_URL_MALFORMAT_USER", CURLE_URL_MALFORMAT_USER},
191 #else
192 {"CURLE_URL_MALFORMAT_USER", -1},
193 #endif
194 #if HAVE_DECL_CURLE_COULDNT_RESOLVE_PROXY
195 {"CURLE_COULDNT_RESOLVE_PROXY", CURLE_COULDNT_RESOLVE_PROXY},
196 #else
197 {"CURLE_COULDNT_RESOLVE_PROXY", -1},
198 #endif
199 #if HAVE_DECL_CURLE_COULDNT_RESOLVE_HOST
200 {"CURLE_COULDNT_RESOLVE_HOST", CURLE_COULDNT_RESOLVE_HOST},
201 #else
202 {"CURLE_COULDNT_RESOLVE_HOST", -1},
203 #endif
204 #if HAVE_DECL_CURLE_COULDNT_CONNECT
205 {"CURLE_COULDNT_CONNECT", CURLE_COULDNT_CONNECT},
206 #else
207 {"CURLE_COULDNT_CONNECT", -1},
208 #endif
209 #if HAVE_DECL_CURLE_FTP_WEIRD_SERVER_REPLY
210 {"CURLE_FTP_WEIRD_SERVER_REPLY", CURLE_FTP_WEIRD_SERVER_REPLY},
211 #else
212 {"CURLE_FTP_WEIRD_SERVER_REPLY", -1},
213 #endif
214 #if HAVE_DECL_CURLE_FTP_ACCESS_DENIED
215 {"CURLE_FTP_ACCESS_DENIED", CURLE_FTP_ACCESS_DENIED},
216 #else
217 {"CURLE_FTP_ACCESS_DENIED", -1},
218 #endif
219 #if HAVE_DECL_CURLE_FTP_USER_PASSWORD_INCORRECT
220 {"CURLE_FTP_USER_PASSWORD_INCORRECT", CURLE_FTP_USER_PASSWORD_INCORRECT},
221 #else
222 {"CURLE_FTP_USER_PASSWORD_INCORRECT", -1},
223 #endif
224 #if HAVE_DECL_CURLE_FTP_WEIRD_PASS_REPLY
225 {"CURLE_FTP_WEIRD_PASS_REPLY", CURLE_FTP_WEIRD_PASS_REPLY},
226 #else
227 {"CURLE_FTP_WEIRD_PASS_REPLY", -1},
228 #endif
229 #if HAVE_DECL_CURLE_FTP_WEIRD_USER_REPLY
230 {"CURLE_FTP_WEIRD_USER_REPLY", CURLE_FTP_WEIRD_USER_REPLY},
231 #else
232 {"CURLE_FTP_WEIRD_USER_REPLY", -1},
233 #endif
234 #if HAVE_DECL_CURLE_FTP_WEIRD_PASV_REPLY
235 {"CURLE_FTP_WEIRD_PASV_REPLY", CURLE_FTP_WEIRD_PASV_REPLY},
236 #else
237 {"CURLE_FTP_WEIRD_PASV_REPLY", -1},
238 #endif
239 #if HAVE_DECL_CURLE_FTP_WEIRD_227_FORMAT
240 {"CURLE_FTP_WEIRD_227_FORMAT", CURLE_FTP_WEIRD_227_FORMAT},
241 #else
242 {"CURLE_FTP_WEIRD_227_FORMAT", -1},
243 #endif
244 #if HAVE_DECL_CURLE_FTP_CANT_GET_HOST
245 {"CURLE_FTP_CANT_GET_HOST", CURLE_FTP_CANT_GET_HOST},
246 #else
247 {"CURLE_FTP_CANT_GET_HOST", -1},
248 #endif
249 #if HAVE_DECL_CURLE_FTP_CANT_RECONNECT
250 {"CURLE_FTP_CANT_RECONNECT", CURLE_FTP_CANT_RECONNECT},
251 #else
252 {"CURLE_FTP_CANT_RECONNECT", -1},
253 #endif
254 #if HAVE_DECL_CURLE_FTP_COULDNT_SET_BINARY
255 {"CURLE_FTP_COULDNT_SET_BINARY", CURLE_FTP_COULDNT_SET_BINARY},
256 #else
257 {"CURLE_FTP_COULDNT_SET_BINARY", -1},
258 #endif
259 #if HAVE_DECL_CURLE_PARTIAL_FILE
260 {"CURLE_PARTIAL_FILE", CURLE_PARTIAL_FILE},
261 #else
262 {"CURLE_PARTIAL_FILE", -1},
263 #endif
264 #if HAVE_DECL_CURLE_FTP_COULDNT_RETR_FILE
265 {"CURLE_FTP_COULDNT_RETR_FILE", CURLE_FTP_COULDNT_RETR_FILE},
266 #else
267 {"CURLE_FTP_COULDNT_RETR_FILE", -1},
268 #endif
269 #if HAVE_DECL_CURLE_FTP_WRITE_ERROR
270 {"CURLE_FTP_WRITE_ERROR", CURLE_FTP_WRITE_ERROR},
271 #else
272 {"CURLE_FTP_WRITE_ERROR", -1},
273 #endif
274 #if HAVE_DECL_CURLE_FTP_QUOTE_ERROR
275 {"CURLE_FTP_QUOTE_ERROR", CURLE_FTP_QUOTE_ERROR},
276 #else
277 {"CURLE_FTP_QUOTE_ERROR", -1},
278 #endif
279 #if HAVE_DECL_CURLE_HTTP_RETURNED_ERROR
280 {"CURLE_HTTP_RETURNED_ERROR", CURLE_HTTP_RETURNED_ERROR},
281 #else
282 {"CURLE_HTTP_RETURNED_ERROR", -1},
283 #endif
284 #if HAVE_DECL_CURLE_WRITE_ERROR
285 {"CURLE_WRITE_ERROR", CURLE_WRITE_ERROR},
286 #else
287 {"CURLE_WRITE_ERROR", -1},
288 #endif
289 #if HAVE_DECL_CURLE_MALFORMAT_USER
290 {"CURLE_MALFORMAT_USER", CURLE_MALFORMAT_USER},
291 #else
292 {"CURLE_MALFORMAT_USER", -1},
293 #endif
294 #if HAVE_DECL_CURLE_FTP_COULDNT_STOR_FILE
295 {"CURLE_FTP_COULDNT_STOR_FILE", CURLE_FTP_COULDNT_STOR_FILE},
296 #else
297 {"CURLE_FTP_COULDNT_STOR_FILE", -1},
298 #endif
299 #if HAVE_DECL_CURLE_READ_ERROR
300 {"CURLE_READ_ERROR", CURLE_READ_ERROR},
301 #else
302 {"CURLE_READ_ERROR", -1},
303 #endif
304 #if HAVE_DECL_CURLE_OUT_OF_MEMORY
305 {"CURLE_OUT_OF_MEMORY", CURLE_OUT_OF_MEMORY},
306 #else
307 {"CURLE_OUT_OF_MEMORY", -1},
308 #endif
309 #if HAVE_DECL_CURLE_OPERATION_TIMEOUTED
310 {"CURLE_OPERATION_TIMEOUTED", CURLE_OPERATION_TIMEOUTED},
311 #else
312 {"CURLE_OPERATION_TIMEOUTED", -1},
313 #endif
314 #if HAVE_DECL_CURLE_FTP_COULDNT_SET_ASCII
315 {"CURLE_FTP_COULDNT_SET_ASCII", CURLE_FTP_COULDNT_SET_ASCII},
316 #else
317 {"CURLE_FTP_COULDNT_SET_ASCII", -1},
318 #endif
319 #if HAVE_DECL_CURLE_FTP_PORT_FAILED
320 {"CURLE_FTP_PORT_FAILED", CURLE_FTP_PORT_FAILED},
321 #else
322 {"CURLE_FTP_PORT_FAILED", -1},
323 #endif
324 #if HAVE_DECL_CURLE_FTP_COULDNT_USE_REST
325 {"CURLE_FTP_COULDNT_USE_REST", CURLE_FTP_COULDNT_USE_REST},
326 #else
327 {"CURLE_FTP_COULDNT_USE_REST", -1},
328 #endif
329 #if HAVE_DECL_CURLE_FTP_COULDNT_GET_SIZE
330 {"CURLE_FTP_COULDNT_GET_SIZE", CURLE_FTP_COULDNT_GET_SIZE},
331 #else
332 {"CURLE_FTP_COULDNT_GET_SIZE", -1},
333 #endif
334 #if HAVE_DECL_CURLE_HTTP_RANGE_ERROR
335 {"CURLE_HTTP_RANGE_ERROR", CURLE_HTTP_RANGE_ERROR},
336 #else
337 {"CURLE_HTTP_RANGE_ERROR", -1},
338 #endif
339 #if HAVE_DECL_CURLE_HTTP_POST_ERROR
340 {"CURLE_HTTP_POST_ERROR", CURLE_HTTP_POST_ERROR},
341 #else
342 {"CURLE_HTTP_POST_ERROR", -1},
343 #endif
344 #if HAVE_DECL_CURLE_SSL_CONNECT_ERROR
345 {"CURLE_SSL_CONNECT_ERROR", CURLE_SSL_CONNECT_ERROR},
346 #else
347 {"CURLE_SSL_CONNECT_ERROR", -1},
348 #endif
349 #if HAVE_DECL_CURLE_BAD_DOWNLOAD_RESUME
350 {"CURLE_BAD_DOWNLOAD_RESUME", CURLE_BAD_DOWNLOAD_RESUME},
351 #else
352 {"CURLE_BAD_DOWNLOAD_RESUME", -1},
353 #endif
354 #if HAVE_DECL_CURLE_FILE_COULDNT_READ_FILE
355 {"CURLE_FILE_COULDNT_READ_FILE", CURLE_FILE_COULDNT_READ_FILE},
356 #else
357 {"CURLE_FILE_COULDNT_READ_FILE", -1},
358 #endif
359 #if HAVE_DECL_CURLE_LDAP_CANNOT_BIND
360 {"CURLE_LDAP_CANNOT_BIND", CURLE_LDAP_CANNOT_BIND},
361 #else
362 {"CURLE_LDAP_CANNOT_BIND", -1},
363 #endif
364 #if HAVE_DECL_CURLE_LDAP_SEARCH_FAILED
365 {"CURLE_LDAP_SEARCH_FAILED", CURLE_LDAP_SEARCH_FAILED},
366 #else
367 {"CURLE_LDAP_SEARCH_FAILED", -1},
368 #endif
369 #if HAVE_DECL_CURLE_LIBRARY_NOT_FOUND
370 {"CURLE_LIBRARY_NOT_FOUND", CURLE_LIBRARY_NOT_FOUND},
371 #else
372 {"CURLE_LIBRARY_NOT_FOUND", -1},
373 #endif
374 #if HAVE_DECL_CURLE_FUNCTION_NOT_FOUND
375 {"CURLE_FUNCTION_NOT_FOUND", CURLE_FUNCTION_NOT_FOUND},
376 #else
377 {"CURLE_FUNCTION_NOT_FOUND", -1},
378 #endif
379 #if HAVE_DECL_CURLE_ABORTED_BY_CALLBACK
380 {"CURLE_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK},
381 #else
382 {"CURLE_ABORTED_BY_CALLBACK", -1},
383 #endif
384 #if HAVE_DECL_CURLE_BAD_FUNCTION_ARGUMENT
385 {"CURLE_BAD_FUNCTION_ARGUMENT", CURLE_BAD_FUNCTION_ARGUMENT},
386 #else
387 {"CURLE_BAD_FUNCTION_ARGUMENT", -1},
388 #endif
389 #if HAVE_DECL_CURLE_BAD_CALLING_ORDER
390 {"CURLE_BAD_CALLING_ORDER", CURLE_BAD_CALLING_ORDER},
391 #else
392 {"CURLE_BAD_CALLING_ORDER", -1},
393 #endif
394 #if HAVE_DECL_CURLE_INTERFACE_FAILED
395 {"CURLE_INTERFACE_FAILED", CURLE_INTERFACE_FAILED},
396 #else
397 {"CURLE_INTERFACE_FAILED", -1},
398 #endif
399 #if HAVE_DECL_CURLE_BAD_PASSWORD_ENTERED
400 {"CURLE_BAD_PASSWORD_ENTERED", CURLE_BAD_PASSWORD_ENTERED},
401 #else
402 {"CURLE_BAD_PASSWORD_ENTERED", -1},
403 #endif
404 #if HAVE_DECL_CURLE_TOO_MANY_REDIRECTS
405 {"CURLE_TOO_MANY_REDIRECTS", CURLE_TOO_MANY_REDIRECTS},
406 #else
407 {"CURLE_TOO_MANY_REDIRECTS", -1},
408 #endif
409 #if HAVE_DECL_CURLE_UNKNOWN_TELNET_OPTION
410 {"CURLE_UNKNOWN_TELNET_OPTION", CURLE_UNKNOWN_TELNET_OPTION},
411 #else
412 {"CURLE_UNKNOWN_TELNET_OPTION", -1},
413 #endif
414 #if HAVE_DECL_CURLE_TELNET_OPTION_SYNTAX
415 {"CURLE_TELNET_OPTION_SYNTAX", CURLE_TELNET_OPTION_SYNTAX},
416 #else
417 {"CURLE_TELNET_OPTION_SYNTAX", -1},
418 #endif
419 #if HAVE_DECL_CURLE_SSL_PEER_CERTIFICATE
420 {"CURLE_SSL_PEER_CERTIFICATE", CURLE_SSL_PEER_CERTIFICATE},
421 #else
422 {"CURLE_SSL_PEER_CERTIFICATE", -1},
423 #endif
424 #if HAVE_DECL_CURLE_GOT_NOTHING
425 {"CURLE_GOT_NOTHING", CURLE_GOT_NOTHING},
426 #else
427 {"CURLE_GOT_NOTHING", -1},
428 #endif
429 #if HAVE_DECL_CURLE_SSL_ENGINE_NOTFOUND
430 {"CURLE_SSL_ENGINE_NOTFOUND", CURLE_SSL_ENGINE_NOTFOUND},
431 #else
432 {"CURLE_SSL_ENGINE_NOTFOUND", -1},
433 #endif
434 #if HAVE_DECL_CURLE_SSL_ENGINE_SETFAILED
435 {"CURLE_SSL_ENGINE_SETFAILED", CURLE_SSL_ENGINE_SETFAILED},
436 #else
437 {"CURLE_SSL_ENGINE_SETFAILED", -1},
438 #endif
439 #if HAVE_DECL_CURLE_SEND_ERROR
440 {"CURLE_SEND_ERROR", CURLE_SEND_ERROR},
441 #else
442 {"CURLE_SEND_ERROR", -1},
443 #endif
444 #if HAVE_DECL_CURLE_RECV_ERROR
445 {"CURLE_RECV_ERROR", CURLE_RECV_ERROR},
446 #else
447 {"CURLE_RECV_ERROR", -1},
448 #endif
449 #if HAVE_DECL_CURLE_SHARE_IN_USE
450 {"CURLE_SHARE_IN_USE", CURLE_SHARE_IN_USE},
451 #else
452 {"CURLE_SHARE_IN_USE", -1},
453 #endif
454 #if HAVE_DECL_CURLE_SSL_CERTPROBLEM
455 {"CURLE_SSL_CERTPROBLEM", CURLE_SSL_CERTPROBLEM},
456 #else
457 {"CURLE_SSL_CERTPROBLEM", -1},
458 #endif
459 #if HAVE_DECL_CURLE_SSL_CIPHER
460 {"CURLE_SSL_CIPHER", CURLE_SSL_CIPHER},
461 #else
462 {"CURLE_SSL_CIPHER", -1},
463 #endif
464 #if HAVE_DECL_CURLE_SSL_CACERT
465 {"CURLE_SSL_CACERT", CURLE_SSL_CACERT},
466 #else
467 {"CURLE_SSL_CACERT", -1},
468 #endif
469 #if HAVE_DECL_CURLE_BAD_CONTENT_ENCODING
470 {"CURLE_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING},
471 #else
472 {"CURLE_BAD_CONTENT_ENCODING", -1},
473 #endif
474 #if HAVE_DECL_CURLE_LDAP_INVALID_URL
475 {"CURLE_LDAP_INVALID_URL", CURLE_LDAP_INVALID_URL},
476 #else
477 {"CURLE_LDAP_INVALID_URL", -1},
478 #endif
479 #if HAVE_DECL_CURLE_FILESIZE_EXCEEDED
480 {"CURLE_FILESIZE_EXCEEDED", CURLE_FILESIZE_EXCEEDED},
481 #else
482 {"CURLE_FILESIZE_EXCEEDED", -1},
483 #endif
484 #if HAVE_DECL_CURLE_FTP_SSL_FAILED
485 {"CURLE_FTP_SSL_FAILED", CURLE_FTP_SSL_FAILED},
486 #else
487 {"CURLE_FTP_SSL_FAILED", -1},
488 #endif
489 #if HAVE_DECL_CURLE_SEND_FAIL_REWIND
490 {"CURLE_SEND_FAIL_REWIND", CURLE_SEND_FAIL_REWIND},
491 #else
492 {"CURLE_SEND_FAIL_REWIND", -1},
493 #endif
494 #if HAVE_DECL_CURLE_SSL_ENGINE_INITFAILED
495 {"CURLE_SSL_ENGINE_INITFAILED", CURLE_SSL_ENGINE_INITFAILED},
496 #else
497 {"CURLE_SSL_ENGINE_INITFAILED", -1},
498 #endif
499 #if HAVE_DECL_CURLE_LOGIN_DENIED
500 {"CURLE_LOGIN_DENIED", CURLE_LOGIN_DENIED},
501 #else
502 {"CURLE_LOGIN_DENIED", -1},
503 #endif
504 #if HAVE_DECL_CURLE_TFTP_NOTFOUND
505 {"CURLE_TFTP_NOTFOUND", CURLE_TFTP_NOTFOUND},
506 #else
507 {"CURLE_TFTP_NOTFOUND", -1},
508 #endif
509 #if HAVE_DECL_CURLE_TFTP_PERM
510 {"CURLE_TFTP_PERM", CURLE_TFTP_PERM},
511 #else
512 {"CURLE_TFTP_PERM", -1},
513 #endif
514 #if HAVE_DECL_CURLE_REMOTE_DISK_FULL
515 {"CURLE_REMOTE_DISK_FULL", CURLE_REMOTE_DISK_FULL},
516 #else
517 {"CURLE_REMOTE_DISK_FULL", -1},
518 #endif
519 #if HAVE_DECL_CURLE_TFTP_ILLEGAL
520 {"CURLE_TFTP_ILLEGAL", CURLE_TFTP_ILLEGAL},
521 #else
522 {"CURLE_TFTP_ILLEGAL", -1},
523 #endif
524 #if HAVE_DECL_CURLE_TFTP_UNKNOWNID
525 {"CURLE_TFTP_UNKNOWNID", CURLE_TFTP_UNKNOWNID},
526 #else
527 {"CURLE_TFTP_UNKNOWNID", -1},
528 #endif
529 #if HAVE_DECL_CURLE_REMOTE_FILE_EXISTS
530 {"CURLE_REMOTE_FILE_EXISTS", CURLE_REMOTE_FILE_EXISTS},
531 #else
532 {"CURLE_REMOTE_FILE_EXISTS", -1},
533 #endif
534 #if HAVE_DECL_CURLE_TFTP_NOSUCHUSER
535 {"CURLE_TFTP_NOSUCHUSER", CURLE_TFTP_NOSUCHUSER},
536 #else
537 {"CURLE_TFTP_NOSUCHUSER", -1},
538 #endif
539 #if HAVE_DECL_CURLE_CONV_FAILED
540 {"CURLE_CONV_FAILED", CURLE_CONV_FAILED},
541 #else
542 {"CURLE_CONV_FAILED", -1},
543 #endif
544 #if HAVE_DECL_CURLE_CONV_REQD
545 {"CURLE_CONV_REQD", CURLE_CONV_REQD},
546 #else
547 {"CURLE_CONV_REQD", -1},
548 #endif
549 #if HAVE_DECL_CURLE_SSL_CACERT_BADFILE
550 {"CURLE_SSL_CACERT_BADFILE", CURLE_SSL_CACERT_BADFILE},
551 #else
552 {"CURLE_SSL_CACERT_BADFILE", -1},
553 #endif
554 #if HAVE_DECL_CURLE_REMOTE_FILE_NOT_FOUND
555 {"CURLE_REMOTE_FILE_NOT_FOUND", CURLE_REMOTE_FILE_NOT_FOUND},
556 #else
557 {"CURLE_REMOTE_FILE_NOT_FOUND", -1},
558 #endif
559 #if HAVE_DECL_CURLE_SSH
560 {"CURLE_SSH", CURLE_SSH},
561 #else
562 {"CURLE_SSH", -1},
563 #endif
564 #if HAVE_DECL_CURLE_SSL_SHUTDOWN_FAILED
565 {"CURLE_SSL_SHUTDOWN_FAILED", CURLE_SSL_SHUTDOWN_FAILED},
566 #else
567 {"CURLE_SSL_SHUTDOWN_FAILED", -1},
568 #endif
569 #if HAVE_DECL_CURLE_AGAIN
570 {"CURLE_AGAIN", CURLE_AGAIN},
571 #else
572 {"CURLE_AGAIN", -1},
573 #endif
574 {NULL, (CURLcode)0}
577 typedef struct CURLOptionMapping CURLOptionMapping;
579 struct CURLOptionMapping
581 void (*optionHandler)(Connection *, value);
582 char *name;
583 OcamlValue ocamlValue;
586 static char* strdup_ml(value v)
588 char* p = NULL;
589 p = (char*)malloc(caml_string_length(v)+1);
590 memcpy(p,String_val(v),caml_string_length(v)+1); // caml strings have terminating zero
591 return p;
594 static value ml_copy_string(char const* p, size_t size)
596 value v = caml_alloc_string(size);
597 memcpy(&Byte(v,0),p,size);
598 return v;
601 /* prepends to the beginning of list */
602 static struct curl_slist* curl_slist_prepend_ml(struct curl_slist* list, value v)
604 /* FIXME check NULLs */
605 struct curl_slist* new_item = (struct curl_slist*)malloc(sizeof(struct curl_slist));
607 new_item->next = list;
608 new_item->data = strdup_ml(v);
610 return new_item;
613 static void free_curl_slist(struct curl_slist *slist)
615 if (NULL == slist)
616 return;
618 curl_slist_free_all(slist);
621 static void raiseError(Connection *conn, CURLcode code)
623 CAMLparam0();
624 CAMLlocal1(exceptionData);
625 value *exception;
626 char *errorString = "Unknown Error";
627 int i;
629 for (i = 0; errorMap[i].name != NULL; i++)
631 if (errorMap[i].error == code)
633 errorString = errorMap[i].name;
634 break;
638 exceptionData = caml_alloc_tuple(3);
640 Store_field(exceptionData, 0, Val_int(code));
641 Store_field(exceptionData, 1, Val_int(code));
642 Store_field(exceptionData, 2, caml_copy_string(errorString));
644 if (conn != NULL && conn->curl_ERRORBUFFER != NULL)
646 Store_field(Field(conn->ocamlValues, Ocaml_ERRORBUFFER), 0, caml_copy_string(conn->curl_ERRORBUFFER));
649 exception = caml_named_value("CurlException");
651 if (exception == NULL)
652 caml_failwith("CurlException not registered");
654 caml_raise_with_arg(*exception, exceptionData);
656 CAMLreturn0;
659 static void resetOcamlValues(Connection* connection)
661 int i;
663 for (i = 0; i < OcamlValuesSize; i++)
664 Store_field(connection->ocamlValues, i, Val_unit);
667 static Connection* allocConnection(CURL* h)
669 Connection* connection = (Connection *)malloc(sizeof(Connection));
671 connection->ocamlValues = caml_alloc(OcamlValuesSize, 0);
672 resetOcamlValues(connection);
673 caml_register_global_root(&connection->ocamlValues);
675 connection->handle = h;
676 curl_easy_setopt(h, CURLOPT_PRIVATE, connection);
678 connection->refcount = 0;
680 connection->curl_ERRORBUFFER = NULL;
681 connection->curl_POSTFIELDS = NULL;
682 connection->curl_HTTPHEADER = NULL;
683 connection->httpPostBuffers = NULL;
684 connection->httpPostFirst = NULL;
685 connection->httpPostLast = NULL;
686 connection->curl_QUOTE = NULL;
687 connection->curl_POSTQUOTE = NULL;
688 connection->curl_HTTP200ALIASES = NULL;
689 connection->curl_RESOLVE = NULL;
690 connection->curl_MAIL_RCPT = NULL;
691 connection->curl_CONNECT_TO = NULL;
693 return connection;
696 static Connection *newConnection(void)
698 CURL* h;
700 caml_enter_blocking_section();
701 h = curl_easy_init();
702 caml_leave_blocking_section();
704 return allocConnection(h);
707 static void free_if(void* p) { if (NULL != p) free(p); }
709 static void removeConnection(Connection *connection, int finalization)
711 const char* fin_url = NULL;
713 if (!connection->handle)
715 return; /* already cleaned up */
718 if (finalization)
720 /* cannot engage OCaml runtime at finalization, just report leak */
721 if (CURLE_OK != curl_easy_getinfo(connection->handle, CURLINFO_EFFECTIVE_URL, &fin_url) || NULL == fin_url)
723 fin_url = "unknown";
725 fprintf(stderr,"Curl: handle %p leaked, conn %p, url %s\n", connection->handle, connection, fin_url);
726 fflush(stderr);
728 else
730 caml_enter_blocking_section();
731 curl_easy_cleanup(connection->handle);
732 caml_leave_blocking_section();
735 connection->handle = NULL;
737 caml_remove_global_root(&connection->ocamlValues);
739 free_if(connection->curl_ERRORBUFFER);
740 free_if(connection->curl_POSTFIELDS);
741 free_curl_slist(connection->curl_HTTPHEADER);
742 free_curl_slist(connection->httpPostBuffers);
743 if (connection->httpPostFirst != NULL)
744 curl_formfree(connection->httpPostFirst);
745 free_curl_slist(connection->curl_RESOLVE);
746 free_curl_slist(connection->curl_QUOTE);
747 free_curl_slist(connection->curl_POSTQUOTE);
748 free_curl_slist(connection->curl_HTTP200ALIASES);
749 free_curl_slist(connection->curl_MAIL_RCPT);
750 free_curl_slist(connection->curl_CONNECT_TO);
753 static Connection* getConnection(CURL* h)
755 Connection* p = NULL;
757 if (CURLE_OK != curl_easy_getinfo(h, CURLINFO_PRIVATE, &p) || NULL == p)
759 caml_failwith("Unknown handle");
762 return p;
765 #if 1
766 static void checkConnection(Connection * connection)
768 (void)connection;
770 #else
771 static void checkConnection(Connection *connection)
773 if (connection != getConnection(connection->handle))
775 caml_failwith("Invalid Connection");
778 #endif
780 void op_curl_easy_finalize(value v)
782 Connection* conn = Connection_val(v);
783 /* same connection may be referenced by several different
784 OCaml values, see e.g. caml_curl_multi_remove_finished */
785 conn->refcount--;
786 if (0 == conn->refcount)
788 removeConnection(conn, 1);
789 free(conn);
793 int op_curl_easy_compare(value v1, value v2)
795 size_t p1 = (size_t)Connection_val(v1);
796 size_t p2 = (size_t)Connection_val(v2);
797 return (p1 == p2 ? 0 : (p1 > p2 ? 1 : -1)); /* compare addresses */
800 intnat op_curl_easy_hash(value v)
802 return (size_t)Connection_val(v); /* address */
805 static struct custom_operations curl_easy_ops = {
806 "ygrek.curl_easy",
807 op_curl_easy_finalize,
808 op_curl_easy_compare,
809 op_curl_easy_hash,
810 custom_serialize_default,
811 custom_deserialize_default,
812 #if defined(custom_compare_ext_default)
813 custom_compare_ext_default,
814 #endif
817 value caml_curl_alloc(Connection* conn)
819 value v = caml_alloc_custom(&curl_easy_ops, sizeof(Connection*), 0, 1);
820 Connection_val(v) = conn;
821 conn->refcount++;
822 return v;
825 static size_t cb_WRITEFUNCTION(char *ptr, size_t size, size_t nmemb, void *data)
827 caml_leave_blocking_section();
829 CAMLparam0();
830 CAMLlocal2(result, str);
831 Connection *conn = (Connection *)data;
833 checkConnection(conn);
835 str = ml_copy_string(ptr,size*nmemb);
837 result = caml_callback_exn(Field(conn->ocamlValues, Ocaml_WRITEFUNCTION), str);
839 size_t r = Is_exception_result(result) ? 0 : Int_val(result);
840 CAMLdrop;
842 caml_enter_blocking_section();
843 return r;
846 static size_t cb_READFUNCTION(void *ptr, size_t size, size_t nmemb, void *data)
848 caml_leave_blocking_section();
850 CAMLparam0();
851 CAMLlocal1(result);
852 Connection *conn = (Connection *)data;
853 size_t length;
855 checkConnection(conn);
857 result = caml_callback_exn(Field(conn->ocamlValues, Ocaml_READFUNCTION),
858 Val_int(size*nmemb));
860 if (Is_exception_result(result))
862 CAMLreturnT(size_t,CURL_READFUNC_ABORT);
865 length = caml_string_length(result);
867 size_t r;
869 if (length <= size*nmemb)
871 memcpy(ptr, String_val(result), length);
872 r = length;
874 else
876 r = CURL_READFUNC_ABORT;
878 CAMLdrop;
880 caml_enter_blocking_section();
881 return r;
884 static size_t cb_HEADERFUNCTION(char *ptr, size_t size, size_t nmemb, void *data)
886 caml_leave_blocking_section();
888 CAMLparam0();
889 CAMLlocal2(result,str);
890 Connection *conn = (Connection *)data;
892 checkConnection(conn);
894 str = ml_copy_string(ptr,size*nmemb);
896 result = caml_callback_exn(Field(conn->ocamlValues, Ocaml_HEADERFUNCTION), str);
898 size_t r = Is_exception_result(result) ? 0 : Int_val(result);
899 CAMLdrop;
901 caml_enter_blocking_section();
902 return r;
905 static int cb_PROGRESSFUNCTION(void *data,
906 double dlTotal,
907 double dlNow,
908 double ulTotal,
909 double ulNow)
911 caml_leave_blocking_section();
913 CAMLparam0();
914 CAMLlocal1(result);
915 CAMLlocalN(callbackData, 4);
916 Connection *conn = (Connection *)data;
918 checkConnection(conn);
920 callbackData[0] = caml_copy_double(dlTotal);
921 callbackData[1] = caml_copy_double(dlNow);
922 callbackData[2] = caml_copy_double(ulTotal);
923 callbackData[3] = caml_copy_double(ulNow);
925 result = caml_callbackN_exn(Field(conn->ocamlValues, Ocaml_PROGRESSFUNCTION),
926 4, callbackData);
928 int r = Is_exception_result(result) ? 1 : Bool_val(result);
929 CAMLdrop;
931 caml_enter_blocking_section();
932 return r;
935 static int cb_DEBUGFUNCTION(CURL *debugConnection,
936 curl_infotype infoType,
937 char *buffer,
938 size_t bufferLength,
939 void *data)
941 caml_leave_blocking_section();
943 CAMLparam0();
944 CAMLlocal3(camlDebugConnection, camlInfoType, camlMessage);
945 Connection *conn = (Connection *)data;
946 (void)debugConnection; /* not used */
948 checkConnection(conn);
950 camlDebugConnection = caml_curl_alloc(conn);
951 camlMessage = ml_copy_string(buffer,bufferLength);
952 camlInfoType = Val_long(infoType <= CURLINFO_SSL_DATA_OUT ? infoType : CURLINFO_END);
954 caml_callback3_exn(Field(conn->ocamlValues, Ocaml_DEBUGFUNCTION),
955 camlDebugConnection,
956 camlInfoType,
957 camlMessage);
959 CAMLdrop;
961 caml_enter_blocking_section();
962 return 0;
965 static curlioerr cb_IOCTLFUNCTION(CURL *ioctl,
966 int cmd,
967 void *data)
969 caml_leave_blocking_section();
971 CAMLparam0();
972 CAMLlocal3(camlResult, camlConnection, camlCmd);
973 Connection *conn = (Connection *)data;
974 curlioerr result = CURLIOE_OK;
975 (void)ioctl; /* not used */
977 checkConnection(conn);
979 if (cmd == CURLIOCMD_NOP)
980 camlCmd = Val_long(0);
981 else if (cmd == CURLIOCMD_RESTARTREAD)
982 camlCmd = Val_long(1);
983 else
984 caml_failwith("Invalid IOCTL Cmd!");
986 camlConnection = caml_curl_alloc(conn);
988 camlResult = caml_callback2_exn(Field(conn->ocamlValues, Ocaml_IOCTLFUNCTION),
989 camlConnection,
990 camlCmd);
992 if (Is_exception_result(camlResult))
994 result = CURLIOE_FAILRESTART;
996 else
997 switch (Long_val(camlResult))
999 case 0: /* CURLIOE_OK */
1000 result = CURLIOE_OK;
1001 break;
1003 case 1: /* CURLIOE_UNKNOWNCMD */
1004 result = CURLIOE_UNKNOWNCMD;
1005 break;
1007 case 2: /* CURLIOE_FAILRESTART */
1008 result = CURLIOE_FAILRESTART;
1009 break;
1011 default: /* Incorrect return value, but let's handle it */
1012 result = CURLIOE_FAILRESTART;
1013 break;
1015 CAMLdrop;
1017 caml_enter_blocking_section();
1018 return result;
1021 #if HAVE_DECL_CURLOPT_SEEKFUNCTION
1022 static int cb_SEEKFUNCTION(void *data,
1023 curl_off_t offset,
1024 int origin)
1026 caml_leave_blocking_section();
1028 CAMLparam0();
1029 CAMLlocal3(camlResult, camlOffset, camlOrigin);
1030 Connection *conn = (Connection *)data;
1032 camlOffset = caml_copy_int64(offset);
1034 if (origin == SEEK_SET)
1035 camlOrigin = Val_long(0);
1036 else if (origin == SEEK_CUR)
1037 camlOrigin = Val_long(1);
1038 else if (origin == SEEK_END)
1039 camlOrigin = Val_long(2);
1040 else
1041 caml_failwith("Invalid seek code");
1043 camlResult = caml_callback2_exn(Field(conn->ocamlValues,
1044 Ocaml_SEEKFUNCTION),
1045 camlOffset,
1046 camlOrigin);
1048 int result;
1049 if (Is_exception_result(camlResult))
1050 result = CURL_SEEKFUNC_FAIL;
1051 else
1052 switch (Int_val(camlResult))
1054 case 0: result = CURL_SEEKFUNC_OK; break;
1055 case 1: result = CURL_SEEKFUNC_FAIL; break;
1056 case 2: result = CURL_SEEKFUNC_CANTSEEK; break;
1057 default: caml_failwith("Invalid seek result");
1059 CAMLdrop;
1061 caml_enter_blocking_section();
1062 return result;
1064 #endif
1066 #if HAVE_DECL_CURLOPT_OPENSOCKETFUNCTION
1067 static int cb_OPENSOCKETFUNCTION(void *data,
1068 curlsocktype purpose,
1069 struct curl_sockaddr *addr)
1071 caml_leave_blocking_section();
1073 CAMLparam0();
1074 CAMLlocal1(result);
1075 Connection *conn = (Connection *)data;
1076 int sock = -1;
1077 (void)purpose; /* not used */
1079 sock = socket(addr->family, addr->socktype, addr->protocol);
1081 if (-1 != sock)
1083 /* FIXME windows */
1084 result = caml_callback_exn(Field(conn->ocamlValues, Ocaml_OPENSOCKETFUNCTION), Val_int(sock));
1085 if (Is_exception_result(result))
1087 close(sock);
1088 sock = -1;
1091 CAMLdrop;
1093 caml_enter_blocking_section();
1094 return ((sock == -1) ? CURL_SOCKET_BAD : sock);
1096 #endif
1099 ** curl_global_init helper function
1102 value caml_curl_global_init(value initOption)
1104 CAMLparam1(initOption);
1106 switch (Long_val(initOption))
1108 case 0: /* CURLINIT_GLOBALALL */
1109 CAMLreturn(Val_long(curl_global_init(CURL_GLOBAL_ALL)));
1110 break;
1112 case 1: /* CURLINIT_GLOBALSSL */
1113 CAMLreturn(Val_long(curl_global_init(CURL_GLOBAL_SSL)));
1114 break;
1116 case 2: /* CURLINIT_GLOBALWIN32 */
1117 CAMLreturn(Val_long(curl_global_init(CURL_GLOBAL_WIN32)));
1118 break;
1120 case 3: /* CURLINIT_GLOBALNOTHING */
1121 CAMLreturn(Val_long(curl_global_init(CURL_GLOBAL_NOTHING)));
1122 break;
1124 default:
1125 caml_failwith("Invalid Initialization Option");
1126 break;
1129 /* Keep compiler happy, we should never get here due to caml_failwith() */
1130 CAMLreturn(Val_unit);
1134 ** curl_global_cleanup helper function
1137 value caml_curl_global_cleanup(void)
1139 CAMLparam0();
1141 curl_global_cleanup();
1143 CAMLreturn(Val_unit);
1147 ** curl_easy_init helper function
1149 value caml_curl_easy_init(void)
1151 CAMLparam0();
1152 CAMLlocal1(result);
1154 result = caml_curl_alloc(newConnection());
1156 CAMLreturn(result);
1159 value caml_curl_easy_reset(value conn)
1161 CAMLparam1(conn);
1162 Connection *connection = Connection_val(conn);
1164 checkConnection(connection);
1165 curl_easy_reset(connection->handle);
1166 curl_easy_setopt(connection->handle, CURLOPT_PRIVATE, connection);
1167 resetOcamlValues(connection);
1169 CAMLreturn(Val_unit);
1173 ** curl_easy_setopt helper utility functions
1176 #define SETOPT_FUNCTION(name) \
1177 static void handle_##name##FUNCTION(Connection *conn, value option) \
1179 CAMLparam1(option); \
1180 CURLcode result = CURLE_OK; \
1181 Store_field(conn->ocamlValues, Ocaml_##name##FUNCTION, option); \
1182 result = curl_easy_setopt(conn->handle, CURLOPT_##name##FUNCTION, cb_##name##FUNCTION); \
1183 if (result != CURLE_OK) raiseError(conn, result); \
1184 result = curl_easy_setopt(conn->handle, CURLOPT_##name##DATA, conn); \
1185 if (result != CURLE_OK) raiseError(conn, result); \
1186 CAMLreturn0; \
1189 SETOPT_FUNCTION( WRITE)
1190 SETOPT_FUNCTION( READ)
1191 SETOPT_FUNCTION( HEADER)
1192 SETOPT_FUNCTION( PROGRESS)
1193 SETOPT_FUNCTION( DEBUG)
1195 #if HAVE_DECL_CURLOPT_SEEKFUNCTION
1196 SETOPT_FUNCTION( SEEK)
1197 #endif
1199 #if HAVE_DECL_CURLOPT_IOCTLFUNCTION
1200 SETOPT_FUNCTION( IOCTL)
1201 #endif
1203 #if HAVE_DECL_CURLOPT_OPENSOCKETFUNCTION
1204 SETOPT_FUNCTION( OPENSOCKET)
1205 #endif
1207 static void handle_slist(Connection *conn, struct curl_slist** slist, OcamlValue caml_option, CURLoption curl_option, value option)
1209 CAMLparam1(option);
1210 CURLcode result = CURLE_OK;
1212 Store_field(conn->ocamlValues, caml_option, option);
1214 free_curl_slist(*slist);
1215 *slist = NULL;
1217 while (Val_emptylist != option)
1219 *slist = curl_slist_append(*slist, String_val(Field(option, 0)));
1221 option = Field(option, 1);
1224 result = curl_easy_setopt(conn->handle, curl_option, *slist);
1226 if (result != CURLE_OK)
1227 raiseError(conn, result);
1229 CAMLreturn0;
1232 static long convert_bit_list(long *map, size_t map_size, value option)
1234 CAMLparam1(option);
1235 long bits = 0;
1236 int index;
1238 while (Val_emptylist != option)
1240 index = Int_val(Field(option, 0));
1241 if ((index < 0) || ((size_t)index >= map_size))
1242 caml_invalid_argument("convert_bit_list");
1244 bits |= map[index];
1246 option = Field(option, 1);
1249 CAMLreturnT(long, bits);
1252 #define SETOPT_STRING(name) \
1253 static void handle_##name(Connection *conn, value option) \
1255 CAMLparam1(option); \
1256 CURLcode result = CURLE_OK; \
1258 Store_field(conn->ocamlValues, Ocaml_##name, option); \
1260 result = curl_easy_setopt(conn->handle, CURLOPT_##name, String_val(option)); \
1262 if (result != CURLE_OK) \
1263 raiseError(conn, result); \
1265 CAMLreturn0; \
1268 #define SETOPT_VAL_(func_name, curl_option, conv_val) \
1269 static void func_name(Connection *conn, value option) \
1271 CAMLparam1(option); \
1272 CURLcode result = CURLE_OK; \
1274 result = curl_easy_setopt(conn->handle, curl_option, conv_val(option)); \
1276 if (result != CURLE_OK) \
1277 raiseError(conn, result); \
1279 CAMLreturn0; \
1282 #define SETOPT_VAL(name, conv) SETOPT_VAL_(handle_##name, CURLOPT_##name, conv)
1283 #define SETOPT_BOOL(name) SETOPT_VAL(name, Bool_val)
1284 #define SETOPT_LONG(name) SETOPT_VAL(name, Long_val)
1285 #define SETOPT_INT64(name) SETOPT_VAL(name, Int64_val)
1287 #define SETOPT_SLIST(name) \
1288 static void handle_##name(Connection* conn, value option) \
1290 handle_slist(conn,&(conn->curl_##name),Ocaml_##name,CURLOPT_##name,option); \
1293 SETOPT_STRING( URL)
1294 SETOPT_LONG( INFILESIZE)
1295 SETOPT_STRING( PROXY)
1296 SETOPT_LONG( PROXYPORT)
1297 SETOPT_BOOL( HTTPPROXYTUNNEL)
1298 SETOPT_BOOL( VERBOSE)
1299 SETOPT_BOOL( HEADER)
1300 SETOPT_BOOL( NOPROGRESS)
1302 #if HAVE_DECL_CURLOPT_NOSIGNAL
1303 SETOPT_BOOL( NOSIGNAL)
1304 #endif
1306 SETOPT_BOOL( NOBODY)
1307 SETOPT_BOOL( FAILONERROR)
1308 SETOPT_BOOL( UPLOAD)
1309 SETOPT_BOOL( POST)
1310 SETOPT_BOOL( FTPLISTONLY)
1311 SETOPT_BOOL( FTPAPPEND)
1314 static void handle_NETRC(Connection *conn, value option)
1316 CAMLparam1(option);
1317 CURLcode result = CURLE_OK;
1318 long netrc;
1320 switch (Long_val(option))
1322 case 0: /* CURL_NETRC_OPTIONAL */
1323 netrc = CURL_NETRC_OPTIONAL;
1324 break;
1326 case 1:/* CURL_NETRC_IGNORED */
1327 netrc = CURL_NETRC_IGNORED;
1328 break;
1330 case 2: /* CURL_NETRC_REQUIRED */
1331 netrc = CURL_NETRC_REQUIRED;
1332 break;
1334 default:
1335 caml_failwith("Invalid NETRC Option");
1336 break;
1339 result = curl_easy_setopt(conn->handle,
1340 CURLOPT_NETRC,
1341 netrc);
1343 if (result != CURLE_OK)
1344 raiseError(conn, result);
1346 CAMLreturn0;
1349 #if HAVE_DECL_CURLOPT_ENCODING
1350 static void handle_ENCODING(Connection *conn, value option)
1352 CAMLparam1(option);
1353 CURLcode result = CURLE_OK;
1355 switch (Long_val(option))
1357 case 0: /* CURL_ENCODING_NONE */
1358 result = curl_easy_setopt(conn->handle,
1359 CURLOPT_ENCODING,
1360 "identity");
1361 break;
1363 case 1: /* CURL_ENCODING_DEFLATE */
1364 result = curl_easy_setopt(conn->handle,
1365 CURLOPT_ENCODING,
1366 "deflate");
1367 break;
1369 case 2: /* CURL_ENCODING_GZIP */
1370 result = curl_easy_setopt(conn->handle,
1371 CURLOPT_ENCODING,
1372 "gzip");
1373 break;
1375 case 3: /* CURL_ENCODING_ANY */
1376 result = curl_easy_setopt(conn->handle,
1377 CURLOPT_ENCODING,
1378 "");
1379 break;
1381 default:
1382 caml_failwith("Invalid Encoding Option");
1383 break;
1386 if (result != CURLE_OK)
1387 raiseError(conn, result);
1389 CAMLreturn0;
1391 #endif
1394 SETOPT_BOOL( FOLLOWLOCATION)
1395 SETOPT_BOOL( TRANSFERTEXT)
1396 SETOPT_BOOL( PUT)
1397 SETOPT_STRING( USERPWD)
1398 SETOPT_STRING( PROXYUSERPWD)
1399 SETOPT_STRING( RANGE)
1401 static void handle_ERRORBUFFER(Connection *conn, value option)
1403 CAMLparam1(option);
1404 CURLcode result = CURLE_OK;
1406 Store_field(conn->ocamlValues, Ocaml_ERRORBUFFER, option);
1408 if (conn->curl_ERRORBUFFER != NULL)
1409 free(conn->curl_ERRORBUFFER);
1411 conn->curl_ERRORBUFFER = (char*)malloc(sizeof(char) * CURL_ERROR_SIZE);
1413 result = curl_easy_setopt(conn->handle,
1414 CURLOPT_ERRORBUFFER,
1415 conn->curl_ERRORBUFFER);
1417 if (result != CURLE_OK)
1418 raiseError(conn, result);
1420 CAMLreturn0;
1423 SETOPT_LONG( TIMEOUT)
1425 static void handle_POSTFIELDS(Connection *conn, value option)
1427 CAMLparam1(option);
1428 CURLcode result = CURLE_OK;
1430 Store_field(conn->ocamlValues, Ocaml_POSTFIELDS, option);
1432 if (conn->curl_POSTFIELDS != NULL)
1433 free(conn->curl_POSTFIELDS);
1435 conn->curl_POSTFIELDS = strdup_ml(option);
1437 result = curl_easy_setopt(conn->handle,
1438 CURLOPT_POSTFIELDS,
1439 conn->curl_POSTFIELDS);
1441 if (result != CURLE_OK)
1442 raiseError(conn, result);
1444 CAMLreturn0;
1447 SETOPT_LONG( POSTFIELDSIZE)
1448 SETOPT_STRING( REFERER)
1449 SETOPT_STRING( USERAGENT)
1450 SETOPT_STRING( FTPPORT)
1451 SETOPT_LONG( LOW_SPEED_LIMIT)
1452 SETOPT_LONG( LOW_SPEED_TIME)
1453 SETOPT_LONG( RESUME_FROM)
1454 SETOPT_STRING( COOKIE)
1456 SETOPT_SLIST( HTTPHEADER)
1458 static void handle_HTTPPOST(Connection *conn, value option)
1460 CAMLparam1(option);
1461 CAMLlocal3(listIter, formItem, contentType);
1462 CURLcode result = CURLE_OK;
1464 listIter = option;
1466 Store_field(conn->ocamlValues, Ocaml_HTTPPOST, option);
1468 free_curl_slist(conn->httpPostBuffers);
1469 if (conn->httpPostFirst != NULL)
1470 curl_formfree(conn->httpPostFirst);
1472 conn->httpPostBuffers = NULL;
1473 conn->httpPostFirst = NULL;
1474 conn->httpPostLast = NULL;
1476 while (!Is_long(listIter))
1478 formItem = Field(listIter, 0);
1480 switch (Tag_val(formItem))
1482 case 0: /* CURLFORM_CONTENT */
1483 if (Wosize_val(formItem) < 3)
1485 caml_failwith("Incorrect CURLFORM_CONTENT parameters");
1488 if (Is_long(Field(formItem, 2)) &&
1489 Long_val(Field(formItem, 2)) == 0)
1491 curl_formadd(&conn->httpPostFirst,
1492 &conn->httpPostLast,
1493 CURLFORM_COPYNAME,
1494 String_val(Field(formItem, 0)),
1495 CURLFORM_NAMELENGTH,
1496 caml_string_length(Field(formItem, 0)),
1497 CURLFORM_COPYCONTENTS,
1498 String_val(Field(formItem, 1)),
1499 CURLFORM_CONTENTSLENGTH,
1500 caml_string_length(Field(formItem, 1)),
1501 CURLFORM_END);
1503 else if (Is_block(Field(formItem, 2)))
1505 contentType = Field(formItem, 2);
1507 curl_formadd(&conn->httpPostFirst,
1508 &conn->httpPostLast,
1509 CURLFORM_COPYNAME,
1510 String_val(Field(formItem, 0)),
1511 CURLFORM_NAMELENGTH,
1512 caml_string_length(Field(formItem, 0)),
1513 CURLFORM_COPYCONTENTS,
1514 String_val(Field(formItem, 1)),
1515 CURLFORM_CONTENTSLENGTH,
1516 caml_string_length(Field(formItem, 1)),
1517 CURLFORM_CONTENTTYPE,
1518 String_val(Field(contentType, 0)),
1519 CURLFORM_END);
1521 else
1523 caml_failwith("Incorrect CURLFORM_CONTENT parameters");
1525 break;
1527 case 1: /* CURLFORM_FILECONTENT */
1528 if (Wosize_val(formItem) < 3)
1530 caml_failwith("Incorrect CURLFORM_FILECONTENT parameters");
1533 if (Is_long(Field(formItem, 2)) &&
1534 Long_val(Field(formItem, 2)) == 0)
1536 curl_formadd(&conn->httpPostFirst,
1537 &conn->httpPostLast,
1538 CURLFORM_COPYNAME,
1539 String_val(Field(formItem, 0)),
1540 CURLFORM_NAMELENGTH,
1541 caml_string_length(Field(formItem, 0)),
1542 CURLFORM_FILECONTENT,
1543 String_val(Field(formItem, 1)),
1544 CURLFORM_END);
1546 else if (Is_block(Field(formItem, 2)))
1548 contentType = Field(formItem, 2);
1550 curl_formadd(&conn->httpPostFirst,
1551 &conn->httpPostLast,
1552 CURLFORM_COPYNAME,
1553 String_val(Field(formItem, 0)),
1554 CURLFORM_NAMELENGTH,
1555 caml_string_length(Field(formItem, 0)),
1556 CURLFORM_FILECONTENT,
1557 String_val(Field(formItem, 1)),
1558 CURLFORM_CONTENTTYPE,
1559 String_val(Field(contentType, 0)),
1560 CURLFORM_END);
1562 else
1564 caml_failwith("Incorrect CURLFORM_FILECONTENT parameters");
1566 break;
1568 case 2: /* CURLFORM_FILE */
1569 if (Wosize_val(formItem) < 3)
1571 caml_failwith("Incorrect CURLFORM_FILE parameters");
1574 if (Is_long(Field(formItem, 2)) &&
1575 Long_val(Field(formItem, 2)) == 0)
1577 curl_formadd(&conn->httpPostFirst,
1578 &conn->httpPostLast,
1579 CURLFORM_COPYNAME,
1580 String_val(Field(formItem, 0)),
1581 CURLFORM_NAMELENGTH,
1582 caml_string_length(Field(formItem, 0)),
1583 CURLFORM_FILE,
1584 String_val(Field(formItem, 1)),
1585 CURLFORM_END);
1587 else if (Is_block(Field(formItem, 2)))
1589 contentType = Field(formItem, 2);
1591 curl_formadd(&conn->httpPostFirst,
1592 &conn->httpPostLast,
1593 CURLFORM_COPYNAME,
1594 String_val(Field(formItem, 0)),
1595 CURLFORM_NAMELENGTH,
1596 caml_string_length(Field(formItem, 0)),
1597 CURLFORM_FILE,
1598 String_val(Field(formItem, 1)),
1599 CURLFORM_CONTENTTYPE,
1600 String_val(Field(contentType, 0)),
1601 CURLFORM_END);
1603 else
1605 caml_failwith("Incorrect CURLFORM_FILE parameters");
1607 break;
1609 case 3: /* CURLFORM_BUFFER */
1610 if (Wosize_val(formItem) < 4)
1612 caml_failwith("Incorrect CURLFORM_BUFFER parameters");
1615 if (Is_long(Field(formItem, 3)) &&
1616 Long_val(Field(formItem, 3)) == 0)
1618 conn->httpPostBuffers = curl_slist_prepend_ml(conn->httpPostBuffers, Field(formItem, 2));
1620 curl_formadd(&conn->httpPostFirst,
1621 &conn->httpPostLast,
1622 CURLFORM_COPYNAME,
1623 String_val(Field(formItem, 0)),
1624 CURLFORM_NAMELENGTH,
1625 caml_string_length(Field(formItem, 0)),
1626 CURLFORM_BUFFER,
1627 String_val(Field(formItem, 1)),
1628 CURLFORM_BUFFERPTR,
1629 conn->httpPostBuffers->data,
1630 CURLFORM_BUFFERLENGTH,
1631 caml_string_length(Field(formItem, 2)),
1632 CURLFORM_END);
1634 else if (Is_block(Field(formItem, 3)))
1636 conn->httpPostBuffers = curl_slist_prepend_ml(conn->httpPostBuffers, Field(formItem, 2));
1638 contentType = Field(formItem, 3);
1640 curl_formadd(&conn->httpPostFirst,
1641 &conn->httpPostLast,
1642 CURLFORM_COPYNAME,
1643 String_val(Field(formItem, 0)),
1644 CURLFORM_NAMELENGTH,
1645 caml_string_length(Field(formItem, 0)),
1646 CURLFORM_BUFFER,
1647 String_val(Field(formItem, 1)),
1648 CURLFORM_BUFFERPTR,
1649 conn->httpPostBuffers->data,
1650 CURLFORM_BUFFERLENGTH,
1651 caml_string_length(Field(formItem, 2)),
1652 CURLFORM_CONTENTTYPE,
1653 String_val(Field(contentType, 0)),
1654 CURLFORM_END);
1656 else
1658 caml_failwith("Incorrect CURLFORM_BUFFER parameters");
1660 break;
1663 listIter = Field(listIter, 1);
1666 result = curl_easy_setopt(conn->handle,
1667 CURLOPT_HTTPPOST,
1668 conn->httpPostFirst);
1670 if (result != CURLE_OK)
1671 raiseError(conn, result);
1673 CAMLreturn0;
1676 SETOPT_STRING( SSLCERT)
1677 SETOPT_STRING( SSLCERTTYPE)
1678 SETOPT_STRING( SSLCERTPASSWD)
1679 SETOPT_STRING( SSLKEY)
1680 SETOPT_STRING( SSLKEYTYPE)
1681 SETOPT_STRING( SSLKEYPASSWD)
1682 SETOPT_STRING( SSLENGINE)
1683 SETOPT_BOOL( SSLENGINE_DEFAULT)
1684 SETOPT_BOOL( CRLF)
1686 SETOPT_SLIST( QUOTE)
1687 SETOPT_SLIST( POSTQUOTE)
1689 SETOPT_STRING( COOKIEFILE)
1690 #if HAVE_DECL_CURLOPT_CERTINFO
1691 SETOPT_BOOL( CERTINFO)
1692 #endif
1694 #if !defined(CURL_SSLVERSION_TLSv1_0)
1695 #define CURL_SSLVERSION_TLSv1_0 CURL_SSLVERSION_TLSv1
1696 #endif
1698 #if !defined(CURL_SSLVERSION_TLSv1_1)
1699 #define CURL_SSLVERSION_TLSv1_1 CURL_SSLVERSION_TLSv1
1700 #endif
1702 #if !defined(CURL_SSLVERSION_TLSv1_2)
1703 #define CURL_SSLVERSION_TLSv1_2 CURL_SSLVERSION_TLSv1
1704 #endif
1706 #if !defined(CURL_SSLVERSION_TLSv1_3)
1707 #define CURL_SSLVERSION_TLSv1_3 CURL_SSLVERSION_TLSv1
1708 #endif
1710 static void handle_SSLVERSION(Connection *conn, value option)
1712 CAMLparam1(option);
1713 CURLcode result = CURLE_OK;
1714 int v = CURL_SSLVERSION_DEFAULT;
1716 switch (Long_val(option))
1718 case 0: v = CURL_SSLVERSION_DEFAULT; break;
1719 case 1: v = CURL_SSLVERSION_TLSv1; break;
1720 case 2: v = CURL_SSLVERSION_SSLv2; break;
1721 case 3: v = CURL_SSLVERSION_SSLv3; break;
1722 case 4: v = CURL_SSLVERSION_TLSv1_0; break;
1723 case 5: v = CURL_SSLVERSION_TLSv1_1; break;
1724 case 6: v = CURL_SSLVERSION_TLSv1_2; break;
1725 case 7: v = CURL_SSLVERSION_TLSv1_3; break;
1726 default:
1727 caml_failwith("Invalid SSLVERSION Option");
1728 break;
1731 result = curl_easy_setopt(conn->handle, CURLOPT_SSLVERSION, v);
1733 if (result != CURLE_OK)
1734 raiseError(conn, result);
1736 CAMLreturn0;
1739 static void handle_TIMECONDITION(Connection *conn, value option)
1741 CAMLparam1(option);
1742 CURLcode result = CURLE_OK;
1743 int timecond = CURL_TIMECOND_NONE;
1745 switch (Long_val(option))
1747 case 0: timecond = CURL_TIMECOND_NONE; break;
1748 case 1: timecond = CURL_TIMECOND_IFMODSINCE; break;
1749 case 2: timecond = CURL_TIMECOND_IFUNMODSINCE; break;
1750 case 3: timecond = CURL_TIMECOND_LASTMOD; break;
1751 default:
1752 caml_failwith("Invalid TIMECOND Option");
1753 break;
1756 result = curl_easy_setopt(conn->handle, CURLOPT_TIMECONDITION, timecond);
1758 if (result != CURLE_OK)
1759 raiseError(conn, result);
1761 CAMLreturn0;
1764 SETOPT_VAL( TIMEVALUE, Int32_val)
1765 SETOPT_STRING( CUSTOMREQUEST)
1766 SETOPT_STRING( INTERFACE)
1768 static void handle_KRB4LEVEL(Connection *conn, value option)
1770 CAMLparam1(option);
1771 CURLcode result = CURLE_OK;
1773 switch (Long_val(option))
1775 case 0: /* KRB4_NONE */
1776 result = curl_easy_setopt(conn->handle,
1777 CURLOPT_KRB4LEVEL,
1778 NULL);
1779 break;
1781 case 1: /* KRB4_CLEAR */
1782 result = curl_easy_setopt(conn->handle,
1783 CURLOPT_KRB4LEVEL,
1784 "clear");
1785 break;
1787 case 2: /* KRB4_SAFE */
1788 result = curl_easy_setopt(conn->handle,
1789 CURLOPT_KRB4LEVEL,
1790 "safe");
1791 break;
1793 case 3: /* KRB4_CONFIDENTIAL */
1794 result = curl_easy_setopt(conn->handle,
1795 CURLOPT_KRB4LEVEL,
1796 "confidential");
1797 break;
1799 case 4: /* KRB4_PRIVATE */
1800 result = curl_easy_setopt(conn->handle,
1801 CURLOPT_KRB4LEVEL,
1802 "private");
1803 break;
1805 default:
1806 caml_failwith("Invalid KRB4 Option");
1807 break;
1810 if (result != CURLE_OK)
1811 raiseError(conn, result);
1813 CAMLreturn0;
1816 SETOPT_BOOL( SSL_VERIFYPEER)
1817 SETOPT_STRING( CAINFO)
1818 SETOPT_STRING( CAPATH)
1819 SETOPT_BOOL( FILETIME)
1820 SETOPT_LONG( MAXREDIRS)
1821 SETOPT_LONG( MAXCONNECTS)
1823 static void handle_CLOSEPOLICY(Connection *conn, value option)
1825 CAMLparam1(option);
1826 CURLcode result = CURLE_OK;
1828 switch (Long_val(option))
1830 case 0: /* CLOSEPOLICY_OLDEST */
1831 result = curl_easy_setopt(conn->handle,
1832 CURLOPT_CLOSEPOLICY,
1833 CURLCLOSEPOLICY_OLDEST);
1834 break;
1836 case 1: /* CLOSEPOLICY_LEAST_RECENTLY_USED */
1837 result = curl_easy_setopt(conn->handle,
1838 CURLOPT_CLOSEPOLICY,
1839 CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
1840 break;
1842 default:
1843 caml_failwith("Invalid CLOSEPOLICY Option");
1844 break;
1847 if (result != CURLE_OK)
1848 raiseError(conn, result);
1850 CAMLreturn0;
1853 SETOPT_BOOL( FRESH_CONNECT)
1854 SETOPT_BOOL( FORBID_REUSE)
1855 SETOPT_STRING( RANDOM_FILE)
1856 SETOPT_STRING( EGDSOCKET)
1857 SETOPT_LONG( CONNECTTIMEOUT)
1858 SETOPT_BOOL( HTTPGET)
1860 static void handle_SSL_VERIFYHOST(Connection *conn, value option)
1862 CAMLparam1(option);
1863 CURLcode result = CURLE_OK;
1865 switch (Long_val(option))
1867 case 0: /* SSLVERIFYHOST_NONE */
1868 case 1: /* SSLVERIFYHOST_EXISTENCE */
1869 case 2: /* SSLVERIFYHOST_HOSTNAME */
1870 result = curl_easy_setopt(conn->handle,
1871 CURLOPT_SSL_VERIFYHOST,
1872 /* map EXISTENCE to HOSTNAME */
1873 Long_val(option) == 0 ? 0 : 2);
1874 break;
1876 default:
1877 caml_failwith("Invalid SSLVERIFYHOST Option");
1878 break;
1881 if (result != CURLE_OK)
1882 raiseError(conn, result);
1884 CAMLreturn0;
1887 SETOPT_STRING( COOKIEJAR)
1888 SETOPT_STRING( SSL_CIPHER_LIST)
1890 static void handle_HTTP_VERSION(Connection *conn, value option)
1892 CAMLparam1(option);
1893 CURLcode result = CURLE_OK;
1895 long version = CURL_HTTP_VERSION_NONE;
1897 switch (Long_val(option))
1899 case 0: version = CURL_HTTP_VERSION_NONE; break;
1900 case 1: version = CURL_HTTP_VERSION_1_0; break;
1901 case 2: version = CURL_HTTP_VERSION_1_1; break;
1902 case 3:
1903 #if defined(CURL_HTTP_VERSION_2)
1904 version = CURL_HTTP_VERSION_2;
1905 #elif defined(CURL_HTTP_VERSION_2_0)
1906 version = CURL_HTTP_VERSION_2_0;
1907 #endif
1908 break;
1909 case 4:
1910 #if defined(CURL_HTTP_VERSION_2TLS)
1911 version = CURL_HTTP_VERSION_2TLS;
1912 #endif
1913 break;
1914 default:
1915 caml_invalid_argument("CURLOPT_HTTP_VERSION");
1916 break;
1919 result = curl_easy_setopt(conn->handle, CURLOPT_HTTP_VERSION, version);
1921 if (result != CURLE_OK)
1922 raiseError(conn, result);
1924 CAMLreturn0;
1927 SETOPT_BOOL( FTP_USE_EPSV)
1928 SETOPT_LONG( DNS_CACHE_TIMEOUT)
1929 SETOPT_BOOL( DNS_USE_GLOBAL_CACHE)
1931 static void handle_PRIVATE(Connection *conn, value option)
1933 CAMLparam1(option);
1934 Store_field(conn->ocamlValues, Ocaml_PRIVATE, option);
1935 CAMLreturn0;
1938 #if HAVE_DECL_CURLOPT_HTTP200ALIASES
1939 SETOPT_SLIST( HTTP200ALIASES)
1940 #endif
1942 #if HAVE_DECL_CURLOPT_UNRESTRICTED_AUTH
1943 SETOPT_BOOL( UNRESTRICTED_AUTH)
1944 #endif
1946 #if HAVE_DECL_CURLOPT_FTP_USE_EPRT
1947 SETOPT_BOOL( FTP_USE_EPRT)
1948 #endif
1950 #if HAVE_DECL_CURLOPT_HTTPAUTH
1951 static void handle_HTTPAUTH(Connection *conn, value option)
1953 CAMLparam1(option);
1954 CAMLlocal1(listIter);
1955 CURLcode result = CURLE_OK;
1956 long auth = CURLAUTH_NONE;
1958 listIter = option;
1960 while (!Is_long(listIter))
1962 switch (Long_val(Field(listIter, 0)))
1964 case 0: /* CURLAUTH_BASIC */
1965 auth |= CURLAUTH_BASIC;
1966 break;
1968 case 1: /* CURLAUTH_DIGEST */
1969 auth |= CURLAUTH_DIGEST;
1970 break;
1972 case 2: /* CURLAUTH_GSSNEGOTIATE */
1973 auth |= CURLAUTH_GSSNEGOTIATE;
1974 break;
1976 case 3: /* CURLAUTH_NTLM */
1977 auth |= CURLAUTH_NTLM;
1978 break;
1980 case 4: /* CURLAUTH_ANY */
1981 auth |= CURLAUTH_ANY;
1982 break;
1984 case 5: /* CURLAUTH_ANYSAFE */
1985 auth |= CURLAUTH_ANYSAFE;
1986 break;
1988 default:
1989 caml_failwith("Invalid HTTPAUTH Value");
1990 break;
1993 listIter = Field(listIter, 1);
1996 result = curl_easy_setopt(conn->handle,
1997 CURLOPT_HTTPAUTH,
1998 auth);
2000 if (result != CURLE_OK)
2001 raiseError(conn, result);
2003 CAMLreturn0;
2005 #endif
2007 #if HAVE_DECL_CURLOPT_FTP_CREATE_MISSING_DIRS
2008 SETOPT_BOOL( FTP_CREATE_MISSING_DIRS)
2009 #endif
2011 #if HAVE_DECL_CURLOPT_PROXYAUTH
2012 static void handle_PROXYAUTH(Connection *conn, value option)
2014 CAMLparam1(option);
2015 CAMLlocal1(listIter);
2016 CURLcode result = CURLE_OK;
2017 long auth = CURLAUTH_NONE;
2019 listIter = option;
2021 while (!Is_long(listIter))
2023 switch (Long_val(Field(listIter, 0)))
2025 case 0: /* CURLAUTH_BASIC */
2026 auth |= CURLAUTH_BASIC;
2027 break;
2029 case 1: /* CURLAUTH_DIGEST */
2030 auth |= CURLAUTH_DIGEST;
2031 break;
2033 case 2: /* CURLAUTH_GSSNEGOTIATE */
2034 auth |= CURLAUTH_GSSNEGOTIATE;
2035 break;
2037 case 3: /* CURLAUTH_NTLM */
2038 auth |= CURLAUTH_NTLM;
2039 break;
2041 case 4: /* CURLAUTH_ANY */
2042 auth |= CURLAUTH_ANY;
2043 break;
2045 case 5: /* CURLAUTH_ANYSAFE */
2046 auth |= CURLAUTH_ANYSAFE;
2047 break;
2049 default:
2050 caml_failwith("Invalid HTTPAUTH Value");
2051 break;
2054 listIter = Field(listIter, 1);
2057 result = curl_easy_setopt(conn->handle,
2058 CURLOPT_PROXYAUTH,
2059 auth);
2061 if (result != CURLE_OK)
2062 raiseError(conn, result);
2064 CAMLreturn0;
2066 #endif
2068 #if HAVE_DECL_CURLOPT_FTP_RESPONSE_TIMEOUT
2069 SETOPT_LONG( FTP_RESPONSE_TIMEOUT)
2070 #endif
2072 #if HAVE_DECL_CURLOPT_IPRESOLVE
2073 static void handle_IPRESOLVE(Connection *conn, value option)
2075 CAMLparam1(option);
2076 CURLcode result = CURLE_OK;
2078 switch (Long_val(option))
2080 case 0: /* CURL_IPRESOLVE_WHATEVER */
2081 result = curl_easy_setopt(conn->handle,
2082 CURLOPT_IPRESOLVE,
2083 CURL_IPRESOLVE_WHATEVER);
2084 break;
2086 case 1: /* CURL_IPRESOLVE_V4 */
2087 result = curl_easy_setopt(conn->handle,
2088 CURLOPT_IPRESOLVE,
2089 CURL_IPRESOLVE_V4);
2090 break;
2092 case 2: /* CURL_IPRESOLVE_V6 */
2093 result = curl_easy_setopt(conn->handle,
2094 CURLOPT_IPRESOLVE,
2095 CURL_IPRESOLVE_V6);
2096 break;
2098 default:
2099 caml_failwith("Invalid IPRESOLVE Value");
2100 break;
2103 if (result != CURLE_OK)
2104 raiseError(conn, result);
2106 CAMLreturn0;
2108 #endif
2110 #if HAVE_DECL_CURLOPT_MAXFILESIZE
2111 SETOPT_VAL( MAXFILESIZE, Int32_val)
2112 #endif
2114 #if HAVE_DECL_CURLOPT_INFILESIZE_LARGE
2115 SETOPT_INT64( INFILESIZE_LARGE)
2116 #endif
2118 #if HAVE_DECL_CURLOPT_RESUME_FROM_LARGE
2119 SETOPT_INT64( RESUME_FROM_LARGE)
2120 #endif
2122 #if HAVE_DECL_CURLOPT_MAXFILESIZE_LARGE
2123 SETOPT_INT64( MAXFILESIZE_LARGE)
2124 #endif
2126 #if HAVE_DECL_CURLOPT_NETRC_FILE
2127 SETOPT_STRING( NETRC_FILE)
2128 #endif
2130 #if HAVE_DECL_CURLOPT_FTP_SSL
2131 static void handle_FTP_SSL(Connection *conn, value option)
2133 CAMLparam1(option);
2134 CURLcode result = CURLE_OK;
2136 switch (Long_val(option))
2138 case 0: /* CURLFTPSSL_NONE */
2139 result = curl_easy_setopt(conn->handle,
2140 CURLOPT_FTP_SSL,
2141 CURLFTPSSL_NONE);
2142 break;
2144 case 1: /* CURLFTPSSL_TRY */
2145 result = curl_easy_setopt(conn->handle,
2146 CURLOPT_FTP_SSL,
2147 CURLFTPSSL_TRY);
2148 break;
2150 case 2: /* CURLFTPSSL_CONTROL */
2151 result = curl_easy_setopt(conn->handle,
2152 CURLOPT_FTP_SSL,
2153 CURLFTPSSL_CONTROL);
2154 break;
2156 case 3: /* CURLFTPSSL_ALL */
2157 result = curl_easy_setopt(conn->handle,
2158 CURLOPT_FTP_SSL,
2159 CURLFTPSSL_ALL);
2160 break;
2162 default:
2163 caml_failwith("Invalid FTP_SSL Value");
2164 break;
2167 if (result != CURLE_OK)
2168 raiseError(conn, result);
2170 CAMLreturn0;
2172 #endif
2174 #if HAVE_DECL_CURLOPT_POSTFIELDSIZE_LARGE
2175 SETOPT_INT64( POSTFIELDSIZE_LARGE)
2176 #endif
2178 #if HAVE_DECL_CURLOPT_TCP_NODELAY
2179 /* not using SETOPT_BOOL here because of TCP_NODELAY defined in winsock.h */
2180 SETOPT_VAL_( handle_TCP_NODELAY, CURLOPT_TCP_NODELAY, Bool_val)
2181 #endif
2183 #if HAVE_DECL_CURLOPT_FTPSSLAUTH
2184 static void handle_FTPSSLAUTH(Connection *conn, value option)
2186 CAMLparam1(option);
2187 CURLcode result = CURLE_OK;
2189 switch (Long_val(option))
2191 case 0: /* CURLFTPAUTH_DEFAULT */
2192 result = curl_easy_setopt(conn->handle,
2193 CURLOPT_FTPSSLAUTH,
2194 CURLFTPAUTH_DEFAULT);
2195 break;
2197 case 1: /* CURLFTPAUTH_SSL */
2198 result = curl_easy_setopt(conn->handle,
2199 CURLOPT_FTPSSLAUTH,
2200 CURLFTPAUTH_SSL);
2201 break;
2203 case 2: /* CURLFTPAUTH_TLS */
2204 result = curl_easy_setopt(conn->handle,
2205 CURLOPT_FTPSSLAUTH,
2206 CURLFTPAUTH_TLS);
2207 break;
2209 default:
2210 caml_failwith("Invalid FTPSSLAUTH value");
2211 break;
2214 if (result != CURLE_OK)
2215 raiseError(conn, result);
2217 CAMLreturn0;
2219 #endif
2221 #if HAVE_DECL_CURLOPT_FTP_ACCOUNT
2222 SETOPT_STRING( FTP_ACCOUNT)
2223 #endif
2225 #if HAVE_DECL_CURLOPT_COOKIELIST
2226 SETOPT_STRING( COOKIELIST)
2227 #endif
2229 #if HAVE_DECL_CURLOPT_IGNORE_CONTENT_LENGTH
2230 SETOPT_BOOL( IGNORE_CONTENT_LENGTH)
2231 #endif
2233 #if HAVE_DECL_CURLOPT_FTP_SKIP_PASV_IP
2234 SETOPT_BOOL( FTP_SKIP_PASV_IP)
2235 #endif
2237 #if HAVE_DECL_CURLOPT_FTP_FILEMETHOD
2238 static void handle_FTP_FILEMETHOD(Connection *conn, value option)
2240 CAMLparam1(option);
2241 CURLcode result = CURLE_OK;
2243 switch (Long_val(option))
2245 case 0: /* CURLFTPMETHOD_DEFAULT */
2246 result = curl_easy_setopt(conn->handle,
2247 CURLOPT_FTP_FILEMETHOD,
2248 CURLFTPMETHOD_DEFAULT);
2249 break;
2251 case 1: /* CURLFTMETHOD_MULTICWD */
2252 result = curl_easy_setopt(conn->handle,
2253 CURLOPT_FTP_FILEMETHOD,
2254 CURLFTPMETHOD_MULTICWD);
2255 break;
2257 case 2: /* CURLFTPMETHOD_NOCWD */
2258 result = curl_easy_setopt(conn->handle,
2259 CURLOPT_FTP_FILEMETHOD,
2260 CURLFTPMETHOD_NOCWD);
2261 break;
2263 case 3: /* CURLFTPMETHOD_SINGLECWD */
2264 result = curl_easy_setopt(conn->handle,
2265 CURLOPT_FTP_FILEMETHOD,
2266 CURLFTPMETHOD_SINGLECWD);
2268 default:
2269 caml_failwith("Invalid FTP_FILEMETHOD value");
2270 break;
2273 if (result != CURLE_OK)
2274 raiseError(conn, result);
2276 CAMLreturn0;
2278 #endif
2280 #if HAVE_DECL_CURLOPT_LOCALPORT
2281 SETOPT_LONG( LOCALPORT)
2282 #endif
2284 #if HAVE_DECL_CURLOPT_LOCALPORTRANGE
2285 SETOPT_LONG( LOCALPORTRANGE)
2286 #endif
2288 #if HAVE_DECL_CURLOPT_CONNECT_ONLY
2289 SETOPT_BOOL( CONNECT_ONLY)
2290 #endif
2292 #if HAVE_DECL_CURLOPT_MAX_SEND_SPEED_LARGE
2293 SETOPT_INT64( MAX_SEND_SPEED_LARGE)
2294 #endif
2296 #if HAVE_DECL_CURLOPT_MAX_RECV_SPEED_LARGE
2297 SETOPT_INT64( MAX_RECV_SPEED_LARGE)
2298 #endif
2300 #if HAVE_DECL_CURLOPT_FTP_ALTERNATIVE_TO_USER
2301 SETOPT_STRING( FTP_ALTERNATIVE_TO_USER)
2302 #endif
2304 #if HAVE_DECL_CURLOPT_SSL_SESSIONID_CACHE
2305 SETOPT_BOOL( SSL_SESSIONID_CACHE)
2306 #endif
2308 #if HAVE_DECL_CURLOPT_SSH_AUTH_TYPES
2309 static void handle_SSH_AUTH_TYPES(Connection *conn, value option)
2311 CAMLparam1(option);
2312 CAMLlocal1(listIter);
2313 CURLcode result = CURLE_OK;
2314 long authTypes = CURLSSH_AUTH_NONE;
2316 listIter = option;
2318 while (!Is_long(listIter))
2320 switch (Long_val(Field(listIter, 0)))
2322 case 0: /* CURLSSH_AUTH_ANY */
2323 authTypes |= CURLSSH_AUTH_ANY;
2324 break;
2326 case 1: /* CURLSSH_AUTH_PUBLICKEY */
2327 authTypes |= CURLSSH_AUTH_PUBLICKEY;
2328 break;
2330 case 2: /* CURLSSH_AUTH_PASSWORD */
2331 authTypes |= CURLSSH_AUTH_PASSWORD;
2332 break;
2334 case 3: /* CURLSSH_AUTH_HOST */
2335 authTypes |= CURLSSH_AUTH_HOST;
2336 break;
2338 case 4: /* CURLSSH_AUTH_KEYBOARD */
2339 authTypes |= CURLSSH_AUTH_KEYBOARD;
2340 break;
2342 default:
2343 caml_failwith("Invalid CURLSSH_AUTH_TYPES Value");
2344 break;
2347 listIter = Field(listIter, 1);
2350 result = curl_easy_setopt(conn->handle,
2351 CURLOPT_SSH_AUTH_TYPES,
2352 authTypes);
2354 if (result != CURLE_OK)
2355 raiseError(conn, result);
2357 CAMLreturn0;
2359 #endif
2361 #if HAVE_DECL_CURLOPT_SSH_PUBLIC_KEYFILE
2362 SETOPT_STRING( SSH_PUBLIC_KEYFILE)
2363 #endif
2365 #if HAVE_DECL_CURLOPT_SSH_PRIVATE_KEYFILE
2366 SETOPT_STRING( SSH_PRIVATE_KEYFILE)
2367 #endif
2369 #if HAVE_DECL_CURLOPT_FTP_SSL_CCC
2370 static void handle_FTP_SSL_CCC(Connection *conn, value option)
2372 CAMLparam1(option);
2373 CURLcode result = CURLE_OK;
2375 switch (Long_val(option))
2377 case 0: /* CURLFTPSSL_CCC_NONE */
2378 result = curl_easy_setopt(conn->handle,
2379 CURLOPT_FTP_SSL_CCC,
2380 CURLFTPSSL_CCC_NONE);
2381 break;
2383 case 1: /* CURLFTPSSL_CCC_PASSIVE */
2384 result = curl_easy_setopt(conn->handle,
2385 CURLOPT_FTP_SSL_CCC,
2386 CURLFTPSSL_CCC_PASSIVE);
2387 break;
2389 case 2: /* CURLFTPSSL_CCC_ACTIVE */
2390 result = curl_easy_setopt(conn->handle,
2391 CURLOPT_FTP_SSL_CCC,
2392 CURLFTPSSL_CCC_ACTIVE);
2393 break;
2395 default:
2396 caml_failwith("Invalid FTPSSL_CCC value");
2397 break;
2400 if (result != CURLE_OK)
2401 raiseError(conn, result);
2403 CAMLreturn0;
2405 #endif
2407 #if HAVE_DECL_CURLOPT_TIMEOUT_MS
2408 SETOPT_LONG( TIMEOUT_MS)
2409 #endif
2411 #if HAVE_DECL_CURLOPT_CONNECTTIMEOUT_MS
2412 SETOPT_LONG( CONNECTTIMEOUT_MS)
2413 #endif
2415 #if HAVE_DECL_CURLOPT_HTTP_TRANSFER_DECODING
2416 SETOPT_BOOL( HTTP_TRANSFER_DECODING)
2417 #endif
2419 #if HAVE_DECL_CURLOPT_HTTP_CONTENT_DECODING
2420 SETOPT_BOOL( HTTP_CONTENT_DECODING)
2421 #endif
2423 #if HAVE_DECL_CURLOPT_NEW_FILE_PERMS
2424 SETOPT_LONG( NEW_FILE_PERMS)
2425 #endif
2427 #if HAVE_DECL_CURLOPT_NEW_DIRECTORY_PERMS
2428 SETOPT_LONG( NEW_DIRECTORY_PERMS)
2429 #endif
2431 #if HAVE_DECL_CURLOPT_POST301
2432 SETOPT_BOOL( POST301)
2433 #endif
2435 #if HAVE_DECL_CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
2436 SETOPT_STRING( SSH_HOST_PUBLIC_KEY_MD5)
2437 #endif
2439 #if HAVE_DECL_CURLOPT_COPYPOSTFIELDS
2440 SETOPT_STRING( COPYPOSTFIELDS)
2441 #endif
2443 #if HAVE_DECL_CURLOPT_PROXY_TRANSFER_MODE
2444 SETOPT_BOOL( PROXY_TRANSFER_MODE)
2445 #endif
2447 #if HAVE_DECL_CURLOPT_AUTOREFERER
2448 SETOPT_BOOL( AUTOREFERER)
2449 #endif
2451 #if HAVE_DECL_CURLOPT_PROXYTYPE
2452 static void handle_PROXYTYPE(Connection *conn, value option)
2454 CAMLparam1(option);
2455 CURLcode result = CURLE_OK;
2456 long proxy_type;
2458 switch (Long_val(option))
2460 case 0: proxy_type = CURLPROXY_HTTP; break;
2461 case 1: proxy_type = CURLPROXY_HTTP_1_0; break;
2462 case 2: proxy_type = CURLPROXY_SOCKS4; break;
2463 case 3: proxy_type = CURLPROXY_SOCKS5; break;
2464 case 4: proxy_type = CURLPROXY_SOCKS4A; break;
2465 case 5: proxy_type = CURLPROXY_SOCKS5_HOSTNAME; break;
2466 default:
2467 caml_failwith("Invalid curl proxy type");
2470 result = curl_easy_setopt(conn->handle,
2471 CURLOPT_PROXYTYPE,
2472 proxy_type);
2474 if (result != CURLE_OK)
2475 raiseError(conn, result);
2477 CAMLreturn0;
2479 #endif
2481 #if HAVE_DECL_CURLOPT_PROTOCOLS || HAVE_DECL_CURLOPT_REDIR_PROTOCOLS
2483 long protoMap[] =
2485 CURLPROTO_ALL,
2486 CURLPROTO_HTTP, CURLPROTO_HTTPS, CURLPROTO_FTP, CURLPROTO_FTPS, CURLPROTO_SCP, CURLPROTO_SFTP,
2487 CURLPROTO_TELNET, CURLPROTO_LDAP, CURLPROTO_LDAPS, CURLPROTO_DICT, CURLPROTO_FILE, CURLPROTO_TFTP,
2488 /* factor out with autoconf? */
2489 #if defined(CURLPROTO_IMAP)
2490 CURLPROTO_IMAP,
2491 #else
2493 #endif
2494 #if defined(CURLPROTO_IMAPS)
2495 CURLPROTO_IMAPS,
2496 #else
2498 #endif
2499 #if defined(CURLPROTO_POP3)
2500 CURLPROTO_POP3,
2501 #else
2503 #endif
2504 #if defined(CURLPROTO_POP3S)
2505 CURLPROTO_POP3S,
2506 #else
2508 #endif
2509 #if defined(CURLPROTO_SMTP)
2510 CURLPROTO_SMTP,
2511 #else
2513 #endif
2514 #if defined(CURLPROTO_SMTPS)
2515 CURLPROTO_SMTPS,
2516 #else
2518 #endif
2519 #if defined(CURLPROTO_RTSP)
2520 CURLPROTO_RTSP,
2521 #else
2523 #endif
2524 #if defined(CURLPROTO_RTMP)
2525 CURLPROTO_RTMP,
2526 #else
2528 #endif
2529 #if defined(CURLPROTO_RTMPT)
2530 CURLPROTO_RTMPT,
2531 #else
2533 #endif
2534 #if defined(CURLPROTO_RTMPE)
2535 CURLPROTO_RTMPE,
2536 #else
2538 #endif
2539 #if defined(CURLPROTO_RTMPTE)
2540 CURLPROTO_RTMPTE,
2541 #else
2543 #endif
2544 #if defined(CURLPROTO_RTMPS)
2545 CURLPROTO_RTMPS,
2546 #else
2548 #endif
2549 #if defined(CURLPROTO_RTMPTS)
2550 CURLPROTO_RTMPTS,
2551 #else
2553 #endif
2554 #if defined(CURLPROTO_GOPHER)
2555 CURLPROTO_GOPHER,
2556 #else
2558 #endif
2561 static void handle_PROTOCOLSOPTION(CURLoption curlopt, Connection *conn, value option)
2563 CAMLparam1(option);
2564 CURLcode result = CURLE_OK;
2565 long bits = convert_bit_list(protoMap, sizeof(protoMap) / sizeof(protoMap[0]), option);
2567 result = curl_easy_setopt(conn->handle, curlopt, bits);
2569 if (result != CURLE_OK)
2570 raiseError(conn, result);
2572 CAMLreturn0;
2574 #endif
2576 #if HAVE_DECL_CURLOPT_PROTOCOLS
2577 static void handle_PROTOCOLS(Connection *conn, value option)
2579 handle_PROTOCOLSOPTION(CURLOPT_PROTOCOLS, conn, option);
2581 #endif
2583 #if HAVE_DECL_CURLOPT_REDIR_PROTOCOLS
2584 static void handle_REDIR_PROTOCOLS(Connection *conn, value option)
2586 handle_PROTOCOLSOPTION(CURLOPT_REDIR_PROTOCOLS, conn, option);
2588 #endif
2590 #if HAVE_DECL_CURLOPT_RESOLVE
2591 SETOPT_SLIST( RESOLVE)
2592 #endif
2594 #if HAVE_DECL_CURLOPT_DNS_SERVERS
2595 SETOPT_STRING( DNS_SERVERS)
2596 #endif
2598 #if HAVE_DECL_CURLOPT_MAIL_FROM
2599 SETOPT_STRING( MAIL_FROM)
2600 #endif
2602 #if HAVE_DECL_CURLOPT_MAIL_RCPT
2603 SETOPT_SLIST( MAIL_RCPT)
2604 #endif
2606 #if HAVE_DECL_CURLOPT_PIPEWAIT
2607 SETOPT_BOOL( PIPEWAIT)
2608 #endif
2610 #if HAVE_DECL_CURLOPT_USERNAME
2611 SETOPT_STRING( USERNAME)
2612 #endif
2614 #if HAVE_DECL_CURLOPT_PASSWORD
2615 SETOPT_STRING( PASSWORD)
2616 #endif
2618 #if HAVE_DECL_CURLOPT_LOGIN_OPTIONS
2619 SETOPT_STRING( LOGIN_OPTIONS)
2620 #endif
2622 #if HAVE_DECL_CURLOPT_CONNECT_TO
2623 SETOPT_SLIST( CONNECT_TO)
2624 #endif
2627 ** curl_easy_setopt helper function
2630 #define MAP(name) { handle_ ## name, "CURLOPT_"#name, Ocaml_##name }
2631 #define MAP_NO(name) { NULL, "CURLOPT_"#name , Ocaml_##name }
2632 #define IMM(name) { handle_ ## name, "CURLOPT_"#name, (OcamlValue)(-1) }
2633 #define IMM_NO(name) { NULL, "CURLOPT_"#name , (OcamlValue)(-1) }
2635 CURLOptionMapping implementedOptionMap[] =
2637 MAP(WRITEFUNCTION),
2638 MAP(READFUNCTION),
2639 IMM(INFILESIZE),
2640 MAP(URL),
2641 MAP(PROXY),
2642 IMM(PROXYPORT),
2643 IMM(HTTPPROXYTUNNEL),
2644 IMM(VERBOSE),
2645 IMM(HEADER),
2646 IMM(NOPROGRESS),
2647 #if HAVE_DECL_CURLOPT_NOSIGNAL
2648 IMM(NOSIGNAL),
2649 #else
2650 IMM_NO(NOSIGNAL),
2651 #endif
2652 IMM(NOBODY),
2653 IMM(FAILONERROR),
2654 IMM(UPLOAD),
2655 IMM(POST),
2656 IMM(FTPLISTONLY),
2657 IMM(FTPAPPEND),
2658 IMM(NETRC),
2659 #if HAVE_DECL_CURLOPT_ENCODING
2660 IMM(ENCODING),
2661 #else
2662 IMM_NO(ENCODING),
2663 #endif
2664 IMM(FOLLOWLOCATION),
2665 IMM(TRANSFERTEXT),
2666 IMM(PUT),
2667 MAP(USERPWD),
2668 MAP(PROXYUSERPWD),
2669 MAP(RANGE),
2670 IMM(ERRORBUFFER), /* mutable buffer, as output value, do not duplicate */
2671 IMM(TIMEOUT),
2672 MAP(POSTFIELDS),
2673 IMM(POSTFIELDSIZE),
2674 MAP(REFERER),
2675 MAP(USERAGENT),
2676 MAP(FTPPORT),
2677 IMM(LOW_SPEED_LIMIT),
2678 IMM(LOW_SPEED_TIME),
2679 IMM(RESUME_FROM),
2680 MAP(COOKIE),
2681 MAP(HTTPHEADER),
2682 MAP(HTTPPOST),
2683 MAP(SSLCERT),
2684 MAP(SSLCERTTYPE),
2685 MAP(SSLCERTPASSWD),
2686 MAP(SSLKEY),
2687 MAP(SSLKEYTYPE),
2688 MAP(SSLKEYPASSWD),
2689 MAP(SSLENGINE),
2690 IMM(SSLENGINE_DEFAULT),
2691 IMM(CRLF),
2692 MAP(QUOTE),
2693 MAP(POSTQUOTE),
2694 MAP(HEADERFUNCTION),
2695 MAP(COOKIEFILE),
2696 IMM(SSLVERSION),
2697 IMM(TIMECONDITION),
2698 IMM(TIMEVALUE),
2699 MAP(CUSTOMREQUEST),
2700 MAP(INTERFACE),
2701 IMM(KRB4LEVEL),
2702 MAP(PROGRESSFUNCTION),
2703 IMM(SSL_VERIFYPEER),
2704 MAP(CAINFO),
2705 MAP(CAPATH),
2706 IMM(FILETIME),
2707 IMM(MAXREDIRS),
2708 IMM(MAXCONNECTS),
2709 IMM(CLOSEPOLICY),
2710 IMM(FRESH_CONNECT),
2711 IMM(FORBID_REUSE),
2712 MAP(RANDOM_FILE),
2713 MAP(EGDSOCKET),
2714 IMM(CONNECTTIMEOUT),
2715 IMM(HTTPGET),
2716 IMM(SSL_VERIFYHOST),
2717 MAP(COOKIEJAR),
2718 MAP(SSL_CIPHER_LIST),
2719 IMM(HTTP_VERSION),
2720 IMM(FTP_USE_EPSV),
2721 IMM(DNS_CACHE_TIMEOUT),
2722 IMM(DNS_USE_GLOBAL_CACHE),
2723 MAP(DEBUGFUNCTION),
2724 MAP(PRIVATE),
2725 #if HAVE_DECL_CURLOPT_HTTP200ALIASES
2726 MAP(HTTP200ALIASES),
2727 #else
2728 MAP_NO(HTTP200ALIASES),
2729 #endif
2730 #if HAVE_DECL_CURLOPT_UNRESTRICTED_AUTH
2731 IMM(UNRESTRICTED_AUTH),
2732 #else
2733 IMM_NO(UNRESTRICTED_AUTH),
2734 #endif
2735 #if HAVE_DECL_CURLOPT_FTP_USE_EPRT
2736 IMM(FTP_USE_EPRT),
2737 #else
2738 IMM_NO(FTP_USE_EPRT),
2739 #endif
2740 #if HAVE_DECL_CURLOPT_HTTPAUTH
2741 IMM(HTTPAUTH),
2742 #else
2743 IMM_NO(HTTPAUTH),
2744 #endif
2745 #if HAVE_DECL_CURLOPT_FTP_CREATE_MISSING_DIRS
2746 IMM(FTP_CREATE_MISSING_DIRS),
2747 #else
2748 IMM_NO(FTP_CREATE_MISSING_DIRS),
2749 #endif
2750 #if HAVE_DECL_CURLOPT_PROXYAUTH
2751 IMM(PROXYAUTH),
2752 #else
2753 IMM_NO(PROXYAUTH),
2754 #endif
2755 #if HAVE_DECL_CURLOPT_FTP_RESPONSE_TIMEOUT
2756 IMM(FTP_RESPONSE_TIMEOUT),
2757 #else
2758 IMM_NO(FTP_RESPONSE_TIMEOUT),
2759 #endif
2760 #if HAVE_DECL_CURLOPT_IPRESOLVE
2761 IMM(IPRESOLVE),
2762 #else
2763 IMM_NO(IPRESOLVE),
2764 #endif
2765 #if HAVE_DECL_CURLOPT_MAXFILESIZE
2766 IMM(MAXFILESIZE),
2767 #else
2768 IMM_NO(MAXFILESIZE),
2769 #endif
2770 #if HAVE_DECL_CURLOPT_INFILESIZE_LARGE
2771 IMM(INFILESIZE_LARGE),
2772 #else
2773 IMM_NO(INFILESIZE_LARGE),
2774 #endif
2775 #if HAVE_DECL_CURLOPT_RESUME_FROM_LARGE
2776 IMM(RESUME_FROM_LARGE),
2777 #else
2778 IMM_NO(RESUME_FROM_LARGE),
2779 #endif
2780 #if HAVE_DECL_CURLOPT_MAXFILESIZE_LARGE
2781 IMM(MAXFILESIZE_LARGE),
2782 #else
2783 IMM_NO(MAXFILESIZE_LARGE),
2784 #endif
2785 #if HAVE_DECL_CURLOPT_NETRC_FILE
2786 MAP(NETRC_FILE),
2787 #else
2788 MAP_NO(NETRC_FILE),
2789 #endif
2790 #if HAVE_DECL_CURLOPT_FTP_SSL
2791 IMM(FTP_SSL),
2792 #else
2793 IMM_NO(FTP_SSL),
2794 #endif
2795 #if HAVE_DECL_CURLOPT_POSTFIELDSIZE_LARGE
2796 IMM(POSTFIELDSIZE_LARGE),
2797 #else
2798 IMM_NO(POSTFIELDSIZE_LARGE),
2799 #endif
2800 #if HAVE_DECL_CURLOPT_TCP_NODELAY
2801 IMM(TCP_NODELAY),
2802 #else
2803 IMM_NO(TCP_NODELAY),
2804 #endif
2805 #if HAVE_DECL_CURLOPT_FTPSSLAUTH
2806 IMM(FTPSSLAUTH),
2807 #else
2808 IMM_NO(FTPSSLAUTH),
2809 #endif
2810 #if HAVE_DECL_CURLOPT_IOCTLFUNCTION
2811 MAP(IOCTLFUNCTION),
2812 #else
2813 MAP_NO(IOCTLFUNCTION),
2814 #endif
2815 #if HAVE_DECL_CURLOPT_FTP_ACCOUNT
2816 MAP(FTP_ACCOUNT),
2817 #else
2818 MAP_NO(FTP_ACCOUNT),
2819 #endif
2820 #if HAVE_DECL_CURLOPT_COOKIELIST
2821 MAP(COOKIELIST),
2822 #else
2823 MAP_NO(COOKIELIST),
2824 #endif
2825 #if HAVE_DECL_CURLOPT_IGNORE_CONTENT_LENGTH
2826 IMM(IGNORE_CONTENT_LENGTH),
2827 #else
2828 IMM_NO(IGNORE_CONTENT_LENGTH),
2829 #endif
2830 #if HAVE_DECL_CURLOPT_FTP_SKIP_PASV_IP
2831 IMM(FTP_SKIP_PASV_IP),
2832 #else
2833 IMM_NO(FTP_SKIP_PASV_IP),
2834 #endif
2835 #if HAVE_DECL_CURLOPT_FTP_FILEMETHOD
2836 IMM(FTP_FILEMETHOD),
2837 #else
2838 IMM_NO(FTP_FILEMETHOD),
2839 #endif
2840 #if HAVE_DECL_CURLOPT_LOCALPORT
2841 IMM(LOCALPORT),
2842 #else
2843 IMM_NO(LOCALPORT),
2844 #endif
2845 #if HAVE_DECL_CURLOPT_LOCALPORTRANGE
2846 IMM(LOCALPORTRANGE),
2847 #else
2848 IMM_NO(LOCALPORTRANGE),
2849 #endif
2850 #if HAVE_DECL_CURLOPT_CONNECT_ONLY
2851 IMM(CONNECT_ONLY),
2852 #else
2853 IMM_NO(CONNECT_ONLY),
2854 #endif
2855 #if HAVE_DECL_CURLOPT_MAX_SEND_SPEED_LARGE
2856 IMM(MAX_SEND_SPEED_LARGE),
2857 #else
2858 IMM_NO(MAX_SEND_SPEED_LARGE),
2859 #endif
2860 #if HAVE_DECL_CURLOPT_MAX_RECV_SPEED_LARGE
2861 IMM(MAX_RECV_SPEED_LARGE),
2862 #else
2863 IMM_NO(MAX_RECV_SPEED_LARGE),
2864 #endif
2865 #if HAVE_DECL_CURLOPT_FTP_ALTERNATIVE_TO_USER
2866 MAP(FTP_ALTERNATIVE_TO_USER),
2867 #else
2868 MAP_NO(FTP_ALTERNATIVE_TO_USER),
2869 #endif
2870 #if HAVE_DECL_CURLOPT_SSL_SESSIONID_CACHE
2871 IMM(SSL_SESSIONID_CACHE),
2872 #else
2873 IMM_NO(SSL_SESSIONID_CACHE),
2874 #endif
2875 #if HAVE_DECL_CURLOPT_SSH_AUTH_TYPES
2876 IMM(SSH_AUTH_TYPES),
2877 #else
2878 IMM_NO(SSH_AUTH_TYPES),
2879 #endif
2880 #if HAVE_DECL_CURLOPT_SSH_PUBLIC_KEYFILE
2881 MAP(SSH_PUBLIC_KEYFILE),
2882 #else
2883 MAP_NO(SSH_PUBLIC_KEYFILE),
2884 #endif
2885 #if HAVE_DECL_CURLOPT_SSH_PRIVATE_KEYFILE
2886 MAP(SSH_PRIVATE_KEYFILE),
2887 #else
2888 MAP_NO(SSH_PRIVATE_KEYFILE),
2889 #endif
2890 #if HAVE_DECL_CURLOPT_FTP_SSL_CCC
2891 IMM(FTP_SSL_CCC),
2892 #else
2893 IMM_NO(FTP_SSL_CCC),
2894 #endif
2895 #if HAVE_DECL_CURLOPT_TIMEOUT_MS
2896 IMM(TIMEOUT_MS),
2897 #else
2898 IMM_NO(TIMEOUT_MS),
2899 #endif
2900 #if HAVE_DECL_CURLOPT_CONNECTTIMEOUT_MS
2901 IMM(CONNECTTIMEOUT_MS),
2902 #else
2903 IMM_NO(CONNECTTIMEOUT_MS),
2904 #endif
2905 #if HAVE_DECL_CURLOPT_HTTP_TRANSFER_DECODING
2906 IMM(HTTP_TRANSFER_DECODING),
2907 #else
2908 IMM_NO(HTTP_TRANSFER_DECODING),
2909 #endif
2910 #if HAVE_DECL_CURLOPT_HTTP_CONTENT_DECODING
2911 IMM(HTTP_CONTENT_DECODING),
2912 #else
2913 IMM_NO(HTTP_CONTENT_DECODING),
2914 #endif
2915 #if HAVE_DECL_CURLOPT_NEW_FILE_PERMS
2916 IMM(NEW_FILE_PERMS),
2917 #else
2918 IMM_NO(NEW_FILE_PERMS),
2919 #endif
2920 #if HAVE_DECL_CURLOPT_NEW_DIRECTORY_PERMS
2921 IMM(NEW_DIRECTORY_PERMS),
2922 #else
2923 IMM_NO(NEW_DIRECTORY_PERMS),
2924 #endif
2925 #if HAVE_DECL_CURLOPT_POST301
2926 IMM(POST301),
2927 #else
2928 IMM_NO(POST301),
2929 #endif
2930 #if HAVE_DECL_CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
2931 MAP(SSH_HOST_PUBLIC_KEY_MD5),
2932 #else
2933 MAP_NO(SSH_HOST_PUBLIC_KEY_MD5),
2934 #endif
2935 #if HAVE_DECL_CURLOPT_COPYPOSTFIELDS
2936 MAP(COPYPOSTFIELDS),
2937 #else
2938 MAP_NO(COPYPOSTFIELDS),
2939 #endif
2940 #if HAVE_DECL_CURLOPT_PROXY_TRANSFER_MODE
2941 IMM(PROXY_TRANSFER_MODE),
2942 #else
2943 IMM_NO(PROXY_TRANSFER_MODE),
2944 #endif
2945 #if HAVE_DECL_CURLOPT_SEEKFUNCTION
2946 MAP(SEEKFUNCTION),
2947 #else
2948 MAP_NO(SEEKFUNCTION),
2949 #endif
2950 #if HAVE_DECL_CURLOPT_AUTOREFERER
2951 IMM(AUTOREFERER),
2952 #else
2953 IMM_NO(AUTOREFERER),
2954 #endif
2955 #if HAVE_DECL_CURLOPT_OPENSOCKETFUNCTION
2956 MAP(OPENSOCKETFUNCTION),
2957 #else
2958 MAP_NO(OPENSOCKETFUNCTION),
2959 #endif
2960 #if HAVE_DECL_CURLOPT_PROXYTYPE
2961 IMM(PROXYTYPE),
2962 #else
2963 IMM_NO(PROXYTYPE),
2964 #endif
2965 #if HAVE_DECL_CURLOPT_PROTOCOLS
2966 IMM(PROTOCOLS),
2967 #else
2968 IMM_NO(PROTOCOLS),
2969 #endif
2970 #if HAVE_DECL_CURLOPT_REDIR_PROTOCOLS
2971 IMM(REDIR_PROTOCOLS),
2972 #else
2973 IMM_NO(REDIR_PROTOCOLS),
2974 #endif
2975 #if HAVE_DECL_CURLOPT_RESOLVE
2976 MAP(RESOLVE),
2977 #else
2978 MAP_NO(RESOLVE),
2979 #endif
2980 #if HAVE_DECL_CURLOPT_DNS_SERVERS
2981 MAP(DNS_SERVERS),
2982 #else
2983 MAP_NO(DNS_SERVERS),
2984 #endif
2985 #if HAVE_DECL_CURLOPT_MAIL_FROM
2986 MAP(MAIL_FROM),
2987 #else
2988 MAP_NO(MAIL_FROM),
2989 #endif
2990 #if HAVE_DECL_CURLOPT_MAIL_RCPT
2991 MAP(MAIL_RCPT),
2992 #else
2993 MAP_NO(MAIL_RCPT),
2994 #endif
2995 #if HAVE_DECL_CURLOPT_PIPEWAIT
2996 IMM(PIPEWAIT),
2997 #else
2998 IMM_NO(PIPEWAIT),
2999 #endif
3000 #if HAVE_DECL_CURLOPT_CERTINFO
3001 IMM(CERTINFO),
3002 #else
3003 IMM_NO(CERTINFO),
3004 #endif
3005 #if HAVE_DECL_CURLOPT_USERNAME
3006 MAP(USERNAME),
3007 #else
3008 MAP_NO(USERNAME),
3009 #endif
3010 #if HAVE_DECL_CURLOPT_PASSWORD
3011 MAP(PASSWORD),
3012 #else
3013 MAP_NO(PASSWORD),
3014 #endif
3015 #if HAVE_DECL_CURLOPT_LOGIN_OPTIONS
3016 MAP(LOGIN_OPTIONS),
3017 #else
3018 MAP_NO(LOGIN_OPTIONS),
3019 #endif
3020 #if HAVE_DECL_CURLOPT_CONNECT_TO
3021 MAP(CONNECT_TO),
3022 #else
3023 MAP_NO(CONNECT_TO),
3024 #endif
3027 static Connection *duplicateConnection(Connection *original)
3029 Connection *connection = NULL;
3030 CURL* h = NULL;
3031 size_t i = 0;
3032 CURLOptionMapping* self = NULL;
3034 caml_enter_blocking_section();
3035 h = curl_easy_duphandle(original->handle);
3036 caml_leave_blocking_section();
3038 connection = allocConnection(h);
3040 for (i = 0; i < sizeof(implementedOptionMap)/sizeof(CURLOptionMapping); i++)
3042 self = &implementedOptionMap[i];
3043 if (-1 == self->ocamlValue) continue;
3044 if (self->optionHandler && (Field(original->ocamlValues, self->ocamlValue) != Val_unit))
3046 self->optionHandler(connection, Field(original->ocamlValues, self->ocamlValue));
3050 return connection;
3053 value caml_curl_easy_setopt(value conn, value option)
3055 CAMLparam2(conn, option);
3056 CAMLlocal1(data);
3057 Connection *connection = Connection_val(conn);
3058 CURLOptionMapping* thisOption = NULL;
3059 static value* exception = NULL;
3061 checkConnection(connection);
3063 data = Field(option, 0);
3065 if (Tag_val(option) < sizeof(implementedOptionMap)/sizeof(CURLOptionMapping))
3067 thisOption = &implementedOptionMap[Tag_val(option)];
3068 if (thisOption->optionHandler)
3070 thisOption->optionHandler(connection, data);
3072 else
3074 if (NULL == exception)
3076 exception = caml_named_value("Curl.NotImplemented");
3077 if (NULL == exception) caml_invalid_argument("Curl.NotImplemented");
3080 caml_raise_with_string(*exception, thisOption->name);
3083 else
3085 caml_failwith("Invalid CURLOPT Option");
3088 CAMLreturn(Val_unit);
3092 ** curl_easy_perform helper function
3095 value caml_curl_easy_perform(value conn)
3097 CAMLparam1(conn);
3098 CURLcode result = CURLE_OK;
3099 Connection *connection = Connection_val(conn);
3101 checkConnection(connection);
3103 caml_enter_blocking_section();
3104 result = curl_easy_perform(connection->handle);
3105 caml_leave_blocking_section();
3107 if (result != CURLE_OK)
3108 raiseError(connection, result);
3110 CAMLreturn(Val_unit);
3114 ** curl_easy_cleanup helper function
3117 value caml_curl_easy_cleanup(value conn)
3119 CAMLparam1(conn);
3120 Connection *connection = Connection_val(conn);
3122 checkConnection(connection);
3124 removeConnection(connection, 0);
3126 CAMLreturn(Val_unit);
3130 ** curl_easy_duphandle helper function
3133 value caml_curl_easy_duphandle(value conn)
3135 CAMLparam1(conn);
3136 CAMLlocal1(result);
3137 Connection *connection = Connection_val(conn);
3139 checkConnection(connection);
3141 result = caml_curl_alloc(duplicateConnection(connection));
3143 CAMLreturn(result);
3147 ** curl_easy_getinfo helper function
3150 enum GetInfoResultType {
3151 StringValue, LongValue, DoubleValue, StringListValue, StringListListValue,
3152 OCamlValue, /* keep last - no matching OCaml CURLINFO_ constructor */
3155 value convertStringList(struct curl_slist *p)
3157 CAMLparam0();
3158 CAMLlocal3(result, current, next);
3160 result = Val_emptylist;
3161 current = Val_emptylist;
3162 next = Val_emptylist;
3164 while (p != NULL)
3166 next = caml_alloc_tuple(2);
3167 Store_field(next, 0, caml_copy_string(p->data));
3168 Store_field(next, 1, Val_emptylist);
3170 if (result == Val_emptylist)
3171 result = next;
3173 if (current != Val_emptylist)
3174 Store_field(current, 1, next);
3176 current = next;
3178 p = p->next;
3181 CAMLreturn(result);
3184 value caml_curl_easy_getinfo(value conn, value option)
3186 CAMLparam2(conn, option);
3187 CAMLlocal3(result, current, next);
3188 CURLcode curlResult;
3189 Connection *connection = Connection_val(conn);
3190 enum GetInfoResultType resultType;
3191 char *strValue = NULL;
3192 double doubleValue;
3193 long longValue;
3194 struct curl_slist *stringListValue = NULL;
3195 #if HAVE_DECL_CURLINFO_CERTINFO
3196 int i;
3197 union {
3198 struct curl_slist *to_info;
3199 struct curl_certinfo *to_certinfo;
3200 } ptr;
3201 #endif
3203 checkConnection(connection);
3205 switch(Long_val(option))
3207 #if HAVE_DECL_CURLINFO_EFFECTIVE_URL
3208 case 0: /* CURLINFO_EFFECTIVE_URL */
3209 resultType = StringValue;
3211 curlResult = curl_easy_getinfo(connection->handle,
3212 CURLINFO_EFFECTIVE_URL,
3213 &strValue);
3214 break;
3215 #else
3216 #pragma message("libcurl does not provide CURLINFO_EFFECTIVE_URL")
3217 #endif
3219 #if HAVE_DECL_CURLINFO_RESPONSE_CODE || HAVE_DECL_CURLINFO_HTTP_CODE
3220 case 1: /* CURLINFO_HTTP_CODE */
3221 case 2: /* CURLINFO_RESPONSE_CODE */
3222 #if HAVE_DECL_CURLINFO_RESPONSE_CODE
3223 resultType = LongValue;
3225 curlResult = curl_easy_getinfo(connection->handle,
3226 CURLINFO_RESPONSE_CODE,
3227 &longValue);
3228 #else
3229 resultType = LongValue;
3231 curlResult = curl_easy_getinfo(connection->handle,
3232 CURLINFO_HTTP_CODE,
3233 &longValue);
3234 #endif
3235 break;
3236 #endif
3238 #if HAVE_DECL_CURLINFO_TOTAL_TIME
3239 case 3: /* CURLINFO_TOTAL_TIME */
3240 resultType = DoubleValue;
3242 curlResult = curl_easy_getinfo(connection->handle,
3243 CURLINFO_TOTAL_TIME,
3244 &doubleValue);
3245 break;
3246 #endif
3248 #if HAVE_DECL_CURLINFO_NAMELOOKUP_TIME
3249 case 4: /* CURLINFO_NAMELOOKUP_TIME */
3250 resultType = DoubleValue;
3252 curlResult = curl_easy_getinfo(connection->handle,
3253 CURLINFO_NAMELOOKUP_TIME,
3254 &doubleValue);
3255 break;
3256 #endif
3258 #if HAVE_DECL_CURLINFO_CONNECT_TIME
3259 case 5: /* CURLINFO_CONNECT_TIME */
3260 resultType = DoubleValue;
3262 curlResult = curl_easy_getinfo(connection->handle,
3263 CURLINFO_CONNECT_TIME,
3264 &doubleValue);
3265 break;
3266 #endif
3268 #if HAVE_DECL_CURLINFO_PRETRANSFER_TIME
3269 case 6: /* CURLINFO_PRETRANSFER_TIME */
3270 resultType = DoubleValue;
3272 curlResult = curl_easy_getinfo(connection->handle,
3273 CURLINFO_PRETRANSFER_TIME,
3274 &doubleValue);
3275 break;
3276 #endif
3278 #if HAVE_DECL_CURLINFO_SIZE_UPLOAD
3279 case 7: /* CURLINFO_SIZE_UPLOAD */
3280 resultType = DoubleValue;
3282 curlResult = curl_easy_getinfo(connection->handle,
3283 CURLINFO_SIZE_UPLOAD,
3284 &doubleValue);
3285 break;
3286 #endif
3288 #if HAVE_DECL_CURLINFO_SIZE_DOWNLOAD
3289 case 8: /* CURLINFO_SIZE_DOWNLOAD */
3290 resultType = DoubleValue;
3292 curlResult = curl_easy_getinfo(connection->handle,
3293 CURLINFO_SIZE_DOWNLOAD,
3294 &doubleValue);
3295 break;
3296 #endif
3298 #if HAVE_DECL_CURLINFO_SPEED_DOWNLOAD
3299 case 9: /* CURLINFO_SPEED_DOWNLOAD */
3300 resultType = DoubleValue;
3302 curlResult = curl_easy_getinfo(connection->handle,
3303 CURLINFO_SPEED_DOWNLOAD,
3304 &doubleValue);
3305 break;
3306 #endif
3308 #if HAVE_DECL_CURLINFO_SPEED_UPLOAD
3309 case 10: /* CURLINFO_SPEED_UPLOAD */
3310 resultType = DoubleValue;
3312 curlResult = curl_easy_getinfo(connection->handle,
3313 CURLINFO_SPEED_UPLOAD,
3314 &doubleValue);
3315 break;
3317 #endif
3319 #if HAVE_DECL_CURLINFO_HEADER_SIZE
3320 case 11: /* CURLINFO_HEADER_SIZE */
3321 resultType = LongValue;
3323 curlResult = curl_easy_getinfo(connection->handle,
3324 CURLINFO_HEADER_SIZE,
3325 &longValue);
3326 break;
3327 #endif
3329 #if HAVE_DECL_CURLINFO_REQUEST_SIZE
3330 case 12: /* CURLINFO_REQUEST_SIZE */
3331 resultType = LongValue;
3333 curlResult = curl_easy_getinfo(connection->handle,
3334 CURLINFO_REQUEST_SIZE,
3335 &longValue);
3336 break;
3337 #endif
3339 #if HAVE_DECL_CURLINFO_SSL_VERIFYRESULT
3340 case 13: /* CURLINFO_SSL_VERIFYRESULT */
3341 resultType = LongValue;
3343 curlResult = curl_easy_getinfo(connection->handle,
3344 CURLINFO_SSL_VERIFYRESULT,
3345 &longValue);
3346 break;
3347 #endif
3349 #if HAVE_DECL_CURLINFO_FILETIME
3350 case 14: /* CURLINFO_FILETIME */
3351 resultType = DoubleValue;
3353 curlResult = curl_easy_getinfo(connection->handle,
3354 CURLINFO_FILETIME,
3355 &longValue);
3357 doubleValue = longValue;
3358 break;
3359 #endif
3361 #if HAVE_DECL_CURLINFO_CONTENT_LENGTH_DOWNLOAD
3362 case 15: /* CURLINFO_CONTENT_LENGTH_DOWNLOAD */
3363 resultType = DoubleValue;
3365 curlResult = curl_easy_getinfo(connection->handle,
3366 CURLINFO_CONTENT_LENGTH_DOWNLOAD,
3367 &doubleValue);
3368 break;
3369 #endif
3371 #if HAVE_DECL_CURLINFO_CONTENT_LENGTH_UPLOAD
3372 case 16: /* CURLINFO_CONTENT_LENGTH_UPLOAD */
3373 resultType = DoubleValue;
3375 curlResult = curl_easy_getinfo(connection->handle,
3376 CURLINFO_CONTENT_LENGTH_UPLOAD,
3377 &doubleValue);
3378 break;
3379 #endif
3381 #if HAVE_DECL_CURLINFO_STARTTRANSFER_TIME
3382 case 17: /* CURLINFO_STARTTRANSFER_TIME */
3383 resultType = DoubleValue;
3385 curlResult = curl_easy_getinfo(connection->handle,
3386 CURLINFO_STARTTRANSFER_TIME,
3387 &doubleValue);
3388 break;
3389 #endif
3391 #if HAVE_DECL_CURLINFO_CONTENT_TYPE
3392 case 18: /* CURLINFO_CONTENT_TYPE */
3393 resultType = StringValue;
3395 curlResult = curl_easy_getinfo(connection->handle,
3396 CURLINFO_CONTENT_TYPE,
3397 &strValue);
3398 break;
3399 #endif
3401 #if HAVE_DECL_CURLINFO_REDIRECT_TIME
3402 case 19: /* CURLINFO_REDIRECT_TIME */
3403 resultType = DoubleValue;
3405 curlResult = curl_easy_getinfo(connection->handle,
3406 CURLINFO_REDIRECT_TIME,
3407 &doubleValue);
3408 break;
3409 #endif
3411 #if HAVE_DECL_CURLINFO_REDIRECT_COUNT
3412 case 20: /* CURLINFO_REDIRECT_COUNT */
3413 resultType = LongValue;
3415 curlResult = curl_easy_getinfo(connection->handle,
3416 CURLINFO_REDIRECT_COUNT,
3417 &longValue);
3418 break;
3419 #endif
3421 case 21: /* CURLINFO_PRIVATE */
3422 resultType = OCamlValue;
3423 curlResult = CURLE_OK;
3424 result = caml_alloc(1, StringValue);
3425 Store_field(result, 0, Field(connection->ocamlValues, Ocaml_PRIVATE));
3426 break;
3428 #if HAVE_DECL_CURLINFO_HTTP_CONNECTCODE
3429 case 22: /* CURLINFO_HTTP_CONNECTCODE */
3430 resultType = LongValue;
3432 curlResult = curl_easy_getinfo(connection->handle,
3433 CURLINFO_HTTP_CONNECTCODE,
3434 &longValue);
3435 break;
3436 #endif
3438 #if HAVE_DECL_CURLINFO_HTTPAUTH_AVAIL
3439 case 23: /* CURLINFO_HTTPAUTH_AVAIL */
3440 resultType = LongValue;
3442 curlResult = curl_easy_getinfo(connection->handle,
3443 CURLINFO_HTTPAUTH_AVAIL,
3444 &longValue);
3445 break;
3446 #endif
3448 #if HAVE_DECL_CURLINFO_PROXYAUTH_AVAIL
3449 case 24: /* CURLINFO_PROXYAUTH_AVAIL */
3450 resultType = LongValue;
3452 curlResult = curl_easy_getinfo(connection->handle,
3453 CURLINFO_PROXYAUTH_AVAIL,
3454 &longValue);
3455 break;
3456 #endif
3458 #if HAVE_DECL_CURLINFO_OS_ERRNO
3459 case 25: /* CURLINFO_OS_ERRNO */
3460 resultType = LongValue;
3462 curlResult = curl_easy_getinfo(connection->handle,
3463 CURLINFO_OS_ERRNO,
3464 &longValue);
3465 break;
3466 #endif
3468 #if HAVE_DECL_CURLINFO_NUM_CONNECTS
3469 case 26: /* CURLINFO_NUM_CONNECTS */
3470 resultType = LongValue;
3472 curlResult = curl_easy_getinfo(connection->handle,
3473 CURLINFO_NUM_CONNECTS,
3474 &longValue);
3475 break;
3476 #endif
3478 #if HAVE_DECL_CURLINFO_SSL_ENGINES
3479 case 27: /* CURLINFO_SSL_ENGINES */
3480 resultType = StringListValue;
3482 curlResult = curl_easy_getinfo(connection->handle,
3483 CURLINFO_SSL_ENGINES,
3484 &stringListValue);
3485 break;
3486 #endif
3488 #if HAVE_DECL_CURLINFO_COOKIELIST
3489 case 28: /* CURLINFO_COOKIELIST */
3490 resultType = StringListValue;
3492 curlResult = curl_easy_getinfo(connection->handle,
3493 CURLINFO_COOKIELIST,
3494 &stringListValue);
3495 break;
3496 #endif
3498 #if HAVE_DECL_CURLINFO_LASTSOCKET
3499 case 29: /* CURLINFO_LASTSOCKET */
3500 resultType = LongValue;
3502 curlResult = curl_easy_getinfo(connection->handle,
3503 CURLINFO_LASTSOCKET,
3504 &longValue);
3505 break;
3506 #endif
3508 #if HAVE_DECL_CURLINFO_FTP_ENTRY_PATH
3509 case 30: /* CURLINFO_FTP_ENTRY_PATH */
3510 resultType = StringValue;
3512 curlResult = curl_easy_getinfo(connection->handle,
3513 CURLINFO_FTP_ENTRY_PATH,
3514 &strValue);
3515 break;
3516 #endif
3518 #if HAVE_DECL_CURLINFO_REDIRECT_URL
3519 case 31: /* CURLINFO_REDIRECT_URL */
3520 resultType = StringValue;
3522 curlResult = curl_easy_getinfo(connection->handle,
3523 CURLINFO_REDIRECT_URL,
3524 &strValue);
3525 break;
3526 #else
3527 #pragma message("libcurl does not provide CURLINFO_REDIRECT_URL")
3528 #endif
3530 #if HAVE_DECL_CURLINFO_PRIMARY_IP
3531 case 32: /* CURLINFO_PRIMARY_IP */
3532 resultType = StringValue;
3534 curlResult = curl_easy_getinfo(connection->handle,
3535 CURLINFO_PRIMARY_IP,
3536 &strValue);
3537 break;
3538 #else
3539 #pragma message("libcurl does not provide CURLINFO_PRIMARY_IP")
3540 #endif
3542 #if HAVE_DECL_CURLINFO_LOCAL_IP
3543 case 33: /* CURLINFO_LOCAL_IP */
3544 resultType = StringValue;
3546 curlResult = curl_easy_getinfo(connection->handle,
3547 CURLINFO_LOCAL_IP,
3548 &strValue);
3549 break;
3550 #else
3551 #pragma message("libcurl does not provide CURLINFO_LOCAL_IP")
3552 #endif
3554 #if HAVE_DECL_CURLINFO_LOCAL_PORT
3555 case 34: /* CURLINFO_LOCAL_PORT */
3556 resultType = LongValue;
3558 curlResult = curl_easy_getinfo(connection->handle,
3559 CURLINFO_LOCAL_PORT,
3560 &longValue);
3561 break;
3562 #else
3563 #pragma message("libcurl does not provide CURLINFO_LOCAL_PORT")
3564 #endif
3566 #if HAVE_DECL_CURLINFO_CONDITION_UNMET
3567 case 35: /* CURLINFO_CONDITION_UNMET */
3568 resultType = LongValue;
3570 curlResult = curl_easy_getinfo(connection->handle,
3571 CURLINFO_CONDITION_UNMET,
3572 &longValue);
3573 break;
3574 #else
3575 #pragma message("libcurl does not provide CURLINFO_CONDITION_UNMET")
3576 #endif
3577 #if HAVE_DECL_CURLINFO_CERTINFO
3578 case 36: /* CURLINFO_CERTINFO */
3579 resultType = StringListListValue;
3580 ptr.to_info = NULL;
3581 curlResult = curl_easy_getinfo(connection->handle,
3582 CURLINFO_CERTINFO,
3583 &ptr.to_info);
3585 result = Val_emptylist;
3586 current = Val_emptylist;
3587 next = Val_emptylist;
3589 if (curlResult != CURLE_OK || !ptr.to_info)
3590 break;
3592 for (i = 0; i < ptr.to_certinfo->num_of_certs; i++) {
3593 next = caml_alloc_tuple(2);
3594 Store_field(next, 0, convertStringList(ptr.to_certinfo->certinfo[i]));
3595 Store_field(next, 1, current);
3596 current = next;
3598 break;
3599 #else
3600 #pragma message("libcurl does not provide CURLINFO_CERTINFO")
3601 #endif
3602 default:
3603 caml_failwith("Invalid CURLINFO Option");
3604 break;
3607 if (curlResult != CURLE_OK)
3608 raiseError(connection, curlResult);
3610 switch (resultType)
3612 case StringValue:
3613 result = caml_alloc(1, StringValue);
3614 Store_field(result, 0, caml_copy_string(strValue?strValue:""));
3615 break;
3617 case LongValue:
3618 result = caml_alloc(1, LongValue);
3619 Store_field(result, 0, Val_long(longValue));
3620 break;
3622 case DoubleValue:
3623 result = caml_alloc(1, DoubleValue);
3624 Store_field(result, 0, caml_copy_double(doubleValue));
3625 break;
3627 case StringListValue:
3628 result = caml_alloc(1, StringListValue);
3629 Store_field(result, 0, convertStringList(stringListValue));
3630 curl_slist_free_all(stringListValue);
3631 break;
3633 case StringListListValue:
3634 result = caml_alloc(1, StringListListValue);
3635 Store_field(result, 0, current);
3636 break;
3638 case OCamlValue:
3639 break;
3642 CAMLreturn(result);
3646 ** curl_escape helper function
3649 value caml_curl_escape(value str)
3651 CAMLparam1(str);
3652 CAMLlocal1(result);
3653 char *curlResult;
3655 curlResult = curl_escape(String_val(str), caml_string_length(str));
3656 result = caml_copy_string(curlResult);
3657 free(curlResult);
3659 CAMLreturn(result);
3663 ** curl_unescape helper function
3666 value caml_curl_unescape(value str)
3668 CAMLparam1(str);
3669 CAMLlocal1(result);
3670 char *curlResult;
3672 curlResult = curl_unescape(String_val(str), caml_string_length(str));
3673 result = caml_copy_string(curlResult);
3674 free(curlResult);
3676 CAMLreturn(result);
3680 ** curl_getdate helper function
3683 value caml_curl_getdate(value str, value now)
3685 CAMLparam2(str, now);
3686 CAMLlocal1(result);
3687 time_t curlResult;
3688 time_t curlNow;
3690 curlNow = (time_t)Double_val(now);
3691 curlResult = curl_getdate(String_val(str), &curlNow);
3692 result = caml_copy_double((double)curlResult);
3694 CAMLreturn(result);
3698 ** curl_version helper function
3701 value caml_curl_version(void)
3703 CAMLparam0();
3704 CAMLlocal1(result);
3705 char *str;
3707 str = curl_version();
3708 result = caml_copy_string(str);
3710 CAMLreturn(result);
3713 struct CURLVersionBitsMapping
3715 int code;
3716 char *name;
3719 struct CURLVersionBitsMapping versionBitsMap[] =
3721 {CURL_VERSION_IPV6, "ipv6"},
3722 {CURL_VERSION_KERBEROS4, "kerberos4"},
3723 {CURL_VERSION_SSL, "ssl"},
3724 {CURL_VERSION_LIBZ, "libz"},
3725 {CURL_VERSION_NTLM, "ntlm"},
3726 {CURL_VERSION_GSSNEGOTIATE, "gssnegotiate"},
3727 {CURL_VERSION_DEBUG, "debug"},
3728 {CURL_VERSION_CURLDEBUG, "curldebug"},
3729 {CURL_VERSION_ASYNCHDNS, "asynchdns"},
3730 {CURL_VERSION_SPNEGO, "spnego"},
3731 {CURL_VERSION_LARGEFILE, "largefile"},
3732 {CURL_VERSION_IDN, "idn"},
3733 {CURL_VERSION_SSPI, "sspi"},
3734 {CURL_VERSION_CONV, "conv"},
3735 #if HAVE_DECL_CURL_VERSION_TLSAUTH_SRP
3736 {CURL_VERSION_TLSAUTH_SRP, "srp"},
3737 #endif
3738 #if HAVE_DECL_CURL_VERSION_NTLM_WB
3739 {CURL_VERSION_NTLM_WB, "wb"},
3740 #endif
3743 value caml_curl_version_info(value unit)
3745 CAMLparam1(unit);
3746 CAMLlocal4(v, vlist, vnum, vfeatures);
3747 const char* const* p = NULL;
3748 size_t i = 0;
3750 curl_version_info_data* data = curl_version_info(CURLVERSION_NOW);
3751 if (NULL == data) caml_failwith("curl_version_info");
3753 vlist = Val_emptylist;
3754 for (p = data->protocols; NULL != *p; p++)
3756 vlist = Val_cons(vlist, caml_copy_string(*p));
3759 vfeatures = Val_emptylist;
3760 for (i = 0; i < sizeof(versionBitsMap)/sizeof(versionBitsMap[0]); i++)
3762 if (0 != (versionBitsMap[i].code & data->features))
3763 vfeatures = Val_cons(vfeatures, caml_copy_string(versionBitsMap[i].name));
3766 vnum = caml_alloc_tuple(3);
3767 Store_field(vnum,0,Val_int(0xFF & (data->version_num >> 16)));
3768 Store_field(vnum,1,Val_int(0xFF & (data->version_num >> 8)));
3769 Store_field(vnum,2,Val_int(0xFF & (data->version_num)));
3771 v = caml_alloc_tuple(12);
3772 Store_field(v,0,caml_copy_string(data->version));
3773 Store_field(v,1,vnum);
3774 Store_field(v,2,caml_copy_string(data->host));
3775 Store_field(v,3,vfeatures);
3776 Store_field(v,4,data->ssl_version ? Val_some(caml_copy_string(data->ssl_version)) : Val_none);
3777 Store_field(v,5,data->libz_version ? Val_some(caml_copy_string(data->libz_version)) : Val_none);
3778 Store_field(v,6,vlist);
3779 Store_field(v,7,caml_copy_string((data->age >= 1 && data->ares) ? data->ares : ""));
3780 Store_field(v,8,Val_int((data->age >= 1) ? data->ares_num : 0));
3781 Store_field(v,9,caml_copy_string((data->age >= 2 && data->libidn) ? data->libidn : ""));
3782 Store_field(v,10,Val_int((data->age >= 3) ? data->iconv_ver_num : 0));
3783 Store_field(v,11,caml_copy_string((data->age >= 3 && data->libssh_version) ? data->libssh_version : ""));
3785 CAMLreturn(v);
3788 value caml_curl_pause(value conn, value opts)
3790 CAMLparam2(conn, opts);
3791 CAMLlocal4(v, vlist, vnum, vfeatures);
3792 Connection *connection = Connection_val(conn);
3793 int bitmask = 0;
3794 CURLcode result;
3796 while (Val_emptylist != opts)
3798 switch (Int_val(Field(opts,0)))
3800 case 0: bitmask |= CURLPAUSE_SEND; break;
3801 case 1: bitmask |= CURLPAUSE_RECV; break;
3802 case 2: bitmask |= CURLPAUSE_ALL; break;
3803 default: caml_failwith("wrong pauseOption");
3805 opts = Field(opts,1);
3808 result = curl_easy_pause(connection->handle,bitmask);
3809 if (result != CURLE_OK)
3810 raiseError(connection, result);
3812 CAMLreturn(Val_unit);
3816 * Curl multi stack support
3818 * Exported thin wrappers for libcurl are prefixed with caml_curl_multi_.
3819 * Other exported functions are prefixed with caml_curlm_, some of them
3820 * can/should be decomposed into smaller parts.
3823 struct ml_multi_handle
3825 CURLM* handle;
3826 value values; /* callbacks */
3829 enum
3831 curlmopt_socket_function,
3832 curlmopt_timer_function,
3834 /* last, not used */
3835 multi_values_total
3838 typedef struct ml_multi_handle ml_multi_handle;
3840 #define Multi_val(v) (*(ml_multi_handle**)Data_custom_val(v))
3841 #define CURLM_val(v) (Multi_val(v)->handle)
3843 static struct custom_operations curl_multi_ops = {
3844 "ygrek.curl_multi",
3845 custom_finalize_default,
3846 custom_compare_default,
3847 custom_hash_default,
3848 custom_serialize_default,
3849 custom_deserialize_default,
3850 #if defined(custom_compare_ext_default)
3851 custom_compare_ext_default,
3852 #endif
3855 value caml_curl_multi_init(value unit)
3857 CAMLparam1(unit);
3858 CAMLlocal1(v);
3859 ml_multi_handle* multi = (ml_multi_handle*)caml_stat_alloc(sizeof(ml_multi_handle));
3860 CURLM* h = curl_multi_init();
3862 if (!h)
3864 caml_stat_free(multi);
3865 caml_failwith("caml_curl_multi_init");
3868 multi->handle = h;
3869 multi->values = caml_alloc(multi_values_total, 0);
3870 caml_register_generational_global_root(&multi->values);
3872 v = caml_alloc_custom(&curl_multi_ops, sizeof(ml_multi_handle*), 0, 1);
3873 Multi_val(v) = multi;
3875 CAMLreturn(v);
3878 value caml_curl_multi_cleanup(value handle)
3880 CAMLparam1(handle);
3881 ml_multi_handle* h = Multi_val(handle);
3883 if (NULL == h)
3884 CAMLreturn(Val_unit);
3886 caml_remove_generational_global_root(&h->values);
3888 if (CURLM_OK != curl_multi_cleanup(h->handle))
3889 caml_failwith("caml_curl_multi_cleanup");
3891 caml_stat_free(h);
3892 Multi_val(handle) = (ml_multi_handle*)NULL;
3894 CAMLreturn(Val_unit);
3897 static CURL* curlm_remove_finished(CURLM* multi_handle, CURLcode* result)
3899 int msgs_in_queue = 0;
3901 while (1)
3903 CURLMsg* msg = curl_multi_info_read(multi_handle, &msgs_in_queue);
3904 if (NULL == msg) return NULL;
3905 if (CURLMSG_DONE == msg->msg)
3907 CURL* easy_handle = msg->easy_handle;
3908 if (result) *result = msg->data.result;
3909 if (CURLM_OK != curl_multi_remove_handle(multi_handle, easy_handle))
3911 /*caml_failwith("curlm_remove_finished");*/
3913 return easy_handle;
3918 value caml_curlm_remove_finished(value v_multi)
3920 CAMLparam1(v_multi);
3921 CAMLlocal2(v_easy, v_tuple);
3922 CURL* handle;
3923 CURLM* multi_handle;
3924 CURLcode result;
3925 Connection* conn = NULL;
3927 multi_handle = CURLM_val(v_multi);
3929 caml_enter_blocking_section();
3930 handle = curlm_remove_finished(multi_handle,&result);
3931 caml_leave_blocking_section();
3933 if (NULL == handle)
3935 CAMLreturn(Val_none);
3937 else
3939 conn = getConnection(handle);
3940 if (conn->curl_ERRORBUFFER != NULL)
3942 Store_field(Field(conn->ocamlValues, Ocaml_ERRORBUFFER), 0, caml_copy_string(conn->curl_ERRORBUFFER));
3944 conn->refcount--;
3945 /* NB: same handle, but different block */
3946 v_easy = caml_curl_alloc(conn);
3947 v_tuple = caml_alloc(2, 0);
3948 Store_field(v_tuple,0,v_easy);
3949 Store_field(v_tuple,1,Val_int(result)); /* CURLcode */
3950 CAMLreturn(Val_some(v_tuple));
3954 static int curlm_wait_data(CURLM* multi_handle)
3956 struct timeval timeout;
3957 CURLMcode ret;
3959 fd_set fdread;
3960 fd_set fdwrite;
3961 fd_set fdexcep;
3962 int maxfd = -1;
3964 FD_ZERO(&fdread);
3965 FD_ZERO(&fdwrite);
3966 FD_ZERO(&fdexcep);
3968 /* set a suitable timeout */
3969 timeout.tv_sec = 1;
3970 timeout.tv_usec = 0;
3972 /* get file descriptors from the transfers */
3973 ret = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
3975 if (ret == CURLM_OK && maxfd >= 0)
3977 int rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
3978 if (-1 != rc) return 0;
3980 return 1;
3983 value caml_curlm_wait_data(value v_multi)
3985 CAMLparam1(v_multi);
3986 int ret;
3987 CURLM* h = CURLM_val(v_multi);
3989 caml_enter_blocking_section();
3990 ret = curlm_wait_data(h);
3991 caml_leave_blocking_section();
3993 CAMLreturn(Val_bool(0 == ret));
3996 value caml_curl_multi_add_handle(value v_multi, value v_easy)
3998 CAMLparam2(v_multi,v_easy);
3999 CURLM* multi = CURLM_val(v_multi);
4000 Connection* conn = Connection_val(v_easy);
4002 /* prevent collection of OCaml value while the easy handle is used
4003 and may invoke callbacks registered on OCaml side */
4004 conn->refcount++;
4006 /* may invoke callbacks so need to be consistent with locks */
4007 caml_enter_blocking_section();
4008 if (CURLM_OK != curl_multi_add_handle(multi, conn->handle))
4010 conn->refcount--; /* not added, revert */
4011 caml_leave_blocking_section();
4012 caml_failwith("caml_curl_multi_add_handle");
4014 caml_leave_blocking_section();
4016 CAMLreturn(Val_unit);
4019 value caml_curl_multi_remove_handle(value v_multi, value v_easy)
4021 CAMLparam2(v_multi,v_easy);
4022 CURLM* multi = CURLM_val(v_multi);
4023 Connection* conn = Connection_val(v_easy);
4025 /* may invoke callbacks so need to be consistent with locks */
4026 caml_enter_blocking_section();
4027 if (CURLM_OK != curl_multi_remove_handle(multi, conn->handle))
4029 caml_leave_blocking_section();
4030 caml_failwith("caml_curl_multi_remove_handle");
4032 conn->refcount--;
4033 caml_leave_blocking_section();
4035 CAMLreturn(Val_unit);
4038 value caml_curl_multi_perform_all(value v_multi)
4040 CAMLparam1(v_multi);
4041 int still_running = 0;
4042 CURLM* h = CURLM_val(v_multi);
4044 caml_enter_blocking_section();
4045 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(h, &still_running));
4046 caml_leave_blocking_section();
4048 CAMLreturn(Val_int(still_running));
4051 value caml_curl_easy_strerror(value v_code)
4053 CAMLparam1(v_code);
4054 CAMLreturn(caml_copy_string(curl_easy_strerror((CURLcode)Int_val(v_code))));
4058 * Wrappers for the curl_multi_socket_action infrastructure
4059 * Based on curl hiperfifo.c example
4062 #ifdef _WIN32
4063 #ifndef Val_socket
4064 #define Val_socket(v) win_alloc_socket(v)
4065 #endif
4066 #ifndef Socket_val
4067 #error Socket_val not defined in unixsupport.h
4068 #endif
4069 #else /* _WIN32 */
4070 #ifndef Socket_val
4071 #define Socket_val(v) Long_val(v)
4072 #endif
4073 #ifndef Val_socket
4074 #define Val_socket(v) Val_int(v)
4075 #endif
4076 #endif /* _WIN32 */
4078 static void raise_multi_error(char const* msg)
4080 static value* exception = NULL;
4082 if (NULL == exception)
4084 exception = caml_named_value("Curl.Multi.Error");
4085 if (NULL == exception) caml_invalid_argument("Curl.Multi.Error");
4088 caml_raise_with_string(*exception, msg);
4091 static void check_mcode(CURLMcode code)
4093 char const *s = NULL;
4094 switch (code)
4096 case CURLM_OK : return;
4097 case CURLM_CALL_MULTI_PERFORM : s="CURLM_CALL_MULTI_PERFORM"; break;
4098 case CURLM_BAD_HANDLE : s="CURLM_BAD_HANDLE"; break;
4099 case CURLM_BAD_EASY_HANDLE : s="CURLM_BAD_EASY_HANDLE"; break;
4100 case CURLM_OUT_OF_MEMORY : s="CURLM_OUT_OF_MEMORY"; break;
4101 case CURLM_INTERNAL_ERROR : s="CURLM_INTERNAL_ERROR"; break;
4102 case CURLM_UNKNOWN_OPTION : s="CURLM_UNKNOWN_OPTION"; break;
4103 case CURLM_LAST : s="CURLM_LAST"; break;
4104 case CURLM_BAD_SOCKET : s="CURLM_BAD_SOCKET"; break;
4105 default : s="CURLM_unknown"; break;
4107 raise_multi_error(s);
4110 value caml_curl_multi_socket_action(value v_multi, value v_fd, value v_kind)
4112 CAMLparam3(v_multi, v_fd, v_kind);
4113 CURLM* h = CURLM_val(v_multi);
4114 int still_running = 0;
4115 CURLMcode rc = CURLM_OK;
4116 curl_socket_t socket;
4117 int kind = 0;
4119 if (Val_none == v_fd)
4121 socket = CURL_SOCKET_TIMEOUT;
4123 else
4125 socket = Socket_val(Field(v_fd, 0));
4128 switch (Int_val(v_kind))
4130 case 0 : break;
4131 case 1 : kind |= CURL_CSELECT_IN; break;
4132 case 2 : kind |= CURL_CSELECT_OUT; break;
4133 case 3 : kind |= CURL_CSELECT_IN | CURL_CSELECT_OUT; break;
4134 default:
4135 raise_multi_error("caml_curl_multi_socket_action");
4138 /* fprintf(stdout,"fd %u kind %u\n",socket, kind); fflush(stdout); */
4140 caml_enter_blocking_section();
4141 do {
4142 rc = curl_multi_socket_action(h, socket, kind, &still_running);
4143 } while (rc == CURLM_CALL_MULTI_PERFORM);
4144 caml_leave_blocking_section();
4146 check_mcode(rc);
4148 CAMLreturn(Val_int(still_running));
4151 value caml_curl_multi_socket_all(value v_multi)
4153 CAMLparam1(v_multi);
4154 int still_running = 0;
4155 CURLMcode rc = CURLM_OK;
4156 CURLM* h = CURLM_val(v_multi);
4158 caml_enter_blocking_section();
4159 do {
4160 rc = curl_multi_socket_all(h, &still_running);
4161 } while (rc == CURLM_CALL_MULTI_PERFORM);
4162 caml_leave_blocking_section();
4164 check_mcode(rc);
4166 CAMLreturn(Val_int(still_running));
4169 static int curlm_sock_cb(CURL *e, curl_socket_t sock, int what, void *cbp, void *sockp)
4171 caml_leave_blocking_section();
4173 CAMLparam0();
4174 CAMLlocal2(v_what,csock);
4175 (void)e;
4176 (void)sockp; /* not used */
4178 /* v_what = Val_int(what); */
4179 switch (what)
4181 case CURL_POLL_NONE : v_what = Val_int(0); break;
4182 case CURL_POLL_IN : v_what = Val_int(1); break;
4183 case CURL_POLL_OUT : v_what = Val_int(2); break;
4184 case CURL_POLL_INOUT : v_what = Val_int(3); break;
4185 case CURL_POLL_REMOVE : v_what = Val_int(4); break;
4186 default:
4187 fprintf(stderr, "curlm_sock_cb sock=%d what=%d\n", sock, what);
4188 fflush(stderr);
4189 raise_multi_error("curlm_sock_cb"); /* FIXME exception from callback */
4191 csock=Val_socket(sock);
4192 caml_callback2(Field(((ml_multi_handle*)cbp)->values,curlmopt_socket_function),
4193 csock, v_what);
4194 CAMLdrop;
4196 caml_enter_blocking_section();
4197 return 0;
4200 value caml_curl_multi_socketfunction(value v_multi, value v_cb)
4202 CAMLparam2(v_multi, v_cb);
4203 ml_multi_handle* multi = Multi_val(v_multi);
4205 Store_field(multi->values, curlmopt_socket_function, v_cb);
4207 curl_multi_setopt(multi->handle, CURLMOPT_SOCKETFUNCTION, curlm_sock_cb);
4208 curl_multi_setopt(multi->handle, CURLMOPT_SOCKETDATA, multi);
4210 CAMLreturn(Val_unit);
4213 static int curlm_timer_cb(CURLM *multi, long timeout_ms, void *userp)
4215 caml_leave_blocking_section();
4217 CAMLparam0();
4218 (void)multi;
4219 caml_callback(Field(((ml_multi_handle*)userp)->values,curlmopt_timer_function), Val_long(timeout_ms));
4220 CAMLdrop;
4222 caml_enter_blocking_section();
4223 return 0;
4226 value caml_curl_multi_timerfunction(value v_multi, value v_cb)
4228 CAMLparam2(v_multi, v_cb);
4229 ml_multi_handle* multi = Multi_val(v_multi);
4231 Store_field(multi->values, curlmopt_timer_function, v_cb);
4233 curl_multi_setopt(multi->handle, CURLMOPT_TIMERFUNCTION, curlm_timer_cb);
4234 curl_multi_setopt(multi->handle, CURLMOPT_TIMERDATA, multi);
4236 CAMLreturn(Val_unit);
4239 value caml_curl_multi_timeout(value v_multi)
4241 CAMLparam1(v_multi);
4242 long ms = 0;
4243 CURLMcode rc = CURLM_OK;
4244 ml_multi_handle* multi = Multi_val(v_multi);
4246 rc = curl_multi_timeout(multi->handle, &ms);
4248 check_mcode(rc);
4250 CAMLreturn(Val_long(ms));
4253 #define SETMOPT_VAL_(func_name, curl_option, conv_val) \
4254 static void func_name(CURLM *handle, value option) \
4256 CAMLparam1(option); \
4257 CURLMcode result = CURLM_OK; \
4259 result = curl_multi_setopt(handle, curl_option, conv_val(option)); \
4261 check_mcode(result); \
4263 CAMLreturn0; \
4266 #define SETMOPT_VAL(name, conv) SETMOPT_VAL_(handle_multi_##name, CURLMOPT_##name, conv)
4267 #define SETMOPT_BOOL(name) SETMOPT_VAL(name, Bool_val)
4268 #define SETMOPT_LONG(name) SETMOPT_VAL(name, Long_val)
4269 #define SETMOPT_INT64(name) SETMOPT_VAL(name, Int64_val)
4271 long pipeliningMap[] =
4273 0, /* CURLPIPE_NOTHING */
4274 1, /* CURLPIPE_HTTP1 */
4275 2, /* CURLPIPE_MULTIPLEX */
4278 static void handle_multi_PIPELINING(CURLM* handle, value option)
4280 CAMLparam1(option);
4281 CURLMcode result = CURLM_OK;
4283 long bits = convert_bit_list(pipeliningMap, sizeof(pipeliningMap) / sizeof(pipeliningMap[0]), option);
4285 result = curl_multi_setopt(handle, CURLMOPT_PIPELINING, bits);
4287 check_mcode(result);
4289 CAMLreturn0;
4292 #if HAVE_DECL_CURLMOPT_MAXCONNECTS
4293 SETMOPT_LONG( MAXCONNECTS)
4294 #endif
4296 #if HAVE_DECL_CURLMOPT_MAX_PIPELINE_LENGTH
4297 SETMOPT_LONG( MAX_PIPELINE_LENGTH)
4298 #endif
4300 #if HAVE_DECL_CURLMOPT_MAX_HOST_CONNECTIONS
4301 SETMOPT_LONG( MAX_HOST_CONNECTIONS)
4302 #endif
4304 typedef struct CURLMOptionMapping CURLMOptionMapping;
4305 #define OPT(name) { handle_multi_## name, "CURLMOPT_"#name}
4306 #define NO_OPT(name) { NULL, "CURLMOPT_"#name}
4308 struct CURLMOptionMapping
4310 void (*optionHandler)(CURLM *, value);
4311 char *name;
4314 CURLMOptionMapping implementedMOptionMap[] = {
4315 OPT( PIPELINING),
4316 #if HAVE_DECL_CURLMOPT_MAXCONNECTS
4317 OPT( MAXCONNECTS),
4318 #else
4319 NO_OPT( MAXCONNECTS),
4320 #endif
4321 #if HAVE_DECL_CURLMOPT_MAX_PIPELINE_LENGTH
4322 OPT( MAX_PIPELINE_LENGTH),
4323 #else
4324 NO_OPT( MAX_PIPELINE_LENGTH),
4325 #endif
4326 #if HAVE_DECL_CURLMOPT_MAX_HOST_CONNECTIONS
4327 OPT( MAX_HOST_CONNECTIONS),
4328 #else
4329 NO_OPT( MAX_HOST_CONNECTIONS),
4330 #endif
4333 value caml_curl_multi_setopt(value v_multi, value option)
4335 CAMLparam2(v_multi, option);
4336 CAMLlocal1(data);
4337 CURLM *handle = Multi_val(v_multi)->handle;
4338 CURLMOptionMapping* thisOption = NULL;
4339 static value* exception = NULL;
4341 data = Field(option, 0);
4343 if (Tag_val(option) < sizeof(implementedMOptionMap)/sizeof(CURLMOptionMapping))
4345 thisOption = &implementedMOptionMap[Tag_val(option)];
4346 if (thisOption->optionHandler)
4348 thisOption->optionHandler(handle, data);
4350 else
4352 if (NULL == exception)
4354 exception = caml_named_value("Curl.NotImplemented");
4355 if (NULL == exception) caml_invalid_argument("Curl.NotImplemented");
4358 caml_raise_with_string(*exception, thisOption->name);
4361 else
4363 caml_failwith("Invalid CURLMOPT Option");
4366 CAMLreturn(Val_unit);
4369 struct used_enum
4371 int last_used;
4372 int last;
4373 char const* name;
4376 #define CURL_ENUM(name,last_used) { CURL_ ## name ## _ ## last_used, CURL_ ## name ## _LAST, #name }
4378 struct used_enum check_enums[] = {
4379 { CURLINFO_SSL_DATA_OUT, CURLINFO_END, "CURLINFO" },
4380 #if defined(CURL_HTTP_VERSION_2TLS) /* FIXME */
4381 CURL_ENUM(HTTP_VERSION, 2TLS),
4382 #endif
4385 value caml_curl_outdated_enums(value v_unit)
4387 CAMLparam0();
4388 CAMLlocal1(v);
4389 size_t i;
4391 v = Val_emptylist;
4393 for (i = 0; i < sizeof(check_enums) / sizeof(struct used_enum); i++)
4395 if (check_enums[i].last_used + 1 != check_enums[i].last)
4397 v = Val_cons(v, caml_copy_string(check_enums[i].name));
4401 CAMLreturn(v);
4404 #ifdef __cplusplus
4406 #endif