Browse Source

OS400: implement EBCDIC support for recent features

- Support CURLVERSION_ELEVENTH.

- New function curl_url_strerror_ccsid().

- curl_easy_setopt_ccsid() supports blobs and 3 recent string options.

- New function curl_easy_header_ccsid().

- New generic latin1<-->ccsid conversion functions curl_from_ccsid() and
  curl_to_ccsid() for user convenience.

- README.OS400 updated accordingly.

- Removed a leftover QsoSSL support identifier.

Closes https://github.com/curl/curl/pull/10994
Patrick Monnerat 1 year ago
parent
commit
a1fa2b30c0
4 changed files with 163 additions and 94 deletions
  1. 33 9
      packages/OS400/README.OS400
  2. 120 84
      packages/OS400/ccsidcurl.c
  3. 9 0
      packages/OS400/ccsidcurl.h
  4. 1 1
      packages/OS400/os400sys.h

+ 33 - 9
packages/OS400/README.OS400

@@ -1,8 +1,8 @@
 
 Implementation notes:
 
-  This is a true OS/400 implementation, not a PASE implementation (for PASE,
-use AIX implementation).
+  This is a true OS/400 ILE implementation, not a PASE implementation (for
+PASE, use AIX implementation).
 
   The biggest problem with OS/400 is EBCDIC. Libcurl implements an internal
 conversion mechanism, but it has been designed for computers that have a
@@ -96,6 +96,7 @@ options:
         CURLOPT_PASSWORD
         CURLOPT_PINNEDPUBLICKEY
         CURLOPT_PRE_PROXY
+        CURLOPT_PROTOCOLS_STR
         CURLOPT_PROXY
         CURLOPT_PROXYPASSWORD
         CURLOPT_PROXYUSERNAME
@@ -117,6 +118,7 @@ options:
         CURLOPT_PROXY_TLSAUTH_USERNAME
         CURLOPT_RANDOM_FILE
         CURLOPT_RANGE
+        CURLOPT_REDIR_PROTOCOLS_STR
         CURLOPT_REFERER
         CURLOPT_REQUEST_TARGET
         CURLOPT_RTSP_SESSION_UID
@@ -126,6 +128,7 @@ options:
         CURLOPT_SERVICE_NAME
         CURLOPT_SOCKS5_GSSAPI_SERVICE
         CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
+        CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256
         CURLOPT_SSH_KNOWNHOSTS
         CURLOPT_SSH_PRIVATE_KEYFILE
         CURLOPT_SSH_PUBLIC_KEYFILE
@@ -145,11 +148,13 @@ options:
         CURLOPT_USERNAME
         CURLOPT_USERPWD
         CURLOPT_XOAUTH2_BEARER
-  Else it is the same as for curl_easy_setopt().
+  All blob options are also supported.
+  In all other cases, it ignores the ccsid parameter and behaves as
+curl_easy_setopt().
   Note that CURLOPT_ERRORBUFFER is not in the list above, since it gives the
 address of an (empty) character buffer, not the address of a string.
-CURLOPT_POSTFIELDS stores the address of static binary data (of type void *) and
-thus is not converted. If CURLOPT_COPYPOSTFIELDS is issued after
+CURLOPT_POSTFIELDS stores the address of static binary data (of type void *)
+and thus is not converted. If CURLOPT_COPYPOSTFIELDS is issued after
 CURLOPT_POSTFIELDSIZE != -1, the data size is adjusted according to the
 CCSID conversion result length.
 
@@ -195,6 +200,13 @@ CCSID. Returned structures should be freed with curl_certinfo_free_all()
 after use.
   Other options are processed like in curl_easy_getinfo().
 
+_ curl_easy_strerror_ccsid(), curl_multi_strerror_ccsid(),
+curl_share_strerror_ccsid() and curl_url_strerror_ccsid() work as their
+non-ccsid version and return a string encoded in the additional ccsid
+parameter. These strings belong to libcurl and may not be freed by the caller.
+A subsequent call to the same procedure in the same thread invalidates the
+previous result.
+
 _ curl_pushheader_bynum_cssid() and curl_pushheader_byname_ccsid()
   Although the prototypes are self-explanatory, the returned string pointer
 should be released with curl_free() after use, as opposite to the non-ccsid
@@ -208,6 +220,17 @@ ASCII, the curl_easy_option_get_name_ccsid() function allows getting it in any
 supported ccsid. However the caller should release the returned pointer with
 curl_free() after use.
 
