summaryrefslogtreecommitdiff
path: root/source/luametatex/source/luaoptional/lmtcurl.c
blob: 6a54174e54459e482abc3cf0206147d569ec5f91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*
    See license.txt in the root of this project.
*/

# include "luametatex.h"
# include "lmtoptional.h"

typedef void* curl_instance ;
typedef int   curl_return_code ;
typedef int   curl_error_code ;

typedef enum curl_option_type {
    curl_ignore   = 0,
    curl_integer  = 1,
    curl_string   = 2,
    curl_function = 3, /* ignored */
    curl_offset   = 4, /* ignored */
} curl_option_type;

/*tex At the \LUA\ end we can have a mapping of useful ones, */

static const int curl_options[] = {
    curl_ignore,    /*   0 */
    curl_string,    /*   1 file | writedata */
    curl_string,    /*   2 url */
    curl_integer,   /*   3 port */
    curl_string,    /*   4 proxy */
    curl_string,    /*   5 userpwd */
    curl_string,    /*   6 proxyuserpwd */
    curl_string,    /*   7 range */
    curl_ignore,    /*   8 */
    curl_string,    /*   9 infile | readdata */
    curl_string,    /*  10 errorbuffer */
    curl_function,  /*  11 writefunction */
    curl_function,  /*  12 readfunction */
    curl_integer,   /*  13 timeout */
    curl_integer,   /*  14 infilesize */
    curl_string,    /*  15 postfields */
    curl_string,    /*  16 referer */
    curl_string,    /*  17 ftpport */
    curl_string,    /*  18 useragent */
    curl_integer,   /*  19 low_speed_limit */
    curl_integer,   /*  20 low_speed_time */
    curl_integer,   /*  21 resume_from */
    curl_string,    /*  22 cookie */
    curl_string,    /*  23 httpheader | rtspheader */
    curl_string,    /*  24 httppost */
    curl_string,    /*  25 sslcert */
    curl_string,    /*  26 keypasswd */
    curl_integer,   /*  27 crlf */
    curl_string,    /*  28 quote */
    curl_string,    /*  29 writeheader | headerdata */
    curl_ignore,    /*  30 */
    curl_string,    /*  31 cookiefile */
    curl_integer,   /*  32 sslversion */
    curl_integer,   /*  33 timecondition */
    curl_integer,   /*  34 timevalue */
    curl_ignore,    /*  35 */
    curl_string,    /*  36 customrequest */
    curl_string,    /*  37 stderr */
    curl_ignore,    /*  38 */
    curl_string,    /*  39 postquote */
    curl_string,    /*  40 writeinfo */
    curl_integer,   /*  41 verbose */
    curl_integer,   /*  42 header */
    curl_integer,   /*  43 noprogress */
    curl_integer,   /*  44 nobody */
    curl_integer,   /*  45 failonerror */
    curl_integer,   /*  46 upload */
    curl_integer,   /*  47 post */
    curl_integer,   /*  48 dirlistonly */
    curl_ignore,    /*  49 */
    curl_integer,   /*  50 append */
    curl_integer,   /*  51 netrc */
    curl_integer,   /*  52 followlocation */
    curl_integer,   /*  53 transfertext */
    curl_integer,   /*  54 put */
    curl_ignore,    /*  55 */
    curl_function,  /*  56 progressfunction */
    curl_string,    /*  57 xferinfodata | progressdata */
    curl_integer,   /*  58 autoreferer */
    curl_integer,   /*  59 proxyport */
    curl_integer,   /*  60 postfieldsize */
    curl_integer,   /*  61 httpproxytunnel */
    curl_string,    /*  62 interface */
    curl_string,    /*  63 krblevel */
    curl_integer,   /*  64 ssl_verifypeer */
    curl_string,    /*  65 cainfo */
    curl_ignore,    /*  66 */
    curl_ignore,    /*  67 */
    curl_integer,   /*  68 maxredirs */
    curl_integer,   /*  69 filetime */
    curl_string,    /*  70 telnetoptions */
    curl_integer,   /*  71 maxconnects */
    curl_integer,   /*  72 closepolicy */
    curl_ignore,    /*  73 */
    curl_integer,   /*  74 fresh_connect */
    curl_integer,   /*  75 forbid_reuse */
    curl_string,    /*  76 random_file */
    curl_string,    /*  77 egdsocket */
    curl_integer,   /*  78 connecttimeout */
    curl_function,  /*  79 headerfunction */
    curl_integer,   /*  80 httpget */
    curl_integer,   /*  81 ssl_verifyhost */
    curl_string,    /*  82 cookiejar */
    curl_string,    /*  83 ssl_cipher_list */
    curl_integer,   /*  84 http_version */
    curl_integer,   /*  85 ftp_use_epsv */
    curl_string,    /*  86 sslcerttype */
    curl_string,    /*  87 sslkey */
    curl_string,    /*  88 sslkeytype */
    curl_string,    /*  89 sslengine */
    curl_integer,   /*  90 sslengine_default */
    curl_integer,   /*  91 dns_use_global_cache */
    curl_integer,   /*  92 dns_cache_timeout */
    curl_string,    /*  93 prequote */
    curl_function,  /*  94 debugfunction */
    curl_string,    /*  95 debugdata */
    curl_integer,   /*  96 cookiesession */
    curl_string,    /*  97 capath */
    curl_integer,   /*  98 buffersize */
    curl_integer,   /*  99 nosignal */
    curl_string,    /* 100 share */
    curl_integer,   /* 101 proxytype */
    curl_string,    /* 102 accept_encoding */
    curl_string,    /* 103 private */
    curl_string,    /* 104 http200aliases */
    curl_integer,   /* 105 unrestricted_auth */
    curl_integer,   /* 106 ftp_use_eprt */
    curl_integer,   /* 107 httpauth */
    curl_function,  /* 108 ssl_ctx_function */
    curl_string,    /* 109 ssl_ctx_data */
    curl_integer,   /* 110 ftp_create_missing_dirs */
    curl_integer,   /* 111 proxyauth */
    curl_integer,   /* 112 server_response_timeout | ftp_response_timeout */
    curl_integer,   /* 113 ipresolve */
    curl_integer,   /* 114 maxfilesize */
    curl_offset,    /* 115 infilesize_large */
    curl_offset,    /* 116 resume_from_large */
    curl_offset,    /* 117 maxfilesize_large */
    curl_string,    /* 118 netrc_file */
    curl_integer,   /* 119 use_ssl */
    curl_offset,    /* 120 postfieldsize_large */
    curl_integer,   /* 121 tcp_nodelay */
    curl_ignore,    /* 122 */
    curl_ignore,    /* 123 */
    curl_ignore,    /* 124 */
    curl_ignore,    /* 125 */
    curl_ignore,    /* 126 */
    curl_ignore,    /* 127 */
    curl_ignore,    /* 128 */
    curl_integer,   /* 129 ftpsslauth */
    curl_function,  /* 130 ioctlfunction */
    curl_string,    /* 131 ioctldata */
    curl_ignore,    /* 132 */
    curl_ignore,    /* 133 */
    curl_string,    /* 134 ftp_account */
    curl_string,    /* 135 cookielist */
    curl_integer,   /* 136 ignore_content_length */
    curl_integer,   /* 137 ftp_skip_pasv_ip */
    curl_integer,   /* 138 ftp_filemethod */
    curl_integer,   /* 139 localport */
    curl_integer,   /* 140 localportrange */
    curl_integer,   /* 141 connect_only */
    curl_function,  /* 142 conv_from_network_function */
    curl_function,  /* 143 conv_to_network_function */
    curl_function,  /* 144 conv_from_utf8_function */
    curl_offset,    /* 145 max_send_speed_large */
    curl_offset,    /* 146 max_recv_speed_large */
    curl_string,    /* 147 ftp_alternative_to_user */
    curl_function,  /* 148 sockoptfunction */
    curl_string,    /* 149 sockoptdata */
    curl_integer,   /* 150 ssl_sessionid_cache */
    curl_integer,   /* 151 ssh_auth_types */
    curl_string,    /* 152 ssh_public_keyfile */
    curl_string,    /* 153 ssh_private_keyfile */
    curl_integer,   /* 154 ftp_ssl_ccc */
    curl_integer,   /* 155 timeout_ms */
    curl_integer,   /* 156 connecttimeout_ms */
    curl_integer,   /* 157 http_transfer_decoding */
    curl_integer,   /* 158 http_content_decoding */
    curl_integer,   /* 159 new_file_perms */
    curl_integer,   /* 160 new_directory_perms */
    curl_integer,   /* 161 postredir */
    curl_string,    /* 162 ssh_host_public_key_md5 */
    curl_function,  /* 163 opensocketfunction */
    curl_string,    /* 164 opensocketdata */
    curl_string,    /* 165 copypostfields */
    curl_integer,   /* 166 proxy_transfer_mode */
    curl_function,  /* 167 seekfunction */
    curl_string,    /* 168 seekdata */
    curl_string,    /* 169 crlfile */
    curl_string,    /* 170 issuercert */
    curl_integer,   /* 171 address_scope */
    curl_integer,   /* 172 certinfo */
    curl_string,    /* 173 username */
    curl_string,    /* 174 password */
    curl_string,    /* 175 proxyusername */
    curl_string,    /* 176 proxypassword */
    curl_string,    /* 177 noproxy */
    curl_integer,   /* 178 tftp_blksize */
    curl_string,    /* 179 socks5_gssapi_service */
    curl_integer,   /* 180 socks5_gssapi_nec */
    curl_integer,   /* 181 protocols */
    curl_integer,   /* 182 redir_protocols */
    curl_string,    /* 183 ssh_knownhosts */
    curl_function,  /* 184 ssh_keyfunction */
    curl_string,    /* 185 ssh_keydata */
    curl_string,    /* 186 mail_from */
    curl_string,    /* 187 mail_rcpt */
    curl_integer,   /* 188 ftp_use_pret */
    curl_integer,   /* 189 rtsp_request */
    curl_string,    /* 190 rtsp_session_id */
    curl_string,    /* 191 rtsp_stream_uri */
    curl_string,    /* 192 rtsp_transport */
    curl_integer,   /* 193 rtsp_client_cseq */
    curl_integer,   /* 194 rtsp_server_cseq */
    curl_string,    /* 195 interleavedata */
    curl_function,  /* 196 interleavefunction */
    curl_integer,   /* 197 wildcardmatch */
    curl_function,  /* 198 chunk_bgn_function */
    curl_function,  /* 199 chunk_end_function */
    curl_function,  /* 200 fnmatch_function */
    curl_string,    /* 201 chunk_data */
    curl_string,    /* 202 fnmatch_data */
    curl_string,    /* 203 resolve */
    curl_string,    /* 204 tlsauth_username */
    curl_string,    /* 205 tlsauth_password */
    curl_string,    /* 206 tlsauth_type */
    curl_integer,   /* 207 transfer_encoding */
    curl_function,  /* 208 closesocketfunction */
    curl_string,    /* 209 closesocketdata */
    curl_integer,   /* 210 gssapi_delegation */
    curl_string,    /* 211 dns_servers */
    curl_integer,   /* 212 accepttimeout_ms */
    curl_integer,   /* 213 tcp_keepalive */
    curl_integer,   /* 214 tcp_keepidle */
    curl_integer,   /* 215 tcp_keepintvl */
    curl_integer,   /* 216 ssl_options */
    curl_string,    /* 217 mail_auth */
    curl_integer,   /* 218 sasl_ir */
    curl_function,  /* 219 xferinfofunction */
    curl_string,    /* 220 xoauth2_bearer */
    curl_string,    /* 221 dns_interface */
    curl_string,    /* 222 dns_local_ip4 */
    curl_string,    /* 223 dns_local_ip6 */
    curl_string,    /* 224 login_options */
    curl_integer,   /* 225 ssl_enable_npn */
    curl_integer,   /* 226 ssl_enable_alpn */
    curl_integer    /* 227 expect_100_timeout_ms */
};

