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