1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 /* gretl_www.c for gretl -- uses libcurl API */
21
22 #include "libgretl.h"
23 #include "libset.h"
24 #include "build.h"
25 #include "version.h"
26 #include "gretl_www.h"
27
28 #include <curl/curl.h>
29 #include <curl/easy.h>
30
31 #define WDEBUG 0
32 #define WBUFSIZE 8192
33
34 /* stave off undeclared symbol errors for old libcurl */
35 #if LIBCURL_VERSION_NUM < 0x072000
36 # define CURLOPT_MAIL_FROM 10186
37 # define CURLOPT_MAIL_RCPT 10187
38 #endif
39 #if LIBCURL_VERSION_NUM < 0x071901
40 # define CURLOPT_USERNAME 10173
41 # define CURLOPT_PASSWORD 10174
42 #endif
43
44 enum {
45 SAVE_NONE,
46 SAVE_TO_FILE,
47 SAVE_TO_BUFFER
48 } save_opt;
49
50 /* paths specific to sourceforge */
51 static const char *updatecgi = "/cgi-bin/gretl_update.cgi";
52 static const char *manual_path = "/project/gretl/manual/";
53 static const char *dataset_path = "/project/gretl/datafiles/";
54 static const char *datapkg_list = "/addons-data/datapkgs.txt";
55 static const char *sffiles = "downloads.sourceforge.net";
56 static const char *sfweb = "gretl.sourceforge.net";
57
58 static int wproxy = 0;
59 static char proxyhost[128] = {0};
60
61 #ifdef WIN32
62 static char certs_path[MAXLEN];
63 #endif
64
65 #define URLLEN 1024
66
67 typedef struct urlinfo_ urlinfo;
68
69 struct urlinfo_ {
70 char url[URLLEN]; /* the URL */
71 int err; /* error code */
72 int verbose; /* verbosity level */
73 int saveopt; /* if saving data: to buffer or file? */
74 size_t buflen; /* size of allocated getbuf */
75 size_t datalen; /* number of bytes received */
76 const char *localfile; /* name of local file to write or NULL */
77 char *getbuf; /* buffer to which to write result or NULL */
78 char agent[32]; /* user-agent string */
79 FILE *fp; /* for saving content locally */
80 int (*progfunc)(); /* progress indicator function */
81 int pstarted; /* progress bar status flag */
82 int timeout; /* seconds till timing out */
83 };
84
85 static const char *sf_dbserver = "gretl.sourceforge.net";
86 static const char *sf_dbcgi = "/cgi-bin/gretldata.cgi";
87 static const char *wf_dbserver = "ricardo.ecn.wfu.edu";
88 static const char *wf_dbcgi = "/gretl/cgi-bin/gretldata.cgi";
89
90 static const char *sf_gretlhost = "gretl.sourceforge.net";
91 static const char *sf_datacgi = "/cgi-bin/gretldata.cgi";
92 static const char *wf_gretlhost = "ricardo.ecn.wfu.edu";
93 static const char *wf_datacgi = "/gretl/cgi-bin/gretldata.cgi";
94
95 static const char *dbserver;
96 static const char *dbcgi;
97 static const char *gretlhost;
98 static const char *datacgi;
99
100 static int SF_CGI;
101
set_server_paths(void)102 static void set_server_paths (void)
103 {
104 if (SF_CGI > 0) {
105 dbserver = sf_dbserver;
106 dbcgi = sf_dbcgi;
107 } else {
108 dbserver = wf_dbserver;
109 dbcgi = wf_dbcgi;
110 }
111 if (SF_CGI > 1) {
112 gretlhost = sf_gretlhost;
113 datacgi = sf_datacgi;
114 } else {
115 gretlhost = wf_gretlhost;
116 datacgi = wf_datacgi;
117 }
118 }
119
gretl_set_sf_cgi(int s)120 void gretl_set_sf_cgi (int s)
121 {
122 if (s > 1) {
123 SF_CGI = 2;
124 } else if (s != 0) {
125 SF_CGI = 1;
126 } else {
127 SF_CGI = 0;
128 }
129 set_server_paths();
130 }
131
urlinfo_init(urlinfo * u,const char * hostname,int saveopt,const char * localfile)132 static void urlinfo_init (urlinfo *u,
133 const char *hostname,
134 int saveopt,
135 const char *localfile)
136 {
137 memset(u->url, 0, URLLEN);
138
139 if (hostname != NULL) {
140 sprintf(u->url, "http://%s", hostname);
141 }
142
143 u->localfile = localfile;
144 u->saveopt = saveopt;
145
146 u->getbuf = NULL;
147 u->fp = NULL;
148
149 u->buflen = 0;
150 u->datalen = 0;
151 u->err = 0;
152
153 #if WDEBUG
154 u->verbose = 1;
155 #else
156 u->verbose = getenv("GRETL_WWW_VERBOSE") != NULL;
157 #endif
158
159 u->progfunc = NULL;
160 u->pstarted = 0;
161 u->timeout = 20; /* revised 2020-08-25, was 0 */
162
163 gretl_error_clear();
164
165 #ifdef BUILD_DATE
166 sprintf(u->agent, "gretl-%s-%s", GRETL_VERSION, BUILD_DATE);
167 #else
168 sprintf(u->agent, "gretl-%s", GRETL_VERSION);
169 #endif
170
171 #ifdef WIN32
172 strcat(u->agent, "w");
173 #endif
174 }
175
urlinfo_set_url(urlinfo * u,const char * url)176 static void urlinfo_set_url (urlinfo *u, const char *url)
177 {
178 memset(u->url, 0, URLLEN);
179 strncat(u->url, url, URLLEN - 1);
180 }
181
urlinfo_finalize(urlinfo * u,char ** getbuf,int * err)182 static void urlinfo_finalize (urlinfo *u, char **getbuf, int *err)
183 {
184 if (u->fp != NULL) {
185 fclose(u->fp);
186 } else if (getbuf != NULL) {
187 *getbuf = u->getbuf;
188 }
189
190 if (*err && u->localfile != NULL) {
191 gretl_remove(u->localfile);
192 }
193
194 if (u->saveopt == SAVE_TO_FILE || u->saveopt == SAVE_TO_BUFFER) {
195 if (u->datalen == 0) {
196 *err = E_DATA;
197 }
198 }
199 }
200
urlinfo_set_show_progress(urlinfo * u)201 static void urlinfo_set_show_progress (urlinfo *u)
202 {
203 int (*show_progress) (double, double, int) = NULL;
204
205 show_progress = get_plugin_function("show_progress");
206 if (show_progress != NULL) {
207 u->progfunc = show_progress;
208 }
209 }
210
progress_func(void * clientp,double dltotal,double dlnow,double ultotal,double ulnow)211 static int progress_func (void *clientp, double dltotal, double dlnow,
212 double ultotal, double ulnow)
213 {
214 urlinfo *u = (urlinfo *) clientp;
215 int ret = 0;
216
217 if (u->pstarted) {
218 ret = u->progfunc(dlnow, dltotal, SP_TOTAL);
219 } else if (u->progfunc != NULL && dltotal > 1024) {
220 u->progfunc(dlnow, dltotal, SP_LOAD_INIT);
221 u->pstarted = 1;
222 }
223
224 return (ret == SP_RETURN_CANCELED) ? 1 : 0;
225 }
226
stop_progress_bar(urlinfo * u)227 static void stop_progress_bar (urlinfo *u)
228 {
229 if (u->progfunc != NULL && u->pstarted) {
230 u->progfunc(0, 1024, SP_FINISH);
231 u->pstarted = 0;
232 }
233 }
234
grow_read_buffer(urlinfo * u,size_t bgot)235 static int grow_read_buffer (urlinfo *u, size_t bgot)
236 {
237 size_t newlen = 2 * u->buflen;
238 char *newbuf;
239
240 while (newlen < u->datalen + bgot) {
241 newlen *= 2;
242 }
243
244 newbuf = realloc(u->getbuf, newlen);
245
246 if (newbuf == NULL) {
247 return E_ALLOC;
248 } else {
249 size_t zerolen = newlen - u->datalen;
250
251 /* zero the additional memory chunk */
252 memset(newbuf + u->datalen, 0, zerolen);
253 u->getbuf = newbuf;
254 u->buflen = newlen;
255 #if WDEBUG
256 fprintf(stderr, "u->getbuf realloc'd at %p (len %d)\n",
257 (void *) u->getbuf, (int) u->buflen);
258 #endif
259 return 0;
260 }
261 }
262
gretl_write_func(void * buf,size_t size,size_t nmemb,void * data)263 static size_t gretl_write_func (void *buf, size_t size, size_t nmemb,
264 void *data)
265 {
266 urlinfo *u = (urlinfo *) data;
267 size_t bgot = size * nmemb;
268 size_t ret = 0;
269
270 #if WDEBUG > 1
271 fprintf(stderr, "write_func: size = %d, nmemb = %d\n",
272 (int) size, (int) nmemb);
273 #endif
274
275 if (u == NULL || u->err) {
276 return 0;
277 }
278
279 if (u->saveopt == SAVE_TO_FILE) {
280 if (u->fp == NULL) {
281 u->fp = gretl_fopen(u->localfile, "wb");
282 if (u->fp == NULL) {
283 u->err = E_FOPEN;
284 return 0;
285 }
286 }
287 ret = fwrite(buf, size, nmemb, u->fp);
288 } else if (u->saveopt == SAVE_TO_BUFFER) {
289 if (u->getbuf == NULL) {
290 u->getbuf = calloc(WBUFSIZE, 1);
291 #if WDEBUG
292 fprintf(stderr, "u->getbuf started at %p\n", (void *) u->getbuf);
293 #endif
294 if (u->getbuf == NULL) {
295 u->err = E_ALLOC;
296 return 0;
297 }
298 u->buflen = WBUFSIZE;
299 }
300 if (u->datalen + bgot > u->buflen) {
301 u->err = grow_read_buffer(u, bgot);
302 if (u->err) {
303 return 0;
304 }
305 }
306 memcpy(u->getbuf + u->datalen, buf, bgot);
307 ret = nmemb;
308 }
309
310 if (ret != 0) {
311 u->datalen += ret * size;
312 }
313
314 return ret;
315 }
316
is_db_transaction(int opt)317 static int is_db_transaction (int opt)
318 {
319 return (opt == LIST_DBS ||
320 opt == CHECK_DB ||
321 opt == GRAB_IDX ||
322 opt == GRAB_DATA ||
323 opt == GRAB_NBO_DATA);
324 }
325
progress_bar_wanted(int opt)326 static int progress_bar_wanted (int opt)
327 {
328 if (gretl_in_gui_mode()) {
329 return (opt == GRAB_IDX ||
330 opt == GRAB_DATA ||
331 opt == GRAB_NBO_DATA ||
332 opt == GRAB_FILE ||
333 opt == GRAB_FUNC ||
334 opt == GRAB_PDF ||
335 opt == GRAB_PKG ||
336 opt == GRAB_FOREIGN);
337 }
338
339 return 0;
340 }
341
print_option(int opt)342 static const char *print_option (int opt)
343 {
344 switch (opt) {
345 case LIST_DBS:
346 return "LIST_DBS";
347 case GRAB_IDX:
348 return "GRAB_IDX";
349 case GRAB_DATA:
350 return "GRAB_DATA";
351 case GRAB_NBO_DATA:
352 return "GRAB_NBO_DATA";
353 case GRAB_FILE:
354 return "GRAB_FILE";
355 case LIST_FUNCS:
356 return "LIST_FUNCS";
357 case GRAB_FUNC:
358 return "GRAB_FUNC";
359 case UPLOAD:
360 return "UPLOAD";
361 case CHECK_DB:
362 return "CHECK_DB";
363 case LIST_PKGS:
364 return "LIST_PKGS";
365 case GRAB_FUNC_INFO:
366 return "GRAB_FUNC_INFO";
367 case FUNC_FULLNAME:
368 return "FUNC_FULLNAME";
369 case LIST_CATS:
370 return "LIST_CATS";
371 case ALL_CATS:
372 return "ALL_CATS";
373 default:
374 break;
375 }
376
377 return NULL;
378 }
379
urlinfo_set_params(urlinfo * u,CGIOpt opt,const char * fname,const char * series,int filter)380 static void urlinfo_set_params (urlinfo *u, CGIOpt opt,
381 const char *fname,
382 const char *series,
383 int filter)
384 {
385 strcat(u->url, "?opt=");
386 strcat(u->url, print_option(opt));
387
388 if (fname != NULL) {
389 if (opt == GRAB_FILE || opt == GRAB_FUNC ||
390 opt == GRAB_FUNC_INFO || opt == FUNC_FULLNAME) {
391 strcat(u->url, "&fname=");
392 } else {
393 strcat(u->url, "&dbase=");
394 }
395 strcat(u->url, fname);
396 }
397
398 if (series != NULL) {
399 strcat(u->url, "&series=");
400 strcat(u->url, series);
401 }
402
403 if (filter > 0) {
404 char fstr[12];
405
406 sprintf(fstr, "%d", filter);
407 strcat(u->url, "&filter=");
408 strcat(u->url, fstr);
409 }
410 }
411
412 #ifdef WIN32
413
certs_path_init(void)414 static void certs_path_init (void)
415 {
416 # ifndef PKGBUILD
417 char *pfx = getenv("MINGW_PREFIX");
418
419 if (pfx != NULL) {
420 /* we may get a spurious space at the end */
421 gchar *tmp = g_strchomp(g_strdup(pfx));
422
423 sprintf(certs_path, "%s/share/curl/curl-ca-bundle.crt", tmp);
424 g_free(tmp);
425 if (gretl_stat(certs_path, NULL) != 0) {
426 fprintf(stderr, "curl 1: didn't find certs at '%s'\n",
427 certs_path);
428 *certs_path = '\0';
429 } else {
430 return;
431 }
432 } else {
433 /* hard-wired fallback */
434 strcpy(certs_path, "c:/msys64/mingw64/share/curl/curl-ca-bundle.crt");
435 if (gretl_stat(certs_path, NULL) != 0) {
436 fprintf(stderr, "curl 2: didn't find certs at '%s'\n",
437 certs_path);
438 *certs_path = '\0';
439 } else {
440 return;
441 }
442 }
443 # endif
444 sprintf(certs_path, "%scurl-ca-bundle.crt", gretl_home());
445 if (gretl_stat(certs_path, NULL) != 0) {
446 fprintf(stderr, "curl 3: didn't find certs at '%s'\n",
447 certs_path);
448 }
449 }
450
451 #endif /* WIN32 */
452
gretl_curl_toggle(int on)453 static int gretl_curl_toggle (int on)
454 {
455 static int init_done;
456
457 if (on) {
458 if (!init_done) {
459 CURLcode err = curl_global_init(CURL_GLOBAL_DEFAULT);
460
461 if (err) {
462 gretl_errmsg_set("Failed to initialize libcurl");
463 return 1;
464 } else {
465 #ifdef WIN32
466 certs_path_init();
467 #endif
468 init_done = 1;
469 }
470 }
471 } else if (init_done) {
472 curl_global_cleanup();
473 }
474
475 return 0;
476 }
477
set_curl_proxy(urlinfo * u,CURL * curl)478 static void set_curl_proxy (urlinfo *u, CURL *curl)
479 {
480 CURLcode err;
481
482 err = curl_easy_setopt(curl, CURLOPT_PROXY, proxyhost);
483
484 if (err != CURLE_OK) {
485 fprintf(stderr, "trying to set http proxy '%s':\n", proxyhost);
486 fprintf(stderr, "cURL error %d (%s)", err, curl_easy_strerror(err));
487 } else if (u->verbose) {
488 fprintf(stderr, "using http proxy '%s'\n", proxyhost);
489 }
490 }
491
common_curl_setup(CURL ** pcurl)492 static int common_curl_setup (CURL **pcurl)
493 {
494 int err;
495
496 err = gretl_curl_toggle(1);
497 if (err) {
498 return err;
499 }
500
501 *pcurl = curl_easy_init();
502
503 if (*pcurl == NULL) {
504 gretl_errmsg_set("curl_easy_init failed");
505 err = 1;
506 } else {
507 #if WDEBUG
508 curl_easy_setopt(*pcurl, CURLOPT_VERBOSE, 1);
509 #else
510 curl_easy_setopt(*pcurl, CURLOPT_VERBOSE,
511 getenv("GRETL_WWW_VERBOSE") != NULL);
512 #endif
513 #ifdef WIN32
514 /* be on the safe side: 'http' can turn into 'https'
515 at the server */
516 curl_easy_setopt(*pcurl, CURLOPT_CAINFO, certs_path);
517 #endif
518 }
519
520 return err;
521 }
522
curl_get(urlinfo * u)523 static int curl_get (urlinfo *u)
524 {
525 CURL *curl = NULL;
526 CURLcode res;
527 int err;
528
529 err = common_curl_setup(&curl);
530 if (err) {
531 return err;
532 }
533
534 if (u->verbose) {
535 fprintf(stderr, "curl_get: %s\n", u->url);
536 }
537 curl_easy_setopt(curl, CURLOPT_URL, u->url);
538 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, gretl_write_func);
539 curl_easy_setopt(curl, CURLOPT_WRITEDATA, u);
540 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
541 curl_easy_setopt(curl, CURLOPT_USERAGENT, u->agent);
542
543 if (u->timeout > 0) {
544 curl_easy_setopt(curl, CURLOPT_TIMEOUT, u->timeout);
545 }
546
547 if (wproxy && *proxyhost != '\0') {
548 set_curl_proxy(u, curl);
549 }
550
551 if (u->progfunc != NULL) {
552 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_func);
553 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, u);
554 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
555 } else {
556 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
557 }
558
559 res = curl_easy_perform(curl);
560
561 if (res == CURLE_SSL_CACERT) {
562 fprintf(stderr, "Error CURLE_SSL_CACERT from curl_easy_perform()\n");
563 }
564
565 #if 0 // def WIN32
566 if (res == CURLE_SSL_CACERT) {
567 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
568 /* does this re-run provoke a crash? */
569 res = curl_easy_perform(curl);
570 }
571 #endif
572
573 if (u->progfunc != NULL) {
574 stop_progress_bar(u);
575 }
576
577 if (res != CURLE_OK) {
578 gretl_errmsg_sprintf("cURL error %d (%s)", res,
579 curl_easy_strerror(res));
580 err = u->err ? u->err : 1;
581 }
582
583 curl_easy_cleanup(curl);
584
585 return err;
586 }
587
588 /* grab data from an internet host.
589
590 @host: name of host to access.
591
592 @opt: specifies the task; see the CGIOpt enumeration.
593
594 @fname: name of file to be downloaded or accessed, or NULL
595 if the request is just a query of some sort.
596
597 @dbseries: name of database series to download, or NULL;
598 used only for certain values of @opt.
599
600 @localfile: name of local file to which data should be
601 written, or NULL.
602
603 @getbuf: pointer to char *buffer to which data should be
604 written, or NULL. The content will be allocated here, if
605 applicable.
606
607 Exactly one of @localfile and @getbuf should be non-NULL.
608 */
609
retrieve_url(const char * hostname,CGIOpt opt,const char * fname,const char * dbseries,const char * localfile,int filter,char ** getbuf)610 static int retrieve_url (const char *hostname,
611 CGIOpt opt,
612 const char *fname,
613 const char *dbseries,
614 const char *localfile,
615 int filter,
616 char **getbuf)
617 {
618 int saveopt = SAVE_NONE;
619 urlinfo u = {0};
620 int err = 0;
621
622 if (getbuf != NULL) {
623 *getbuf = NULL;
624 saveopt = SAVE_TO_BUFFER;
625 } else if (localfile != NULL) {
626 saveopt = SAVE_TO_FILE;
627 }
628
629 urlinfo_init(&u, hostname, saveopt, localfile);
630
631 if (is_db_transaction(opt)) {
632 strcat(u.url, dbcgi);
633 } else if (opt == GRAB_FOREIGN || opt == QUERY_SF) {
634 strcat(u.url, fname);
635 } else if (opt == GRAB_PDF) {
636 strcat(u.url, manual_path);
637 strcat(u.url, fname);
638 } else if (opt == GRAB_PKG) {
639 strcat(u.url, dataset_path);
640 strcat(u.url, fname);
641 } else if (opt == GRAB_FILE) {
642 strcat(u.url, updatecgi);
643 } else if (opt == LIST_PKGS) {
644 strcat(u.url, datapkg_list);
645 } else {
646 strcat(u.url, datacgi);
647 }
648
649 if (strstr(gretlhost, "ricardo") == NULL) {
650 fprintf(stderr, "using gretlhost = '%s'\n", gretlhost);
651 }
652
653 if (opt != GRAB_PDF && opt != GRAB_FOREIGN &&
654 opt != GRAB_PKG && opt != QUERY_SF &&
655 opt != LIST_PKGS) {
656 /* a gretl-server download */
657 urlinfo_set_params(&u, opt, fname, dbseries, filter);
658 }
659
660 #if WDEBUG
661 fprintf(stderr, "retrieve_url: '%s'\n", u.url);
662 #endif
663
664 if (progress_bar_wanted(opt)) {
665 urlinfo_set_show_progress(&u);
666 }
667
668 err = curl_get(&u);
669
670 urlinfo_finalize(&u, getbuf, &err);
671
672 return err;
673 }
674
675 /* public interfaces follow */
676
gretl_www_init(const char * proxy,int use_proxy)677 int gretl_www_init (const char *proxy, int use_proxy)
678 {
679 set_server_paths();
680
681 if (use_proxy && proxy != NULL && *proxy != '\0') {
682 *proxyhost = '\0';
683 strncat(proxyhost, proxy, sizeof proxyhost - 1);
684 wproxy = 1;
685 } else {
686 wproxy = 0;
687 }
688
689 return 0;
690 }
691
gretl_www_cleanup(void)692 void gretl_www_cleanup (void)
693 {
694 gretl_curl_toggle(0);
695 }
696
get_update_info(char ** saver,int verbose)697 int get_update_info (char **saver, int verbose)
698 {
699 urlinfo u;
700 int err = 0;
701
702 urlinfo_init(&u, sfweb, SAVE_TO_BUFFER, NULL);
703 strcat(u.url, updatecgi);
704
705 if (verbose) {
706 strcat(u.url, "?opt=MANUAL_QUERY");
707 } else {
708 strcat(u.url, "?opt=QUERY");
709 }
710
711 err = curl_get(&u);
712 urlinfo_finalize(&u, saver, &err);
713
714 return err;
715 }
716
717 /* The content of the function package to be uploaded is in @buf;
718 the (short, pathless) filename for this package is in @fname.
719 If @retbuf is non-NULL it gets a copy of the response from the
720 server.
721 */
722
upload_function_package(const char * login,const char * pass,const char * fname,const char * buf,size_t buflen,char ** retbuf)723 int upload_function_package (const char *login, const char *pass,
724 const char *fname, const char *buf,
725 size_t buflen, char **retbuf)
726 {
727 struct curl_httppost *post = NULL;
728 struct curl_httppost *last = NULL;
729 int zipfile = has_suffix(fname, ".zip");
730 char sizestr[32];
731 CURL *curl = NULL;
732 CURLcode res;
733 int saveopt = SAVE_NONE;
734 urlinfo u;
735 int err = 0;
736
737 if (retbuf != NULL) {
738 *retbuf = NULL;
739 saveopt = SAVE_TO_BUFFER;
740 }
741
742 urlinfo_init(&u, gretlhost, saveopt, NULL);
743 strcat(u.url, datacgi);
744
745 err = common_curl_setup(&curl);
746 if (err) {
747 return err;
748 }
749
750 curl_easy_setopt(curl, CURLOPT_URL, u.url);
751 curl_easy_setopt(curl, CURLOPT_USERAGENT, u.agent);
752
753 if (saveopt == SAVE_TO_BUFFER) {
754 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, gretl_write_func);
755 curl_easy_setopt(curl, CURLOPT_FILE, &u);
756 }
757
758 if (wproxy && *proxyhost != '\0') {
759 set_curl_proxy(&u, curl);
760 }
761
762 curl_formadd(&post, &last,
763 CURLFORM_COPYNAME, "login",
764 CURLFORM_PTRCONTENTS, login,
765 CURLFORM_END);
766 curl_formadd(&post, &last,
767 CURLFORM_COPYNAME, "pass",
768 CURLFORM_PTRCONTENTS, pass,
769 CURLFORM_END);
770 if (zipfile) {
771 sprintf(sizestr, "%d", (int) buflen);
772 curl_formadd(&post, &last,
773 CURLFORM_COPYNAME, "datasize",
774 CURLFORM_PTRCONTENTS, sizestr,
775 CURLFORM_END);
776 curl_formadd(&post, &last,
777 CURLFORM_COPYNAME, "pkg",
778 CURLFORM_BUFFER, fname,
779 CURLFORM_CONTENTTYPE, "application/x-zip-compressed",
780 CURLFORM_BUFFERPTR, buf,
781 CURLFORM_BUFFERLENGTH, buflen,
782 CURLFORM_END);
783 } else {
784 curl_formadd(&post, &last,
785 CURLFORM_COPYNAME, "pkg",
786 CURLFORM_BUFFER, fname,
787 CURLFORM_CONTENTTYPE, "text/plain; charset=utf-8",
788 CURLFORM_BUFFERPTR, buf,
789 CURLFORM_BUFFERLENGTH, strlen(buf),
790 CURLFORM_END);
791 }
792
793 curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
794 res = curl_easy_perform(curl);
795
796 if (res != CURLE_OK) {
797 gretl_errmsg_sprintf("cURL error %d (%s)", res,
798 curl_easy_strerror(res));
799 err = u.err ? u.err : 1;
800 }
801
802 curl_formfree(post);
803 curl_easy_cleanup(curl);
804
805 if (retbuf != NULL) {
806 *retbuf = u.getbuf;
807 }
808
809 return err;
810 }
811
812 /* Upload functionality for email text and attachment */
813
814 struct uploader {
815 gchar *contents;
816 gchar *data;
817 gsize length;
818 };
819
get_payload(void * buf,size_t size,size_t nitems,void * ptr)820 static size_t get_payload (void *buf, size_t size,
821 size_t nitems, void *ptr)
822 {
823 struct uploader *ul = ptr;
824 size_t bufmax = size * nitems;
825 size_t ret = 0;
826
827 if (size == 0 || nitems == 0 || bufmax < 1) {
828 return 0;
829 } else if (ul->length == 0) {
830 return 0;
831 }
832
833 if (ul->length <= bufmax) {
834 memcpy(buf, ul->data, ul->length);
835 ret = ul->length;
836 ul->length = 0;
837 } else {
838 memcpy(buf, ul->data, bufmax);
839 ul->data += bufmax;
840 ul->length -= bufmax;
841 ret = bufmax;
842 }
843
844 return ret;
845 }
846
847 /* See also https://curl.haxx.se/libcurl/c/CURLOPT_READDATA.html */
848
curl_send_mail(const char * from_addr,const char * to_addr,const char * server,const char * username,const char * password,const char * filename)849 int curl_send_mail (const char *from_addr,
850 const char *to_addr,
851 const char *server,
852 const char *username,
853 const char *password,
854 const char *filename)
855 {
856 GError *gerr = NULL;
857 CURL *curl = NULL;
858 CURLcode res = CURLE_OK;
859 struct curl_slist *recip = NULL;
860 struct uploader ul;
861 int err;
862
863 err = common_curl_setup(&curl);
864 if (err) {
865 return err;
866 }
867
868 if (!g_file_get_contents(filename, &ul.contents,
869 &ul.length, &gerr)) {
870 gretl_errmsg_set(gerr->message);
871 g_error_free(gerr);
872 curl_easy_cleanup(curl);
873 return E_FOPEN;
874 } else {
875 ul.data = ul.contents;
876 }
877
878 curl_easy_setopt(curl, CURLOPT_USERNAME, username);
879 if (password != NULL && *password != '\0') {
880 curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
881 }
882 curl_easy_setopt(curl, CURLOPT_URL, server);
883 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from_addr);
884
885 recip = curl_slist_append(recip, to_addr);
886 curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recip);
887
888 /* We're using a callback function, since the libcurl doc
889 states that otherwise the curl DLL on Windows may crash.
890 */
891 curl_easy_setopt(curl, CURLOPT_READFUNCTION, get_payload);
892 curl_easy_setopt(curl, CURLOPT_READDATA, &ul);
893 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
894
895 /* Send the message */
896 res = curl_easy_perform(curl);
897
898 /* Check for errors */
899 if (res != CURLE_OK) {
900 gretl_errmsg_sprintf("cURL error %d (%s)", res,
901 curl_easy_strerror(res));
902 err = E_DATA;
903 }
904
905 curl_slist_free_all(recip);
906 curl_easy_cleanup(curl);
907 g_free(ul.contents);
908
909 return err;
910 }
911
curl_does_smtp(void)912 int curl_does_smtp (void)
913 {
914 static int smtp_ok = -1;
915
916 if (smtp_ok < 0) {
917 curl_version_info_data *vdata;
918
919 vdata = curl_version_info(CURLVERSION_NOW);
920 smtp_ok = vdata->version_num >= 0x073100;
921 }
922
923 return smtp_ok;
924 }
925
list_remote_dbs(char ** getbuf)926 int list_remote_dbs (char **getbuf)
927 {
928 return retrieve_url(dbserver, LIST_DBS, NULL, NULL,
929 NULL, 0, getbuf);
930 }
931
list_remote_function_packages(char ** getbuf,int filter)932 int list_remote_function_packages (char **getbuf, int filter)
933 {
934 return retrieve_url(gretlhost, LIST_FUNCS, NULL, NULL,
935 NULL, filter, getbuf);
936 }
937
list_remote_function_categories(char ** getbuf,gretlopt opt)938 int list_remote_function_categories (char **getbuf, gretlopt opt)
939 {
940 CGIOpt cval = (opt & OPT_A) ? ALL_CATS : LIST_CATS;
941
942 return retrieve_url(gretlhost, cval, NULL, NULL,
943 NULL, 0, getbuf);
944 }
945
query_sourceforge(const char * query,char ** getbuf)946 int query_sourceforge (const char *query, char **getbuf)
947 {
948 return retrieve_url(sfweb, QUERY_SF, query, NULL,
949 NULL, 0, getbuf);
950 }
951
list_remote_data_packages(char ** getbuf)952 int list_remote_data_packages (char **getbuf)
953 {
954 return retrieve_url(sfweb, LIST_PKGS, NULL, NULL,
955 NULL, 0, getbuf);
956 }
957
retrieve_remote_db_index(const char * dbname,char ** getbuf)958 int retrieve_remote_db_index (const char *dbname, char **getbuf)
959 {
960 return retrieve_url(dbserver, GRAB_IDX, dbname, NULL,
961 NULL, 0, getbuf);
962 }
963
retrieve_remote_db(const char * dbname,const char * localname,int opt)964 int retrieve_remote_db (const char *dbname,
965 const char *localname,
966 int opt)
967 {
968 return retrieve_url(dbserver, opt, dbname, NULL,
969 localname, 0, NULL);
970 }
971
check_remote_db(const char * dbname)972 int check_remote_db (const char *dbname)
973 {
974 char *getbuf = NULL;
975 int err;
976
977 err = retrieve_url(dbserver, CHECK_DB, dbname, NULL,
978 NULL, 0, &getbuf);
979
980 if (!err && getbuf != NULL) {
981 err = strncmp(getbuf, "OK", 2) != 0;
982 }
983
984 free(getbuf);
985
986 if (err) {
987 err = E_FOPEN;
988 }
989
990 return err;
991 }
992
check_downloaded_file(const char * fname,const char * dl)993 static int check_downloaded_file (const char *fname,
994 const char *dl)
995 {
996 int err = 0;
997
998 if (has_suffix(fname, ".zip") &&
999 !gretl_is_pkzip_file(fname)) {
1000 fprintf(stderr, "download: zip suffix but not pkzip\n");
1001 err = E_DATA;
1002 } else if (has_suffix(fname, ".gfn") &&
1003 !gretl_is_xml_file(fname)) {
1004 fprintf(stderr, "download: gfn suffix but not XML\n");
1005 err = E_DATA;
1006 }
1007
1008 if (err) {
1009 /* let's see what we got */
1010 FILE *fp = gretl_fopen(fname, "rb");
1011 int msg_done = 0;
1012
1013 if (fp != NULL) {
1014 char buf[128] = {0};
1015 size_t n;
1016
1017 n = fread(buf, 1, 127, fp);
1018 if (n > 8 && g_utf8_validate(buf, -1, NULL)) {
1019 gretl_errmsg_set(g_strchomp(buf));
1020 msg_done = 1;
1021 }
1022 fclose(fp);
1023 gretl_remove(fname);
1024 }
1025
1026 if (!msg_done) {
1027 gretl_errmsg_sprintf("%s\ndownload failed", dl);
1028 }
1029 }
1030
1031 return err;
1032 }
1033
1034 /**
1035 * retrieve_remote_function_package:
1036 * @pkgname: name of function package to retrieve, e.g. "foo.gfn".
1037 * @localname: full path to which the package file should be
1038 * written on the local machine.
1039 *
1040 * Retrieves the specified file from the gretl data server.
1041 *
1042 * Returns: 0 on success, non-zero on failure.
1043 */
1044
retrieve_remote_function_package(const char * pkgname,const char * localname)1045 int retrieve_remote_function_package (const char *pkgname,
1046 const char *localname)
1047 {
1048 int err;
1049
1050 err = retrieve_url(gretlhost, GRAB_FUNC, pkgname, NULL,
1051 localname, 0, NULL);
1052 if (!err) {
1053 err = check_downloaded_file(localname, pkgname);
1054 }
1055
1056 return err;
1057 }
1058
1059 /**
1060 * retrieve_remote_gfn_content:
1061 * @zipname: name of function package, e.g. "foo.zip".
1062 * @localname: full path to which the gfn file should be
1063 * written on the local machine.
1064 *
1065 * Retrieves the gfn file from within a function package on
1066 * the gretl server that takes the form of a zip file.
1067 *
1068 * Returns: 0 on success, non-zero on failure.
1069 */
1070
retrieve_remote_gfn_content(const char * zipname,const char * localname)1071 int retrieve_remote_gfn_content (const char *zipname,
1072 const char *localname)
1073 {
1074 return retrieve_url(gretlhost, GRAB_FUNC_INFO, zipname, NULL,
1075 localname, 0, NULL);
1076 }
1077
1078 /**
1079 * retrieve_remote_pkg_filename:
1080 * @pkgname: name of function package, without extension.
1081 * @err: location to receive error code.
1082 *
1083 * Returns: the completed package filename, with .gfn or
1084 * .zip extension, or NULL on failure.
1085 */
1086
retrieve_remote_pkg_filename(const char * pkgname,int * err)1087 char *retrieve_remote_pkg_filename (const char *pkgname,
1088 int *err)
1089 {
1090 char *fname = NULL;
1091 char *buf = NULL;
1092
1093 *err = retrieve_url(gretlhost, FUNC_FULLNAME, pkgname, NULL,
1094 NULL, 0, &buf);
1095
1096 if (!*err) {
1097 if (buf == NULL) {
1098 *err = E_DATA;
1099 } else {
1100 if (strstr(buf, "not found")) {
1101 gretl_errmsg_set(buf);
1102 *err = E_DATA;
1103 } else {
1104 char tmp[64];
1105
1106 sscanf(buf, "%63s", tmp);
1107 fname = gretl_strdup(tmp);
1108 }
1109 free(buf);
1110 }
1111 }
1112
1113 return fname;
1114 }
1115
get_uri_for_addon(const char * pkgname,int * err)1116 char *get_uri_for_addon (const char *pkgname, int *err)
1117 {
1118 const char *SF = "http://downloads.sourceforge.net/"
1119 "project/gretl/addons";
1120 char *p, pkgdir[16];
1121 gchar *query, *pkgbase;
1122 char *buf = NULL;
1123 char *uri = NULL;
1124
1125 /* we want the plain package name: allow for the
1126 possibility that @pkgname has been passed with
1127 its ".zip" extension
1128 */
1129 pkgbase = g_strdup(pkgname);
1130 if ((p = strrchr(pkgbase, '.')) != NULL) {
1131 *p = '\0';
1132 }
1133
1134 query = g_strdup_printf("/addons-data/pkgdir.php?gretl_version=%s"
1135 "&pkg=%s", GRETL_VERSION, pkgbase);
1136 *err = query_sourceforge(query, &buf);
1137 g_free(query);
1138
1139 if (!*err && buf == NULL) {
1140 /* shouldn't happen */
1141 *err = E_DATA;
1142 }
1143
1144 if (!*err && buf != NULL && strstr(buf, "<head>")) {
1145 /* got some sort of garbage */
1146 *err = E_DATA;
1147 }
1148
1149 if (!*err) {
1150 p = strchr(buf, ':');
1151 if (p == NULL ||
1152 sscanf(p + 2, "%15s", pkgdir) != 1 ||
1153 strcmp(pkgdir, "none") == 0) {
1154 *err = E_DATA;
1155 }
1156 }
1157
1158 free(buf);
1159
1160 if (*err) {
1161 gretl_errmsg_sprintf(_("Couldn't find %s for gretl %s"),
1162 pkgbase, GRETL_VERSION);
1163 } else {
1164 int n = strlen(SF) + strlen(pkgdir) + strlen(pkgbase) + 7;
1165
1166 uri = calloc(n + 7, 1);
1167 sprintf(uri, "%s/%s/%s.zip", SF, pkgdir, pkgbase);
1168 }
1169
1170 g_free(pkgbase);
1171
1172 return uri;
1173 }
1174
1175 /**
1176 * retrieve_remote_datafiles_package:
1177 * @pkgname: name of data files package to retrieve, e.g.
1178 * "wooldridge.tar.gz".
1179 * @localname: full path to which the package file should be
1180 * written on the local machine.
1181 *
1182 * Retrieves the specified package from sourceforge.
1183 *
1184 * Returns: 0 on success, non-zero on failure.
1185 */
1186
retrieve_remote_datafiles_package(const char * pkgname,const char * localname)1187 int retrieve_remote_datafiles_package (const char *pkgname,
1188 const char *localname)
1189 {
1190 return retrieve_url(sffiles, GRAB_PKG, pkgname, NULL,
1191 localname, 0, NULL);
1192 }
1193
1194 /**
1195 * retrieve_remote_db_data:
1196 * @dbname: name of gretl database to access.
1197 * @varname: name of the variable (series) to retrieve.
1198 * @getbuf: location to receive allocated buffer containing
1199 * the data.
1200 * @opt: either GRAB_NBO_DATA to get data in network byte
1201 * order, or GRAB_DATA to get the data in little-endian order.
1202 *
1203 * Retrieves the specified data from the gretl data server.
1204 *
1205 * Returns: 0 on success, non-zero on failure.
1206 */
1207
retrieve_remote_db_data(const char * dbname,const char * varname,char ** getbuf,int opt)1208 int retrieve_remote_db_data (const char *dbname,
1209 const char *varname,
1210 char **getbuf,
1211 int opt)
1212 {
1213 return retrieve_url(dbserver, opt, dbname, varname,
1214 NULL, 0, getbuf);
1215 }
1216
1217 /**
1218 * retrieve_manfile:
1219 * @fname: name of manual file to retrieve.
1220 * @localname: full path to which the file should be written
1221 * on the local machine.
1222 *
1223 * Retrieves the specified manual file in PDF format from
1224 * sourceforge.
1225 *
1226 * Returns: 0 on success, non-zero on failure.
1227 */
1228
retrieve_manfile(const char * fname,const char * localname)1229 int retrieve_manfile (const char *fname, const char *localname)
1230 {
1231 return retrieve_url(sffiles, GRAB_PDF, fname, NULL,
1232 localname, 0, NULL);
1233 }
1234
proto_length(const char * s)1235 static int proto_length (const char *s)
1236 {
1237 if (s == NULL) {
1238 return 0;
1239 } else if (!strncmp(s, "http://", 7)) {
1240 return 7;
1241 } else if (!strncmp(s, "https://", 8)) {
1242 return 8;
1243 } else if (!strncmp(s, "ftp://", 6)) {
1244 return 6;
1245 } else {
1246 return 0;
1247 }
1248 }
1249
1250 /**
1251 * retrieve_public_file:
1252 * @uri: complete URI for file to grab: protocol, host and path.
1253 * @localname: full path to which the file should be written
1254 * on the local machine. This cannot be NULL, but it can be
1255 * empty, in which case it should be of length %MAXLEN, and
1256 * on successful return it will be filled with an
1257 * automatically assigned local name, based on the name
1258 * of the file on the server.
1259 *
1260 * Retrieves the specified resource and writes it to
1261 * @localname, if possible.
1262 *
1263 * Returns: 0 on success, non-zero on failure.
1264 */
1265
retrieve_public_file(const char * uri,char * localname)1266 int retrieve_public_file (const char *uri, char *localname)
1267 {
1268 int pl = proto_length(uri);
1269 int err = 0;
1270
1271 if (pl == 0) {
1272 return E_DATA;
1273 } else if (*localname == '\0') {
1274 /* extract the filename from the uri */
1275 const char *s = strrchr(uri + pl, '/');
1276
1277 if (s == NULL || *(s+1) == '\0') {
1278 err = E_DATA;
1279 } else {
1280 /* save to user's dotdir by default */
1281 strcat(localname, gretl_dotdir());
1282 strcat(localname, s + 1);
1283 }
1284 }
1285
1286 if (!err) {
1287 urlinfo u;
1288
1289 urlinfo_init(&u, NULL, SAVE_TO_FILE, localname);
1290 urlinfo_set_url(&u, uri);
1291 if (gretl_in_gui_mode()) {
1292 urlinfo_set_show_progress(&u);
1293 }
1294 err = curl_get(&u);
1295 urlinfo_finalize(&u, NULL, &err);
1296 }
1297
1298 if (err) {
1299 const char *s = gretl_errmsg_get();
1300
1301 if (*s == '\0') {
1302 /* no error message in place */
1303 gretl_errmsg_sprintf("%s\ndownload failed", uri);
1304 }
1305 } else {
1306 err = check_downloaded_file(localname, uri);
1307 }
1308
1309 return err;
1310 }
1311
1312 /**
1313 * retrieve_public_file_as_buffer:
1314 * @uri: complete URI for file to grab: protocol, host and path.
1315 * @len: location to receive length of data retreived (bytes).
1316 * @err: location to receive error code.
1317 *
1318 * Returns: allocated buffer containing the specified resource,
1319 * or NULL on failure.
1320 */
1321
retrieve_public_file_as_buffer(const char * uri,size_t * len,int * err)1322 char *retrieve_public_file_as_buffer (const char *uri, size_t *len,
1323 int *err)
1324 {
1325 char *buf = NULL;
1326
1327 if (proto_length(uri) == 0) {
1328 *err = E_DATA;
1329 return NULL;
1330 } else {
1331 urlinfo u;
1332
1333 urlinfo_init(&u, NULL, SAVE_TO_BUFFER, NULL);
1334 urlinfo_set_url(&u, uri);
1335 *err = curl_get(&u);
1336 urlinfo_finalize(&u, &buf, err);
1337 *len = (*err)? 0 : u.datalen;
1338 }
1339
1340 if (*err) {
1341 const char *s = gretl_errmsg_get();
1342
1343 if (*s == '\0') {
1344 /* no error message in place */
1345 gretl_errmsg_sprintf("%s\ndownload failed", uri);
1346 }
1347 }
1348
1349 return buf;
1350 }
1351
1352 /* below: back-end to the implementation of the hansl "curl"
1353 function
1354 */
1355
1356 struct GetBuf {
1357 char **pbuf;
1358 size_t written;
1359 };
1360
curl_bufwrite(void * buf,size_t sz,size_t nmemb,void * p)1361 static size_t curl_bufwrite (void *buf, size_t sz, size_t nmemb, void *p)
1362 {
1363 struct GetBuf *out = (struct GetBuf *) p;
1364 char *mem;
1365
1366 if (out == NULL || out->pbuf == NULL || nmemb == 0) {
1367 return 0;
1368 }
1369
1370 sz *= nmemb;
1371 mem = realloc(*out->pbuf, out->written + sz + 1);
1372
1373 if (mem != NULL) {
1374 memset(mem + out->written, 0, sz + 1);
1375 *out->pbuf = mem;
1376 mem = memcpy(mem + out->written, buf, sz);
1377 out->written += sz;
1378 }
1379
1380 return (mem == NULL)? 0 : nmemb;
1381 }
1382
1383 /**
1384 * gretl_curl:
1385 * @url: complete URL: protocol, host and path.
1386 * @header: optional HTTP header (or NULL).
1387 * @postdata: string to send as data for POST (or NULL).
1388 * @include: if non-zero, include the received header with
1389 * the body output.
1390 * @output: location to receive the output.
1391 * @errmsg: location to receive cURL error message, or NULL.
1392 *
1393 * Somewhat flexible URI "grabber", allowing use of the POST
1394 * method with header and data to be sent to the host.
1395 *
1396 * Returns: 0 on success, non-zero code on error.
1397 */
1398
gretl_curl(const char * url,const char * header,const char * postdata,int include,char ** output,char ** errmsg)1399 int gretl_curl (const char *url, const char *header,
1400 const char *postdata, int include,
1401 char **output, char **errmsg)
1402 {
1403 CURL *curl = NULL;
1404 struct curl_slist *hlist = NULL;
1405 struct GetBuf getbuf = {
1406 output, /* pointer to buffer */
1407 0 /* bytes written */
1408 };
1409 CURLcode res;
1410 int err = 0;
1411
1412 err = common_curl_setup(&curl);
1413 if (err) {
1414 return err;
1415 }
1416
1417 if (header != NULL) {
1418 hlist = curl_slist_append(hlist, header);
1419 }
1420
1421 curl_easy_setopt(curl, CURLOPT_URL, url);
1422 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &getbuf);
1423 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_bufwrite);
1424 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
1425
1426 if (include) {
1427 curl_easy_setopt(curl, CURLOPT_HEADER, 1);
1428 }
1429
1430 if (hlist != NULL) {
1431 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hlist);
1432 }
1433
1434 if (postdata != NULL) {
1435 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (void *) postdata);
1436 }
1437
1438 if (wproxy && *proxyhost != '\0') {
1439 curl_easy_setopt(curl, CURLOPT_PROXY, proxyhost);
1440 }
1441
1442 res = curl_easy_perform(curl);
1443
1444 if (res != CURLE_OK) {
1445 const char *cmsg = curl_easy_strerror(res);
1446
1447 gretl_errmsg_sprintf("cURL error %d (%s)", res, cmsg);
1448 if (*output != NULL) {
1449 free(*output);
1450 *output = NULL;
1451 }
1452 if (errmsg != NULL) {
1453 *errmsg = gretl_strdup(cmsg);
1454 }
1455 err = E_DATA;
1456 }
1457
1458 if (hlist != NULL) {
1459 curl_slist_free_all(hlist);
1460 }
1461 curl_easy_cleanup(curl);
1462
1463 return err;
1464 }
1465
1466 /**
1467 * try_http:
1468 * @s: string: filename or URL.
1469 * @fname: location for writing name of local file;
1470 * must be of at least %MAXLEN bytes.
1471 * @http: location to receive 1 if @s turns out to be
1472 * a URL using HTTP(S) protocol, or NULL.
1473 *
1474 * Check the string @s, that may be a straight filename or may
1475 * be a URL. If it's a filename, just copy @s to @fname.
1476 * Otherwise try to download the resource and write its content
1477 * to a temporary file, whose name is then written into @fname.
1478 *
1479 * Returns: 0 on success, non-zero code on error.
1480 */
1481
try_http(const char * s,char * fname,int * http)1482 int try_http (const char *s, char *fname, int *http)
1483 {
1484 int err = 0;
1485
1486 if (strncmp(s, "http://", 7) == 0 ||
1487 strncmp(s, "https://", 8) == 0) {
1488 #ifdef USE_CURL
1489 err = retrieve_public_file(s, fname);
1490 if (!err && http != NULL) {
1491 *http = 1;
1492 }
1493 #else
1494 gretl_errmsg_set(_("Internet access not supported"));
1495 err = E_DATA;
1496 #endif
1497 }
1498
1499 return err;
1500 }
1501