# define curl_option_min             1
# define curl_option_max           227
# define curl_option_writedata       1
# define curl_option_url             2
# define curl_option_writefunction  11

# define curl_integer_base       0 /* long */
# define curl_string_base    10000
# define curl_object_base    10000
# define curl_function_base  20000
# define curl_offset_base    30000
# define curl_offset_blob    40000

typedef size_t (*curl_write_callback) (
    char   *buffer,
    size_t  size,
    size_t  nitems,
    void   *userdata
);

typedef struct curllib_state_info {

    int initialized;
    int padding;

    char * (*curl_version) (
        void
    );

    void (*curl_free) (
        void* p
    );

    curl_instance (*curl_easy_init) (
        void
    );

    void (*curl_easy_cleanup) (
        curl_instance handle
    );

    curl_return_code (*curl_easy_perform) (
        curl_instance handle
    );

    curl_return_code (*curl_easy_setopt) (
        curl_instance handle,
        int           option,
        ...
    );

    char* (*curl_easy_escape) (
        curl_instance  handle,
        const char    *url,
        int            length
    );

    char* (*curl_easy_unescape) (
        curl_instance  handle,
        const char    *url,
        int            length,
        int           *outlength
    );

    const char* (*curl_easy_strerror) (
        curl_error_code errcode
    );

} curllib_state_info;

