/*
 *  Rexx/CURL
 *  Copyright (C) 2001-2005 Mark Hessling <M.Hessling@qut.edu.au>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * Error handling:
 *                              error_rexxcurl       error_curl
 *
 * ------------------------------------------------------------
 * wrong # params           ******* syntax error **********
 * internal error                   set                 N/A
 * cURL runtime errors              -1                  set
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "rexxcurl.h"

char *RxPackageName = "rexxcurl";

#define INTERR_CURL_ERROR            1
#define INTERR_CURL_ERROR_STRING     "Error from cURL"
#define INTERR_INVALID_NUMBER        2
#define INTERR_INVALID_NUMBER_STRING "Invalid Number"
#define INTERR_INVALID_OPTION        3
#define INTERR_INVALID_OPTION_STRING "Invalid Option"
#define INTERR_NO_MEMORY             4
#define INTERR_NO_MEMORY_STRING      "Out of memory"
#define INTERR_INVALID_HANDLE        5
#define INTERR_INVALID_HANDLE_STRING "Invalid cURL handle"
#define INTERR_INVALID_FILE          6
#define INTERR_INVALID_FILE_STRING   "Invalid filename"
#define INTERR_INVALID_BOOL          7
#define INTERR_INVALID_BOOL_STRING   "Invalid boolean"
#define INTERR_INVALID_STEM          8
#define INTERR_INVALID_STEM_STRING   "Expecting a stem as parameter"
#define INTERR_INVALID_VARIABLE      9
#define INTERR_INVALID_VARIABLE_STRING "Invalid variable:"
#define INTERR_READONLY_VARIABLE     10
#define INTERR_READONLY_VARIABLE_STRING "Cannot set readonly variable"
#define INTERR_TOO_FEW_ARGS          11
#define INTERR_TOO_FEW_ARGS_STRING   "Too few arguments supplied"
#define INTERR_MANDATORY_FIELD       12
#define INTERR_MANDATORY_FIELD_STRING   "Field must be specified"

#define RXCURLOPT_STRING        1  /* string */
#define RXCURLOPT_LIST          2  /* stem for slist */
#define RXCURLOPT_LONG          3  /* 32bit number */
#define RXCURLOPT_OUTFILE       4  /* output file */
#define RXCURLOPT_INFILE        5  /* intput file */
#define RXCURLOPT_BOOL          6  /* bool */
#define RXCURLOPT_POLICY        7  /* policy */
#define RXCURLOPT_POST_DATA     8  /* stem for httppostdata */
#define RXCURLOPT_POST_FIELDS   9  /* stem for httppostfields */
#define RXCURLOPT_OUTSTEM      10  /* stem for outstem */
#define RXCURLOPT_HEADERSTEM   11  /* stem for headerstem */
#define RXCURLOPT_PROXYTYPE    12  /* proxytype */
#define RXCURLOPT_HTTP_VERSION 13  /* http_version */
#define RXCURLOPT_NETRC        14  /* netrc */
#define RXCURLOPT_TIMECOND     15  /* timecondition */
#define RXCURLOPT_IPRESOLVE    16  /* ipresolve */
#define RXCURLOPT_BITMAP       17  /* generic bitmap */
#define RXCURLOPT_BITMAP_AUTH  18  /* authorisation bitmap */
#define RXCURLOPT_LONGLONG     19  /* 64bit number */
#define RXCURLOPT_CALLBACK     20  /* libcurl callback */
#define RXCURLOPT_FTPSSL       21  /* ftp ssl */
#define RXCURLOPT_FTPSSLAUTH   22  /* ftp ssl auth */
#define RXCURLOPT_POST_FORM    23  /* option for processing after curl_formadd() calls */

#define RXCURLINFO_STRING       1  /* string */
#define RXCURLINFO_LONG         2  /* number */
#define RXCURLINFO_DOUBLE       3  /* double */
#define RXCURLINFO_LIST         4  /* stem for slist */

static char *curl_errors[] =
{
   "OK",                      /* 0 */
   "UNSUPPORTED_PROTOCOL",    /* 1 */
   "FAILED_INIT",             /* 2 */
   "URL_MALFORMAT",           /* 3 */
   "URL_MALFORMAT_USER",      /* 4 */
   "COULDNT_RESOLVE_PROXY",   /* 5 */
   "COULDNT_RESOLVE_HOST",    /* 6 */
   "COULDNT_CONNECT",         /* 7 */
   "FTP_WEIRD_SERVER_REPLY",  /* 8 */
   "FTP_ACCESS_DENIED",       /* 9 */
   "FTP_USER_PASSWORD_INCORRECT", /* 10 */
   "FTP_WEIRD_PASS_REPLY",    /* 11 */
   "FTP_WEIRD_USER_REPLY",    /* 12 */
   "FTP_WEIRD_PASV_REPLY",    /* 13 */
   "FTP_WEIRD_227_FORMAT",    /* 14 */
   "FTP_CANT_GET_HOST",       /* 15 */
   "FTP_CANT_RECONNECT",      /* 16 */
   "FTP_COULDNT_SET_BINARY",  /* 17 */
   "PARTIAL_FILE",            /* 18 */
   "FTP_COULDNT_RETR_FILE",   /* 19 */
   "FTP_WRITE_ERROR",         /* 20 */
   "FTP_QUOTE_ERROR",         /* 21 */
   "HTTP_NOT_FOUND",          /* 22 */
   "WRITE_ERROR",             /* 23 */
   "MALFORMAT_USER",          /* 24 - user name is illegally specified */
   "FTP_COULDNT_STOR_FILE",   /* 25 - failed FTP upload */
   "READ_ERROR",              /* 26 - could open/read from file */
   "OUT_OF_MEMORY",           /* 27 */
   "OPERATION_TIMEOUTED",     /* 28 - the timeout time was reached */
   "FTP_COULDNT_SET_ASCII",   /* 29 - TYPE A failed */
   "FTP_PORT_FAILED",         /* 30 - FTP PORT operation failed */
   "FTP_COULDNT_USE_REST",    /* 31 - the REST command failed */
   "FTP_COULDNT_GET_SIZE",    /* 32 - the SIZE command failed */
   "HTTP_RANGE_ERROR",        /* 33 - RANGE "command" didn"'t work */
   "HTTP_POST_ERROR",         /* 34 */
   "SSL_CONNECT_ERROR",       /* 35 - wrong when connecting with SSL */
   "FTP_BAD_DOWNLOAD_RESUME", /* 36 - couldn"'t resume download */
   "FILE_COULDNT_READ_FILE",  /* 37 */
   "LDAP_CANNOT_BIND",        /* 38 */
   "LDAP_SEARCH_FAILED",      /* 39 */
   "LIBRARY_NOT_FOUND",       /* 40 */
   "FUNCTION_NOT_FOUND",      /* 41 */
   "ABORTED_BY_CALLBACK",     /* 42 */
   "BAD_FUNCTION_ARGUMENT",   /* 43 */
   "BAD_CALLING_ORDER",       /* 44 */
   "HTTP_PORT_FAILED",        /* 45 - HTTP Interface operation failed */
   "BAD_PASSWORD_ENTERED",    /* 46 - my_getpass() returns fail */
   "TOO_MANY_REDIRECTS ",     /* 47 - catch endless re-direct loops */
   "UNKNOWN_TELNET_OPTION",   /* 48 - User specified an unknown option */
   "TELNET_OPTION_SYNTAX ",   /* 49 - Malformed telnet option */
   "OBSOLETE",                /* 50 - removed after 7.7.3 */
   "SSL_PEER_CERTIFICATE",    /* 51 - peer"'s certificate wasn't ok */
   "GOT_NOTHING",             /* 52 - when this is a specific error */
   "SSL_ENGINE_NOTFOUND",     /* 53 - SSL crypto engine not found */
   "SSL_ENGINE_SETFAILED",    /* 54 - can not set SSL crypto engine as default */
   "SEND_ERROR",              /* 55 - failed sending network data */
   "RECV_ERROR",              /* 56 - failure in receiving network data */
   "SHARE_IN_USE",            /* 57 - share is in use */
   "SSL_CERTPROBLEM",         /* 58 - problem with the local certificate */
   "SSL_CIPHER",              /* 59 - couldn't use specified cipher */
   "SSL_CACERT",              /* 60 - problem with the CA cert (path?) */
   "BAD_CONTENT_ENCODING",    /* 61 - Unrecognized transfer encoding */
   "LDAP_INVALID_URL",        /* 62 - Invalid LDAP URL */
   "FILESIZE_EXCEEDED",       /* 63 - Maximum file size exceeded */
   "FTP_SSL_FAILED",          /* 64 - Requested FTP SSL level failed */
   "SEND_FAIL_REWIND",        /* 65 - When doing a send operation curl had to rewind the data to retransmit, but the rewinding operation failed */
   "SSL_ENGINE_INITFAILED",   /* 66 - Initiating the SSL Engine failed */
   "LOGIN_DENIED",            /* 67 - The remote server denied curl to login (Added in 7.13.1) */
};
static char *curl_formadd_errors[] =
{
   "OK",                  /* 0 */
   "MEMORY",              /* 1 */
   "OPTION_TWICE",        /* 2 */
   "NULL",                /* 3 */
   "UNKNOWN_OPTION",      /* 4 */
   "INCOMPLETE",          /* 5 */
   "ILLEGAL_ARRAY",       /* 6 */
   "DISABLED",            /* 7 */
};

typedef struct
{
   char *name;
   int  number;
   int optiontype; /* things like STEM, STRING, INT */
} curl_options;

static curl_options RexxCurlOptions[] =
{
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "BUFFERSIZE"      ,CURLOPT_BUFFERSIZE      ,RXCURLOPT_LONG        },
#endif
   { "CAINFO"          ,CURLOPT_CAINFO          ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "CAPATH"          ,CURLOPT_CAPATH          ,RXCURLOPT_STRING      },
