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-2024 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  // Callback function concatenating received data.
  46  
  47  static size_t a68_curl_concat (void *data, size_t len, size_t n, void *buf)
  48  {
  49  // Sanity checks.
  50    if ((char *) data == NO_TEXT || (DATA_T *) buf == NO_DATA) {
  51      return 0;
  52    } else if (n == 0 || len == 0) {
  53      return 0;
  54    }
  55    ABEND (len >= (2 * GIGABYTE) / n, ERROR_OUT_OF_CORE, NO_TEXT);
  56    size_t new_len = n * len;
  57    ABEND (new_len + 1 > 2 * GIGABYTE - ((DATA_T *) buf)->len, ERROR_OUT_OF_CORE, NO_TEXT);
  58    char *stale = ((DATA_T *) buf)->ref;
  59    ((DATA_T *) buf)->ref = malloc (((DATA_T *) buf)->len + new_len + 1);
  60    ABEND (((DATA_T *) buf)->ref == NO_TEXT, ERROR_OUT_OF_CORE, NO_TEXT);
  61    if (stale != NO_TEXT) {
  62      MOVE (((DATA_T *) buf)->ref, stale, ((DATA_T *) buf)->len + 1);
  63      free (stale);
  64    }
  65    MOVE (& (((DATA_T *) buf)->ref[((DATA_T *) buf)->len]), data, new_len);
  66    ((DATA_T *) buf)->len += new_len;
  67    ((DATA_T *) buf)->ref[((DATA_T *) buf)->len] = NULL_CHAR;
  68    return new_len;
  69  }
  70  
  71  void genie_curl_content (NODE_T * p, char *protocol)
  72  {
  73    A68_REF path_string, domain_string, content_string;
  74    A68_INT port;
  75    errno = 0;
  76  // Pop arguments.
  77    POP_OBJECT (p, &port, A68_INT);
  78    CHECK_INIT (p, INITIALISED (&port), M_INT); // Unused for now.
  79    POP_REF (p, &path_string);
  80    CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
  81    POP_REF (p, &domain_string);
  82    CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
  83    POP_REF (p, &content_string);
  84    CHECK_REF (p, content_string, M_REF_STRING);
  85    *DEREF (A68_REF, &content_string) = empty_string (p);
  86  // Set buffers.
  87    reset_transput_buffer (DOMAIN_BUFFER);
  88    add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
  89    reset_transput_buffer (PATH_BUFFER);
  90    add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
  91  // Compose request.
  92    reset_transput_buffer (REQUEST_BUFFER);
  93    if (protocol != NO_TEXT) {
  94      add_string_transput_buffer (p, REQUEST_BUFFER, protocol);
  95    }
  96    add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (DOMAIN_BUFFER));
  97    add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
  98  // cURL connects to host, negotiates and collects data.
  99    DATA_T data = {NO_TEXT, 0};
 100    curl_global_init (CURL_GLOBAL_ALL);
 101    CURL *handle = curl_easy_init ();
 102    curl_easy_setopt (handle, CURLOPT_URL, get_transput_buffer (REQUEST_BUFFER));
 103    curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, a68_curl_concat);
 104    curl_easy_setopt (handle, CURLOPT_WRITEDATA, (void *) &data);
 105    curl_easy_setopt (handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
 106    CURLcode rc = curl_easy_perform (handle);
 107  // Wrap it up.
 108    if (rc != CURLE_OK) {
 109      errno = rc;
 110    } else {
 111      *DEREF (A68_REF, &content_string) = c_to_a_string (p, data.ref, data.len);
 112    }
 113    if (data.ref != NO_TEXT) {
 114      free (data.ref);
 115    }
 116    curl_easy_cleanup (handle);
 117    curl_global_cleanup ();
 118    PUSH_VALUE (p, errno, A68_INT);
 119  }
 120  
 121  //! @brief PROC (REF STRING, STRING, STRING, INT) INT http content 
 122  
 123  void genie_http_content (NODE_T * p)
 124  {
 125     genie_curl_content (p, "http://");
 126  }
 127  
 128  //! @brief PROC (REF STRING, STRING, STRING, INT) INT https content 
 129  
 130  void genie_https_content (NODE_T * p)
 131  {
 132     genie_curl_content (p, "https://");
 133  }
 134  
 135  #endif