static curllib_state_info curllib_state = {

    .initialized        = 0,
    .padding            = 0,

    .curl_version       = NULL,
    .curl_free          = NULL,
    .curl_easy_init     = NULL,
    .curl_easy_cleanup  = NULL,
    .curl_easy_perform  = NULL,
    .curl_easy_setopt   = NULL,
    .curl_easy_escape   = NULL,
    .curl_easy_unescape = NULL,
    .curl_easy_strerror = NULL,

};

static int curllib_initialize(lua_State * L)
{
    if (! curllib_state.initialized) {
        const char *filename = lua_tostring(L, 1);
        if (filename) {

            lmt_library lib = lmt_library_load(filename);

            curllib_state.curl_version       = lmt_library_find(lib, "curl_version");
            curllib_state.curl_free          = lmt_library_find(lib, "curl_free");
            curllib_state.curl_easy_init     = lmt_library_find(lib, "curl_easy_init");
            curllib_state.curl_easy_cleanup  = lmt_library_find(lib, "curl_easy_cleanup");
            curllib_state.curl_easy_perform  = lmt_library_find(lib, "curl_easy_perform");
            curllib_state.curl_easy_setopt   = lmt_library_find(lib, "curl_easy_setopt");
            curllib_state.curl_easy_escape   = lmt_library_find(lib, "curl_easy_escape");
            curllib_state.curl_easy_unescape = lmt_library_find(lib, "curl_easy_unescape");
            curllib_state.curl_easy_strerror = lmt_library_find(lib, "curl_easy_strerror");

            curllib_state.initialized = lmt_library_okay(lib);
        }
    }
    lua_pushboolean(L, curllib_state.initialized);
    return 1;
}