+_ curl_easy_header_ccsid() works as its non-CCSID counterpart but requires an
+additional ccsid parameter specifying the name parameter encoding. The output
+hout parameter is kept in libcurl's encoding and should not be altered.
+
+_ curl_from_ccsid() and curl_to_ccsid() are string encoding conversion
+functions between ASCII (latin1) and the given CCSID. The first parameter is
+the source string, the second is the CCSID and the returned value is a pointer
+to the dynamically allocated string. These functions do not impact on Curl's
+behavior and are only provided for user convenience. After use, returned values
+must be released with curl_free().
+
 
   Standard compilation environment does support neither autotools nor make;
 in fact, very few common utilities are available. As a consequence, the
@@ -270,7 +293,8 @@ _ Copy any file in the current directory to makelog (i.e.:
   cp initscript.sh makelog): this is intended to create the makelog file with
   an ASCII CCSID!
 _ Enter the command "sh makefile.sh > makelog 2>&1"
-_ Examine the makelog file to check for compilation errors.
+_ Examine the makelog file to check for compilation errors. CZM0383 warnings on
+  C or system standard API come from QADRT inlining and can safely be ignored.
 
   Leaving file initscript.sh unchanged, this will produce the following OS/400
 objects:
@@ -316,9 +340,9 @@ _ Do not use original source include files unless you know what you are doing.
 
 ILE/RPG support:
 
-  Since 95% of the OS/400 programmers use ILE/RPG exclusively, a definition
-  /INCLUDE member is provided for this language. To include all libcurl
-  definitions in an ILE/RPG module, line
+  Since most of the ILE OS/400 programmers use ILE/RPG exclusively, a
+definition /INCLUDE member is provided for this language. To include all
+libcurl definitions in an ILE/RPG module, line
 
      h bnddir('CURL/CURL')
 

+ 120 - 84
packages/OS400/ccsidcurl.c

@@ -235,28 +235,50 @@ slist_convert(int dccsid, struct curl_slist *from, int sccsid)
 }
 
 
-char *curl_version_ccsid(unsigned int ccsid)
+static char *
+keyed_string(localkey_t key, const char *ascii, unsigned int ccsid)
 {
   int i;
-  char *aversion;
-  char *eversion;
+  char *ebcdic;
 
-  aversion = curl_version();
+  if(!ascii)
+    return (char *) NULL;
 
-  if(!aversion)
-    return aversion;
+  i = MAX_CONV_EXPANSION * (strlen(ascii) + 1);
 
-  i = strlen(aversion) + 1;
-  i *= MAX_CONV_EXPANSION;
+  ebcdic = Curl_thread_buffer(key, i);
+  if(!ebcdic)
+    return ebcdic;
 
-  eversion = Curl_thread_buffer(LK_CURL_VERSION, i);
-  if(!eversion)
+  if(convert(ebcdic, i, ccsid, ascii, -1, ASCII_CCSID) < 0)
     return (char *) NULL;
 
-  if(convert(eversion, i, ccsid, aversion, -1, ASCII_CCSID) < 0)
-    return (char *) NULL;
+  return ebcdic;
+}
+
+
+const char *
+curl_to_ccsid(const char *s, unsigned int ccsid)
+{
+  if(s)
+    s = dynconvert(ccsid, s, -1, ASCII_CCSID);
+  return s;
+}
 
-  return eversion;
+
+const char *
+curl_from_ccsid(const char *s, unsigned int ccsid)
+{
+  if(s)
+    s = dynconvert(ASCII_CCSID, s, -1, ccsid);
+  return s;
+}
+
+
+char *
+curl_version_ccsid(unsigned int ccsid)
+{
+  return keyed_string(LK_CURL_VERSION, curl_version(), ccsid);
 }
 
 
@@ -414,18 +436,19 @@ curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid)
     offsetof(curl_version_info_data, capath),
     offsetof(curl_version_info_data, zstd_version),
     offsetof(curl_version_info_data, hyper_version),
-    offsetof(curl_version_info_data, gsasl_version)
+    offsetof(curl_version_info_data, gsasl_version),
+    offsetof(curl_version_info_data, feature_names)
   };
 
   /* The assertion below is possible, because although the second operand
      is an enum member, the first is a #define. In that case, the OS/400 C
      compiler seems to compare string values after substitution. */
 
-#if CURLVERSION_NOW != CURLVERSION_TENTH
+#if CURLVERSION_NOW != CURLVERSION_ELEVENTH
 #error curl_version_info_data structure has changed: upgrade this procedure.
 #endif
 
-  /* If caller has been compiled with a new version, error. */
+  /* If caller has been compiled with a newer version, error. */
 
   if(stamp > CURLVERSION_NOW)
     return (curl_version_info_data *) NULL;
