1281 lines
28 KiB
C
1281 lines
28 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
*
|
|
* This software is licensed as described in the file COPYING, which
|
|
* you should have received as part of this distribution. The terms
|
|
* are also available at http://curl.haxx.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
/* CCSID API wrappers for OS/400. */
|
|
|
|
#include <iconv.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
|
|
#pragma enum(int)
|
|
|
|
#include "curl.h"
|
|
#include "mprintf.h"
|
|
#include "slist.h"
|
|
#include "urldata.h"
|
|
#include "url.h"
|
|
#include "getinfo.h"
|
|
#include "ccsidcurl.h"
|
|
|
|
#include "os400sys.h"
|
|
|
|
#ifndef SIZE_MAX
|
|
#define SIZE_MAX ((size_t) ~0) /* Is unsigned on OS/400. */
|
|
#endif
|
|
|
|
|
|
#define ASCII_CCSID 819 /* Use ISO-8859-1 as ASCII. */
|
|
#define NOCONV_CCSID 65535 /* No conversion. */
|
|
#define ICONV_ID_SIZE 32 /* Size of iconv_open() code identifier. */
|
|
#define ICONV_OPEN_ERROR(t) ((t).return_value == -1)
|
|
|
|
#define ALLOC_GRANULE 8 /* Alloc. granule for curl_formadd_ccsid(). */
|
|
|
|
|
|
static void
|
|
makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid)
|
|
|
|
{
|
|
/**
|
|
*** Convert a CCSID to the corresponding IBM iconv_open() character
|
|
*** code identifier.
|
|
*** This code is specific to the OS400 implementation of the iconv library.
|
|
*** CCSID 65535 (no conversion) is replaced by the ASCII CCSID.
|
|
*** CCSID 0 is interpreted by the OS400 as the job's CCSID.
|
|
**/
|
|
|
|
ccsid &= 0xFFFF;
|
|
|
|
if(ccsid == NOCONV_CCSID)
|
|
ccsid = ASCII_CCSID;
|
|
|
|
memset(buf, 0, ICONV_ID_SIZE);
|
|
curl_msprintf(buf, "IBMCCSID%05u0000000", ccsid);
|
|
}
|
|
|
|
|
|
static iconv_t
|
|
iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin,
|
|
unsigned int cstr)
|
|
|
|
{
|
|
char fromcode[ICONV_ID_SIZE];
|
|
char tocode[ICONV_ID_SIZE];
|
|
|
|
/**
|
|
*** Like iconv_open(), but character codes are given as CCSIDs.
|
|
*** If `cstr' is non-zero, conversion is set up to stop whenever a
|
|
*** null character is encountered.
|
|
*** See iconv_open() IBM description in "National Language Support API".
|
|
**/
|
|
|
|
makeOS400IconvCode(fromcode, ccsidin);
|
|
makeOS400IconvCode(tocode, ccsidout);
|
|
memset(tocode + 13, 0, sizeof tocode - 13); /* Dest. code id format. */
|
|
|
|
if(cstr)
|
|
fromcode[18] = '1'; /* Set null-terminator flag. */
|
|
|
|
return iconv_open(tocode, fromcode);
|
|
}
|
|
|
|
|
|
static int
|
|
convert(char * d, size_t dlen, int dccsid,
|
|
const char * s, int slen, int sccsid)
|
|
|
|
{
|
|
int i;
|
|
iconv_t cd;
|
|
size_t lslen;
|
|
|
|
/**
|
|
*** Convert `sccsid'-coded `slen'-data bytes at `s' into `dccsid'-coded
|
|
*** data stored in the `dlen'-byte buffer at `d'.
|
|
*** If `slen' < 0, source string is null-terminated.
|
|
*** CCSID 65535 (no conversion) is replaced by the ASCII CCSID.
|
|
*** Return the converted destination byte count, or -1 if error.
|
|
**/
|
|
|
|
if(sccsid == 65535)
|
|
sccsid = ASCII_CCSID;
|
|
|
|
if(dccsid == 65535)
|
|
dccsid = ASCII_CCSID;
|
|
|
|
if(sccsid == dccsid) {
|
|
lslen = slen >= 0? slen: strlen(s) + 1;
|
|
i = lslen < dlen? lslen: dlen;
|
|
|
|
if(s != d && i > 0)
|
|
memcpy(d, s, i);
|
|
|
|
return i;
|
|
}
|
|
|
|
if(slen < 0) {
|
|
lslen = 0;
|
|
cd = iconv_open_CCSID(dccsid, sccsid, 1);
|
|
}
|
|
else {
|
|
lslen = (size_t) slen;
|
|
cd = iconv_open_CCSID(dccsid, sccsid, 0);
|
|
}
|
|
|
|
if(ICONV_OPEN_ERROR(cd))
|
|
return -1;
|
|
|
|
i = dlen;
|
|
|
|
if((int) iconv(cd, (char * *) &s, &lslen, &d, &dlen) < 0)
|
|
i = -1;
|
|
else
|
|
i -= dlen;
|
|
|
|
iconv_close(cd);
|
|
return i;
|
|
}
|
|
|
|
|
|
static char *
|
|
dynconvert(int dccsid, const char * s, int slen, int sccsid)
|
|
|
|
{
|
|
char * d;
|
|
char * cp;
|
|
size_t dlen;
|
|
int l;
|
|
static const char nullbyte = 0;
|
|
|
|
/* Like convert, but the destination is allocated and returned. */
|
|
|
|
dlen = (size_t) (slen < 0? strlen(s): slen) + 1;
|
|
dlen *= MAX_CONV_EXPANSION; /* Allow some expansion. */
|
|
d = malloc(dlen);
|
|
|
|
if(!d)
|
|
return (char *) NULL;
|
|
|
|
l = convert(d, dlen, dccsid, s, slen, sccsid);
|
|
|
|
if(l < 0) {
|
|
free(d);
|
|
return (char *) NULL;
|
|
}
|
|
|
|
if(slen < 0) {
|
|
/* Need to null-terminate even when source length is given.
|
|
Since destination code size is unknown, use a conversion to generate
|
|
terminator. */
|
|
|
|
int l2 = convert(d + l, dlen - l, dccsid, &nullbyte, -1, ASCII_CCSID);
|
|
|
|
if(l2 < 0) {
|
|
free(d);
|
|
return (char *) NULL;
|
|
}
|
|
|
|
l += l2;
|
|
}
|
|
|
|
if((size_t) l < dlen) {
|
|
cp = realloc(d, l); /* Shorten to minimum needed. */
|
|
|
|
if(cp)
|
|
d = cp;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
static struct curl_slist *
|
|
slist_convert(int dccsid, struct curl_slist * from, int sccsid)
|
|
|
|
{
|
|
struct curl_slist * to = (struct curl_slist *) NULL;
|
|
|
|
for(; from; from = from->next) {
|
|
char * cp = dynconvert(dccsid, from->data, -1, sccsid);
|
|
|
|
if(!cp) {
|
|
curl_slist_free_all(to);
|
|
return (struct curl_slist *) NULL;
|
|
}
|
|
to = Curl_slist_append_nodup(to, cp);
|
|
}
|
|
return to;
|
|
}
|
|
|
|
|
|
char *
|
|
curl_version_ccsid(unsigned int ccsid)
|
|
|
|
{
|
|
int i;
|
|
char * aversion;
|
|
char * eversion;
|
|
|
|
aversion = curl_version();
|
|
|
|
if(!aversion)
|
|
return aversion;
|
|
|
|
i = strlen(aversion) + 1;
|
|
i *= MAX_CONV_EXPANSION;
|
|
|
|
if(!(eversion = Curl_thread_buffer(LK_CURL_VERSION, i)))
|
|
return (char *) NULL;
|
|
|
|
if(convert(eversion, i, ccsid, aversion, -1, ASCII_CCSID) < 0)
|
|
return (char *) NULL;
|
|
|
|
return eversion;
|
|
}
|
|
|
|
|
|
char *
|
|
curl_easy_escape_ccsid(CURL * handle, const char * string, int length,
|
|
unsigned int sccsid, unsigned int dccsid)
|
|
|
|
{
|
|
char * s;
|
|
char * d;
|
|
|
|
if(!string) {
|
|
errno = EINVAL;
|
|
return (char *) NULL;
|
|
}
|
|
|
|
s = dynconvert(ASCII_CCSID, string, length? length: -1, sccsid);
|
|
|
|
if(!s)
|
|
return (char *) NULL;
|
|
|
|
d = curl_easy_escape(handle, s, 0);
|
|
free(s);
|
|
|
|
if(!d)
|
|
return (char *) NULL;
|
|
|
|
s = dynconvert(dccsid, d, -1, ASCII_CCSID);
|
|
free(d);
|
|
return s;
|
|
}
|
|
|
|
|
|
char *
|
|
curl_easy_unescape_ccsid(CURL * handle, const char * string, int length,
|
|
int * outlength,
|
|
unsigned int sccsid, unsigned int dccsid)
|
|
|
|
{
|
|
char * s;
|
|
char * d;
|
|
|
|
if(!string) {
|
|
errno = EINVAL;
|
|
return (char *) NULL;
|
|
}
|
|
|
|
s = dynconvert(ASCII_CCSID, string, length? length: -1, sccsid);
|
|
|
|
if(!s)
|
|
return (char *) NULL;
|
|
|
|
d = curl_easy_unescape(handle, s, 0, outlength);
|
|
free(s);
|
|
|
|
if(!d)
|
|
return (char *) NULL;
|
|
|
|
s = dynconvert(dccsid, d, -1, ASCII_CCSID);
|
|
free(d);
|
|
|
|
if(s && outlength)
|
|
*outlength = strlen(s);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
struct curl_slist *
|
|
curl_slist_append_ccsid(struct curl_slist * list,
|
|
const char * data, unsigned int ccsid)
|
|
|
|
{
|
|
char * s;
|
|
|
|
s = (char *) NULL;
|
|
|
|
if(!data)
|
|
return curl_slist_append(list, data);
|
|
|
|
s = dynconvert(ASCII_CCSID, data, -1, ccsid);
|
|
|
|
if(!s)
|
|
return (struct curl_slist *) NULL;
|
|
|
|
list = curl_slist_append(list, s);
|
|
free(s);
|
|
return list;
|
|
}
|
|
|
|
|
|
time_t
|
|
curl_getdate_ccsid(const char * p, const time_t * unused, unsigned int ccsid)
|
|
|
|
{
|
|
char * s;
|
|
time_t t;
|
|
|
|
if(!p)
|
|
return curl_getdate(p, unused);
|
|
|
|
s = dynconvert(ASCII_CCSID, p, -1, ccsid);
|
|
|
|
if(!s)
|
|
return (time_t) -1;
|
|
|
|
t = curl_getdate(s, unused);
|
|
free(s);
|
|
return t;
|
|
}
|
|
|
|
|
|
static int
|
|
convert_version_info_string(const char * * stringp,
|
|
char * * bufp, int * left, unsigned int ccsid)
|
|
|
|
{
|
|
/* Helper for curl_version_info_ccsid(): convert a string if defined.
|
|
Result is stored in the `*left'-byte buffer at `*bufp'.
|
|
`*bufp' and `*left' are updated accordingly.
|
|
Return 0 if ok, else -1. */
|
|
|
|
if(*stringp) {
|
|
int l = convert(*bufp, *left, ccsid, *stringp, -1, ASCII_CCSID);
|
|
|
|
if(l <= 0)
|
|
return -1;
|
|
|
|
*stringp = *bufp;
|
|
*bufp += l;
|
|
*left -= l;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
curl_version_info_data *
|
|
curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid)
|
|
|
|
{
|
|
curl_version_info_data * p;
|
|
char * cp;
|
|
int n;
|
|
int nproto;
|
|
curl_version_info_data * id;
|
|
|
|
/* 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_FOURTH
|
|
#error curl_version_info_data structure has changed: upgrade this procedure.
|
|
#endif
|
|
|
|
/* If caller has been compiled with a new version, error. */
|
|
|
|
if(stamp > CURLVERSION_NOW)
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
p = curl_version_info(stamp);
|
|
|
|
if(!p)
|
|
return p;
|
|
|
|
/* Measure thread space needed. */
|
|
|
|
n = 0;
|
|
nproto = 0;
|
|
|
|
if(p->protocols) {
|
|
while(p->protocols[nproto])
|
|
n += strlen(p->protocols[nproto++]);
|
|
|
|
n += nproto++;
|
|
}
|
|
|
|
if(p->version)
|
|
n += strlen(p->version) + 1;
|
|
|
|
if(p->host)
|
|
n += strlen(p->host) + 1;
|
|
|
|
if(p->ssl_version)
|
|
n += strlen(p->ssl_version) + 1;
|
|
|
|
if(p->libz_version)
|
|
n += strlen(p->libz_version) + 1;
|
|
|
|
if(p->ares)
|
|
n += strlen(p->ares) + 1;
|
|
|
|
if(p->libidn)
|
|
n += strlen(p->libidn) + 1;
|
|
|
|
if(p->libssh_version)
|
|
n += strlen(p->libssh_version) + 1;
|
|
|
|
/* Allocate thread space. */
|
|
|
|
n *= MAX_CONV_EXPANSION;
|
|
|
|
if(nproto)
|
|
n += nproto * sizeof(const char *);
|
|
|
|
cp = Curl_thread_buffer(LK_VERSION_INFO_DATA, n);
|
|
id = (curl_version_info_data *) Curl_thread_buffer(LK_VERSION_INFO,
|
|
sizeof *id);
|
|
|
|
if(!id || !cp)
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
/* Copy data and convert strings. */
|
|
|
|
memcpy((char *) id, (char *) p, sizeof *p);
|
|
|
|
if(id->protocols) {
|
|
int i = nproto * sizeof id->protocols[0];
|
|
|
|
id->protocols = (const char * const *) cp;
|
|
memcpy(cp, (char *) p->protocols, i);
|
|
cp += i;
|
|
n -= i;
|
|
|
|
for(i = 0; id->protocols[i]; i++)
|
|
if(convert_version_info_string(((const char * *) id->protocols) + i,
|
|
&cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
}
|
|
|
|
if(convert_version_info_string(&id->version, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->host, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->ssl_version, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->libz_version, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->ares, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->libidn, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
if(convert_version_info_string(&id->libssh_version, &cp, &n, ccsid))
|
|
return (curl_version_info_data *) NULL;
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
if(!(buf = Curl_thread_buffer(LK_EASY_STRERROR, i)))
|
|
return (const char *) NULL;
|
|
|
|
if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
|
|
return (const char *) NULL;
|
|
|
|
return (const char *) buf;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
if(!(buf = Curl_thread_buffer(LK_SHARE_STRERROR, i)))
|
|
return (const char *) NULL;
|
|
|
|
if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
|
|
return (const char *) NULL;
|
|
|
|
return (const char *) buf;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
if(!(buf = Curl_thread_buffer(LK_MULTI_STRERROR, i)))
|
|
return (const char *) NULL;
|
|
|
|
if(convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
|
|
return (const char *) NULL;
|
|
|
|
return (const char *) buf;
|
|
}
|
|
|
|
|
|
void
|
|
curl_certinfo_free_all(struct curl_certinfo *info)
|
|
|
|
{
|
|
/* Free all memory used by certificate info. */
|
|
if(info) {
|
|
if(info->certinfo) {
|
|
int i;
|
|
|
|
for(i = 0; i < info->num_of_certs; i++)
|
|
curl_slist_free_all(info->certinfo[i]);
|
|
free((char *) info->certinfo);
|
|
}
|
|
free((char *) info);
|
|
}
|
|
}
|
|
|
|
|
|
CURLcode
|
|
curl_easy_getinfo_ccsid(CURL * curl, CURLINFO info, ...)
|
|
|
|
{
|
|
va_list arg;
|
|
void * paramp;
|
|
CURLcode ret;
|
|
unsigned int ccsid;
|
|
char * * cpp;
|
|
struct SessionHandle * data;
|
|
struct curl_slist * * slp;
|
|
struct curl_certinfo * cipf;
|
|
struct curl_certinfo * cipt;
|
|
|
|
/* WARNING: unlike curl_easy_get_info(), the strings returned by this
|
|
procedure have to be free'ed. */
|
|
|
|
data = (struct SessionHandle *) curl;
|
|
va_start(arg, info);
|
|
paramp = va_arg(arg, void *);
|
|
ret = Curl_getinfo(data, info, paramp);
|
|
|
|
if(ret == CURLE_OK)
|
|
switch ((int) info & CURLINFO_TYPEMASK) {
|
|
|
|
case CURLINFO_STRING:
|
|
ccsid = va_arg(arg, unsigned int);
|
|
cpp = (char * *) paramp;
|
|
|
|
if(*cpp) {
|
|
*cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID);
|
|
|
|
if(!*cpp)
|
|
ret = CURLE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
break;
|
|
|
|
case CURLINFO_SLIST:
|
|
ccsid = va_arg(arg, unsigned int);
|
|
switch (info) {
|
|
case CURLINFO_CERTINFO:
|
|
cipf = *(struct curl_certinfo * *) paramp;
|
|
if(cipf) {
|
|
if(!(cipt = (struct curl_certinfo *) malloc(sizeof *cipt)))
|
|
ret = CURLE_OUT_OF_MEMORY;
|
|
else {
|
|
cipt->certinfo = (struct curl_slist * *)
|
|
calloc(cipf->num_of_certs +
|
|
1, sizeof(struct curl_slist *));
|
|
if(!cipt->certinfo)
|
|
ret = CURLE_OUT_OF_MEMORY;
|
|
else {
|
|
int i;
|
|
|
|
cipt->num_of_certs = cipf->num_of_certs;
|
|
for(i = 0; i < cipf->num_of_certs; i++)
|
|
if(cipf->certinfo[i])
|
|
if(!(cipt->certinfo[i] = slist_convert(ccsid,
|
|
cipf->certinfo[i],
|
|
ASCII_CCSID))) {
|
|
ret = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ret != CURLE_OK) {
|
|
curl_certinfo_free_all(cipt);
|
|
cipt = (struct curl_certinfo *) NULL;
|
|
}
|
|
|
|
*(struct curl_certinfo * *) paramp = cipt;
|
|
}
|
|
|
|
break;
|
|
|
|
case CURLINFO_TLS_SESSION:
|
|
break;
|
|
|
|
default:
|
|
slp = (struct curl_slist * *) paramp;
|
|
if(*slp)
|
|
if(!(*slp = slist_convert(ccsid, *slp, ASCII_CCSID)))
|
|
ret = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
va_end(arg);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
Curl_is_formadd_string(CURLformoption option)
|
|
|
|
{
|
|
switch (option) {
|
|
|
|
case CURLFORM_FILENAME:
|
|
case CURLFORM_CONTENTTYPE:
|
|
case CURLFORM_BUFFER:
|
|
case CURLFORM_FILE:
|
|
case CURLFORM_FILECONTENT:
|
|
case CURLFORM_COPYCONTENTS:
|
|
case CURLFORM_COPYNAME:
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
Curl_formadd_release_local(struct curl_forms * forms, int nargs, int skip)
|
|
|
|
{
|
|
while(nargs--)
|
|
if(nargs != skip)
|
|
if(Curl_is_formadd_string(forms[nargs].option))
|
|
if(forms[nargs].value)
|
|
free((char *) forms[nargs].value);
|
|
|
|
free((char *) forms);
|
|
}
|
|
|
|
|
|
static int
|
|
Curl_formadd_convert(struct curl_forms * forms,
|
|
int formx, int lengthx, unsigned int ccsid)
|
|
|
|
{
|
|
int l;
|
|
char * cp;
|
|
char * cp2;
|
|
|
|
if(formx < 0 || !forms[formx].value)
|
|
return 0;
|
|
|
|
if(lengthx >= 0)
|
|
l = (int) forms[lengthx].value;
|
|
else
|
|
l = strlen(forms[formx].value) + 1;
|
|
|
|
cp = malloc(MAX_CONV_EXPANSION * l);
|
|
|
|
if(!cp)
|
|
return -1;
|
|
|
|
l = convert(cp, MAX_CONV_EXPANSION * l, ASCII_CCSID,
|
|
forms[formx].value, l, ccsid);
|
|
|
|
if(l < 0) {
|
|
free(cp);
|
|
return -1;
|
|
}
|
|
|
|
cp2 = realloc(cp, l); /* Shorten buffer to the string size. */
|
|
|
|
if(cp2)
|
|
cp = cp2;
|
|
|
|
forms[formx].value = cp;
|
|
|
|
if(lengthx >= 0)
|
|
forms[lengthx].value = (char *) l; /* Update length after conversion. */
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
CURLFORMcode
|
|
curl_formadd_ccsid(struct curl_httppost * * httppost,
|
|
struct curl_httppost * * last_post, ...)
|
|
|
|
{
|
|
va_list arg;
|
|
CURLformoption option;
|
|
CURLFORMcode result;
|
|
struct curl_forms * forms;
|
|
struct curl_forms * lforms;
|
|
struct curl_forms * tforms;
|
|
unsigned int lformlen;
|
|
const char * value;
|
|
unsigned int ccsid;
|
|
int nargs;
|
|
int namex;
|
|
int namelengthx;
|
|
int contentx;
|
|
int lengthx;
|
|
unsigned int contentccsid;
|
|
unsigned int nameccsid;
|
|
|
|
/* A single curl_formadd() call cannot be splitted in several calls to deal
|
|
with all parameters: the original parameters are thus copied to a local
|
|
curl_forms array and converted to ASCII when needed.
|
|
CURLFORM_PTRNAME is processed as if it were CURLFORM_COPYNAME.
|
|
CURLFORM_COPYNAME and CURLFORM_NAMELENGTH occurrence order in
|
|
parameters is not defined; for this reason, the actual conversion is
|
|
delayed to the end of parameter processing. The same applies to
|
|
CURLFORM_COPYCONTENTS/CURLFORM_CONTENTSLENGTH, but these may appear
|
|
several times in the parameter list; the problem resides here in knowing
|
|
which CURLFORM_CONTENTSLENGTH applies to which CURLFORM_COPYCONTENTS and
|
|
when we can be sure to have both info for conversion: end of parameter
|
|
list is such a point, but CURLFORM_CONTENTTYPE is also used here as a
|
|
natural separator between content data definitions; this seems to be
|
|
in accordance with FormAdd() behavior. */
|
|
|
|
/* Allocate the local curl_forms array. */
|
|
|
|
lformlen = ALLOC_GRANULE;
|
|
lforms = malloc(lformlen * sizeof *lforms);
|
|
|
|
if(!lforms)
|
|
return CURL_FORMADD_MEMORY;
|
|
|
|
/* Process the arguments, copying them into local array, latching conversion
|
|
indexes and converting when needed. */
|
|
|
|
result = CURL_FORMADD_OK;
|
|
nargs = 0;
|
|
contentx = -1;
|
|
lengthx = -1;
|
|
namex = -1;
|
|
namelengthx = -1;
|
|
forms = (struct curl_forms *) NULL;
|
|
va_start(arg, last_post);
|
|
|
|
for(;;) {
|
|
/* Make sure there is still room for an item in local array. */
|
|
|
|
if(nargs >= lformlen) {
|
|
lformlen += ALLOC_GRANULE;
|
|
tforms = realloc(lforms, lformlen * sizeof *lforms);
|
|
|
|
if(!tforms) {
|
|
result = CURL_FORMADD_MEMORY;
|
|
break;
|
|
}
|
|
|
|
lforms = tforms;
|
|
}
|
|
|
|
/* Get next option. */
|
|
|
|
if(forms) {
|
|
/* Get option from array. */
|
|
|
|
option = forms->option;
|
|
value = forms->value;
|
|
forms++;
|
|
}
|
|
else {
|
|
/* Get option from arguments. */
|
|
|
|
option = va_arg(arg, CURLformoption);
|
|
|
|
if(option == CURLFORM_END)
|
|
break;
|
|
}
|
|
|
|
/* Dispatch by option. */
|
|
|
|
switch (option) {
|
|
|
|
case CURLFORM_END:
|
|
forms = (struct curl_forms *) NULL; /* Leave array mode. */
|
|
continue;
|
|
|
|
case CURLFORM_ARRAY:
|
|
if(!forms) {
|
|
forms = va_arg(arg, struct curl_forms *);
|
|
continue;
|
|
}
|
|
|
|
result = CURL_FORMADD_ILLEGAL_ARRAY;
|
|
break;
|
|
|
|
case CURLFORM_COPYNAME:
|
|
option = CURLFORM_PTRNAME; /* Static for now. */
|
|
|
|
case CURLFORM_PTRNAME:
|
|
if(namex >= 0)
|
|
result = CURL_FORMADD_OPTION_TWICE;
|
|
|
|
namex = nargs;
|
|
|
|
if(!forms) {
|
|
value = va_arg(arg, char *);
|
|
nameccsid = (unsigned int) va_arg(arg, long);
|
|
}
|
|
else {
|
|
nameccsid = (unsigned int) forms->value;
|
|
forms++;
|
|
}
|
|
|
|
break;
|
|
|
|
case CURLFORM_COPYCONTENTS:
|
|
if(contentx >= 0)
|
|
result = CURL_FORMADD_OPTION_TWICE;
|
|
|
|
contentx = nargs;
|
|
|
|
if(!forms) {
|
|
value = va_arg(arg, char *);
|
|
contentccsid = (unsigned int) va_arg(arg, long);
|
|
}
|
|
else {
|
|
contentccsid = (unsigned int) forms->value;
|
|
forms++;
|
|
}
|
|
|
|
break;
|
|
|
|
case CURLFORM_PTRCONTENTS:
|
|
case CURLFORM_BUFFERPTR:
|
|
if(!forms)
|
|
value = va_arg(arg, char *); /* No conversion. */
|
|
|
|
break;
|
|
|
|
case CURLFORM_CONTENTSLENGTH:
|
|
lengthx = nargs;
|
|
|
|
if(!forms)
|
|
value = (char *) va_arg(arg, long);
|
|
|
|
break;
|
|
|
|
case CURLFORM_NAMELENGTH:
|
|
namelengthx = nargs;
|
|
|
|
if(!forms)
|
|
value = (char *) va_arg(arg, long);
|
|
|
|
break;
|
|
|
|
case CURLFORM_BUFFERLENGTH:
|
|
if(!forms)
|
|
value = (char *) va_arg(arg, long);
|
|
|
|
break;
|
|
|
|
case CURLFORM_CONTENTHEADER:
|
|
if(!forms)
|
|
value = (char *) va_arg(arg, struct curl_slist *);
|
|
|
|
break;
|
|
|
|
case CURLFORM_STREAM:
|
|
if(!forms)
|
|
value = (char *) va_arg(arg, void *);
|
|
|
|
break;
|
|
|
|
case CURLFORM_CONTENTTYPE:
|
|
/* If a previous content has been encountered, convert it now. */
|
|
|
|
if(Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0) {
|
|
result = CURL_FORMADD_MEMORY;
|
|
break;
|
|
}
|
|
|
|
contentx = -1;
|
|
lengthx = -1;
|
|
/* Fall into default. */
|
|
|
|
default:
|
|
/* Must be a convertible string. */
|
|
|
|
if(!Curl_is_formadd_string(option)) {
|
|
result = CURL_FORMADD_UNKNOWN_OPTION;
|
|
break;
|
|
}
|
|
|
|
if(!forms) {
|
|
value = va_arg(arg, char *);
|
|
ccsid = (unsigned int) va_arg(arg, long);
|
|
}
|
|
else {
|
|
ccsid = (unsigned int) forms->value;
|
|
forms++;
|
|
}
|
|
|
|
/* Do the conversion. */
|
|
|
|
lforms[nargs].value = value;
|
|
|
|
if(Curl_formadd_convert(lforms, nargs, -1, ccsid) < 0) {
|
|
result = CURL_FORMADD_MEMORY;
|
|
break;
|
|
}
|
|
|
|
value = lforms[nargs].value;
|
|
}
|
|
|
|
if(result != CURL_FORMADD_OK)
|
|
break;
|
|
|
|
lforms[nargs].value = value;
|
|
lforms[nargs++].option = option;
|
|
}
|
|
|
|
va_end(arg);
|
|
|
|
/* Convert the name and the last content, now that we know their lengths. */
|
|
|
|
if(result == CURL_FORMADD_OK && namex >= 0) {
|
|
if(Curl_formadd_convert(lforms, namex, namelengthx, nameccsid) < 0)
|
|
result = CURL_FORMADD_MEMORY;
|
|
else
|
|
lforms[namex].option = CURLFORM_COPYNAME; /* Force copy. */
|
|
}
|
|
|
|
if(result == CURL_FORMADD_OK) {
|
|
if(Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0)
|
|
result = CURL_FORMADD_MEMORY;
|
|
else
|
|
contentx = -1;
|
|
}
|
|
|
|
/* Do the formadd with our converted parameters. */
|
|
|
|
if(result == CURL_FORMADD_OK) {
|
|
lforms[nargs].option = CURLFORM_END;
|
|
result = curl_formadd(httppost, last_post,
|
|
CURLFORM_ARRAY, lforms, CURLFORM_END);
|
|
}
|
|
|
|
/* Terminate. */
|
|
|
|
Curl_formadd_release_local(lforms, nargs, contentx);
|
|
return result;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
curl_formget_callback append;
|
|
void * arg;
|
|
unsigned int ccsid;
|
|
} cfcdata;
|
|
|
|
|
|
static size_t
|
|
Curl_formget_callback_ccsid(void * arg, const char * buf, size_t len)
|
|
|
|
{
|
|
cfcdata * p;
|
|
char * b;
|
|
int l;
|
|
size_t ret;
|
|
|
|
p = (cfcdata *) arg;
|
|
|
|
if((long) len <= 0)
|
|
return (*p->append)(p->arg, buf, len);
|
|
|
|
b = malloc(MAX_CONV_EXPANSION * len);
|
|
|
|
if(!b)
|
|
return (size_t) -1;
|
|
|
|
l = convert(b, MAX_CONV_EXPANSION * len, p->ccsid, buf, len, ASCII_CCSID);
|
|
|
|
if(l < 0) {
|
|
free(b);
|
|
return (size_t) -1;
|
|
}
|
|
|
|
ret = (*p->append)(p->arg, b, l);
|
|
free(b);
|
|
return ret == l? len: -1;
|
|
}
|
|
|
|
|
|
int
|
|
curl_formget_ccsid(struct curl_httppost * form, void * arg,
|
|
curl_formget_callback append, unsigned int ccsid)
|
|
|
|
{
|
|
cfcdata lcfc;
|
|
|
|
lcfc.append = append;
|
|
lcfc.arg = arg;
|
|
lcfc.ccsid = ccsid;
|
|
return curl_formget(form, (void *) &lcfc, Curl_formget_callback_ccsid);
|
|
}
|
|
|
|
|
|
CURLcode
|
|
curl_easy_setopt_ccsid(CURL * curl, CURLoption tag, ...)
|
|
|
|
{
|
|
CURLcode result;
|
|
va_list arg;
|
|
struct SessionHandle * data;
|
|
char * s;
|
|
char * cp;
|
|
unsigned int ccsid;
|
|
curl_off_t pfsize;
|
|
static char testwarn = 1;
|
|
|
|
/* Warns if this procedure has not been updated when the dupstring enum
|
|
changes.
|
|
We (try to) do it only once: there is no need to issue several times
|
|
the same message; but since threadsafeness is not handled here,
|
|
this may occur (and we don't care!). */
|
|
|
|
if(testwarn) {
|
|
testwarn = 0;
|
|
|
|
if((int) STRING_LASTZEROTERMINATED != (int) STRING_BEARER + 1 ||
|
|
(int) STRING_LAST != (int) STRING_COPYPOSTFIELDS + 1)
|
|
curl_mfprintf(stderr,
|
|
"*** WARNING: curl_easy_setopt_ccsid() should be reworked ***\n");
|
|
}
|
|
|
|
data = (struct SessionHandle *) curl;
|
|
va_start(arg, tag);
|
|
|
|
switch (tag) {
|
|
|
|
case CURLOPT_CAINFO:
|
|
case CURLOPT_CAPATH:
|
|
case CURLOPT_COOKIE:
|
|
case CURLOPT_COOKIEFILE:
|
|
case CURLOPT_COOKIEJAR:
|
|
case CURLOPT_COOKIELIST:
|
|
case CURLOPT_CRLFILE:
|
|
case CURLOPT_CUSTOMREQUEST:
|
|
case CURLOPT_DNS_SERVERS:
|
|
case CURLOPT_EGDSOCKET:
|
|
case CURLOPT_ENCODING:
|
|
case CURLOPT_FTP_ACCOUNT:
|
|
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
|
|
case CURLOPT_FTPPORT:
|
|
case CURLOPT_INTERFACE:
|
|
case CURLOPT_ISSUERCERT:
|
|
case CURLOPT_KEYPASSWD:
|
|
case CURLOPT_KRBLEVEL:
|
|
case CURLOPT_LOGIN_OPTIONS:
|
|
case CURLOPT_MAIL_FROM:
|
|
case CURLOPT_MAIL_AUTH:
|
|
case CURLOPT_NETRC_FILE:
|
|
case CURLOPT_NOPROXY:
|
|
case CURLOPT_PASSWORD:
|
|
case CURLOPT_PINNEDPUBLICKEY:
|
|
case CURLOPT_PROXY:
|
|
case CURLOPT_PROXYPASSWORD:
|
|
case CURLOPT_PROXYUSERNAME:
|
|
case CURLOPT_PROXYUSERPWD:
|
|
case CURLOPT_RANDOM_FILE:
|
|
case CURLOPT_RANGE:
|
|
case CURLOPT_REFERER:
|
|
case CURLOPT_RTSP_SESSION_ID:
|
|
case CURLOPT_RTSP_STREAM_URI:
|
|
case CURLOPT_RTSP_TRANSPORT:
|
|
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
|
|
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
|
|
case CURLOPT_SSH_KNOWNHOSTS:
|
|
case CURLOPT_SSH_PRIVATE_KEYFILE:
|
|
case CURLOPT_SSH_PUBLIC_KEYFILE:
|
|
case CURLOPT_SSLCERT:
|
|
case CURLOPT_SSLCERTTYPE:
|
|
case CURLOPT_SSL_CIPHER_LIST:
|
|
case CURLOPT_SSLENGINE:
|
|
case CURLOPT_SSLKEY:
|
|
case CURLOPT_SSLKEYTYPE:
|
|
case CURLOPT_TLSAUTH_PASSWORD:
|
|
case CURLOPT_TLSAUTH_TYPE:
|
|
case CURLOPT_TLSAUTH_USERNAME:
|
|
case CURLOPT_URL:
|
|
case CURLOPT_USERAGENT:
|
|
case CURLOPT_USERNAME:
|
|
case CURLOPT_USERPWD:
|
|
case CURLOPT_XOAUTH2_BEARER:
|
|
s = va_arg(arg, char *);
|
|
ccsid = va_arg(arg, unsigned int);
|
|
|
|
if(s) {
|
|
s = dynconvert(ASCII_CCSID, s, -1, ccsid);
|
|
|
|
if(!s) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = curl_easy_setopt(curl, tag, s);
|
|
|
|
if(s)
|
|
free(s);
|
|
|
|
break;
|
|
|
|
case CURLOPT_COPYPOSTFIELDS:
|
|
/* Special case: byte count may have been given by CURLOPT_POSTFIELDSIZE
|
|
prior to this call. In this case, convert the given byte count and
|
|
replace the length according to the conversion result. */
|
|
s = va_arg(arg, char *);
|
|
ccsid = va_arg(arg, unsigned int);
|
|
|
|
pfsize = data->set.postfieldsize;
|
|
|
|
if(!s || !pfsize || ccsid == NOCONV_CCSID || ccsid == ASCII_CCSID) {
|
|
result = curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, s);
|
|
break;
|
|
}
|
|
|
|
if(pfsize == -1) {
|
|
/* Data is null-terminated. */
|
|
s = dynconvert(ASCII_CCSID, s, -1, ccsid);
|
|
|
|
if(!s) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
/* Data length specified. */
|
|
size_t len;
|
|
|
|
if(pfsize < 0 || pfsize > SIZE_MAX) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
|
|
len = pfsize;
|
|
pfsize = 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, s, len, ccsid);
|
|
|
|
if(pfsize < 0) {
|
|
free(cp);
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
|
|
data->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. */
|
|
break;
|
|
|
|
case CURLOPT_ERRORBUFFER: /* This is an output buffer. */
|
|
default:
|
|
result = Curl_setopt(data, tag, arg);
|
|
break;
|
|
}
|
|
|
|
va_end(arg);
|
|
return result;
|
|
}
|
|
|
|
|
|
char *
|
|
curl_form_long_value(long value)
|
|
|
|
{
|
|
/* ILE/RPG cannot cast an integer to a pointer. This procedure does it. */
|
|
|
|
return (char *) value;
|
|
}
|