/* fetch(url, { options }) | fetch({ options }) */

/* we don't need threads so we can just use the local init */

static size_t curllib_write_cb(char *data, size_t n, size_t l, void *b)
{
    luaL_addlstring((luaL_Buffer *) b, data, n * l);
    return n * l;
}

/*tex
    Always assume a table as we need to sanitize keys anyway. A former variant also accepted strings
    but why have more code than needed.
*/

static int curllib_fetch(lua_State * L)
{
    if (curllib_state.initialized) {
        if (lua_type(L,1) == LUA_TTABLE) {
            curl_instance *curl = curllib_state.curl_easy_init();
            if (curl)  {
                luaL_Buffer buffer;
                luaL_buffinit(L, &buffer);
                curllib_state.curl_easy_setopt(curl, curl_object_base + curl_option_writedata, &buffer);
                curllib_state.curl_easy_setopt(curl, curl_function_base + curl_option_writefunction, &curllib_write_cb);
                lua_pushnil(L);  /* first key */
                while (lua_next(L, 1) != 0) {
                    if (lua_type(L, -2) == LUA_TNUMBER) {
                        int o = lmt_tointeger(L, -2);
                        if (o >= curl_option_min && o <= curl_option_max) {
                            switch (curl_options[o]) {
                                case curl_string:
                                    if (lua_type(L, -1) == LUA_TSTRING) {
                                        curllib_state.curl_easy_setopt(curl, curl_string_base + o, lua_tostring(L, -1));
                                    } else {
                                     // return luaL_error(L, "curl option %d must be a string", o);
                                    }
                                    break;
                                case curl_integer:
                                    switch (lua_type(L, -1)) {
                                        case LUA_TNUMBER:
                                            curllib_state.curl_easy_setopt(curl, curl_integer_base + o, lua_tointeger(L, -1));
                                            break;
                                        case LUA_TBOOLEAN:
                                            curllib_state.curl_easy_setopt(curl, curl_integer_base + o, lua_toboolean(L, -1));
                                            break;
                                        default:
                                         // return luaL_error(L, "curl option %d must be a number of boolean", o);
                                            break;
                                    }
                                    break;
                            }
                        } else {
                         // return luaL_error(L, "curl option %d is invalid", o);
                        }
                    } else {
                     // return luaL_error(L, "curl option id should en a number");
                    }
                    lua_pop(L, 1); /* removes 'value' and keeps 'key' for next iteration */
                }
                int result = curllib_state.curl_easy_perform(curl);
                if (result) {
                    lua_pushboolean(L, 0);
                    lua_pushstring(L, curllib_state.curl_easy_strerror(result));
                    result = 2;
                } else {
                    luaL_pushresult(&buffer);
                    result = 1;
                }
                curllib_state.curl_easy_cleanup(curl);
                return result;
            }
        }
    }
    return 0;
}