@@ -498,75 +521,28 @@ curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid)
 const char *
 curl_easy_strerror_ccsid(CURLcode error, unsigned int ccsid)
 {
-  int i;
-  const char *s;
-  char *buf;
-
-  s = curl_easy_strerror(error);
-
-  if(!s)
-    return s;
-
-  i = MAX_CONV_EXPANSION * (strlen(s) + 1);
-
-  buf = Curl_thread_buffer(LK_EASY_STRERROR, i);
-  if(!buf)
-    return (const char *) NULL;
-
-  if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
-    return (const char *) NULL;
-
-  return (const char *) buf;
+  return keyed_string(LK_EASY_STRERROR, curl_easy_strerror(error), ccsid);
 }
 
 
 const char *
 curl_share_strerror_ccsid(CURLSHcode error, unsigned int ccsid)
 {
-  int i;
-  const char *s;
-  char *buf;
-
-  s = curl_share_strerror(error);
-
-  if(!s)
-    return s;
-
-  i = MAX_CONV_EXPANSION * (strlen(s) + 1);
-
-  buf = Curl_thread_buffer(LK_SHARE_STRERROR, i);
-  if(!buf)
-    return (const char *) NULL;
-
-  if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
-    return (const char *) NULL;
-
-  return (const char *) buf;
+  return keyed_string(LK_SHARE_STRERROR, curl_share_strerror(error), ccsid);
 }
 
 
 const char *
 curl_multi_strerror_ccsid(CURLMcode error, unsigned int ccsid)
 {
-  int i;
-  const char *s;
-  char *buf;
-
-  s = curl_multi_strerror(error);
-
-  if(!s)
-    return s;
-
-  i = MAX_CONV_EXPANSION * (strlen(s) + 1);
-
-  buf = Curl_thread_buffer(LK_MULTI_STRERROR, i);
-  if(!buf)
-    return (const char *) NULL;
+  return keyed_string(LK_MULTI_STRERROR, curl_multi_strerror(error), ccsid);
+}
 
-  if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
-    return (const char *) NULL;
 
-  return (const char *) buf;
+const char *
+curl_url_strerror_ccsid(CURLUcode error, unsigned int ccsid)
+{
+  return keyed_string(LK_URL_STRERROR, curl_url_strerror(error), ccsid);
 }
 
 
@@ -1087,17 +1063,15 @@ curl_formget_ccsid(struct curl_httppost *form, void *arg,
 
 
 CURLcode
-curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
+curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...)
 {
   CURLcode result;
   va_list arg;
-  struct Curl_easy *data;
   char *s;
-  char *cp;
+  char *cp = NULL;
   unsigned int ccsid;
   curl_off_t pfsize;
 
-  data = (struct Curl_easy *) curl;
   va_start(arg, tag);
 
   switch(tag) {
@@ -1137,6 +1111,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
   case CURLOPT_PASSWORD:
   case CURLOPT_PINNEDPUBLICKEY:
   case CURLOPT_PRE_PROXY:
+  case CURLOPT_PROTOCOLS_STR:
   case CURLOPT_PROXY:
   case CURLOPT_PROXYPASSWORD:
   case CURLOPT_PROXYUSERNAME:
@@ -1158,6 +1133,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
   case CURLOPT_PROXY_TLSAUTH_USERNAME:
   case CURLOPT_RANDOM_FILE:
   case CURLOPT_RANGE:
+  case CURLOPT_REDIR_PROTOCOLS_STR:
   case CURLOPT_REFERER:
   case CURLOPT_REQUEST_TARGET:
   case CURLOPT_RTSP_SESSION_ID:
@@ -1167,6 +1143,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
   case CURLOPT_SERVICE_NAME:
   case CURLOPT_SOCKS5_GSSAPI_SERVICE:
   case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
+  case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
   case CURLOPT_SSH_KNOWNHOSTS:
   case CURLOPT_SSH_PRIVATE_KEYFILE:
   case CURLOPT_SSH_PUBLIC_KEYFILE:
@@ -1199,7 +1176,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
       }
     }
 
-    result = curl_easy_setopt(curl, tag, s);
+    result = curl_easy_setopt(easy, tag, s);
     free(s);
     break;
 
@@ -1210,10 +1187,10 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
     s = va_arg(arg, char *);
     ccsid = va_arg(arg, unsigned int);
 
-    pfsize = data->set.postfieldsize;
+    pfsize = easy->set.postfieldsize;
 
     if(!s || !pfsize || ccsid == NOCONV_CCSID || ccsid == ASCII_CCSID) {
-      result = curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, s);
+      result = curl_easy_setopt(easy, CURLOPT_COPYPOSTFIELDS, s);
       break;
     }
 