#endif
   { "CLOSEPOLICY"     ,CURLOPT_CLOSEPOLICY     ,RXCURLOPT_POLICY      },
   { "CONNECTTIMEOUT"  ,CURLOPT_CONNECTTIMEOUT  ,RXCURLOPT_LONG        },
   { "COOKIE"          ,CURLOPT_COOKIE          ,RXCURLOPT_STRING      },
   { "COOKIEFILE"      ,CURLOPT_COOKIEFILE      ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070900
   { "COOKIEJAR"       ,CURLOPT_COOKIEJAR       ,RXCURLOPT_STRING      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070e01
   { "COOKIELIST"      ,CURLOPT_COOKIELIST      ,RXCURLOPT_STRING      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "COOKIESESSION"   ,CURLOPT_COOKIESESSION   ,RXCURLOPT_BOOL        },
#endif
   { "CRLF"            ,CURLOPT_CRLF            ,RXCURLOPT_BOOL        },
   { "CUSTOMREQUEST"   ,CURLOPT_CUSTOMREQUEST   ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "DNSCACHETIMEOUT" ,CURLOPT_DNS_CACHE_TIMEOUT,RXCURLOPT_LONG       },
   { "DNSUSEGLOBALCACHE",CURLOPT_DNS_USE_GLOBAL_CACHE,RXCURLOPT_BOOL   },
#endif
   { "EGDSOCKET"       ,CURLOPT_EGDSOCKET       ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "ENCODING"        ,CURLOPT_ENCODING        ,RXCURLOPT_STRING      },
#endif
   { "ERRFILE"         ,CURLOPT_STDERR          ,RXCURLOPT_OUTFILE     },
   { "FAILONERROR"     ,CURLOPT_FAILONERROR     ,RXCURLOPT_BOOL        },
   { "FILETIME"        ,CURLOPT_FILETIME        ,RXCURLOPT_BOOL        },
   { "FOLLOWLOCATION"  ,CURLOPT_FOLLOWLOCATION  ,RXCURLOPT_BOOL        },
   { "FORBIDREUSE"     ,CURLOPT_FORBID_REUSE    ,RXCURLOPT_BOOL        },
   { "FRESHCONNECT"    ,CURLOPT_FRESH_CONNECT   ,RXCURLOPT_BOOL        },
   { "FTPAPPEND"       ,CURLOPT_FTPAPPEND       ,RXCURLOPT_BOOL        },
   { "FTPCMDS"         ,CURLOPT_QUOTE           ,RXCURLOPT_LIST        },
   { "FTPCMDSAFTER"    ,CURLOPT_POSTQUOTE       ,RXCURLOPT_LIST        },
   { "FTPCMDSBEFORE"   ,CURLOPT_PREQUOTE        ,RXCURLOPT_LIST        },
#if LIBCURL_VERSION_NUM >= 0x070a07
   { "FTPCREATEMISSINGDIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, RXCURLOPT_BOOL },
#endif
   { "FTPCRLF"         ,CURLOPT_CRLF            ,RXCURLOPT_BOOL        },
   { "FTPLISTONLY"     ,CURLOPT_FTPLISTONLY     ,RXCURLOPT_BOOL        },
   { "FTPPORT"         ,CURLOPT_FTPPORT         ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "FTPRESPONSETIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT, RXCURLOPT_LONG},
#endif
#if LIBCURL_VERSION_NUM >= 0x070e00
   { "FTPSKIPPASVIP"   ,CURLOPT_FTP_SKIP_PASV_IP,RXCURLOPT_BOOL        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070b00
   { "FTPSSL"          ,CURLOPT_FTP_SSL         ,RXCURLOPT_FTPSSL      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070c02
   { "FTPSSLAUTH"      ,CURLOPT_FTPSSLAUTH      ,RXCURLOPT_FTPSSLAUTH  },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a05
   { "FTPUSEEPRT"      ,CURLOPT_FTP_USE_EPRT    ,RXCURLOPT_BOOL        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "FTPUSEEPSV"      ,CURLOPT_FTP_USE_EPSV    ,RXCURLOPT_BOOL        },
#endif
   { "HEADER"          ,CURLOPT_HEADER          ,RXCURLOPT_BOOL        },
   { "HEADERFILE"      ,CURLOPT_WRITEHEADER     ,RXCURLOPT_OUTFILE     },
   { "HEADERSTEM"      ,CURLOPT_WRITEHEADER     ,RXCURLOPT_HEADERSTEM  },
#if LIBCURL_VERSION_NUM >= 0x070a03
   { "HTTP200ALIASES"  ,CURLOPT_HTTP200ALIASES  ,RXCURLOPT_LIST        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a06
   { "HTTPAUTH"        ,CURLOPT_HTTPAUTH        ,RXCURLOPT_BITMAP_AUTH },
#endif
#if LIBCURL_VERSION_NUM >= 0x070801
   { "HTTPGET"         ,CURLOPT_HTTPGET         ,RXCURLOPT_BOOL        },
#endif
   { "HTTPHEADER"      ,CURLOPT_HTTPHEADER      ,RXCURLOPT_LIST        },
   { "HTTPPOST"        ,CURLOPT_POST            ,RXCURLOPT_BOOL        },
   { "HTTPPOSTDATA"    ,CURLOPT_HTTPPOST        ,RXCURLOPT_POST_DATA   },
   { "HTTPPOSTFIELDS"  ,CURLOPT_POSTFIELDS      ,RXCURLOPT_POST_FIELDS },
   { "HTTPPOSTFORM"    ,CURLOPT_HTTPPOST        ,RXCURLOPT_POST_FORM   },
   { "HTTPPROXYTUNNEL" ,CURLOPT_HTTPPROXYTUNNEL ,RXCURLOPT_BOOL        },
   { "HTTPPUT"         ,CURLOPT_PUT             ,RXCURLOPT_BOOL        },
   { "HTTPVERSION"     ,CURLOPT_HTTP_VERSION    ,RXCURLOPT_HTTP_VERSION},
#if LIBCURL_VERSION_NUM >= 0x070e01
   { "IGNORECONTENTLENGTH",CURLOPT_IGNORE_CONTENT_LENGTH,RXCURLOPT_LONG},
#endif
   { "INFILE"          ,CURLOPT_INFILE          ,RXCURLOPT_INFILE      },
   { "INTERFACE"       ,CURLOPT_INTERFACE       ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "IPRESOLVE"       ,CURLOPT_IPRESOLVE       ,RXCURLOPT_IPRESOLVE   },
#endif
   { "KRB4LEVEL"       ,CURLOPT_KRB4LEVEL       ,RXCURLOPT_STRING      },
   { "LOWSPEEDLIMIT"   ,CURLOPT_LOW_SPEED_LIMIT ,RXCURLOPT_LONG        },
   { "LOWSPEEDTIME"    ,CURLOPT_LOW_SPEED_TIME  ,RXCURLOPT_LONG        },
   { "MAXCONNECTS"     ,CURLOPT_MAXCONNECTS     ,RXCURLOPT_LONG        },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "MAXFILESIZE"     ,CURLOPT_MAXFILESIZE     ,RXCURLOPT_LONGLONG    },
#endif
   { "MAXREDIRS"       ,CURLOPT_MAXREDIRS       ,RXCURLOPT_LONG        },
   { "NETRC"           ,CURLOPT_NETRC           ,RXCURLOPT_NETRC       },
#if LIBCURL_VERSION_NUM >= 0x070a09
   { "NETRC_FILE"      ,CURLOPT_NETRC_FILE      ,RXCURLOPT_INFILE      },
#endif
   { "NOBODY"          ,CURLOPT_NOBODY          ,RXCURLOPT_BOOL        },
#if LIBCURL_VERSION_NUM >= 0x070900
   { "NOPROGRESS"      ,CURLOPT_NOPROGRESS      ,RXCURLOPT_BOOL        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "NOSIGNAL"        ,CURLOPT_NOSIGNAL        ,RXCURLOPT_BOOL        },
#endif
   { "OUTFILE"         ,CURLOPT_FILE            ,RXCURLOPT_OUTFILE     },
   { "OUTSTEM"         ,CURLOPT_FILE            ,RXCURLOPT_OUTSTEM     },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "PREQUOTE"        ,CURLOPT_PREQUOTE        ,RXCURLOPT_LIST        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a03
   { "PRIVATE"         ,CURLOPT_PRIVATE         ,RXCURLOPT_STRING      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "POSTQUOTE"       ,CURLOPT_POSTQUOTE       ,RXCURLOPT_LIST        },
#endif
#if defined( REXXCALLBACK )
   { "PROGRESSFUNCTION",CURLOPT_PROGRESSFUNCTION,RXCURLOPT_CALLBACK    },
#endif
   { "PROXY"           ,CURLOPT_PROXY           ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a07
   { "PROXYAUTH"       ,CURLOPT_PROXYAUTH       ,RXCURLOPT_BITMAP_AUTH },
#endif
#if LIBCURL_VERSION_NUM >= 0x070900
   { "PROXYPORT"       ,CURLOPT_PROXYPORT       ,RXCURLOPT_LONG        },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "PROXYTYPE"       ,CURLOPT_PROXYTYPE       ,RXCURLOPT_PROXYTYPE   },
#endif
   { "PROXYUSERPWD"    ,CURLOPT_PROXYUSERPWD    ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "QUOTE"           ,CURLOPT_QUOTE           ,RXCURLOPT_LIST        },
#endif
   { "RANDOMFILE"      ,CURLOPT_RANDOM_FILE     ,RXCURLOPT_STRING      },
   { "RANGE"           ,CURLOPT_RANGE           ,RXCURLOPT_STRING      },
   { "REFERER"         ,CURLOPT_REFERER         ,RXCURLOPT_STRING      },
   { "RESUMEFROM"      ,CURLOPT_RESUME_FROM     ,RXCURLOPT_LONGLONG    },
#if LIBCURL_VERSION_NUM >= 0x070d00
   { "SOURCEPOSTQUOTE" ,CURLOPT_SOURCE_POSTQUOTE,RXCURLOPT_LIST        },
   { "SOURCEPREQUOTE"  ,CURLOPT_SOURCE_PREQUOTE ,RXCURLOPT_LIST        },
   { "SOURCEQUOTE"     ,CURLOPT_SOURCE_QUOTE    ,RXCURLOPT_LIST        },
   { "SOURCEURL"       ,CURLOPT_SOURCE_URL      ,RXCURLOPT_STRING      },
   { "SOURCEUSERPWD"   ,CURLOPT_SOURCE_USERPWD  ,RXCURLOPT_STRING      },
#endif
   { "SSLCERT"         ,CURLOPT_SSLCERT         ,RXCURLOPT_STRING      },
   { "SSLCERTPASSWD"   ,CURLOPT_SSLCERTPASSWD   ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "SSLCERTTYPE"     ,CURLOPT_SSLCERTTYPE     ,RXCURLOPT_STRING      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070900
   { "SSLCIPHERLIST"   ,CURLOPT_SSL_CIPHER_LIST ,RXCURLOPT_STRING      },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "SSLENGINE"       ,CURLOPT_SSLENGINE       ,RXCURLOPT_STRING      },
   { "SSLENGINEDEFAULT",CURLOPT_SSLENGINE_DEFAULT,RXCURLOPT_STRING     },
   { "SSLKEY"          ,CURLOPT_SSLKEY          ,RXCURLOPT_STRING      },
   { "SSLKEYPASSWD"    ,CURLOPT_SSLKEYPASSWD    ,RXCURLOPT_STRING      },
   { "SSLKEYTYPE"      ,CURLOPT_SSLKEYTYPE      ,RXCURLOPT_STRING      },
#endif
   { "SSLPEERCERT"     ,CURLOPT_CAINFO          ,RXCURLOPT_STRING      },
#if LIBCURL_VERSION_NUM >= 0x070801
   { "SSLVERIFYHOST"   ,CURLOPT_SSL_VERIFYHOST  ,RXCURLOPT_LONG        },
#endif
   { "SSLVERIFYPEER"   ,CURLOPT_SSL_VERIFYPEER  ,RXCURLOPT_BOOL        },
   { "SSLVERSION"      ,CURLOPT_SSLVERSION      ,RXCURLOPT_LONG        },
#if LIBCURL_VERSION_NUM >= 0x070d00
   { "TCPNODELAY"       ,CURLOPT_TCP_NODELAY    ,RXCURLOPT_BOOL        },
#endif
   { "TIMECONDITION"   ,CURLOPT_TIMECONDITION   ,RXCURLOPT_TIMECOND    },
   { "TIMEOUT"         ,CURLOPT_TIMEOUT         ,RXCURLOPT_LONG        },
   { "TIMEVALUE"       ,CURLOPT_TIMEVALUE       ,RXCURLOPT_LONG        },
   { "TRANSFERTEXT"    ,CURLOPT_TRANSFERTEXT    ,RXCURLOPT_BOOL        },
#if LIBCURL_VERSION_NUM >= 0x070908
   { "UNRESTRICTEDAUTH",CURLOPT_UNRESTRICTED_AUTH  ,RXCURLOPT_BOOL     },
#endif
   { "UPLOAD"          ,CURLOPT_UPLOAD          ,RXCURLOPT_BOOL        },
   { "URL"             ,CURLOPT_URL             ,RXCURLOPT_STRING      },
   { "USERAGENT"       ,CURLOPT_USERAGENT       ,RXCURLOPT_STRING      },
   { "USERPWD"         ,CURLOPT_USERPWD         ,RXCURLOPT_STRING      },
   { "VERBOSE"         ,CURLOPT_VERBOSE         ,RXCURLOPT_BOOL        },
   { NULL              ,0                       ,0                     }
};

static curl_options RexxCurlGetinfos[] =
{
   { "CONNECT_TIME"           ,CURLINFO_CONNECT_TIME           ,RXCURLINFO_DOUBLE},
   { "CONTENT_LENGTH_DOWNLOAD",CURLINFO_CONTENT_LENGTH_DOWNLOAD,RXCURLINFO_DOUBLE},
   { "CONTENT_LENGTH_UPLOAD"  ,CURLINFO_CONTENT_LENGTH_UPLOAD  ,RXCURLINFO_DOUBLE},
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "CONTENT_TYPE"           ,CURLINFO_CONTENT_TYPE           ,RXCURLINFO_STRING},
#endif
#if LIBCURL_VERSION_NUM >= 0x070e01
   { "COOKIELIST"             ,CURLINFO_COOKIELIST             ,RXCURLINFO_LIST  },
#endif
   { "EFFECTIVE_URL"          ,CURLINFO_EFFECTIVE_URL          ,RXCURLINFO_STRING},
   { "FILE_TIME"              ,CURLINFO_FILETIME               ,RXCURLINFO_LONG  },
   { "HEADER_SIZE"            ,CURLINFO_HEADER_SIZE            ,RXCURLINFO_LONG  },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "HTTPAUTH_AVAIL"         ,CURLINFO_HTTPAUTH_AVAIL         ,RXCURLINFO_LONG  },
#endif
   { "HTTP_CODE"              ,CURLINFO_HTTP_CODE              ,RXCURLINFO_LONG  },
   { "NAMELOOKUP_TIME"        ,CURLINFO_NAMELOOKUP_TIME        ,RXCURLINFO_DOUBLE},
#if LIBCURL_VERSION_NUM >= 0x070c03
   { "NUM_CONNECTS"           ,CURLINFO_NUM_CONNECTS           ,RXCURLINFO_LONG  },
#endif
#if LIBCURL_VERSION_NUM >= 0x070c02
   { "OS_ERRNO"               ,CURLINFO_OS_ERRNO               ,RXCURLINFO_LONG  },
#endif
   { "PRETRANSFER_TIME"       ,CURLINFO_PRETRANSFER_TIME       ,RXCURLINFO_DOUBLE},
#if LIBCURL_VERSION_NUM >= 0x070a03
   { "PRIVATE"                ,CURLINFO_PRIVATE                ,RXCURLINFO_STRING},
#endif
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "PROXYAUTH_AVAIL"        ,CURLINFO_PROXYAUTH_AVAIL        ,RXCURLINFO_LONG  },
#endif
#if LIBCURL_VERSION_NUM >= 0x070907
   { "REDIRECT_COUNT"         ,CURLINFO_REDIRECT_COUNT         ,RXCURLINFO_LONG  },
   { "REDIRECT_TIME"          ,CURLINFO_REDIRECT_TIME          ,RXCURLINFO_DOUBLE},
#endif
   { "REQUEST_SIZE"           ,CURLINFO_REQUEST_SIZE           ,RXCURLINFO_LONG  },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "RESPONSE_CODE"          ,CURLINFO_RESPONSE_CODE          ,RXCURLINFO_LONG  },
#endif
   { "SIZE_DOWNLOAD"          ,CURLINFO_SIZE_DOWNLOAD          ,RXCURLINFO_DOUBLE},
   { "SIZE_UPLOAD"            ,CURLINFO_SIZE_UPLOAD            ,RXCURLINFO_DOUBLE},
   { "SPEED_DOWNLOAD"         ,CURLINFO_SPEED_DOWNLOAD         ,RXCURLINFO_DOUBLE},
   { "SPEED_UPLOAD"           ,CURLINFO_SPEED_UPLOAD           ,RXCURLINFO_DOUBLE},
#if LIBCURL_VERSION_NUM >= 0x070c03
   { "SSL_ENGINES"            ,CURLINFO_SSL_ENGINES            ,RXCURLINFO_LIST  },
#endif
   { "SSL_VERIFYRESULT"       ,CURLINFO_SSL_VERIFYRESULT       ,RXCURLINFO_LONG  },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "STARTTRANSFER_TIME"     ,CURLINFO_STARTTRANSFER_TIME     ,RXCURLINFO_DOUBLE},
#endif
   { "TOTAL_TIME"             ,CURLINFO_TOTAL_TIME             ,RXCURLINFO_DOUBLE},
   { NULL                     ,0                               ,0                }
};