static int curllib_escape(lua_State * L)
{
    if (curllib_state.initialized) {
        curl_instance *curl = curllib_state.curl_easy_init();
        if (curl) {
            size_t length = 0;
            const char * url = lua_tolstring(L, 1, &length);
            char *s = curllib_state.curl_easy_escape(curl, url, (int) length);
            if (s) {
                lua_pushstring(L,(const char *) s);
                curllib_state.curl_free(s);
                curllib_state.curl_easy_cleanup(curl);
                return 1;
            }
        }
    }
    return 0;
}

static int curllib_unescape(lua_State * L)
{
    if (curllib_state.initialized) {
        curl_instance *curl = curllib_state.curl_easy_init();
        if (curl) {
            size_t length = 0;
            const char *url = lua_tolstring(L, 1, &length);
            int l = 0;
            char *s = curllib_state.curl_easy_unescape(curl, url, (int) length, &l);
            if (s) {
                lua_pushlstring(L, s, l);
                curllib_state.curl_free(s);
                curllib_state.curl_easy_cleanup(curl);
                return 1;
            }
        }
    }
    return 0;
}

static int curllib_getversion(lua_State * L)
{
    if (curllib_state.initialized) {
        char *version = curllib_state.curl_version();
        if (version) {
            lua_pushstring(L, version);
            return 1;
        }
    }
    return 0;
}

static struct luaL_Reg curllib_function_list[] = {
    { "initialize", curllib_initialize },
    { "fetch",      curllib_fetch      },
    { "escape",     curllib_escape     },
    { "unescape",   curllib_unescape   },
    { "getversion", curllib_getversion },
    { NULL,         NULL               },
};

int luaopen_curl(lua_State * L)
{
    lmt_library_register(L, "curl", curllib_function_list);
    return 0;
}