rts-curl.c
1 //! @file rts-curl.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! HTTP/HTTPS client.
25
26 #include "a68g.h"
27 #include "a68g-genie.h"
28 #include "a68g-prelude.h"
29 #include "a68g-transput.h"
30
31 #if defined (HAVE_CURL)
32
33 #if defined (HAVE_CURL_CURL_H)
34 #include <curl/curl.h>
35 #endif
36
37 typedef struct DATA_T DATA_T;
38 struct DATA_T {
39 char *ref;
40 size_t len;
41 };
42
43 #define NO_DATA ((DATA_T *) NULL)
44
45 // Below, 0 means default, which are 300 s / endless respectively.
46
47 static INT_T a68g_curl_connecttimeout = 0, a68g_curl_timeout = 0;
48
49 // Callback function concatenating received data.
50
51 static size_t a68g_curl_concat (void *data, size_t len, size_t n, void *buf)
52 {
53 // Sanity checks.
54 if ((char *) data == NO_TEXT || (DATA_T *) buf == NO_DATA) {
55 return 0;
56 } else if (n == 0 || len == 0) {
57 return 0;
58 }
59 ABEND (len >= (2 * A68G_GIGA) / n, ERROR_MEMORY_FULL, NO_TEXT);
60 size_t new_len = n * len;
61 ABEND (new_len + 1 > 2 * A68G_GIGA - ((DATA_T *) buf)->len, ERROR_MEMORY_FULL, NO_TEXT);
62 char *stale = ((DATA_T *) buf)->ref;
63 ((DATA_T *) buf)->ref = malloc (((DATA_T *) buf)->len + new_len + 1);
64 ABEND (((DATA_T *) buf)->ref == NO_TEXT, ERROR_MEMORY_FULL, NO_TEXT);
65 if (stale != NO_TEXT) {
66 MOVE (((DATA_T *) buf)->ref, stale, ((DATA_T *) buf)->len + 1);
67 a68g_free (stale);
68 }
69 MOVE (& (((DATA_T *) buf)->ref[((DATA_T *) buf)->len]), data, new_len);
70 ((DATA_T *) buf)->len += new_len;
71 ((DATA_T *) buf)->ref[((DATA_T *) buf)->len] = NULL_CHAR;
72 return new_len;
73 }
74
75 void genie_curl_content (NODE_T * p, char *protocol)
76 {
77 A68G_REF path_string, domain_string, content_string;
78 A68G_INT port;
79 errno = 0;
80 // Pop arguments.
81 POP_OBJECT (p, &port, A68G_INT);
82 CHECK_INIT (p, INITIALISED (&port), M_INT); // Unused for now.
83 POP_REF (p, &path_string);
84 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
85 POP_REF (p, &domain_string);
86 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
87 POP_REF (p, &content_string);
88 CHECK_REF (p, content_string, M_REF_STRING);
89 *DEREF (A68G_REF, &content_string) = empty_string (p);
90 // Set buffers.
91 reset_transput_buffer (DOMAIN_BUFFER);
92 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
93 reset_transput_buffer (PATH_BUFFER);
94 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
95 // Compose request.
96 reset_transput_buffer (REQUEST_BUFFER);
97 if (protocol != NO_TEXT) {
98 add_string_transput_buffer (p, REQUEST_BUFFER, protocol);
99 }
100 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (DOMAIN_BUFFER));
101 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
102 // cURL connects to host, negotiates and collects data.
103 DATA_T data = {NO_TEXT, 0};
104 curl_global_init (CURL_GLOBAL_ALL);
105 CURL *handle = curl_easy_init ();
106 curl_easy_setopt (handle, CURLOPT_URL, get_transput_buffer (REQUEST_BUFFER));
107 curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, a68g_curl_concat);
108 curl_easy_setopt (handle, CURLOPT_WRITEDATA, (void *) &data);
109 curl_easy_setopt (handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
110 curl_easy_setopt (handle, CURLOPT_CONNECTTIMEOUT, (long) a68g_curl_connecttimeout);
111 curl_easy_setopt (handle, CURLOPT_TIMEOUT, (long) a68g_curl_timeout);
112 CURLcode rc = curl_easy_perform (handle);
113 // Wrap it up.
114 if (rc != CURLE_OK) {
115 errno = rc;
116 } else {
117 *DEREF (A68G_REF, &content_string) = c_to_a_string (p, data.ref, data.len);
118 }
119 if (data.ref != NO_TEXT) {
120 a68g_free (data.ref);
121 }
122 curl_easy_cleanup (handle);
123 curl_global_cleanup ();
124 PUSH_VALUE (p, errno, A68G_INT);
125 }
126
127 //! @brief PROC (INT, INT) VOID http timeout
128
129 void genie_curl_timeout (NODE_T *p)
130 {
131 A68G_INT i, j;
132 POP_OBJECT (p, &j, A68G_INT);
133 POP_OBJECT (p, &i, A68G_INT);
134 a68g_curl_connecttimeout = VALUE (&i);
135 a68g_curl_timeout = VALUE (&j);
136 }
137
138 //! @brief PROC (REF STRING, STRING, STRING, INT) INT http content
139
140 void genie_http_content (NODE_T * p)
141 {
142 genie_curl_content (p, "http://");
143 }
144
145 //! @brief PROC (REF STRING, STRING, STRING, INT) INT https content
146
147 void genie_https_content (NODE_T * p)
148 {
149 genie_curl_content (p, "https://");
150 }
151
152 #endif
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|