static curl_options RexxCurlSubOptions[] =
{
   { "OLDEST"                 ,CURLCLOSEPOLICY_OLDEST             ,RXCURLOPT_POLICY },
   { "LEAST_RECENTLY_USED"    ,CURLCLOSEPOLICY_LEAST_RECENTLY_USED,RXCURLOPT_POLICY },
   { "LEAST_TRAFFIC"          ,CURLCLOSEPOLICY_LEAST_TRAFFIC      ,RXCURLOPT_POLICY },
   { "SLOWEST"                ,CURLCLOSEPOLICY_SLOWEST            ,RXCURLOPT_POLICY },
   { "CALLBACK"               ,CURLCLOSEPOLICY_CALLBACK           ,RXCURLOPT_POLICY },
   { "NONE"                   ,CURL_HTTP_VERSION_NONE             ,RXCURLOPT_HTTP_VERSION },
   { "VERSION_1_0"            ,CURL_HTTP_VERSION_1_0              ,RXCURLOPT_HTTP_VERSION },
   { "VERSION_1_1"            ,CURL_HTTP_VERSION_1_1              ,RXCURLOPT_HTTP_VERSION },
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "OPTIONAL"               ,CURL_NETRC_OPTIONAL                ,RXCURLOPT_NETRC },
   { "IGNORED"                ,CURL_NETRC_IGNORED                 ,RXCURLOPT_NETRC },
   { "REQUIRED"               ,CURL_NETRC_REQUIRED                ,RXCURLOPT_NETRC },
#endif
   { "IFMODSINCE"             ,CURL_TIMECOND_IFMODSINCE           ,RXCURLOPT_TIMECOND },
   { "IFUNMODSINCE"           ,CURL_TIMECOND_IFUNMODSINCE         ,RXCURLOPT_TIMECOND },
   { "LASTMOD"                ,CURL_TIMECOND_LASTMOD              ,RXCURLOPT_TIMECOND },
#if LIBCURL_VERSION_NUM >= 0x070a02
   { "HTTP"                   ,CURLPROXY_HTTP                     ,RXCURLOPT_PROXYTYPE },
   { "SOCKS4"                 ,CURLPROXY_SOCKS4                   ,RXCURLOPT_PROXYTYPE },
   { "SOCKS5"                 ,CURLPROXY_SOCKS5                   ,RXCURLOPT_PROXYTYPE },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a08
   { "IPRESOLVE_WHATEVER"     ,CURL_IPRESOLVE_WHATEVER            ,RXCURLOPT_IPRESOLVE },
   { "IPRESOLVE_V4"           ,CURL_IPRESOLVE_V4                  ,RXCURLOPT_IPRESOLVE },
   { "IPRESOLVE_V6"           ,CURL_IPRESOLVE_V6                  ,RXCURLOPT_IPRESOLVE },
#endif
#if LIBCURL_VERSION_NUM >= 0x070a06
   { "AUTH_BASIC"             ,CURLAUTH_BASIC                     ,RXCURLOPT_BITMAP_AUTH },
   { "AUTH_DIGEST"            ,CURLAUTH_DIGEST                    ,RXCURLOPT_BITMAP_AUTH },
   { "AUTH_GSSNEGOTIATE"      ,CURLAUTH_GSSNEGOTIATE              ,RXCURLOPT_BITMAP_AUTH },
   { "AUTH_NTLM"              ,CURLAUTH_NTLM                      ,RXCURLOPT_BITMAP_AUTH },
   { "AUTH_ANY"               ,CURLAUTH_ANY                       ,RXCURLOPT_BITMAP_AUTH },
   { "AUTH_ANYSAFE"           ,CURLAUTH_ANYSAFE                   ,RXCURLOPT_BITMAP_AUTH },
#endif
#if LIBCURL_VERSION_NUM >= 0x070b00
   { "NONE"                   ,CURLFTPSSL_NONE                    ,RXCURLOPT_FTPSSL },
   { "TRY"                    ,CURLFTPSSL_TRY                     ,RXCURLOPT_FTPSSL },
   { "CONTROL"                ,CURLFTPSSL_CONTROL                 ,RXCURLOPT_FTPSSL },
   { "ALL"                    ,CURLFTPSSL_ALL                     ,RXCURLOPT_FTPSSL },
#endif
#if LIBCURL_VERSION_NUM >= 0x070c02
   { "DEFAULT"                ,CURLFTPAUTH_DEFAULT                ,RXCURLOPT_FTPSSLAUTH },
   { "SSL"                    ,CURLFTPAUTH_SSL                    ,RXCURLOPT_FTPSSLAUTH },
   { "TLS"                    ,CURLFTPAUTH_TLS                    ,RXCURLOPT_FTPSSLAUTH },
#endif
   { NULL                     ,0                                  ,0 }
};

#define NUMBER_REXXCURL_OPTIONS (sizeof(RexxCurlOptions)/sizeof(curl_options))
static char UsedOptions[NUMBER_REXXCURL_OPTIONS];
static FILE *FilePtrs[NUMBER_REXXCURL_OPTIONS];
static char *StringPtrs[NUMBER_REXXCURL_OPTIONS];
static struct curl_slist *SListPtrs[NUMBER_REXXCURL_OPTIONS];
static struct curl_httppost *HttpPostFirstPtrs[NUMBER_REXXCURL_OPTIONS];
static struct curl_httppost *HttpPostLastPtrs[NUMBER_REXXCURL_OPTIONS];
static rx_long_long max_long;

rxfunc( CurlLoadFuncs );
rxfunc( CurlDropFuncs );
rxfunc( CurlInit );
rxfunc( CurlSetopt );
rxfunc( CurlPerform );
rxfunc( CurlGetinfo );
rxfunc( CurlCleanup );
rxfunc( CurlVariable );
rxfunc( CurlQueryFunction );
rxfunc( CurlFormAdd );
rxfunc( CurlFormFree );

/*-----------------------------------------------------------------------------
 * Table of CURL Functions. Used to install/de-install functions.
 * If you change this table, don't forget to change the table at the end
 * of this file.
 *----------------------------------------------------------------------------*/