@@ -1251,30 +1228,68 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
       pfsize = convert(cp, pfsize, ASCII_CCSID, s, len, ccsid);
 
       if(pfsize < 0) {
-        free(cp);
         result = CURLE_OUT_OF_MEMORY;
         break;
       }
 
-      data->set.postfieldsize = pfsize;         /* Replace data size. */
+      easy->set.postfieldsize = pfsize;         /* Replace data size. */
       s = cp;
     }
 
-    result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s);
-    data->set.str[STRING_COPYPOSTFIELDS] = s;   /* Give to library. */
+    result = curl_easy_setopt(easy, CURLOPT_POSTFIELDS, s);
+    easy->set.str[STRING_COPYPOSTFIELDS] = s;   /* Give to library. */
     break;
 
-  case CURLOPT_ERRORBUFFER:                     /* This is an output buffer. */
   default:
-    result = Curl_vsetopt(curl, tag, arg);
+    if(tag / 10000 == CURLOPTTYPE_BLOB) {
+      struct curl_blob *bp = va_arg(arg, struct curl_blob *);
+      struct curl_blob blob;
+
+      ccsid = va_arg(arg, unsigned int);
+
+      if(bp && bp->data && bp->len &&
+         ccsid != NOCONV_CCSID && ccsid != ASCII_CCSID) {
+        pfsize = (curl_off_t) bp->len * MAX_CONV_EXPANSION;
+
+        if(pfsize > SIZE_MAX)
+          pfsize = SIZE_MAX;
+
+        cp = malloc(pfsize);
+
+        if(!cp) {
+          result = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+
+        pfsize = convert(cp, pfsize, ASCII_CCSID, bp->data, bp->len, ccsid);
+
+        if(pfsize < 0) {
+          result = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+
+        blob.data = cp;
+        blob.len = pfsize;
+        blob.flags = bp->flags | CURL_BLOB_COPY;
+        bp = &blob;
+      }
+      result = curl_easy_setopt(easy, tag, &blob);
+      break;
+    }
+    /* FALLTHROUGH */
+  case CURLOPT_ERRORBUFFER:                     /* This is an output buffer. */
+    result = Curl_vsetopt(easy, tag, arg);
     break;
   }
 
   va_end(arg);
+  free(cp);
   return result;
 }
 
 
+/* ILE/RPG helper functions. */
+
 char *
 curl_form_long_value(long value)
 {
@@ -1460,3 +1475,24 @@ curl_easy_option_get_name_ccsid(const struct curl_easyoption *option,
 
   return (const char *) name;
 }
+
+/* Header API CCSID support. */
+CURLHcode
+curl_easy_header_ccsid(CURL *easy, const char *name, size_t index,
+                       unsigned int origin, int request,
+                       struct curl_header **hout, unsigned int ccsid)
+{
+  CURLHcode result = CURLHE_BAD_ARGUMENT;
+
+  if(name) {
+    char *s = dynconvert(ASCII_CCSID, name, -1, ccsid);
+
+    result = CURLHE_OUT_OF_MEMORY;
+    if(s) {
+      result = curl_easy_header(easy, s, index, origin, request, hout);
+      free(s);
+    }
+  }
+
+  return result;
+}

+ 9 - 0
packages/OS400/ccsidcurl.h

@@ -96,5 +96,14 @@ CURL_EXTERN const struct curl_easyoption *curl_easy_option_by_name_ccsid(
 CURL_EXTERN const char *curl_easy_option_get_name_ccsid(
                                          const struct curl_easyoption *option,
                                          unsigned int ccsid);
+CURL_EXTERN const char *curl_url_strerror_ccsid(CURLUcode error,
+                                                unsigned int ccsid);
+CURL_EXTERN CURLHcode curl_easy_header_ccsid(CURL *easy, const char *name,
+                                             size_t index, unsigned int origin,
+                                             int request,
+                                             struct curl_header **hout,
+                                             unsigned int ccsid);
+CURL_EXTERN const char *curl_from_ccsid(const char *s, unsigned int ccsid);
+CURL_EXTERN const char *curl_to_ccsid(const char *s, unsigned int ccsid);
 
 #endif

+ 1 - 1
packages/OS400/os400sys.h

@@ -32,7 +32,6 @@
 /* Per-thread item identifiers. */
 
 typedef enum {
-        LK_SSL_ERROR,
         LK_GSK_ERROR,
         LK_LDAP_ERROR,
         LK_CURL_VERSION,
@@ -41,6 +40,7 @@ typedef enum {
         LK_EASY_STRERROR,
         LK_SHARE_STRERROR,
         LK_MULTI_STRERROR,
+        LK_URL_STRERROR,
         LK_ZLIB_VERSION,
         LK_ZLIB_MSG,
         LK_LAST