static RexxFunction RxCURLFunctions[] = {
   { "CURLINIT"         ,CurlInit        ,"CurlInit"        , 1  },
   { "CURLCLEANUP"      ,CurlCleanup     ,"CurlCleanup"     , 1  },
   { "CURLSETOPT"       ,CurlSetopt      ,"CurlSetopt"      , 1  },
   { "CURLPERFORM"      ,CurlPerform     ,"CurlPerform"     , 1  },
   { "CURLGETINFO"      ,CurlGetinfo     ,"CurlGetinfo"     , 1  },
   { "CURLVARIABLE"     ,CurlVariable    ,"CurlVariable"    , 1  },
   { "CURLQUERYFUNCTION",CurlQueryFunction,"CurlQueryFunction", 1  },
   { "CURLFORMADD"      ,CurlFormAdd     ,"CurlFormAdd"     , 1  },
   { "CURLFORMFREE"     ,CurlFormFree    ,"CurlFormFree"    , 1  },
   { "CURLDROPFUNCS"    ,CurlDropFuncs   ,"CurlDropFuncs"   , 1  },
   { "CURLLOADFUNCS"    ,CurlLoadFuncs   ,"CurlLoadFuncs"   , 0  }, /* Don't load for DLL */
   { NULL, NULL, NULL,0 }
};


#define DEFAULT_REXXCURL_ERROR        "CURLERROR."
#define INTERRM_PREFIX        "INTERRM"
#define INTCODE_PREFIX        "INTCODE"
#define CURLERRM_PREFIX       "CURLERRM"
#define CURLCODE_PREFIX       "CURLCODE"

static REXXCURLDATA RexxCURLData = { 0, 0, "", "", 0, 0, NULL, 0, 0, 0, 0, 0, NULL };
static CURLcode win32_init(void);
static void win32_cleanup(void);

/*
 * This to go in rxpack.c
 */
/*-----------------------------------------------------------------------------
 * Converts a RXSTRING to signed long long. Return 0 if OK and -1 if error.
 * Assumes a string of decimal digits with or without signs and does not check for overflow!
 *----------------------------------------------------------------------------*/
int RxStrToLongLong( void *RxPackageGlobalData, RXSTRING *ptr, rx_long_long *result )
{
   unsigned int  i=0;
   char          *p=NULL;
   rx_long_long  sum=0;
   int           neg=0;

   p = (char *)ptr->strptr;
   for (i = ptr->strlength; i; i--)
   {
      if (isdigit(*p))
         sum = sum * 10 + (*p - '0');
      else if ( i == ptr->strlength && *p == '-' )
         neg = 1;
      else if ( i == ptr->strlength && *p == '+' )
         ;
      else
         return -1;
      p++;
   }
   if ( neg )
      sum *= -1;
   *result = sum;
   return 0;
}

/*
 * These functions used by loader.c to obtain package-specific info
 */
int RexxCURLInitialiser( RxPackageGlobalDataDef *MyGlob )
{
   unsigned long tmp_long;

   /*
    * Get ourselves the maximum value of an unsigned long into max_long
    * to use when determining if we need to use long longs later
    */
   memset( &tmp_long, 0xFF, sizeof(tmp_long) );
   max_long = (rx_long_long)tmp_long;
   RexxCURLData.RxPackageGlobalData = MyGlob;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "RexxCURLInitialiser", NULL );
   return 0;
}

int RexxCURLTerminator( RxPackageGlobalDataDef *MyGlob )
{
   InternalTrace( MyGlob, "RexxCURLTerminator", NULL );
   curl_global_cleanup();
#if defined(_MSC_VER)
   win32_cleanup();
#endif
   return 0;
}

/*
 * Every package MUST have one of these, otherwise ooRexx will crash
 */
LONG APIENTRY RexxCURLInitHandler( LONG ExitNum, LONG Subfun, PEXIT PBlock )
{
   InternalTrace( RexxCURLData.RxPackageGlobalData, "RexxCURLInitHandler", NULL );
#if defined(_MSC_VER)
   if ( win32_init() )
      return 1;
#endif
   curl_global_init(CURL_GLOBAL_ALL);
   strcpy( RexxCURLData.rexxcurl_error_prefix, DEFAULT_REXXCURL_ERROR );
   return 0L;
}

RexxSubcomHandler *getRexxCURLSubcomHandler( void )
{
   return NULL;
}

RexxFunction *getRexxCURLFunctions( void )
{
   return RxCURLFunctions;
}

RxPackageConstantDef *getRexxCURLConstants( void )
{
   return NULL;
}

RexxExitHandler *getRexxCURLInitHandler( void )
{
   return RexxCURLInitHandler;
}

PackageInitialiser *getRexxCURLInitialiser( void )
{
   return RexxCURLInitialiser;
}

PackageTerminator *getRexxCURLTerminator( void )
{
   return RexxCURLTerminator;
}

#if defined(_MSC_VER)
static void win32_cleanup(void)
{
  WSACleanup();
}

static CURLcode win32_init(void)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;
  wVersionRequested = MAKEWORD(1, 1);

  err = WSAStartup(wVersionRequested, &wsaData);

  if (err != 0)
    /* Tell the user that we couldn't find a useable */
    /* winsock.dll.     */
    return 1;

  /* Confirm that the Windows Sockets DLL supports 1.1.*/
  /* Note that if the DLL supports versions greater */
  /* than 1.1 in addition to 1.1, it will still return */
  /* 1.1 in wVersion since that is the version we */
  /* requested. */

  if ( LOBYTE( wsaData.wVersion ) != 1 ||
       HIBYTE( wsaData.wVersion ) != 1 ) {
    /* Tell the user that we couldn't find a useable */

    /* winsock.dll. */
    WSACleanup();
    return 1;
  }
  return 0; /* 0 is ok */
}
#endif

/*-----------------------------------------------------------------------------
 * This function is the callback for PROGRESSFUNCTION option.
 * Ignore the client data pointer; we won't implement this.
 *----------------------------------------------------------------------------*/
int rexxcurl_progress_callback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
{
   int num_args = 4;
   char rx_dltotal[20];
   char rx_dlnow[20];
   char rx_ultotal[20];
   char rx_ulnow[20];
   PRXSTRING argv;
   RXSTRING RetStr;
   int rc;
   SHORT rcode=0;

   if ( RexxCURLData.have_rexxcallback )
   {
      argv = (PRXSTRING)malloc( num_args*sizeof(RXSTRING) );
      if ( argv == NULL )
         return -1;
      sprintf( rx_dltotal, "%f", dltotal );
      sprintf( rx_dlnow, "%f", dlnow );
      sprintf( rx_ultotal, "%f", ultotal );
      sprintf( rx_ulnow, "%f", ulnow );
      MAKERXSTRING( argv[0], rx_dltotal, strlen( rx_dltotal ) );
      MAKERXSTRING( argv[1], rx_dlnow, strlen( rx_dlnow ) );
      MAKERXSTRING( argv[2], rx_ultotal, strlen( rx_ultotal ) );
      MAKERXSTRING( argv[3], rx_ulnow, strlen( rx_ulnow ) );

      MAKERXSTRING( RetStr, NULL, 0 );
#if defined( REXXCALLBACK )
      rc = RexxCallBack( StringPtrs[RexxCURLData.progress_index], num_args, argv, &rcode, &RetStr );
#else
      rc = 0;
#endif
      if ( RetStr.strptr )
         RexxFreeMemory( RetStr.strptr );
      free( argv );
      /* free_cb_argv( curr );*/
   }
   return rcode;
}

/*-----------------------------------------------------------------------------
 * This function returns the size of the specified file. Returns -1 if the
 * file does not exist or is not a "normal" file eg a directory
 *----------------------------------------------------------------------------*/
rx_long_long get_file_size( char *fn )
{
   struct stat stat_buf;
   size_t rc;
   rx_long_long fs;

   rc = stat( fn, &stat_buf ) ;
   if (rc == 0)
   {
      if ( (stat_buf.st_mode & S_IFMT) == S_IFDIR)
         fs = -1;
      else
         fs = (rx_long_long)stat_buf.st_size;
   }
   else
      fs = (rx_long_long)rc;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "get_file_size", "Size of file: %s is: %ld", fn, fs );
   return fs;
}

/*-----------------------------------------------------------------------------
 * This function is necessary for Win32 platform
 *----------------------------------------------------------------------------*/
size_t file_write_function( void *ptr, size_t size, size_t nmemb, void *stream )
{
   size_t num_bytes;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "file_write_function", "Size of chunk to write: %ld", size*nmemb );
   num_bytes = fwrite( ptr, size, nmemb, stream );
   return num_bytes;
}

/*-----------------------------------------------------------------------------
 * This function is necessary for Win32 platform
 *----------------------------------------------------------------------------*/
size_t file_read_function( void *ptr, size_t size, size_t nmemb, void *stream )
{
   size_t num_bytes;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "file_read_function", "Size of chunk to read: %ld", size*nmemb );
   num_bytes = fread( ptr, size, nmemb, stream );
   return num_bytes;
}

/*-----------------------------------------------------------------------------
 * This function creates a compound Rexx variable with the supplied name
 * and vale.
 *----------------------------------------------------------------------------*/
int create_rexx_compound( char *stem, int tail, char *value, int valuelen )
{
   char name[350];
   int namelen;

   namelen = sprintf( name, "%s%d", stem, tail );
   SetRexxVariable( RexxCURLData.RxPackageGlobalData, name, namelen, value, valuelen );
   return 0;
}
/*-----------------------------------------------------------------------------
 * This function writes the output from the site to a stem. Called from
 * OUTSTEM option
 *----------------------------------------------------------------------------*/
size_t outstem_write_function( void *ptr, size_t size, size_t nmemb, void *stream )
{
   size_t num_bytes=size*nmemb;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "outstem_write_function", "Size of chunk to write: %ld", size*nmemb );
   if ( RexxCURLData.outstem_strlength)
      RexxCURLData.outstem_strptr = (char *)realloc( RexxCURLData.outstem_strptr, RexxCURLData.outstem_strlength + num_bytes + 1);
   else
      RexxCURLData.outstem_strptr = (char *)malloc( RexxCURLData.outstem_strlength + num_bytes + 1);

   if ( RexxCURLData.outstem_strptr == NULL )
   {
      return -1;
   }
   memcpy( RexxCURLData.outstem_strptr+RexxCURLData.outstem_strlength, ptr, num_bytes );
   RexxCURLData.outstem_strlength += num_bytes;
   RexxCURLData.outstem_strptr[RexxCURLData.outstem_strlength] = '\0';
   return num_bytes;
}
/*-----------------------------------------------------------------------------
 * This function creates the compound variables for the stem.
 *----------------------------------------------------------------------------*/
int outstem_create( void )
{
   char *tmp,*ptr;
   char eol[2]={10,0};

   if ( RexxCURLData.outstem_strptr )
   {
      ptr = RexxCURLData.outstem_strptr;
      tmp = strstr( ptr, (char *)eol );

      while( tmp != NULL )
      {
         *tmp = '\0';
         create_rexx_compound( StringPtrs[RexxCURLData.outstem_index], ++RexxCURLData.outstem_tail, (char *)ptr, strlen( ptr ) );
         ptr = tmp+1;
         tmp = strstr( ptr, (char *)eol );
      }
      if ( *ptr != '\0' )
      {
         create_rexx_compound( StringPtrs[RexxCURLData.outstem_index], ++RexxCURLData.outstem_tail, (char *)ptr, strlen( ptr ) );
      }
   }

   return 0;
}
/*-----------------------------------------------------------------------------
 * This function writes the output from the site to a stem. Called from
 * HEADERSTEM option
 *----------------------------------------------------------------------------*/
size_t headerstem_write_function( void *ptr, size_t size, size_t nmemb, void *stream )
{
   size_t num_bytes=size*nmemb;
   char *tmp;

   tmp = strtok( ptr, "\n" );
   while( tmp != NULL )
   {
      create_rexx_compound( StringPtrs[RexxCURLData.headerstem_index], ++RexxCURLData.headerstem_tail, (char *)tmp, strlen( tmp ) );
      tmp = strtok( NULL, "\n" );
   }

   return num_bytes;
}

/*-----------------------------------------------------------------------------
 * Clear the cURL error message.
 *----------------------------------------------------------------------------*/
void ClearCURLError( void )
{
   char var[350];
   int varlen;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "ClearCURLError", NULL );

   /*
    * Set CURLERROR.CURLERRM variable
    */
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, CURLERRM_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, "", 0 );
   /*
    * Set CURLERROR.CURLCODE variable
    */
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, CURLCODE_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, "0", 1 );
   RexxCURLData.g_curl_error = 0;
   return;
}

/*-----------------------------------------------------------------------------
 * Set the cURL error message.
 *----------------------------------------------------------------------------*/
void SetCURLError( CURLcode curlcode, char *curlmsg)
{
   char var[350];
   char msg[350];
   int varlen;
   int msglen;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "SetCURLError", "%d,%s", curlcode, curlmsg );
   RexxCURLData.g_curl_error = curlcode;
   /*
    * Set CURLERROR.CURLERRM variable
    */
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, CURLERRM_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, curlmsg, strlen(curlmsg) );
   /*
    * Set CURLERROR.CURLCODE variable
    */
   msglen = sprintf( msg, "%ld", RexxCURLData.g_curl_error );
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, CURLCODE_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, msg, msglen );
   return;
}

/*-----------------------------------------------------------------------------
 * Clear the internal error message.
 *----------------------------------------------------------------------------*/
int ClearIntError( void )
{
   char var[350];
   int varlen;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "ClearIntError", NULL );

   RexxCURLData.g_rexxcurl_error = 0;
   /*
    * Set CURLERROR.INTERRM variable
    */
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, INTERRM_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, "", 0 );
   /*
    * Set CURLERROR.INTCODE variable
    */
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, INTCODE_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, "0", 1 );

   return( RexxCURLData.g_rexxcurl_error );
}

/*-----------------------------------------------------------------------------
 * Set the internal error message.
 *----------------------------------------------------------------------------*/
int SetIntError(char *fn, int lineno, int errcode, char *errmsg)
{
   char msg[350];
   char var[350];
   int msglen;
   int varlen;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "SetIntError", "%s,%d,%d,%s", fn, lineno, errcode, errmsg );

   RexxCURLData.g_rexxcurl_error = -errcode;

   /*
    * Set CURLERROR.INTERRM variable
    */
   if ( RxGetRunFlags( RexxCURLData.RxPackageGlobalData ) & MODE_INTERNAL )
      msglen = sprintf(msg, "Rexx/CURL-%02d: %s [%s:%d]", errcode, errmsg, fn, lineno);
   else
      msglen = sprintf(msg, "REXX/CURL-%02d: %s", errcode, errmsg);
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, INTERRM_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, msg, msglen );

   /*
    * Set CURLERROR.INTCODE variable
    */
   msglen = sprintf( msg, "%ld", RexxCURLData.g_rexxcurl_error );
   varlen = sprintf( var,"%s%s", RexxCURLData.rexxcurl_error_prefix, INTCODE_PREFIX );
   (void)SetRexxVariable( RexxCURLData.RxPackageGlobalData, var, varlen, msg, msglen );

   return( RexxCURLData.g_rexxcurl_error );
}

static void init_options()
{
   int i;

   InternalTrace( RexxCURLData.RxPackageGlobalData, "init_options", NULL );
   for ( i = 0; i < NUMBER_REXXCURL_OPTIONS; i++ )
   {
      FilePtrs[i] = NULL;
      StringPtrs[i] = NULL;
      SListPtrs[i] = NULL;
      HttpPostFirstPtrs[i] = NULL;
      HttpPostLastPtrs[i] = NULL;
      UsedOptions[i] = '\0';
   }
}

static void reset_options()
{
   int i;
   for ( i = 0; i < NUMBER_REXXCURL_OPTIONS; i++ )
   {
      if ( UsedOptions[i] )
      {
         if ( FilePtrs[i] )
         {
            fclose( FilePtrs[i] );
            FilePtrs[i] = NULL;
         }
         if ( StringPtrs[i] )
         {
            free( StringPtrs[i] );
            StringPtrs[i] = NULL;
         }
         if ( SListPtrs[i] )
         {
            curl_slist_free_all( SListPtrs[i] );
            SListPtrs[i] = NULL;
         }
         if ( HttpPostFirstPtrs[i] )
         {
            curl_formfree( HttpPostFirstPtrs[i] );
            HttpPostFirstPtrs[i] = NULL;
            HttpPostLastPtrs[i] = NULL;
         }
         UsedOptions[i] = '\0';
      }
   }
}

static int find_option( char *str, int len )
{
   register int i = 0;

   for ( i = 0; RexxCurlOptions[i].name != NULL; i++ )
   {
      if ( memcmpi( str, RexxCurlOptions[i].name, len ) == 0 )
         return i;
   }
   return ( -1 );
}

static int find_getinfo( char *str, int len )
{
   register int i = 0;

   for ( i = 0; RexxCurlGetinfos[i].name != NULL; i++ )
   {
      if ( memcmpi( str, RexxCurlGetinfos[i].name, len ) == 0 )
         return i;
   }
   return ( -1 );
}

static int find_suboption( char *str, int len, int optiontype )
{
   register int i = 0;

   for ( i = 0; RexxCurlSubOptions[i].name != NULL; i++ )
   {
      if ( memcmpi( str, RexxCurlSubOptions[i].name, len ) == 0
      &&   RexxCurlSubOptions[i].optiontype == optiontype )
         return i;
   }
   return ( -1 );
}

/*====== Here come the real interface functions to curl ======*/

rxfunc( CurlInit )
{
   CURL *curl;

   ClearCURLError( );
   ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 0, 0 ) )
      return( 1 );
   /*
    * Do some initialisations
    */
   init_options();
   curl = curl_easy_init();
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlInit", "After curl_easy_init" );
   /*
    * Determine if the Rexx interpreter has RexxCallBack()
    * If USE_REGINA then we have it.
    * If USE_REXXTRANS then we MIGHT have it; we need to call RxTransHaveRexxCallBack()
    * to check.
    * If REXXCALLBACK defined, then we also have it.
    * All other situations, we DON'T have it.
    * We need to determine this BEFORE calling SetPackageConstants()
    */
#if defined( USE_REGINA )
   RexxCURLData.have_rexxcallback = 1;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlInit", "RexxCallback available: Regina" );
#elif defined ( USE_REXXTRANS )
   if ( RexxTransHaveRexxCallBack() )
      RexxCURLData.have_rexxcallback = 1;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlInit", "RexxCallback %s: RexxTrans", RexxCURLData.have_rexxcallback ? "available" : "unavailable" );
#elif defined( REXXCALLBACK )
   RexxCURLData.have_rexxcallback = 1;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlInit", "RexxCallback available: Other Interpreter" );
#else
   RexxCURLData.have_rexxcallback = 0;
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlInit", "RexxCallback unavailable: Other Interpreter" );
#endif
   return RxReturnPointer( RexxCURLData.RxPackageGlobalData, retstr, (void *)curl ) ;
}

rxfunc( CurlCleanup )
{
   long curl;

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 1, 1 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnNumber( RexxCURLData.RxPackageGlobalData, retstr, INTERR_INVALID_HANDLE ) ;
   }
   curl_easy_cleanup( (CURL *)curl );
   reset_options();
   memset( UsedOptions, 0, sizeof(char)*NUMBER_REXXCURL_OPTIONS );
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

rxfunc( CurlSetopt )
{
   ULONG rc = 0L;
   long curl;
   int i,opt,sub_opt;
   long long_opt;
   rx_long_long longlong_opt;
   CURLcode curl_rc;
   RXSTRING value;
   char *tmp;
   char tmp_format[100];
   char *opentype="wb";

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 2, 0 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   opt = find_option( argv[1].strptr, argv[1].strlength );
   if ( opt == (-1) )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   /*
    * HTTPPOSTDATA does NOT require any further arguments, so only if it is NOT
    * this option, check for the mandatory 3rd argument
    */
   if ( RexxCurlOptions[opt].optiontype != RXCURLOPT_POST_FORM )
   {
      if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 3, 0 ) )
         return( 1 );
   }

   switch( RexxCurlOptions[opt].optiontype )
   {
      case RXCURLOPT_STRING:
         if ( StringPtrs[opt] )
            free( StringPtrs[opt] );
         StringPtrs[opt] = (char *)malloc( argv[2].strlength + 1 );
         if ( StringPtrs[opt] == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            break;
         }
         memcpy( StringPtrs[opt], argv[2].strptr, argv[2].strlength );
         StringPtrs[opt][argv[2].strlength] = '\0';
         /*
          * Do any extra processing here for some options
          */
         switch( RexxCurlOptions[opt].number )
         {
            default:
               break;
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, StringPtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_OUTFILE:
         /* check if APPEND passed as optional argument */
         if ( argc == 4
         &&   argv[3].strlength )
         {
            if ( memcmpi( argv[3].strptr, "APPEND", 6 ) == 0 )
               opentype = "ab";
         }
         /* parameter must be the name of a file to write to or blank to turn it off */
         if ( argv[2].strlength )
         {
            FilePtrs[opt] = fopen( argv[2].strptr, opentype );
            if ( FilePtrs[opt] == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_FILE, INTERR_INVALID_FILE_STRING );
               break;
            }
         }
         else
         {
            if ( FilePtrs[opt] )
            {
               fclose( FilePtrs[opt] );
               FilePtrs[opt] = NULL;
            }
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, FilePtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         switch ( RexxCurlOptions[opt].number )
         {
            case CURLOPT_FILE:
               if ( argv[2].strlength )
               {
                  /*
                   * Always set the write function if we have an output file
                   */
                  rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_WRITEFUNCTION, &file_write_function );
                  if ( curl_rc != CURLE_OK )
                  {
                     SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
                     SetCURLError( curl_rc, curl_errors[rc] );
                     break;
                  }
               }
               break;
            default:
               break;
         }
         break;
      case RXCURLOPT_INFILE:
         /* parameter must be the name of a file to read from or blank to turn it off */
         if ( argv[2].strlength )
         {
            FilePtrs[opt] = fopen( argv[2].strptr, "rb" );
            if ( FilePtrs[opt] == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_FILE, INTERR_INVALID_FILE_STRING );
               break;
            }
         }
         else
         {
            if ( FilePtrs[opt] )
            {
               fclose( FilePtrs[opt] );
               FilePtrs[opt] = NULL;
            }
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, FilePtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         /*
          * Now do some extra stuff depending on the option specified...
          */
         switch ( RexxCurlOptions[opt].number )
         {
            case CURLOPT_INFILE:
               if ( argv[2].strlength )
               {
                  /*
                   * Always set the read function if we have an input file. This is because
                   * Win32 port requires it!
                   */
                  rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_READFUNCTION, &file_read_function );
                  if ( curl_rc != CURLE_OK )
                  {
                     SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
                     SetCURLError( curl_rc, curl_errors[rc] );
                     break;
                  }
               }
               break;
            default:
               break;
         }

         break;
      case RXCURLOPT_LONG:
         if ( StrToNumber( &argv[2], &long_opt ) != 0 )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_NUMBER, INTERR_INVALID_NUMBER_STRING );
            break;
         }
         FunctionTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt", "Setting LONG value: %ld", long_opt );
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, long_opt );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_LONGLONG:
      {
         CURLoption opt_num;
         int opt_long_long = 0;

         /*
          * Do we try and compare the passed value against MAX_LONG and call
          * the LONG option setting instead of the LARGE option???????
          */
         opt_num = RexxCurlOptions[opt].number;
         switch ( RexxCurlOptions[opt].number )
         {
#if LIBCURL_VERSION_NUM >= 0x070b00
            case CURLOPT_RESUME_FROM:
               opt_num = CURLOPT_RESUME_FROM_LARGE;
               opt_long_long = 1;
               break;
#endif
#if LIBCURL_VERSION_NUM >= 0x070a09
            case CURLOPT_MAXFILESIZE:
               opt_num = CURLOPT_MAXFILESIZE_LARGE;
               opt_long_long = 1;
               break;
#endif
            default:
               break;
         }
         if ( opt_long_long )
         {
            if ( RxStrToLongLong( NULL, &argv[2], &longlong_opt ) != 0 )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_NUMBER, INTERR_INVALID_NUMBER_STRING );
               break;
            }
            sprintf( tmp_format, "Setting LONGLONG value: %s", RX_LL_FORMAT );
            FunctionTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt", tmp_format, longlong_opt );
            rc = curl_rc = curl_easy_setopt( (CURL *)curl, opt_num, longlong_opt );
         }
         else
         {
            if ( RxStrToLong( NULL, &argv[2], &long_opt ) != 0 )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_NUMBER, INTERR_INVALID_NUMBER_STRING );
               break;
            }
            FunctionTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt", "Setting LONG(LONG) value: %ld", long_opt );
            rc = curl_rc = curl_easy_setopt( (CURL *)curl, opt_num, long_opt );
         }
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      }
      case RXCURLOPT_BOOL:
         if ( StrToBool( &argv[2], (unsigned long *)&long_opt ) != 0 )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_BOOL, INTERR_INVALID_BOOL_STRING );
            break;
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, long_opt );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_BITMAP_AUTH:
         long_opt = 0L;
         for ( i = 2; i < (int)argc; i++ )
         {
            sub_opt = find_suboption( argv[i].strptr, argv[i].strlength, RexxCurlOptions[opt].optiontype );
            if ( sub_opt == (-1) )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
               break;
            }
            long_opt |= RexxCurlSubOptions[sub_opt].number;
         }
         if ( sub_opt == (-1) )
            break;
         FunctionTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt", "Setting BITMAP value: %x", long_opt );
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, long_opt );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_POLICY:
      case RXCURLOPT_PROXYTYPE:
      case RXCURLOPT_HTTP_VERSION:
      case RXCURLOPT_NETRC:
      case RXCURLOPT_TIMECOND:
      case RXCURLOPT_IPRESOLVE:
         /*
          * This handles sub-options that result in a long value passed to curl_easy_setopt()
          */
         sub_opt = find_suboption( argv[2].strptr, argv[2].strlength, RexxCurlOptions[opt].optiontype );
         if ( sub_opt == (-1) )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
            break;
         }
         long_opt = RexxCurlSubOptions[sub_opt].number;
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, long_opt );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_LIST:
         /*
          * Check that arg[2] is a stem, then convert the stem into
          * a cURL linked list
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         argv[2].strptr[argv[2].strlength] = '\0';
         if ( GetRexxVariableInteger( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &sub_opt, 0 ) == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         if ( SListPtrs[opt] )
         {
            curl_slist_free_all( SListPtrs[opt] );
            SListPtrs[opt] = NULL;
         }
         for ( i = 1; i <= sub_opt; i++ )
         {
            if ( GetRexxVariable( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &value, i ) == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
            InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt(RXCURLOPT_LIST)", "Variable: <%s.%d> Value: <%s>", argv[2].strptr, i, value.strptr );
            SListPtrs[opt] = curl_slist_append( SListPtrs[opt], value.strptr );
            /*
             * Free the memory allocated in GetRexxVariable()
             */
            free( value.strptr );
            if ( SListPtrs[opt] == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, SListPtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_POST_DATA:
         /*
          * Check that arg[2] is a stem, then convert the stem into
          * a cURL linked list
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         argv[2].strptr[argv[2].strlength] = '\0';
         if ( GetRexxVariableInteger( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &sub_opt, 0 ) == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         if ( HttpPostFirstPtrs[opt] )
         {
            curl_formfree( HttpPostFirstPtrs[opt] );
         }
         HttpPostFirstPtrs[opt] = NULL;
         HttpPostLastPtrs[opt] = NULL;
         for ( i = 1; i <= sub_opt; i++ )
         {
            if ( GetRexxVariable( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &value, i ) == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
            InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt(RXCURLOPT_POST_DATA)", "Variable: <%s%d> Value: <%s>", argv[2].strptr, i, value.strptr );
            rc = curl_rc = curl_formadd( &HttpPostFirstPtrs[opt], &HttpPostLastPtrs[opt], CURLFORM_COPYCONTENTS, value.strptr, CURLFORM_CONTENTSLENGTH, value.strlength, CURLFORM_END );
            /*
             * Free the memory allocated in GetRexxVariable()
             */
            free( value.strptr );
            if ( curl_rc != CURLE_OK )
            {
               SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
               SetCURLError( curl_rc, curl_formadd_errors[rc] );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, HttpPostFirstPtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_POST_FORM:
         /*
          * Always look for HTTPPOSTFORM option
          */
         opt = find_option( "HTTPPOSTFORM", 12 );
         if ( opt == (-1) )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, HttpPostFirstPtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_POST_FIELDS:
         /*
          * Check that arg[2] is a stem, then convert the stem into
          * a string of values
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         argv[2].strptr[argv[2].strlength] = '\0';
         if ( GetRexxVariableInteger( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &sub_opt, 0 ) == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         /*
          * Reset the string ptr if previously set
          */
         if ( StringPtrs[opt] )
         {
            free( StringPtrs[opt] );
            StringPtrs[opt] = NULL;
         }

         for ( i = 1; i <= sub_opt; i++ )
         {
            if ( GetRexxVariable( RexxCURLData.RxPackageGlobalData, argv[2].strptr, &value, i ) == NULL )
            {
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
            if ( StringPtrs[opt] == NULL )
            {
               StringPtrs[opt] = (char *)malloc( value.strlength + 2 );
               if ( StringPtrs[opt] == NULL )
               {
                  SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
                  break;
               }
               strcpy( StringPtrs[opt], "" );
               tmp = StringPtrs[opt];
            }
            else
            {
               StringPtrs[opt] = (char *)realloc( StringPtrs[opt], strlen( StringPtrs[opt]) + value.strlength + 2 );
               if ( StringPtrs[opt] == NULL )
               {
                  SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
                  break;
               }
            }
            /*
             * Concatenate '&' and the new value to the previous string value
             */
            strcat( StringPtrs[opt], "&" );
            strcat( StringPtrs[opt], value.strptr );
            /*
             * Free the memory allocated in GetRexxVariable()
             */
            free( value.strptr );
         }
         InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlSetopt(RXCURLOPT_POST_FIELDS)", "Value: <%s>", (StringPtrs[opt]) ? StringPtrs[opt] : "" );
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, RexxCurlOptions[opt].number, StringPtrs[opt] );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         /*
          * Now set the length of the generated string...
          */
         rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_POSTFIELDSIZE, (StringPtrs[opt]) ? strlen( StringPtrs[opt] ) : 0 );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_OUTSTEM:
         /*
          * Check that arg[2] is a stem, then setup the write callback
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         /*
          * Save the stem name for the write function...
          */
         if ( StringPtrs[opt] )
            free( StringPtrs[opt] );
         StringPtrs[opt] = (char *)malloc( argv[2].strlength + 1 );
         if ( StringPtrs[opt] == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            break;
         }
         memcpy( StringPtrs[opt], argv[2].strptr, argv[2].strlength );
         StringPtrs[opt][argv[2].strlength] = '\0';
         /*
          * Set these globals to allow the write function to know which
          * compound variable to write.
          */
         RexxCURLData.outstem_index = opt;
         RexxCURLData.outstem_tail = 0;
         if ( RexxCURLData.outstem_strptr )
            free( RexxCURLData.outstem_strptr );
         RexxCURLData.outstem_strlength = 0;

         rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_WRITEFUNCTION, &outstem_write_function );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_HEADERSTEM:
         /*
          * Check that arg[2] is a stem, then setup the write callback
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         /*
          * Save the stem name for the write function...
          */
         if ( StringPtrs[opt] )
            free( StringPtrs[opt] );
         StringPtrs[opt] = (char *)malloc( argv[2].strlength + 1 );
         if ( StringPtrs[opt] == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            break;
         }
         memcpy( StringPtrs[opt], argv[2].strptr, argv[2].strlength );
         StringPtrs[opt][argv[2].strlength] = '\0';
         /*
          * Set these globals to allow the write function to know which
          * compound variable to write.
          */
         RexxCURLData.headerstem_index = opt;
         RexxCURLData.headerstem_tail = 0;

         rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_HEADERFUNCTION, &headerstem_write_function );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         break;
      case RXCURLOPT_CALLBACK:
         if ( StringPtrs[opt] )
            free( StringPtrs[opt] );
         StringPtrs[opt] = (char *)malloc( argv[2].strlength + 1 );
         if ( StringPtrs[opt] == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            break;
         }
         memcpy( StringPtrs[opt], argv[2].strptr, argv[2].strlength );
         StringPtrs[opt][argv[2].strlength] = '\0';
         /*
          * Do any extra processing here for some options
          */
         switch( RexxCurlOptions[opt].number )
         {
            case CURLOPT_PROGRESSFUNCTION:
               rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_NOPROGRESS, 0 );
               if ( curl_rc != CURLE_OK )
               {
                  SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
                  SetCURLError( curl_rc, curl_errors[rc] );
                  break;
               }
               rc = curl_rc = curl_easy_setopt( (CURL *)curl, CURLOPT_PROGRESSFUNCTION, &rexxcurl_progress_callback );
               if ( curl_rc != CURLE_OK )
               {
                  SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
                  SetCURLError( curl_rc, curl_errors[rc] );
                  break;
               }
               RexxCURLData.progress_index = opt;
               break;
            default:
               break;
         }
         break;
      default:
         /* error */
         break;
   }
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

/*
 * Equivalent of curl_formadd()
 * Args:
 *   curl handle
 *   option - COPYCONTENTS or FILECONTENTS
 *   name of section
 *   data for COPYCONTENTS or multiple content type/filename for FILECONTENTS
 *
 *   call curlformadd curl, 'COPYCONTENTS', 'htmlcode', 'text/html', mydata
 *   call curlformadd curl, 'FILE', 'moreimages', 'image/jpeg', 'pic1.jpg', , 'pic2.jpg'
 *   content.1 = 'image/jpeg'; filename.1 = 'pic1.jpg'
 *   content.2 = 'text/html' ; filename.2 = 'index.html'
 *   content.2 = ''          ; filename.2 = 'pic2.jpg'
 *   content.0 = 3; filename.0 = 3
 *   call curlformadd curl, 'FILE', 'imagesandhtml', 'content.', 'filename.'
 *   call curlsetopt curl, 'HTTPPOSTFORM'
 *   call curlperform curl
 *   call curlformfree curl


array:
  i=0 content.1 -> cf[0].option = CONTENTYTPE; cf[0].value = content.1
  i=0 filename.1 -> cf[1].option = CONTENTYTPE; cf[1].value = filename.1
  i=1 content.2 -> cf[2].option = CONTENTYTPE; cf[2].value = content.1
  i=1 filename.2 -> cf[3].option = CONTENTYTPE; cf[3].value = filename.1
 */
rxfunc( CurlFormAdd )
{
   ULONG rc = 0L;
   long curl;
   int i,opt;
   CURLcode curl_rc;

   int count,idx,num_buffers,contenttype;
   struct curl_forms *cf;
   RXSTRING *values;

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 5, 0 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   /*
    * Always look for HTTPPOSTFORM option
    */
   opt = find_option( "HTTPPOSTFORM", 12 );
   if ( opt == (-1) )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   if ( memcmpi( "COPYCONTENTS", argv[1].strptr, argv[1].strlength ) == 0 )
   {
      if ( !RXVALIDSTRING( argv[2] ) )
      {
         SetIntError( __FILE__, __LINE__, INTERR_MANDATORY_FIELD, INTERR_MANDATORY_FIELD_STRING );
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
      }
      InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd(COPYCONTENTS)", "Name: <%s> ContentType: <%s> Value: <%s>", argv[2].strptr, argv[3].strptr, argv[4].strptr );
      rc = curl_rc = curl_formadd( &HttpPostFirstPtrs[opt],
                                   &HttpPostLastPtrs[opt],
                                   CURLFORM_COPYNAME,
                                   argv[2].strptr,
                                   CURLFORM_NAMELENGTH,
                                   argv[2].strlength,
                                   CURLFORM_CONTENTTYPE,
                                   argv[3].strptr,
                                   CURLFORM_COPYCONTENTS,
                                   argv[4].strptr,
                                   CURLFORM_CONTENTSLENGTH,
                                   argv[4].strlength,
                                   CURLFORM_END );
      if ( curl_rc != CURLE_OK )
      {
         SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
         SetCURLError( curl_rc, curl_formadd_errors[rc] );
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
      }
   }
   else if ( memcmpi( "FILE", argv[1].strptr, argv[1].strlength ) == 0 )
   {
      /*
       * Check if we are using the array option
       */
      if ( argc == 5
      &&   argv[3].strptr[argv[3].strlength-1] == '.'
      &&   argv[4].strptr[argv[4].strlength-1] == '.' )
      {
         /*
          * arg[3] is array of contenttype, arg[4] is array of filenames
          */
         if ( GetRexxVariableInteger( RexxCURLData.RxPackageGlobalData, argv[3].strptr, &count, 0 ) == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
         num_buffers = 1 + (count * 2);
         /*
          * Get an array of curl_forms structures; two for each stem item
          */
         cf = (struct curl_forms *)malloc( num_buffers*sizeof(struct curl_forms) );
         if ( cf == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
         /*
          * Get an array of RXSTRINGs; one for each stem item
          */
         values = (RXSTRING *)malloc( count*sizeof(RXSTRING) );
         if ( values == NULL )
         {
            free( cf );
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }

         for ( idx = 0, i = 0; i < count; i++ )
         {
            /*
             * Get the contenttype stem value
             */
            if ( GetRexxVariable( RexxCURLData.RxPackageGlobalData, argv[3].strptr, &values[i], i+1 ) == NULL )
            {
               free( cf );
               free( values );
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
            /*
             * If the contenttype value is empty, do not add the option
             */
            if ( values[i].strlength )
            {
               cf[idx].value = values[i].strptr;
               cf[idx].option = CURLFORM_CONTENTTYPE;
               InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Setting Contenttype %d Option %d Value: [%s](%d)",
                              i, idx, cf[idx].value, cf[idx].option );
               idx++;
            }
            /*
             * Get the filename stem value
             */
            if ( GetRexxVariable( RexxCURLData.RxPackageGlobalData, argv[4].strptr, &values[i], i+1 ) == NULL )
            {
               free( cf );
               free( values );
               SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
               return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
            }
            cf[idx].option = CURLFORM_FILE;
            cf[idx].value = values[i].strptr;
            InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Setting Filename %d Option %d Value: [%s](%d)",
                           i, idx, cf[idx].value, cf[idx].option );
            idx++;
         }
         cf[idx].option = CURLFORM_END;
         cf[idx].value = NULL;
         InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Executing curl_formadd. Name: [%s]%d",
                           argv[2].strptr, argv[2].strlength );
         rc = curl_rc = curl_formadd( &HttpPostFirstPtrs[opt],
                                      &HttpPostLastPtrs[opt],
                                      CURLFORM_COPYNAME,
                                      argv[2].strptr,
                                      CURLFORM_NAMELENGTH,
                                      argv[2].strlength,
                                      CURLFORM_ARRAY,
                                      cf,
                                      CURLFORM_END );
         /*
          * Free the memory allocated in GetRexxVariable()
          */
         for ( i = 0 ; i < count ; i++ )
         {
            free( values[i].strptr );
         }
         free( cf );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_formadd_errors[rc] );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
      }
      else
      {
         /*
          * We can have any number of pairs of options after the 3rd mandatory option
          */
         if ( argc % 2 == 0 )
         {
            return 1;
         }
         /*
          * Number of file pairs
          */
         count = ( argc - 3 ) / 2;
         num_buffers = 1 + (count * 2);
         /*
          * Get an array of curl_forms structures; one for each stem item
          */
         cf = (struct curl_forms *)malloc( num_buffers*sizeof(struct curl_forms) );
         if ( cf == NULL )
         {
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
         /*
          * Get an array of RXSTRINGs; one for each stem item
          */
         values = (RXSTRING *)malloc( count*sizeof(RXSTRING) );
         if ( values == NULL )
         {
            free( cf );
            SetIntError( __FILE__, __LINE__, INTERR_NO_MEMORY, INTERR_NO_MEMORY_STRING );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
         contenttype = 1;
         for ( idx = 0, i = 3; i < argc; i++ )
         {
            /*
             * If the contenttype value is empty, do not add the option
             */
            if ( contenttype )
            {
               if ( RXVALIDSTRING( argv[i] ) )
               {
                  cf[idx].value = argv[i].strptr;
                  cf[idx].option = CURLFORM_CONTENTTYPE;
                  InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Setting Contenttype %d Option %d Value: [%s](%d)",
                                 i, idx, cf[idx].value, cf[idx].option );
                  idx++;
               }
               contenttype = 0;
            }
            else
            {
               cf[idx].option = CURLFORM_FILE;
               cf[idx].value = argv[i].strptr;
               InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Setting Filename %d Option %d Value: [%s](%d)",
                              i, idx, cf[idx].value, cf[idx].option );
               idx++;
               contenttype = 1;
            }
         }
         cf[idx].option = CURLFORM_END;
         cf[idx].value = NULL;
         InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlFormAdd", "Executing curl_formadd. Name: [%s]%d",
                           argv[2].strptr, argv[2].strlength );
         rc = curl_rc = curl_formadd( &HttpPostFirstPtrs[opt],
                                      &HttpPostLastPtrs[opt],
                                      CURLFORM_COPYNAME,
                                      argv[2].strptr,
                                      CURLFORM_NAMELENGTH,
                                      argv[2].strlength,
                                      CURLFORM_ARRAY,
                                      cf,
                                      CURLFORM_END );
         /*
          * Free the memory allocated in GetRexxVariable()
          */
         for ( i = 0 ; i < count ; i++ )
         {
            free( values[i].strptr );
         }
         free( cf );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_formadd_errors[rc] );
            return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
         }
      }
   }
   else
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

rxfunc( CurlPerform )
{
   long curl;
   CURLcode rc;
   char value[20]; /* big enough for an int */
   int valuelen,i;

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 1, 1 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   /*
    * Set the CURLOPT_ERRORBUFFER here to ensure we get a string
    * error if something goes wrong.
    */
   strcpy( RexxCURLData.curl_error, "" );
   curl_easy_setopt( (CURL *)curl, CURLOPT_ERRORBUFFER, RexxCURLData.curl_error );
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlPerform", "Set ERRORBUFFER OK" );
   RexxCURLData.g_curl_error = rc = curl_easy_perform( (CURL *)curl );
   if ( rc != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
      SetCURLError( rc, (char *)RexxCURLData.curl_error );
   }
   InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlPerform", "curl_easy_perform exited with %d", rc );
   /*
    * For those options that return their data in a stem, we need to set
    * the stem.0 value to the number of stem variables actually created.
    */
   if ( RexxCURLData.outstem_index )
   {
      InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlPerform", "Using OUTSTEM" );
      outstem_create();
      valuelen = sprintf( value, "%d", RexxCURLData.outstem_tail );
      create_rexx_compound( StringPtrs[RexxCURLData.outstem_index], 0, value, valuelen );
      RexxCURLData.outstem_index = RexxCURLData.outstem_tail = RexxCURLData.outstem_strlength = 0;
      if ( RexxCURLData.outstem_strptr )
      {
         free( RexxCURLData.outstem_strptr );
         RexxCURLData.outstem_strptr = NULL;
         RexxCURLData.outstem_strlength = 0;
      }
   }
   if ( RexxCURLData.headerstem_index )
   {
      InternalTrace( RexxCURLData.RxPackageGlobalData, "CurlPerform", "Using HEADERSTEM" );
      valuelen = sprintf( value, "%d", RexxCURLData.headerstem_tail );
      create_rexx_compound( StringPtrs[RexxCURLData.headerstem_index], 0, value, valuelen );
      RexxCURLData.headerstem_index = RexxCURLData.headerstem_tail = 0;
   }
   /*
    * If OUTFILE is used, close it here and set the FILE * to NULL
    */
   for ( i = 0; i < NUMBER_REXXCURL_OPTIONS; i++ )
   {
      if ( RexxCurlOptions[i].optiontype == RXCURLOPT_OUTFILE
      &&   FilePtrs[i] )
      {
         fclose( FilePtrs[i] );
         FilePtrs[i] = NULL;
      }
   }
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

rxfunc( CurlGetinfo )
{
   ULONG rc = 0L;
   CURLcode curl_rc;
   long curl;
   int opt;
   char *return_string = NULL;
   double return_double;
   long return_long;
   struct curl_slist *TmpSListPtrs=NULL;
   struct curl_slist *OrigSListPtrs=NULL;
   char value[20]; /* big enough for an int */
   int valuelen,i;

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 2, 3 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   opt = find_getinfo( argv[1].strptr, argv[1].strlength );
   if ( opt == (-1) )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   switch( RexxCurlGetinfos[opt].optiontype )
   {
      case RXCURLINFO_STRING:
         rc = curl_rc = curl_easy_getinfo( (CURL *)curl, RexxCurlGetinfos[opt].number, &return_string );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, return_string );
         break;
      case RXCURLINFO_DOUBLE:
         rc = curl_rc = curl_easy_getinfo( (CURL *)curl, RexxCurlGetinfos[opt].number, &return_double );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         return RxReturnDouble( RexxCURLData.RxPackageGlobalData, retstr, return_double );
         break;
      case RXCURLINFO_LONG:
         rc = curl_rc = curl_easy_getinfo( (CURL *)curl, RexxCurlGetinfos[opt].number, &return_long );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         return RxReturnNumber( RexxCURLData.RxPackageGlobalData, retstr, return_long );
         break;
      case RXCURLINFO_LIST:
         if ( argc != 3 )
         {
            SetIntError( __FILE__, __LINE__, INTERR_TOO_FEW_ARGS, INTERR_TOO_FEW_ARGS_STRING );
            break;
         }
         /*
          * Check that we have a stem
          */
         if ( argv[2].strptr[argv[2].strlength-1] != '.' )
         {
            SetIntError( __FILE__, __LINE__, INTERR_INVALID_STEM, INTERR_INVALID_STEM_STRING );
            break;
         }
         rc = curl_rc = curl_easy_getinfo( (CURL *)curl, RexxCurlGetinfos[opt].number, &TmpSListPtrs );
         if ( curl_rc != CURLE_OK )
         {
            SetIntError( __FILE__, __LINE__, INTERR_CURL_ERROR, INTERR_CURL_ERROR_STRING );
            SetCURLError( curl_rc, curl_errors[rc] );
            break;
         }
         OrigSListPtrs = TmpSListPtrs;
         for ( i = 1; TmpSListPtrs; i++, TmpSListPtrs=TmpSListPtrs->next )
         {
            create_rexx_compound( argv[2].strptr, i, TmpSListPtrs->data, strlen( TmpSListPtrs->data ) );
         }
         valuelen = sprintf( value, "%d", i-1 );
         create_rexx_compound( argv[2].strptr, 0, value, valuelen );
         if ( OrigSListPtrs )
         {
            curl_slist_free_all( OrigSListPtrs );
         }
         return RxReturnNumber( RexxCURLData.RxPackageGlobalData, retstr, i-1 );
         break;
      default:
         /* error */
         break;
   }
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

rxfunc( CurlFormFree )
{
   long curl;
   int opt;

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 1, 1 ) )
      return( 1 );
   if ( StrToNumber( &argv[0], &curl ) != 0 )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_HANDLE, INTERR_INVALID_HANDLE_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   /*
    * Always look for HTTPPOSTFORM option
    */
   opt = find_option( "HTTPPOSTFORM", 12 );
   if ( opt == (-1) )
   {
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_OPTION, INTERR_INVALID_OPTION_STRING );
      return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
   }
   if ( HttpPostFirstPtrs[opt] )
   {
      curl_formfree( HttpPostFirstPtrs[opt] );
   }
   HttpPostFirstPtrs[opt] = NULL;
   HttpPostLastPtrs[opt] = NULL;
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" ) ;
}

/*
 * If an argument is passed, check if it is a valid function name
 * If no argument is passed, return all function names
 * Curlqueryfunction( var|stem, opt )
 * opt is R(registered), A(vailable)
 *
 * return 0 is successful - stem or func found
 * return 1 if func not found
 */
rxfunc(CurlQueryFunction)
{
   ULONG rc = 0L;
   int i,len,tmplen,idx;
   char *stem;
   char optionchar;
   char varname[255], value[10];
   char stem_name[255];

   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );

   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 1, 2 ) )
      return( 1 );

   stem = RXSTRPTR(argv[0]);
   len = RXSTRLEN(argv[0]);

   if ( argc == 1 )
      optionchar = 'R';
   else
      optionchar = toupper( *argv[1].strptr );

   if ( stem[len-1] != '.' )
   {
      /*
       * Check that the specified function name is available/registered
       */
      rc = 1L; /* not found by default */
      for ( i = 0; RxCURLFunctions[i].ExternalName != NULL; i++ )
      {
         tmplen = strlen( (char *)RxCURLFunctions[i].ExternalName );
         if ( tmplen == len
         &&   memcmpi( (char *)RxCURLFunctions[i].ExternalName, stem, len ) == 0 )
         {
            /*
             * Function is available...
             */
            rc = 0L;
            if ( RexxQueryFunction( RxCURLFunctions[i].InternalName )
            &&   optionchar == 'R' )
            {
               /*
                * Function is not registered and we wanted a registered function
                */
               rc = 1L;
            }
            break;
         }
      }
   }
   else
   {
      strcpy( stem_name, stem );
      make_upper( stem_name );
      idx = 0;
      for ( i = 0; RxCURLFunctions[i].ExternalName != NULL; i++ )
      {
         if ( RexxQueryFunction( RxCURLFunctions[i].InternalName )
         &&   optionchar == 'R' )
         {
            /*
             * Function is not registered, and we wanted only registered functions
             */
            ;
         }
         else
         {
            idx++;
            len = sprintf( varname, "%s%u", stem_name, idx );
            if ( SetRexxVariable( RexxCURLData.RxPackageGlobalData, varname, len, (char *)RxCURLFunctions[i].ExternalName, strlen( RxCURLFunctions[i].ExternalName ) ) == 1 )
               return(1);
         }
      }
      i = sprintf( value, "%u", idx);
      len = sprintf(varname, "%s0", stem_name);
      rc = SetRexxVariable( RexxCURLData.RxPackageGlobalData, varname, len, value, i );
   }
   return RxReturnNumber( NULL, retstr, rc );
}

rxfunc( CurlVariable )
{
   ULONG rc = 0L;
   char buf[250];

   if ( RexxCURLData.g_curl_error) ClearCURLError( );
   if ( RexxCURLData.g_rexxcurl_error) ClearIntError( );
   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, (char *)name, argc, argv );
   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 1, 2 ) )
      return( 1 );
   if ( argv[0].strlength == 5 && memcmp( "DEBUG", argv[0].strptr, argv[0].strlength ) == 0 )
   {
      if ( argc == 1 )
      {
         sprintf( buf, "%d", RxGetRunFlags( RexxCURLData.RxPackageGlobalData ) );
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, buf );
      }
      else
      {
         RxSetRunFlags ( RexxCURLData.RxPackageGlobalData, atoi( (char *)argv[1].strptr ) );
      }
   }
   else if ( argv[0].strlength == 9 && memcmp( "DEBUGFILE", argv[0].strptr, argv[0].strlength ) == 0 )
   {
      if ( argc == 1 )
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, RxGetTraceFile( RexxCURLData.RxPackageGlobalData ) );
      else
      {
         rc = RxSetTraceFile( RexxCURLData.RxPackageGlobalData, (char *)argv[1].strptr );
      }
   }
   else if ( argv[0].strlength == 7 && memcmp( "VERSION", argv[0].strptr, argv[0].strlength ) == 0 )
   {
      if ( argc == 1 )
      {
         sprintf( buf, "%s %s %s %s", RxPackageName, REXXCURL_VERSION, REXXCURL_DATE, curl_version() );
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, buf );
      }
      else
      {
         sprintf( buf, "%s: %s", INTERR_READONLY_VARIABLE_STRING, argv[0].strptr );
         SetIntError( __FILE__, __LINE__, INTERR_READONLY_VARIABLE, buf );
      }
   }
   else if ( argv[0].strlength == 5 && memcmp( "ERROR", argv[0].strptr, argv[0].strlength ) == 0 )
   {
      if ( argc == 1 )
      {
         return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, RexxCURLData.rexxcurl_error_prefix );
      }
      else
      {
         memcpy( RexxCURLData.rexxcurl_error_prefix, argv[1].strptr, argv[1].strlength );
         RexxCURLData.rexxcurl_error_prefix[argv[1].strlength] = '\0';
      }
   }
   else
   {
      sprintf( buf, "%s %s", INTERR_INVALID_VARIABLE_STRING, argv[0].strptr );
      SetIntError( __FILE__, __LINE__, INTERR_INVALID_VARIABLE, buf );
   }
   return RxReturnString( RexxCURLData.RxPackageGlobalData, retstr, "" );
}

/*====== Routines for handling registration of functions ======*/


rxfunc( CurlLoadFuncs )
{
   int rc = 0L;

#if defined(DYNAMIC_LIBRARY)
   RexxCURLData.RxPackageGlobalData = InitRxPackage( RexxCURLData.RxPackageGlobalData, NULL, &rc );
   if ( !QueryRxFunction( RexxCURLData.RxPackageGlobalData, "CURLINIT" ) )
   {
      /*
       * Register all external functions
       */
      if ( !rc )
      {
         rc = RegisterRxFunctions( RexxCURLData.RxPackageGlobalData, RxCURLFunctions, RxPackageName );
      }
   }
   rc = RexxCURLInitHandler( 0, 0, NULL );
#endif
   return RxReturnNumber( RexxCURLData.RxPackageGlobalData, retstr, rc );
}

rxfunc( CurlDropFuncs )
{
   RFH_RETURN_TYPE rc=0;
   int unload=0;

   RexxCURLData.RxPackageGlobalData = FunctionPrologue( RexxCURLData.RxPackageGlobalData, NULL, ( char* )name, argc, argv );

   if ( my_checkparam( RexxCURLData.RxPackageGlobalData, name, argc, 0, 1 ) )
      return( 1 );
   if ( argv[0].strlength == 6
   &&   memcmpi( argv[0].strptr, "UNLOAD", 6 ) == 0 )
      unload = 1;
   TermRxPackage( &RexxCURLData.RxPackageGlobalData, NULL, RxCURLFunctions, RxPackageName, unload );
   return RxReturnNumber( NULL, retstr, rc );
}



/*
 * The following functions are used in rxpackage.c
 */

/*-----------------------------------------------------------------------------
 * Print a usage message.
 *----------------------------------------------------------------------------*/
void RexxCURLUsage

#ifdef HAVE_PROTO
   (void)
#else
   ()
#endif

{
   (void)fprintf(stderr,
      "\nVersion: %s %s %s\n\nUsage:   %s [-h]\n         %s [-idvf<trace file>] [Rexx program name]\n\n",
      RxPackageName,
      REXXCURL_VERSION,
      REXXCURL_DATE,
      RxPackageName,
      RxPackageName);
}


#if defined(USE_REXX6000)
/*
 * This function is used as the entry point for the REXX/6000
 * interpreter
 * If you change this table, don't forget to change the table at the
 * start of this file.
 */
USHORT InitFunc( RXFUNCBLOCK **FuncBlock )
{
   static RXFUNCBLOCK funcarray[] =
   {
      { NULL, NULL, NULL }
   } ;
   *FuncBlock = funcarray;
   return (USHORT)0;
}
#endif

void *getRexxCURLFunctionAddress( char *name )
{
   int i, size = sizeof( RxCURLFunctions ) / sizeof( RexxFunction ) ;

   for (i = 0; i < size; i++)
   {
      if ( strcmp( RxCURLFunctions[i].InternalName, name) == 0 )
         return RxCURLFunctions[i].EntryPoint;
   }
   return NULL;
}
